From 5f3547e81802102d4fe35f7fd5e81828064f6780 Mon Sep 17 00:00:00 2001 From: Fengyun Liu Date: Mon, 4 Jul 2022 20:21:26 +0200 Subject: [PATCH 1/7] Underline assignment correctly in error message --- .../tools/dotc/transform/init/Semantic.scala | 4 +- tests/init/neg/inherit-non-hot.check | 26 +++++----- tests/init/neg/local-warm4.check | 50 +++++++++---------- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index f3f88a764ace..3b7462cea7de 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -1349,12 +1349,12 @@ object Semantic: case Select(qual, _) => eval(qual, thisV, klass) val res = eval(rhs, thisV, klass) - extendTrace(rhs) { + extendTrace(expr) { res.ensureHot("The RHS of reassignment must be fully initialized.") } case id: Ident => val res = eval(rhs, thisV, klass) - extendTrace(rhs) { + extendTrace(expr) { res.ensureHot("The RHS of reassignment must be fully initialized.") } diff --git a/tests/init/neg/inherit-non-hot.check b/tests/init/neg/inherit-non-hot.check index c7e0a02d9b63..2a2f595ed7da 100644 --- a/tests/init/neg/inherit-non-hot.check +++ b/tests/init/neg/inherit-non-hot.check @@ -1,15 +1,15 @@ --- Error: tests/init/neg/inherit-non-hot.scala:6:34 -------------------------------------------------------------------- +-- Error: tests/init/neg/inherit-non-hot.scala:6:32 -------------------------------------------------------------------- 6 | if b == null then b = new B(this) // error - | ^^^^^^^^^^^ - | The RHS of reassignment must be fully initialized. Calling trace: - | -> class C extends A { [ inherit-non-hot.scala:15 ] - | ^ - | -> val bAgain = toB.getBAgain [ inherit-non-hot.scala:16 ] - | ^^^ - | -> def toB: B = [ inherit-non-hot.scala:5 ] - | ^ - | -> if b == null then b = new B(this) // error [ inherit-non-hot.scala:6 ] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ + | The RHS of reassignment must be fully initialized. Calling trace: + | -> class C extends A { [ inherit-non-hot.scala:15 ] + | ^ + | -> val bAgain = toB.getBAgain [ inherit-non-hot.scala:16 ] + | ^^^ + | -> def toB: B = [ inherit-non-hot.scala:5 ] + | ^ + | -> if b == null then b = new B(this) // error [ inherit-non-hot.scala:6 ] + | ^^^^^^^^^^^^^^^ | - | Promoting the value to fully initialized failed due to the following problem: - | Cannot prove that the field val a is fully initialized. + | Promoting the value to fully initialized failed due to the following problem: + | Cannot prove that the field val a is fully initialized. diff --git a/tests/init/neg/local-warm4.check b/tests/init/neg/local-warm4.check index 6a9955c40801..68d2952ce5f8 100644 --- a/tests/init/neg/local-warm4.check +++ b/tests/init/neg/local-warm4.check @@ -1,26 +1,26 @@ --- Error: tests/init/neg/local-warm4.scala:18:20 ----------------------------------------------------------------------- +-- Error: tests/init/neg/local-warm4.scala:18:18 ----------------------------------------------------------------------- 18 | a = newA // error - | ^^^^ - | The RHS of reassignment must be fully initialized. Calling trace: - | -> object localWarm { [ local-warm4.scala:1 ] - | ^ - | -> val a = new A(5) [ local-warm4.scala:26 ] - | ^^^^^^^^ - | -> class A(x: Int) extends Foo(x) { [ local-warm4.scala:6 ] - | ^ - | -> val b = new B(y) [ local-warm4.scala:10 ] - | ^^^^^^^^ - | -> class B(x: Int) extends A(x) { [ local-warm4.scala:13 ] - | ^ - | -> class A(x: Int) extends Foo(x) { [ local-warm4.scala:6 ] - | ^ - | -> increment() [ local-warm4.scala:9 ] - | ^^^^^^^^^^^ - | -> override def increment(): Unit = { [ local-warm4.scala:15 ] - | ^ - | -> updateA() [ local-warm4.scala:21 ] - | ^^^^^^^^^ - | -> def updateA(): Unit = { [ local-warm4.scala:16 ] - | ^ - | -> a = newA // error [ local-warm4.scala:18 ] - | ^^^^ + | ^^^^^^^^ + | The RHS of reassignment must be fully initialized. Calling trace: + | -> object localWarm { [ local-warm4.scala:1 ] + | ^ + | -> val a = new A(5) [ local-warm4.scala:26 ] + | ^^^^^^^^ + | -> class A(x: Int) extends Foo(x) { [ local-warm4.scala:6 ] + | ^ + | -> val b = new B(y) [ local-warm4.scala:10 ] + | ^^^^^^^^ + | -> class B(x: Int) extends A(x) { [ local-warm4.scala:13 ] + | ^ + | -> class A(x: Int) extends Foo(x) { [ local-warm4.scala:6 ] + | ^ + | -> increment() [ local-warm4.scala:9 ] + | ^^^^^^^^^^^ + | -> override def increment(): Unit = { [ local-warm4.scala:15 ] + | ^ + | -> updateA() [ local-warm4.scala:21 ] + | ^^^^^^^^^ + | -> def updateA(): Unit = { [ local-warm4.scala:16 ] + | ^ + | -> a = newA // error [ local-warm4.scala:18 ] + | ^^^^^^^^ From 93d46a8d30921e00f4616db2aa64ea60dd45a604 Mon Sep 17 00:00:00 2001 From: Fengyun Liu Date: Tue, 5 Jul 2022 00:50:42 +0200 Subject: [PATCH 2/7] Promote fields more eagerly upon initialization --- .../dotty/tools/dotc/transform/init/Semantic.scala | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index 3b7462cea7de..aade9d09ce82 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -1036,9 +1036,9 @@ object Semantic: extension (value: Value) /** Promotion of values to hot */ def promote(msg: String): Contextual[Unit] = log("promoting " + value + ", promoted = " + promoted, printer) { - if promoted.isCurrentObjectPromoted then Nil else + if !promoted.isCurrentObjectPromoted then - value.match + value match case Hot => case Cold => @@ -1099,8 +1099,9 @@ object Semantic: */ def tryPromote(msg: String): Contextual[List[Error]] = log("promote " + warm.show + ", promoted = " + promoted, printer) { val classRef = warm.klass.appliedRef - if classRef.memberClasses.nonEmpty || !warm.isFullyFilled then - return PromoteError(msg, trace.toVector) :: Nil + val hasInnerClass = classRef.memberClasses.filter(_.symbol.hasSource).nonEmpty + if hasInnerClass then + return PromoteError(msg + "Promotion cancelled as the value contains inner classes. ", trace.toVector) :: Nil val errors = Reporter.stopEarly { for klass <- warm.klass.baseClasses if klass.hasSource do @@ -1635,7 +1636,8 @@ object Semantic: if thisV.isThisRef || !thisV.asInstanceOf[Warm].isPopulatingParams then tpl.body.foreach { case vdef : ValDef if !vdef.symbol.is(Flags.Lazy) && !vdef.rhs.isEmpty => val res = eval(vdef.rhs, thisV, klass) - thisV.updateField(vdef.symbol, res) + val hasErrors = Reporter.hasErrors { res.promote("try promote value to hot") } + thisV.updateField(vdef.symbol, if hasErrors then res else Hot) fieldsChanged = true case _: MemberDef => From 13453d8f5f4e43b9a13487bbf28782a4d1338295 Mon Sep 17 00:00:00 2001 From: Fengyun Liu Date: Tue, 5 Jul 2022 01:13:18 +0200 Subject: [PATCH 3/7] Show more debug information for reassignment --- .../dotty/tools/dotc/transform/init/Semantic.scala | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index aade9d09ce82..46f4b64c19a0 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -52,7 +52,15 @@ object Semantic: * */ sealed abstract class Value: - def show: String = this.toString() + def show: String = this match + case Warm(klass, outer, ctor, args) => + "Warm[" + klass + "] { outer = " + outer.show + ", args = " + args.map(_.show).mkString("(", ", ", ")") + " }" + case Fun(expr, thisV, klass) => + "Fun { this = " + thisV.show + ", owner = " + klass + " }" + case RefSet(values) => + values.map(_.show).mkString("Set { ", ", ", " }") + case _ => + this.toString() def isHot = this == Hot def isCold = this == Cold @@ -1351,12 +1359,12 @@ object Semantic: eval(qual, thisV, klass) val res = eval(rhs, thisV, klass) extendTrace(expr) { - res.ensureHot("The RHS of reassignment must be fully initialized.") + res.ensureHot("The RHS of reassignment must be fully initialized. Found = " + res.show + ". ") } case id: Ident => val res = eval(rhs, thisV, klass) extendTrace(expr) { - res.ensureHot("The RHS of reassignment must be fully initialized.") + res.ensureHot("The RHS of reassignment must be fully initialized. Found = " + res.show + ". ") } case closureDef(ddef) => From 3f85563426887a787f48a8a5f0c20b095c711ae5 Mon Sep 17 00:00:00 2001 From: Fengyun Liu Date: Tue, 5 Jul 2022 01:31:21 +0200 Subject: [PATCH 4/7] Add color to show --- .../src/dotty/tools/dotc/transform/init/Semantic.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index 46f4b64c19a0..011af4d73dce 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -52,11 +52,13 @@ object Semantic: * */ sealed abstract class Value: - def show: String = this match + def show(using Context): String = this match + case ThisRef(klass) => + "ThisRef[" + klass.show + "]" case Warm(klass, outer, ctor, args) => - "Warm[" + klass + "] { outer = " + outer.show + ", args = " + args.map(_.show).mkString("(", ", ", ")") + " }" + "Warm[" + klass.show + "] { outer = " + outer.show + ", args = " + args.map(_.show).mkString("(", ", ", ")") + " }" case Fun(expr, thisV, klass) => - "Fun { this = " + thisV.show + ", owner = " + klass + " }" + "Fun { this = " + thisV.show + ", owner = " + klass.show + " }" case RefSet(values) => values.map(_.show).mkString("Set { ", ", ", " }") case _ => From 5ac71333e79eb22908c8ca70fca6d37c0d8a7b86 Mon Sep 17 00:00:00 2001 From: Fengyun Liu Date: Tue, 5 Jul 2022 01:32:44 +0200 Subject: [PATCH 5/7] Fix tests --- tests/init/neg/apply2.scala | 4 ++-- tests/init/neg/inherit-non-hot.check | 22 +++++++++---------- tests/init/neg/local-warm4.check | 26 ----------------------- tests/init/{neg => pos}/local-warm4.scala | 2 +- 4 files changed, 14 insertions(+), 40 deletions(-) delete mode 100644 tests/init/neg/local-warm4.check rename tests/init/{neg => pos}/local-warm4.scala (90%) diff --git a/tests/init/neg/apply2.scala b/tests/init/neg/apply2.scala index c6c7fe5fedd2..83f64a6dd3c7 100644 --- a/tests/init/neg/apply2.scala +++ b/tests/init/neg/apply2.scala @@ -3,8 +3,8 @@ object O: println(n) class B: - val a = A(this) // error + val a = A(this) val b = new B - val n = 10 + val n = 10 // error end O diff --git a/tests/init/neg/inherit-non-hot.check b/tests/init/neg/inherit-non-hot.check index 2a2f595ed7da..eb1b3ead006e 100644 --- a/tests/init/neg/inherit-non-hot.check +++ b/tests/init/neg/inherit-non-hot.check @@ -1,15 +1,15 @@ -- Error: tests/init/neg/inherit-non-hot.scala:6:32 -------------------------------------------------------------------- 6 | if b == null then b = new B(this) // error | ^^^^^^^^^^^^^^^ - | The RHS of reassignment must be fully initialized. Calling trace: - | -> class C extends A { [ inherit-non-hot.scala:15 ] - | ^ - | -> val bAgain = toB.getBAgain [ inherit-non-hot.scala:16 ] - | ^^^ - | -> def toB: B = [ inherit-non-hot.scala:5 ] - | ^ - | -> if b == null then b = new B(this) // error [ inherit-non-hot.scala:6 ] - | ^^^^^^^^^^^^^^^ + |The RHS of reassignment must be fully initialized. Found = Warm[class B] { outer = Hot, args = (Cold) }. Calling trace: + |-> class C extends A { [ inherit-non-hot.scala:15 ] + | ^ + |-> val bAgain = toB.getBAgain [ inherit-non-hot.scala:16 ] + | ^^^ + |-> def toB: B = [ inherit-non-hot.scala:5 ] + | ^ + |-> if b == null then b = new B(this) // error [ inherit-non-hot.scala:6 ] + | ^^^^^^^^^^^^^^^ | - | Promoting the value to fully initialized failed due to the following problem: - | Cannot prove that the field val a is fully initialized. + |Promoting the value to fully initialized failed due to the following problem: + |Cannot prove that the field val a is fully initialized. diff --git a/tests/init/neg/local-warm4.check b/tests/init/neg/local-warm4.check deleted file mode 100644 index 68d2952ce5f8..000000000000 --- a/tests/init/neg/local-warm4.check +++ /dev/null @@ -1,26 +0,0 @@ --- Error: tests/init/neg/local-warm4.scala:18:18 ----------------------------------------------------------------------- -18 | a = newA // error - | ^^^^^^^^ - | The RHS of reassignment must be fully initialized. Calling trace: - | -> object localWarm { [ local-warm4.scala:1 ] - | ^ - | -> val a = new A(5) [ local-warm4.scala:26 ] - | ^^^^^^^^ - | -> class A(x: Int) extends Foo(x) { [ local-warm4.scala:6 ] - | ^ - | -> val b = new B(y) [ local-warm4.scala:10 ] - | ^^^^^^^^ - | -> class B(x: Int) extends A(x) { [ local-warm4.scala:13 ] - | ^ - | -> class A(x: Int) extends Foo(x) { [ local-warm4.scala:6 ] - | ^ - | -> increment() [ local-warm4.scala:9 ] - | ^^^^^^^^^^^ - | -> override def increment(): Unit = { [ local-warm4.scala:15 ] - | ^ - | -> updateA() [ local-warm4.scala:21 ] - | ^^^^^^^^^ - | -> def updateA(): Unit = { [ local-warm4.scala:16 ] - | ^ - | -> a = newA // error [ local-warm4.scala:18 ] - | ^^^^^^^^ diff --git a/tests/init/neg/local-warm4.scala b/tests/init/pos/local-warm4.scala similarity index 90% rename from tests/init/neg/local-warm4.scala rename to tests/init/pos/local-warm4.scala index a1b3030ba4de..5dec50115fcd 100644 --- a/tests/init/neg/local-warm4.scala +++ b/tests/init/pos/local-warm4.scala @@ -15,7 +15,7 @@ object localWarm { override def increment(): Unit = { def updateA(): Unit = { val newA = new A(y) - a = newA // error + a = newA // ok: newA can be promoted to hot } y = y + 1 updateA() From 1490c51ab3aa72c9f37e85b85b7d9aae062e6e42 Mon Sep 17 00:00:00 2001 From: Fengyun Liu Date: Tue, 5 Jul 2022 22:02:43 +0200 Subject: [PATCH 6/7] Revert expensive eager promotion of fields --- .../src/dotty/tools/dotc/transform/init/Semantic.scala | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index 011af4d73dce..c8e4ef37b848 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -802,8 +802,11 @@ object Semantic: else // no source code available promoteArgs() - val error = CallUnknown(target, trace.toVector) - reporter.report(error) + // try promoting the receiver as last resort + val hasErrors = Reporter.hasErrors { ref.promote("try promote value to hot") } + if hasErrors then + val error = CallUnknown(target, trace.toVector) + reporter.report(error) Hot else // method call resolves to a field @@ -1646,8 +1649,7 @@ object Semantic: if thisV.isThisRef || !thisV.asInstanceOf[Warm].isPopulatingParams then tpl.body.foreach { case vdef : ValDef if !vdef.symbol.is(Flags.Lazy) && !vdef.rhs.isEmpty => val res = eval(vdef.rhs, thisV, klass) - val hasErrors = Reporter.hasErrors { res.promote("try promote value to hot") } - thisV.updateField(vdef.symbol, if hasErrors then res else Hot) + thisV.updateField(vdef.symbol, res) fieldsChanged = true case _: MemberDef => From 675fcb1870c707a9e72b5628465febea5efa917a Mon Sep 17 00:00:00 2001 From: Fengyun Liu Date: Fri, 8 Jul 2022 00:17:38 +0200 Subject: [PATCH 7/7] Add test case --- tests/init/pos/patternMatcher.scala | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/init/pos/patternMatcher.scala diff --git a/tests/init/pos/patternMatcher.scala b/tests/init/pos/patternMatcher.scala new file mode 100644 index 000000000000..425c48b71260 --- /dev/null +++ b/tests/init/pos/patternMatcher.scala @@ -0,0 +1,8 @@ +import scala.collection.mutable + +class Translater: + val count = new mutable.HashMap[Int, Int] { + override def default(key: Int) = 0 + } + count.get(10) + val n = 10