Skip to content

Commit 98a0977

Browse files
committed
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.
1 parent b5458f1 commit 98a0977

File tree

3 files changed

+75
-13
lines changed

3 files changed

+75
-13
lines changed

compiler/src/dotty/tools/dotc/transform/init/Errors.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ object Errors {
7474

7575
case class AccessCold(field: Symbol, source: Tree, trace: Seq[Tree]) extends Error {
7676
def show(using Context): String =
77-
"Access field " + source.show + " on a value with an unknown initialization status" + "."
77+
"Access field " + source.show + " on a value with an unknown initialization status."
7878
}
7979

8080
case class CallCold(meth: Symbol, source: Tree, trace: Seq[Tree]) extends Error {
@@ -105,4 +105,12 @@ object Errors {
105105
}.mkString)
106106
}
107107
}
108+
109+
/** The checker ignores local classes inside constructors
110+
*
111+
* TODO: remove this restriction.
112+
*/
113+
case class LocalClassInConstructor(klass: ClassSymbol, source: Tree, trace: Seq[Tree]) extends Error {
114+
def show(using Context): String = "Check is skipped for local classes inside constructors."
115+
}
108116
}

compiler/src/dotty/tools/dotc/transform/init/Semantic.scala

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -474,23 +474,24 @@ class Semantic {
474474
Result(Hot, error :: Nil)
475475

476476
case addr: Addr =>
477-
given Trace = trace1
478-
479-
// widen the outer to finitize addresses
480-
val outer = addr match
481-
case Warm(_, _: Warm, _, _) => Cold
482-
case _ => addr
483-
484-
val value = Warm(klass, outer, ctor, args.map(_.value).widenArgs).ensureExists
485-
val res = value.call(ctor, args, superType = NoType, source)
486-
487477
def inSecondaryConstructor(sym: Symbol): Boolean =
488478
!sym.isClass && (sym.isConstructor || inSecondaryConstructor(sym.owner))
489479

490480
// Approximate instances of local classes inside secondary constructor as Cold.
491481
// This way, we avoid complicating the domain for Warm unnecessarily
492-
if inSecondaryConstructor(klass.owner) then Result(Cold, res.errors)
493-
else Result(value, res.errors)
482+
if inSecondaryConstructor(klass.owner) then
483+
val error = LocalClassInConstructor(klass, source, trace.toVector)
484+
Result(Cold, error :: Nil)
485+
else
486+
given Trace = trace1
487+
// widen the outer to finitize addresses
488+
val outer = addr match
489+
case Warm(_, _: Warm, _, _) => Cold
490+
case _ => addr
491+
492+
val value = Warm(klass, outer, ctor, args.map(_.value).widenArgs).ensureExists
493+
val res = value.call(ctor, args, superType = NoType, source)
494+
Result(value, res.errors)
494495

495496
case Fun(body, thisV, klass, env) =>
496497
report.error("unexpected tree in instantiating a function, fun = " + body.show, source)

tests/init/neg/secondary-ctor4.scala

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
class M(x: Int) {
2+
3+
def this(s: String) = {
4+
this(s.size)
5+
6+
class L1(x: Int) { val n: Int = 5 }
7+
8+
class A(b: B, x: Int) {
9+
println(s.size)
10+
11+
class L2(x: Int) { val n: Int = 5 }
12+
13+
def this(b: B) = {
14+
this(b, 5)
15+
println(s.size)
16+
17+
class Inner() {
18+
println(s.size)
19+
println(b.n)
20+
def foo() = println(b.n)
21+
}
22+
Inner().foo() // error: calling method on cold
23+
24+
val l1 = new L1(3)
25+
println(l1.n)
26+
27+
val l2 = new L2(3)
28+
println(l2.n)
29+
30+
(() => new A(b, 3))() // ok
31+
}
32+
}
33+
34+
class B(val d: D) {
35+
val n: Int = 10
36+
}
37+
38+
new A(new B(new D))
39+
40+
trait T {
41+
val m: Int = 10
42+
}
43+
44+
class C(b: B) extends A(b) with T {
45+
def this(b: B, x: Int) = this(b)
46+
}
47+
48+
class D {
49+
val b = new B(this)
50+
val c = new C(b, 5)
51+
}
52+
}
53+
}

0 commit comments

Comments
 (0)