From 98a0977761a7a3151a2cf0a5e2d19c21a9373226 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Tue, 8 Jun 2021 22:40:15 +0200 Subject: [PATCH] Skip check for local classes in constructors This is a temporary solution as we don't want to complicate the domain and implementation for a code pattern that is extremely rare. --- .../tools/dotc/transform/init/Errors.scala | 10 +++- .../tools/dotc/transform/init/Semantic.scala | 25 ++++----- tests/init/neg/secondary-ctor4.scala | 53 +++++++++++++++++++ 3 files changed, 75 insertions(+), 13 deletions(-) create mode 100644 tests/init/neg/secondary-ctor4.scala diff --git a/compiler/src/dotty/tools/dotc/transform/init/Errors.scala b/compiler/src/dotty/tools/dotc/transform/init/Errors.scala index db16c4e81f08..55b5d29db30f 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Errors.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Errors.scala @@ -74,7 +74,7 @@ object Errors { case class AccessCold(field: Symbol, source: Tree, trace: Seq[Tree]) extends Error { def show(using Context): String = - "Access field " + source.show + " on a value with an unknown initialization status" + "." + "Access field " + source.show + " on a value with an unknown initialization status." } case class CallCold(meth: Symbol, source: Tree, trace: Seq[Tree]) extends Error { @@ -105,4 +105,12 @@ object Errors { }.mkString) } } + + /** The checker ignores local classes inside constructors + * + * TODO: remove this restriction. + */ + case class LocalClassInConstructor(klass: ClassSymbol, source: Tree, trace: Seq[Tree]) extends Error { + def show(using Context): String = "Check is skipped for local classes inside constructors." + } } \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index fa52c2c21043..7d7edf75ea13 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -474,23 +474,24 @@ class Semantic { Result(Hot, error :: Nil) case addr: Addr => - given Trace = trace1 - - // widen the outer to finitize addresses - val outer = addr match - case Warm(_, _: Warm, _, _) => Cold - case _ => addr - - val value = Warm(klass, outer, ctor, args.map(_.value).widenArgs).ensureExists - val res = value.call(ctor, args, superType = NoType, source) - def inSecondaryConstructor(sym: Symbol): Boolean = !sym.isClass && (sym.isConstructor || inSecondaryConstructor(sym.owner)) // Approximate instances of local classes inside secondary constructor as Cold. // This way, we avoid complicating the domain for Warm unnecessarily - if inSecondaryConstructor(klass.owner) then Result(Cold, res.errors) - else Result(value, res.errors) + if inSecondaryConstructor(klass.owner) then + val error = LocalClassInConstructor(klass, source, trace.toVector) + Result(Cold, error :: Nil) + else + given Trace = trace1 + // widen the outer to finitize addresses + val outer = addr match + case Warm(_, _: Warm, _, _) => Cold + case _ => addr + + val value = Warm(klass, outer, ctor, args.map(_.value).widenArgs).ensureExists + val res = value.call(ctor, args, superType = NoType, source) + Result(value, res.errors) case Fun(body, thisV, klass, env) => report.error("unexpected tree in instantiating a function, fun = " + body.show, source) diff --git a/tests/init/neg/secondary-ctor4.scala b/tests/init/neg/secondary-ctor4.scala new file mode 100644 index 000000000000..c71b6ec7e86f --- /dev/null +++ b/tests/init/neg/secondary-ctor4.scala @@ -0,0 +1,53 @@ +class M(x: Int) { + + def this(s: String) = { + this(s.size) + + class L1(x: Int) { val n: Int = 5 } + + class A(b: B, x: Int) { + println(s.size) + + class L2(x: Int) { val n: Int = 5 } + + def this(b: B) = { + this(b, 5) + println(s.size) + + class Inner() { + println(s.size) + println(b.n) + def foo() = println(b.n) + } + Inner().foo() // error: calling method on cold + + val l1 = new L1(3) + println(l1.n) + + val l2 = new L2(3) + println(l2.n) + + (() => new A(b, 3))() // ok + } + } + + class B(val d: D) { + val n: Int = 10 + } + + new A(new B(new D)) + + trait T { + val m: Int = 10 + } + + class C(b: B) extends A(b) with T { + def this(b: B, x: Int) = this(b) + } + + class D { + val b = new B(this) + val c = new C(b, 5) + } + } +}