diff --git a/compiler/src/dotty/tools/dotc/transform/init/Errors.scala b/compiler/src/dotty/tools/dotc/transform/init/Errors.scala index 87fecf4dee20..df9c00e84224 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Errors.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Errors.scala @@ -19,9 +19,9 @@ object Errors: def pos(using Context): SourcePosition = trace.last.sourcePos def issue(using Context): Unit = - report.warning(show + stacktrace, this.pos) + report.warning(show, this.pos) - def stacktrace(using Context): String = if trace.isEmpty then "" else " Calling trace:\n" + { + def stacktrace(preamble: String = " Calling trace:\n")(using Context): String = if trace.isEmpty then "" else preamble + { var lastLineNum = -1 var lines: mutable.ArrayBuffer[String] = new mutable.ArrayBuffer trace.foreach { tree => @@ -72,33 +72,62 @@ object Errors: case class AccessNonInit(field: Symbol, trace: Seq[Tree]) extends Error: def source: Tree = trace.last def show(using Context): String = - "Access non-initialized " + field.show + "." + "Access non-initialized " + field.show + "." + stacktrace() override def pos(using Context): SourcePosition = field.sourcePos /** Promote a value under initialization to fully-initialized */ case class PromoteError(msg: String, trace: Seq[Tree]) extends Error: - def show(using Context): String = msg + def show(using Context): String = msg + stacktrace() case class AccessCold(field: Symbol, trace: Seq[Tree]) extends Error: def show(using Context): String = - "Access field on a value with an unknown initialization status." + "Access field on a value with an unknown initialization status." + stacktrace() case class CallCold(meth: Symbol, trace: Seq[Tree]) extends Error: def show(using Context): String = - "Call method on a value with an unknown initialization" + "." + "Call method on a value with an unknown initialization." + stacktrace() case class CallUnknown(meth: Symbol, trace: Seq[Tree]) extends Error: def show(using Context): String = val prefix = if meth.is(Flags.Method) then "Calling the external method " else "Accessing the external field" - prefix + meth.show + " may cause initialization errors" + "." + prefix + meth.show + " may cause initialization errors." + stacktrace() /** Promote a value under initialization to fully-initialized */ case class UnsafePromotion(msg: String, trace: Seq[Tree], error: Error) extends Error: - override def issue(using Context): Unit = - report.warning(show, this.pos) - def show(using Context): String = - msg + stacktrace + "\n" + + msg + stacktrace() + "\n" + "Promoting the value to fully initialized failed due to the following problem:\n" + - error.show + error.stacktrace + error.show + + /** Unsafe leaking a non-hot value as constructor arguments + * + * Invariant: argsIndices.nonEmpty + */ + case class UnsafeLeaking(trace: Seq[Tree], error: Error, nonHotOuterClass: Symbol, argsIndices: List[Int]) extends Error: + def show(using Context): String = + "Problematic object instantiation: " + argumentInfo() + stacktrace() + "\n" + + "It leads to the following error during object initialization:\n" + + error.show + + private def punctuation(i: Int): String = + if i == argsIndices.size - 2 then " and " + else if i == argsIndices.size - 1 then "" + else ", " + + private def argumentInfo()(using Context): String = + val multiple = argsIndices.size > 1 || nonHotOuterClass.exists + val init = + if nonHotOuterClass.exists + then "the outer " + nonHotOuterClass.name.show + ".this" + punctuation(-1) + else "" + + val subject = + argsIndices.zipWithIndex.foldLeft(init) { case (acc, (pos, i)) => + val text1 = "arg " + pos.toString + val text2 = text1 + punctuation(i) + acc + text2 + } + val verb = if multiple then " are " else " is " + val adjective = "not fully initialized." + subject + verb + adjective diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index ff3ac780ffc3..63e1959412e7 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -118,7 +118,7 @@ object Semantic: end Warm /** A function value */ - case class Fun(expr: Tree, thisV: Ref, klass: ClassSymbol, env: Env) extends Value + case class Fun(expr: Tree, thisV: Ref, klass: ClassSymbol) extends Value /** A value which represents a set of addresses * @@ -144,7 +144,7 @@ object Semantic: def hasField(f: Symbol) = fields.contains(f) - /** The environment for method parameters + /** The environment stores values for constructor parameters * * For performance and usability, we restrict parameters to be either `Cold` * or `Hot`. @@ -162,6 +162,9 @@ object Semantic: * key. The reason is that given the same receiver, a method or function may * be called with different arguments -- they are not decided by the receiver * anymore. + * + * TODO: remove Env as it is only used to pass value from `callConstructor` -> `eval` -> `init`. + * It goes through `eval` for caching (termination) purposes. */ object Env: opaque type Env = Map[Symbol, Value] @@ -588,7 +591,7 @@ object Semantic: Hot case fun: Fun => - report.error("unexpected tree in selecting a function, fun = " + fun.expr.show, fun.expr) + report.error("[Internal error] unexpected tree in selecting a function, fun = " + fun.expr.show, fun.expr) Hot case RefSet(refs) => @@ -596,7 +599,7 @@ object Semantic: } } - def call(meth: Symbol, args: List[ArgInfo], receiver: Type, superType: Type, needResolve: Boolean = true): Contextual[Value] = log("call " + meth.show + ", args = " + args, printer, (_: Value).show) { + def call(meth: Symbol, args: List[ArgInfo], receiver: Type, superType: Type, needResolve: Boolean = true): Contextual[Value] = log("call " + meth.show + ", args = " + args.map(_.value.show), printer, (_: Value).show) { def promoteArgs(): Contextual[Unit] = args.foreach(_.promote) def isSyntheticApply(meth: Symbol) = @@ -704,21 +707,19 @@ object Semantic: else value.select(target, needResolve = false) - case Fun(body, thisV, klass, env) => + case Fun(body, thisV, klass) => // meth == NoSymbol for poly functions if meth.name.toString == "tupled" then value // a call like `fun.tupled` else promoteArgs() - withEnv(env) { - eval(body, thisV, klass, cacheResult = true) - } + eval(body, thisV, klass, cacheResult = true) case RefSet(refs) => refs.map(_.call(meth, args, receiver, superType)).join } } - def callConstructor(ctor: Symbol, args: List[ArgInfo]): Contextual[Value] = log("call " + ctor.show + ", args = " + args, printer, (_: Value).show) { + def callConstructor(ctor: Symbol, args: List[ArgInfo]): Contextual[Value] = log("call " + ctor.show + ", args = " + args.map(_.value.show), printer, (_: Value).show) { // init "fake" param fields for the secondary constructor def addParamsAsFields(env: Env, ref: Ref, ctorDef: DefDef) = { val paramSyms = ctorDef.termParamss.flatten.map(_.symbol) @@ -775,7 +776,27 @@ object Semantic: } /** Handle a new expression `new p.C` where `p` is abstracted by `value` */ - def instantiate(klass: ClassSymbol, ctor: Symbol, args: List[ArgInfo]): Contextual[Value] = log("instantiating " + klass.show + ", value = " + value + ", args = " + args, printer, (_: Value).show) { + def instantiate(klass: ClassSymbol, ctor: Symbol, args: List[ArgInfo]): Contextual[Value] = log("instantiating " + klass.show + ", value = " + value + ", args = " + args.map(_.value.show), printer, (_: Value).show) { + def tryLeak(warm: Warm, nonHotOuterClass: Symbol, argValues: List[Value]): Contextual[Value] = + val argInfos2 = args.zip(argValues).map { (argInfo, v) => argInfo.copy(value = v) } + val errors = Reporter.stopEarly { + given Trace = Trace.empty + warm.callConstructor(ctor, argInfos2) + } + if errors.nonEmpty then + val indices = + for + (arg, i) <- argValues.zipWithIndex + if arg.isCold + yield + i + 1 + + val error = UnsafeLeaking(trace.toVector, errors.head, nonHotOuterClass, indices) + reporter.report(error) + Hot + else + warm + if promoted.isCurrentObjectPromoted then Hot else value match { case Hot => @@ -792,9 +813,7 @@ object Semantic: else val outer = Hot val warm = Warm(klass, outer, ctor, args2).ensureObjectExists() - val argInfos2 = args.zip(args2).map { (argInfo, v) => argInfo.copy(value = v) } - warm.callConstructor(ctor, argInfos2) - warm + tryLeak(warm, NoSymbol, args2) case Cold => val error = CallCold(ctor, trace.toVector) @@ -810,13 +829,16 @@ object Semantic: case _ => ref val argsWidened = args.map(_.value).widenArgs - val argInfos2 = args.zip(argsWidened).map { (argInfo, v) => argInfo.copy(value = v) } val warm = Warm(klass, outer, ctor, argsWidened).ensureObjectExists() - warm.callConstructor(ctor, argInfos2) - warm + if argsWidened.exists(_.isCold) then + tryLeak(warm, klass.owner.lexicallyEnclosingClass, argsWidened) + else + val argInfos2 = args.zip(argsWidened).map { (argInfo, v) => argInfo.copy(value = v) } + warm.callConstructor(ctor, argInfos2) + warm - case Fun(body, thisV, klass, env) => - report.error("unexpected tree in instantiating a function, fun = " + body.show, trace.toVector.last) + case Fun(body, thisV, klass) => + report.error("[Internal error] unexpected tree in instantiating a function, fun = " + body.show, trace.toVector.last) Hot case RefSet(refs) => @@ -830,21 +852,14 @@ object Semantic: val sym = tmref.symbol if sym.is(Flags.Param) && sym.owner.isConstructor then - // if we can get the field from the Ref (which can only possibly be - // a secondary constructor parameter), then use it. - if (ref.objekt.hasField(sym)) - ref.objekt.field(sym) - // instances of local classes inside secondary constructors cannot - // reach here, as those values are abstracted by Cold instead of Warm. - // This enables us to simplify the domain without sacrificing - // expressiveness nor soundess, as local classes inside secondary - // constructors are uncommon. - else if sym.isContainedIn(klass) then - env.lookup(sym) - else - // We don't know much about secondary constructor parameters in outer scope. - // It's always safe to approximate them with `Cold`. - Cold + val enclosingClass = sym.owner.enclosingClass.asClass + val thisValue2 = resolveThis(enclosingClass, ref, klass) + thisValue2 match + case Hot => Hot + case ref: Ref => ref.objekt.field(sym) + case _ => + report.error("[Internal error] unexpected this value accessing local variable, sym = " + sym.show + ", thisValue = " + thisValue2.show, trace.toVector.last) + Hot else if sym.is(Flags.Param) then Hot else @@ -861,7 +876,7 @@ object Semantic: case ref: Ref => eval(vdef.rhs, ref, enclosingClass) case _ => - report.error("unexpected defTree when accessing local variable, sym = " + sym.show + ", defTree = " + sym.defTree.show, trace.toVector.last) + report.error("[Internal error] unexpected this value when accessing local variable, sym = " + sym.show + ", thisValue = " + thisValue2.show, trace.toVector.last) Hot end match @@ -932,10 +947,10 @@ object Semantic: if errors.nonEmpty then promoted.remove(warm) reporter.reportAll(errors) - case fun @ Fun(body, thisV, klass, env) => + case fun @ Fun(body, thisV, klass) => if !promoted.contains(fun) then val errors = Reporter.stopEarly { - val res = withEnv(env) { + val res = { given Trace = Trace.empty eval(body, thisV, klass) } @@ -1120,7 +1135,7 @@ object Semantic: args.foreach { arg => val res = if arg.isByName then - Fun(arg.tree, thisV, klass, env) + Fun(arg.tree, thisV, klass) else eval(arg.tree, thisV, klass) @@ -1226,10 +1241,10 @@ object Semantic: } case closureDef(ddef) => - Fun(ddef.rhs, thisV, klass, env) + Fun(ddef.rhs, thisV, klass) case PolyFun(body) => - Fun(body, thisV, klass, env) + Fun(body, thisV, klass) case Block(stats, expr) => eval(stats, thisV, klass) @@ -1302,8 +1317,8 @@ object Semantic: Hot case _ => - throw new Exception("unexpected tree: " + expr.show) - + report.error("[Internal error] unexpected tree", expr) + Hot /** Handle semantics of leaf nodes */ def cases(tp: Type, thisV: Ref, klass: ClassSymbol): Contextual[Value] = log("evaluating " + tp.show, printer, (_: Value).show) { @@ -1331,7 +1346,8 @@ object Semantic: Hot case _ => - throw new Exception("unexpected type: " + tp) + report.error("[Internal error] unexpected type " + tp, trace.toVector.last) + Hot } /** Resolve C.this that appear in `klass` */ @@ -1345,15 +1361,15 @@ object Semantic: val obj = ref.objekt val outerCls = klass.owner.lexicallyEnclosingClass.asClass if !obj.hasOuter(klass) then - val error = PromoteError("outer not yet initialized, target = " + target + ", klass = " + klass + ", object = " + obj, trace.toVector) - report.error(error.show + error.stacktrace, trace.toVector.last) + val error = PromoteError("[Internal error] outer not yet initialized, target = " + target + ", klass = " + klass + ", object = " + obj, trace.toVector) + report.error(error.show, trace.toVector.last) Hot else resolveThis(target, obj.outer(klass), outerCls) case RefSet(refs) => refs.map(ref => resolveThis(target, ref, klass)).join case fun: Fun => - report.warning("unexpected thisV = " + thisV + ", target = " + target.show + ", klass = " + klass.show, trace.toVector.last) + report.error("[Internal error] unexpected thisV = " + thisV + ", target = " + target.show + ", klass = " + klass.show, trace.toVector.last) Cold case Cold => Cold @@ -1381,14 +1397,15 @@ object Semantic: resolveThis(target, thisV, cur) case None => - report.warning("unexpected outerSelect, thisV = " + thisV + ", target = " + target.show + ", hops = " + hops, trace.toVector.last.srcPos) + // TODO: use error once we fix https://github.com/lampepfl/dotty/issues/15465 + report.warning("[Internal error] unexpected outerSelect, thisV = " + thisV + ", target = " + target.show + ", hops = " + hops, trace.toVector.last.srcPos) Cold case RefSet(refs) => refs.map(ref => resolveOuterSelect(target, ref, hops)).join case fun: Fun => - report.warning("unexpected thisV = " + thisV + ", target = " + target.show + ", hops = " + hops, trace.toVector.last.srcPos) + report.error("[Internal error] unexpected thisV = " + thisV + ", target = " + target.show + ", hops = " + hops, trace.toVector.last.srcPos) Cold case Cold => Cold @@ -1516,6 +1533,10 @@ object Semantic: eval(tree, thisV, klass) } + // ensure we try promotion once even if class body is empty + if fieldsChanged && thisV.isThisRef then + thisV.asInstanceOf[ThisRef].tryPromoteCurrentObject() + // The result value is ignored, use Hot to avoid futile fixed point computation Hot } diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 2544fe1bb04e..7b2016715757 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -86,7 +86,7 @@ object Implicits: */ abstract class ImplicitRefs(initctx: Context) { val irefCtx = - if (initctx == NoContext) initctx else initctx.retractMode(Mode.ImplicitsEnabled) + if (initctx eq NoContext) initctx else initctx.retractMode(Mode.ImplicitsEnabled) protected given Context = irefCtx /** The nesting level of this context. Non-zero only in ContextialImplicits */ @@ -549,16 +549,16 @@ object Implicits: override def msg(using Context) = _msg def explanation(using Context) = msg.toString - /** A search failure type for failed synthesis of terms for special types */ + /** A search failure type for failed synthesis of terms for special types */ class SynthesisFailure(reasons: List[String], val expectedType: Type) extends SearchFailureType: def argument = EmptyTree - private def formatReasons = - if reasons.length > 1 then - reasons.mkString("\n\t* ", "\n\t* ", "") - else + private def formatReasons = + if reasons.length > 1 then + reasons.mkString("\n\t* ", "\n\t* ", "") + else reasons.mkString - + def explanation(using Context) = em"Failed to synthesize an instance of type ${clarify(expectedType)}: ${formatReasons}" end Implicits @@ -871,7 +871,7 @@ trait Implicits: SearchFailure(new SynthesisFailure(errors, formal), span).tree else tree.orElse(failed) - + /** Search an implicit argument and report error if not found */ def implicitArgTree(formal: Type, span: Span)(using Context): Tree = { diff --git a/tests/init/neg/apply2.scala b/tests/init/neg/apply2.scala index 6a42cc9a3acc..c6c7fe5fedd2 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) + val a = A(this) // error val b = new B - val n = 10 // error + val n = 10 end O diff --git a/tests/init/neg/as-instance-of-cold-field-access.scala b/tests/init/neg/as-instance-of-cold-field-access.scala index ec567326b4eb..c8de99e96d0e 100644 --- a/tests/init/neg/as-instance-of-cold-field-access.scala +++ b/tests/init/neg/as-instance-of-cold-field-access.scala @@ -1,7 +1,7 @@ final class MyAsInstanceOfClass(o: MyAsInstanceOfClass) { val other: MyAsInstanceOfClass = { - if (o.asInstanceOf[MyAsInstanceOfClass].oRef ne null) o // error - else new MyAsInstanceOfClass(this) + if (o.asInstanceOf[MyAsInstanceOfClass].oRef ne null) o + else new MyAsInstanceOfClass(this) // error } val oRef = o } diff --git a/tests/init/neg/cycle-structure.check b/tests/init/neg/cycle-structure.check index 788d010d39d2..e9c9a94e9a3d 100644 --- a/tests/init/neg/cycle-structure.check +++ b/tests/init/neg/cycle-structure.check @@ -1,28 +1,30 @@ --- Error: tests/init/neg/cycle-structure.scala:2:15 -------------------------------------------------------------------- -2 | val x1 = b.x // error - | ^^^ - | Access field on a value with an unknown initialization status. Calling trace: - | -> case class B(a: A) { [ cycle-structure.scala:7 ] - | ^ - | -> val x = A(this) [ cycle-structure.scala:9 ] - | ^^^^^^^ - | -> case class A(b: B) { [ cycle-structure.scala:1 ] - | ^ - | -> val x1 = b.x // error [ cycle-structure.scala:2 ] - | ^^^ --- Error: tests/init/neg/cycle-structure.scala:8:15 -------------------------------------------------------------------- -8 | val x1 = a.x // error - | ^^^ - | Access field on a value with an unknown initialization status. Calling trace: - | -> case class B(a: A) { [ cycle-structure.scala:7 ] - | ^ - | -> val x = A(this) [ cycle-structure.scala:9 ] - | ^^^^^^^ - | -> case class A(b: B) { [ cycle-structure.scala:1 ] - | ^ - | -> val x = B(this) [ cycle-structure.scala:3 ] - | ^^^^^^^ - | -> case class B(a: A) { [ cycle-structure.scala:7 ] - | ^ - | -> val x1 = a.x // error [ cycle-structure.scala:8 ] - | ^^^ +-- Error: tests/init/neg/cycle-structure.scala:9:13 -------------------------------------------------------------------- +9 | val x = A(this) // error + | ^^^^^^^ + | Problematic object instantiation: arg 1 is not fully initialized. Calling trace: + | -> case class B(a: A) { [ cycle-structure.scala:7 ] + | ^ + | -> val x = A(this) // error [ cycle-structure.scala:9 ] + | ^^^^^^^ + | + | It leads to the following error during object initialization: + | Access field on a value with an unknown initialization status. Calling trace: + | -> case class A(b: B) { [ cycle-structure.scala:1 ] + | ^ + | -> val x1 = b.x [ cycle-structure.scala:2 ] + | ^^^ +-- Error: tests/init/neg/cycle-structure.scala:3:13 -------------------------------------------------------------------- +3 | val x = B(this) // error + | ^^^^^^^ + | Problematic object instantiation: arg 1 is not fully initialized. Calling trace: + | -> case class A(b: B) { [ cycle-structure.scala:1 ] + | ^ + | -> val x = B(this) // error [ cycle-structure.scala:3 ] + | ^^^^^^^ + | + | It leads to the following error during object initialization: + | Access field on a value with an unknown initialization status. Calling trace: + | -> case class B(a: A) { [ cycle-structure.scala:7 ] + | ^ + | -> val x1 = a.x [ cycle-structure.scala:8 ] + | ^^^ diff --git a/tests/init/neg/cycle-structure.scala b/tests/init/neg/cycle-structure.scala index 937df774ee14..1a02a31188b1 100644 --- a/tests/init/neg/cycle-structure.scala +++ b/tests/init/neg/cycle-structure.scala @@ -1,11 +1,11 @@ case class A(b: B) { - val x1 = b.x // error - val x = B(this) + val x1 = b.x + val x = B(this) // error val y = x.a } case class B(a: A) { - val x1 = a.x // error - val x = A(this) + val x1 = a.x + val x = A(this) // error val h = x.b } diff --git a/tests/init/neg/cycle.scala b/tests/init/neg/cycle.scala index 55b7c28acbef..7d871795f0a2 100644 --- a/tests/init/neg/cycle.scala +++ b/tests/init/neg/cycle.scala @@ -1,11 +1,11 @@ class A(x: B) { - println(x.b) // error - val a = new B(this) + println(x.b) + val a = new B(this) // error val d = a.b } class B(x: A) { - println(x.a) // error - val b = new A(this) + println(x.a) + val b = new A(this) // error val d = b.a } \ No newline at end of file diff --git a/tests/init/neg/early-promote5.scala b/tests/init/neg/early-promote5.scala index be21b5e0133b..404f6fdb8d70 100644 --- a/tests/init/neg/early-promote5.scala +++ b/tests/init/neg/early-promote5.scala @@ -1,5 +1,5 @@ abstract class A { - bar(this) // error + bar(this) def bar(x: A): Unit } @@ -14,7 +14,7 @@ class M(val o: Outer, c: Container) extends A with o.B class Container { val o = new Outer - val m = new M(o, this) + val m = new M(o, this) // error val s = "hello" } @@ -22,4 +22,3 @@ class Dummy { val m: Int = n + 4 val n: Int = 10 // error } - diff --git a/tests/init/neg/i15363.check b/tests/init/neg/i15363.check new file mode 100644 index 000000000000..cd000cd72eb1 --- /dev/null +++ b/tests/init/neg/i15363.check @@ -0,0 +1,15 @@ +-- Error: tests/init/neg/i15363.scala:3:10 ----------------------------------------------------------------------------- +3 | val b = new B(this) // error + | ^^^^^^^^^^^ + | Problematic object instantiation: arg 1 is not fully initialized. Calling trace: + | -> class A: [ i15363.scala:1 ] + | ^ + | -> val b = new B(this) // error [ i15363.scala:3 ] + | ^^^^^^^^^^^ + | + | It leads to the following error during object initialization: + | Access field on a value with an unknown initialization status. Calling trace: + | -> class B(a: A): [ i15363.scala:7 ] + | ^ + | -> val x = a.m [ i15363.scala:8 ] + | ^^^ diff --git a/tests/init/neg/i15363.scala b/tests/init/neg/i15363.scala new file mode 100644 index 000000000000..01a2bdfcf530 --- /dev/null +++ b/tests/init/neg/i15363.scala @@ -0,0 +1,9 @@ +class A: + // should report one error here + val b = new B(this) // error + val m = 10 + val n = 20 + +class B(a: A): + val x = a.m + val y = a.n diff --git a/tests/init/neg/secondary-ctor.scala b/tests/init/neg/secondary-ctor.scala index 22eabcd57438..d96e149529d0 100644 --- a/tests/init/neg/secondary-ctor.scala +++ b/tests/init/neg/secondary-ctor.scala @@ -1,7 +1,7 @@ class A(b: B, x: Int) { def this(b: B) = { this(b, 5) - println(b.n) // error + println(b.n) } } @@ -15,5 +15,5 @@ class C(b: B) extends A(b) { class D { val b = new B(this) - val c = new C(b, 5) + val c = new C(b, 5) // error } diff --git a/tests/init/neg/secondary-ctor2.scala b/tests/init/neg/secondary-ctor2.scala index 1260cfbd51d1..462ff2dbcfaa 100644 --- a/tests/init/neg/secondary-ctor2.scala +++ b/tests/init/neg/secondary-ctor2.scala @@ -2,7 +2,7 @@ class A(b: B, x: Int) { def this(b: B) = { this(b, 5) class Inner() { - def foo() = println(b.n) // error: calling method on cold + def foo() = println(b.n) } Inner().foo() @@ -21,5 +21,5 @@ class C(b: B) extends A(b) { class D { val b = new B(this) - val c = new C(b, 5) + val c = new C(b, 5) // error } diff --git a/tests/init/neg/secondary-ctor3.scala b/tests/init/neg/secondary-ctor3.scala index 0001e74fc6b7..3bbfc413e262 100644 --- a/tests/init/neg/secondary-ctor3.scala +++ b/tests/init/neg/secondary-ctor3.scala @@ -7,7 +7,7 @@ def foo() = def this(b: B) = { this(b, 5) class Inner() { - def foo() = println(b.n) // error + def foo() = println(b.n) } Inner().foo() @@ -35,5 +35,5 @@ def foo() = class D { val b = new B(this) - val c = new C(b, 5) + val c = new C(b, 5) // error } diff --git a/tests/init/neg/secondary-ctor4.check b/tests/init/neg/secondary-ctor4.check new file mode 100644 index 000000000000..268e2f6489d0 --- /dev/null +++ b/tests/init/neg/secondary-ctor4.check @@ -0,0 +1,50 @@ +-- Error: tests/init/neg/secondary-ctor4.scala:54:14 ------------------------------------------------------------------- +54 | val c = new C(b, 5) // error + | ^^^^^^^^^^^ + | Problematic object instantiation: the outer M.this and arg 1 are not fully initialized. Calling trace: + | -> class N(d: D) extends M(d) { [ secondary-ctor4.scala:59 ] + | ^ + | -> def this(d: D) = { [ secondary-ctor4.scala:7 ] + | ^ + | -> new A(new B(new D)) // error [ secondary-ctor4.scala:42 ] + | ^^^^^ + | -> class D { [ secondary-ctor4.scala:52 ] + | ^ + | -> val c = new C(b, 5) // error [ secondary-ctor4.scala:54 ] + | ^^^^^^^^^^^ + | + | It leads to the following error during object initialization: + | Access field on a value with an unknown initialization status. Calling trace: + | -> def this(b: B, x: Int) = this(b) [ secondary-ctor4.scala:49 ] + | ^^^^^^^ + | -> class C(b: B) extends A(b) with T { [ secondary-ctor4.scala:48 ] + | ^ + | -> def this(b: B) = { [ secondary-ctor4.scala:17 ] + | ^ + | -> Inner().foo() [ secondary-ctor4.scala:26 ] + | ^^^^^^^ + | -> class Inner() { [ secondary-ctor4.scala:21 ] + | ^ + | -> println(b.n) [ secondary-ctor4.scala:23 ] + | ^^^ +-- Error: tests/init/neg/secondary-ctor4.scala:42:4 -------------------------------------------------------------------- +42 | new A(new B(new D)) // error + | ^^^^^^^^^^^^^^^^^^^ + | Problematic object instantiation: the outer M.this and arg 1 are not fully initialized. Calling trace: + | -> class N(d: D) extends M(d) { [ secondary-ctor4.scala:59 ] + | ^ + | -> def this(d: D) = { [ secondary-ctor4.scala:7 ] + | ^ + | -> new A(new B(new D)) // error [ secondary-ctor4.scala:42 ] + | ^^^^^^^^^^^^^^^^^^^ + | + | It leads to the following error during object initialization: + | Access field on a value with an unknown initialization status. Calling trace: + | -> def this(b: B) = { [ secondary-ctor4.scala:17 ] + | ^ + | -> Inner().foo() [ secondary-ctor4.scala:26 ] + | ^^^^^^^ + | -> class Inner() { [ secondary-ctor4.scala:21 ] + | ^ + | -> println(b.n) [ secondary-ctor4.scala:23 ] + | ^^^ diff --git a/tests/init/neg/secondary-ctor4.scala b/tests/init/neg/secondary-ctor4.scala index e43c063541ed..e80630002638 100644 --- a/tests/init/neg/secondary-ctor4.scala +++ b/tests/init/neg/secondary-ctor4.scala @@ -10,18 +10,18 @@ class M(x: Int) { class L1(x: Int) { val n: Int = 5 } class A(b: B, x: Int) { - println(d.n) // error + println(d.n) class L2(x: Int) { val n: Int = 5 } def this(b: B) = { this(b, 5) - println(d.n) // error + println(d.n) class Inner() { - println(d.n) // error - println(b.n) // error - def foo() = println(b.n) // error + println(d.n) + println(b.n) + def foo() = println(b.n) } Inner().foo() @@ -39,7 +39,7 @@ class M(x: Int) { val n: Int = 10 } - new A(new B(new D)) + new A(new B(new D)) // error trait T { val m: Int = 10 @@ -51,7 +51,7 @@ class M(x: Int) { class D { val b = new B(this) - val c = new C(b, 5) + val c = new C(b, 5) // error } } } diff --git a/tests/init/neg/soundness1.scala b/tests/init/neg/soundness1.scala index ac4cb2721ef5..d528b374f004 100644 --- a/tests/init/neg/soundness1.scala +++ b/tests/init/neg/soundness1.scala @@ -19,11 +19,11 @@ object Test2: object Test3: class A(b: B) { - println(b.a2) // error - val b2 = new B(this) + println(b.a2) + val b2 = new B(this) // error } class B(a: A) { - println(a.b2) // error - val a2 = new A(this) + println(a.b2) + val a2 = new A(this) // error } diff --git a/tests/init/neg/soundness2.scala b/tests/init/neg/soundness2.scala index 34d225e953de..3d2821377111 100644 --- a/tests/init/neg/soundness2.scala +++ b/tests/init/neg/soundness2.scala @@ -1,4 +1,4 @@ class C(c: C) { - val d = c.c2 // error - val c2 = new C(this) + val d = c.c2 + val c2 = new C(this) // error } diff --git a/tests/init/neg/soundness6.scala b/tests/init/neg/soundness6.scala index a3f80df11d1b..09d55dba292c 100644 --- a/tests/init/neg/soundness6.scala +++ b/tests/init/neg/soundness6.scala @@ -1,5 +1,5 @@ class C(c: C) { - println(c.n) // error - val c2 = new C(this) + println(c.n) + val c2 = new C(this) // error val n = 10 } diff --git a/tests/init/pos/i9795/A.scala b/tests/init/pos/i9795.scala similarity index 100% rename from tests/init/pos/i9795/A.scala rename to tests/init/pos/i9795.scala