-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactoring tryPromote to fix non-determinism in Issue #12824 #12989
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @EnzeXing . LGTM, I left some comments for refactoring.
def flatten: Errors = this match { | ||
case unsafe: UnsafePromotion => unsafe.errors.flatMap(_.flatten) | ||
case _ => this :: Nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may still need this, in case the nested error is another UnsafePromotion
. But that's OK as well, so let's remove it for now.
@@ -89,20 +82,16 @@ object Errors { | |||
} | |||
|
|||
/** Promote a value under initialization to fully-initialized */ | |||
case class UnsafePromotion(msg: String, source: Tree, trace: Seq[Tree], errors: Errors) extends Error { | |||
assert(errors.nonEmpty) | |||
case class UnsafePromotion(msg: String, source: Tree, trace: Seq[Tree], error: Error) extends Error { | |||
|
|||
override def issue(using Context): Unit = | |||
report.warning(show, source.srcPos) | |||
|
|||
def show(using Context): String = { | |||
var index = 0 | |||
"Promoting the value to fully-initialized is unsafe. " + msg + "\n" + stacktrace + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's change the sentence to: Cannot prove that the value is fully-initialized
.
@@ -89,20 +82,16 @@ object Errors { | |||
} | |||
|
|||
/** Promote a value under initialization to fully-initialized */ | |||
case class UnsafePromotion(msg: String, source: Tree, trace: Seq[Tree], errors: Errors) extends Error { | |||
assert(errors.nonEmpty) | |||
case class UnsafePromotion(msg: String, source: Tree, trace: Seq[Tree], error: Error) extends Error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see the call site we have many errors -- so maybe keep the original errors: Errors
is OK, but only show the first one.
locally { | ||
given Trace = trace2 | ||
buffer ++= res.ensureHot(msg, source).errors | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor: remove the braces after then
and align if
and else
vertically
buffer ++= res.ensureHot(msg, source).errors | ||
warm.klass.baseClasses.exists { klass => | ||
klass.hasSource && klass.info.decls.exists { member => | ||
if member.isOneOf(Flags.Method | Flags.Lazy | Flags.Deferred) then { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove | Flags.Deferred
: it means the member is only declared but not defined.
I'd write:
if !member.isType && !member.isConstructor && member.hasSource && !member.is(Flags.Deferred) then
if member.isOneOf(Flags.Method | Flags.Lazy) then
...
else
...
buffer.nonEmpty
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did we not have the !member.isType
condition for methods before? Is that just because member.isType
is always false with Flags.Method
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's true.
@liufengyun I have resolved the failed test. Shall you review it again and see if we can get it merged? |
/** Flatten UnsafePromotion errors | ||
*/ | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The trailing spaces need to be removed.
@@ -90,19 +90,15 @@ object Errors { | |||
|
|||
/** Promote a value under initialization to fully-initialized */ | |||
case class UnsafePromotion(msg: String, source: Tree, trace: Seq[Tree], errors: Errors) extends Error { | |||
assert(errors.nonEmpty) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's better to keep this line.
warm.klass.baseClasses.exists { klass => | ||
klass.hasSource && klass.info.decls.exists { member => | ||
if !member.isType && !member.isConstructor && member.hasSource && !member.is(Flags.Deferred) then | ||
if member.isOneOf(Flags.Method | Flags.Lazy) then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we try the following change?
if member.isOneOf(Flags.Method | Flags.Lazy) then | |
if member.is(Flags.Method) then |
locally { | ||
given Trace = trace2 | ||
val args = member.info.paramInfoss.flatten.map(_ => ArgInfo(Hot, EmptyTree)) | ||
val res = warm.call(member, args, superType = NoType, source = source) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we put source
in trace2
and use member.defTree
here? Or simply remove trace2
if it does not make much difference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean evaluating the rhs of defTree directly here? I checked that warm.call
only does one more step: resolve(addr.klass, meth)
. Is that necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I was not clear. I meant only the trace: trace2
and source = source
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. I'll just keep the trace here so that the calling trace is more clear.
locally { | ||
given Trace = trace2 | ||
buffer ++= res.ensureHot(msg, source).errors | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems we can safely remove trace2
in this case.
tests/init/neg/closureLeak.check
Outdated
@@ -1,8 +1,7 @@ | |||
-- Error: tests/init/neg/closureLeak.scala:11:14 ----------------------------------------------------------------------- | |||
11 | l.foreach(a => a.addX(this)) // error | |||
| ^^^^^^^^^^^^^^^^^ | |||
| Promoting the value to fully-initialized is unsafe. May only use initialized value as arguments | |||
| Cannot prove that the value is fully-initialized. May only use initialized value as arguments |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a period
after the line.
@@ -1,5 +1,5 @@ | |||
abstract class A { | |||
bar(this) | |||
bar(this) // error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why we don't have an error before?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously, the fields
in tryPromote
doesn't include c in class M(val o: Outer, c: Container) extends A with o.B
, so promoting val m = new M(o, this)
doesn't trigger the error
@@ -6,7 +6,7 @@ class A(b: B, x: Int) { | |||
} | |||
Inner().foo() | |||
|
|||
val f = () => new A(b, 3) | |||
val f = () => new A(b, 3) // error: Cannot promote |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why it's fine before?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is similar to early-promote5.scala. Previously fields
doesn't include the parameters in the constructor of A.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, there are two minor issues to be addressed:
- remove the trailing space
- clean up the history (e.g. the two commits related to
PromotionError
can be squashed into one commit)
@@ -48,7 +48,7 @@ object Errors { | |||
} | |||
sb.toString | |||
} | |||
|
|||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The trailing space needs to be removed.
Updating the error message of UnsafePromotion
Updating secondary-ctr2.scala
Remove space
f56fba8
to
1f26b8d
Compare
@liufengyun I've finished. |
This PR first tests if the non-determinism will occur in [test_windows_full], and then refactors tryPromote and try to fix it