Skip to content

Commit ae566b4

Browse files
committed
Abstract parameters of non-local constructor parameters as cold
This is a simple fix which should have no impact on expressiveness and usability, as local classes inside secondary constructors are extremely rare.
1 parent d9892f9 commit ae566b4

File tree

5 files changed

+73
-15
lines changed

5 files changed

+73
-15
lines changed

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

+1-1
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 {

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

+7-10
Original file line numberDiff line numberDiff line change
@@ -475,22 +475,14 @@ class Semantic {
475475

476476
case addr: Addr =>
477477
given Trace = trace1
478-
479478
// widen the outer to finitize addresses
480479
val outer = addr match
481480
case Warm(_, _: Warm, _, _) => Cold
482481
case _ => addr
483482

484483
val value = Warm(klass, outer, ctor, args.map(_.value).widenArgs).ensureExists
485484
val res = value.call(ctor, args, superType = NoType, source)
486-
487-
def inSecondaryConstructor(sym: Symbol): Boolean =
488-
!sym.isClass && (sym.isConstructor || inSecondaryConstructor(sym.owner))
489-
490-
// Approximate instances of local classes inside secondary constructor as Cold.
491-
// 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)
485+
Result(value, res.errors)
494486

495487
case Fun(body, thisV, klass, env) =>
496488
report.error("unexpected tree in instantiating a function, fun = " + body.show, source)
@@ -901,7 +893,12 @@ class Semantic {
901893
// This enables us to simplify the domain without sacrificing
902894
// expressiveness nor soundess, as local classes inside secondary
903895
// constructors are uncommon.
904-
Result(env.lookup(sym), Nil)
896+
if sym.isContainedIn(klass) then
897+
Result(env.lookup(sym), Nil)
898+
else
899+
// We don't know much about secondary constructor parameters in outer scope.
900+
// It's always safe to approximate them with `Cold`.
901+
Result(Cold, Nil)
905902
else
906903
default()
907904

tests/init/neg/secondary-ctor2.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ class A(b: B, x: Int) {
22
def this(b: B) = {
33
this(b, 5)
44
class Inner() {
5-
def foo() = println(b.n)
5+
def foo() = println(b.n) // error: calling method on cold
66
}
7-
Inner().foo() // error: calling method on cold
7+
Inner().foo()
88

99
val f = () => new A(b, 3)
1010
f() // ok

tests/init/neg/secondary-ctor3.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ def foo() =
77
def this(b: B) = {
88
this(b, 5)
99
class Inner() {
10-
def foo() = println(b.n)
10+
def foo() = println(b.n) // error
1111
}
12-
Inner().foo() // error: calling method on cold
12+
Inner().foo()
1313

1414
val l1 = new L1(3)
1515
println(l1.n)

tests/init/neg/secondary-ctor4.scala

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

0 commit comments

Comments
 (0)