diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index da87db97b0e3..5cfc15a78026 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -52,7 +52,17 @@ object Semantic: * */ sealed abstract class Value: - def show: String = this.toString() + def show(using Context): String = this match + case ThisRef(klass) => + "ThisRef[" + klass.show + "]" + case Warm(klass, outer, ctor, args) => + "Warm[" + klass.show + "] { outer = " + outer.show + ", args = " + args.map(_.show).mkString("(", ", ", ")") + " }" + case Fun(expr, thisV, klass) => + "Fun { this = " + thisV.show + ", owner = " + klass.show + " }" + case RefSet(values) => + values.map(_.show).mkString("Set { ", ", ", " }") + case _ => + this.toString() def isHot = this == Hot def isCold = this == Cold @@ -792,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 @@ -1036,9 +1049,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 +1112,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 @@ -1352,13 +1366,13 @@ object Semantic: case Select(qual, _) => eval(qual, thisV, klass) val res = eval(rhs, thisV, klass) - extendTrace(rhs) { - res.ensureHot("The RHS of reassignment must be fully initialized.") + extendTrace(expr) { + res.ensureHot("The RHS of reassignment must be fully initialized. Found = " + res.show + ". ") } case id: Ident => val res = eval(rhs, thisV, klass) - extendTrace(rhs) { - res.ensureHot("The RHS of reassignment must be fully initialized.") + extendTrace(expr) { + res.ensureHot("The RHS of reassignment must be fully initialized. Found = " + res.show + ". ") } case closureDef(ddef) => 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 c7e0a02d9b63..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: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. 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 6a9955c40801..000000000000 --- a/tests/init/neg/local-warm4.check +++ /dev/null @@ -1,26 +0,0 @@ --- Error: tests/init/neg/local-warm4.scala:18:20 ----------------------------------------------------------------------- -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() 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