Skip to content
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

Merged
merged 5 commits into from
Jul 15, 2021

Conversation

EnzeXing
Copy link
Contributor

This PR first tests if the non-determinism will occur in [test_windows_full], and then refactors tryPromote and try to fix it

Copy link
Contributor

@liufengyun liufengyun left a 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
}
Copy link
Contributor

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 +
Copy link
Contributor

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 {
Copy link
Contributor

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
}
}
Copy link
Contributor

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 {
Copy link
Contributor

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

Copy link
Contributor

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?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's true.

@EnzeXing
Copy link
Contributor Author

@liufengyun I have resolved the failed test. Shall you review it again and see if we can get it merged?

/** Flatten UnsafePromotion errors
*/
*/
Copy link
Contributor

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)
Copy link
Contributor

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
Copy link
Contributor

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?

Suggested 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)
Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor

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.

Copy link
Contributor Author

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
}
Copy link
Contributor

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.

@@ -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
Copy link
Contributor

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
Copy link
Contributor

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?

Copy link
Contributor Author

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
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

@liufengyun liufengyun left a 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
}

Copy link
Contributor

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
@EnzeXing
Copy link
Contributor Author

@liufengyun I've finished.

@liufengyun liufengyun merged commit 27e221b into scala:master Jul 15, 2021
@Kordyjan Kordyjan added this to the 3.1.0 milestone Aug 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants