Skip to content

Commit

Permalink
Skip check for local classes in constructors
Browse files Browse the repository at this point in the history
This is a temporary solution as we don't want to complicate the domain
and implementation for a code pattern that is extremely rare.
  • Loading branch information
liufengyun committed Jun 8, 2021
1 parent b5458f1 commit 98a0977
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 13 deletions.
10 changes: 9 additions & 1 deletion compiler/src/dotty/tools/dotc/transform/init/Errors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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."
}
}
25 changes: 13 additions & 12 deletions compiler/src/dotty/tools/dotc/transform/init/Semantic.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
53 changes: 53 additions & 0 deletions tests/init/neg/secondary-ctor4.scala
Original file line number Diff line number Diff line change
@@ -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)
}
}
}

0 comments on commit 98a0977

Please sign in to comment.