Skip to content

Commit

Permalink
Alternative fix: abstract parameters of non-local constructor paramet…
Browse files Browse the repository at this point in the history
…ers as cold
  • Loading branch information
liufengyun committed Jun 8, 2021
1 parent 41807c9 commit 7f91775
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 41 deletions.
8 changes: 0 additions & 8 deletions compiler/src/dotty/tools/dotc/transform/init/Errors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,4 @@ 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."
}
}
34 changes: 15 additions & 19 deletions compiler/src/dotty/tools/dotc/transform/init/Semantic.scala
Original file line number Diff line number Diff line change
Expand Up @@ -474,24 +474,15 @@ class Semantic {
Result(Hot, error :: Nil)

case addr: Addr =>
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
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
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)
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 Expand Up @@ -896,13 +887,18 @@ class Semantic {

def default() = Result(Hot, Nil)

if sym.is(Flags.Param) && sym.owner.isConstructor && sym.isContainedIn(klass) then
if sym.is(Flags.Param) && sym.owner.isConstructor then
// 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.
Result(env.lookup(sym), Nil)
if sym.isContainedIn(klass) then
Result(env.lookup(sym), Nil)
else
// We don't know much about secondary constructor parameters in outer scope.
// It's always safe to approximate them with `Cold`.
Result(Cold, Nil)
else
default()

Expand Down
4 changes: 2 additions & 2 deletions tests/init/neg/secondary-ctor2.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ class A(b: B, x: Int) {
def this(b: B) = {
this(b, 5)
class Inner() {
def foo() = println(b.n)
def foo() = println(b.n) // error: calling method on cold
}
Inner().foo() // error: calling method on cold
Inner().foo()

val f = () => new A(b, 3)
f() // ok
Expand Down
4 changes: 2 additions & 2 deletions tests/init/neg/secondary-ctor3.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ def foo() =
def this(b: B) = {
this(b, 5)
class Inner() {
def foo() = println(b.n)
def foo() = println(b.n) // error
}
Inner().foo() // error: calling method on cold
Inner().foo()

val l1 = new L1(3)
println(l1.n)
Expand Down
24 changes: 14 additions & 10 deletions tests/init/neg/secondary-ctor4.scala
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
trait D {
val n: Int = 10
}

class M(x: Int) {

def this(s: String) = {
this(s.size)
def this(d: D) = {
this(d.n)

class L1(x: Int) { val n: Int = 5 }

class A(b: B, x: Int) {
println(s.size)
println(d.n) // error

class L2(x: Int) { val n: Int = 5 }

def this(b: B) = {
this(b, 5)
println(s.size)
println(d.n) // error

class Inner() {
println(s.size)
println(b.n)
def foo() = println(b.n)
println(d.n) // error
println(b.n) // error
def foo() = println(b.n) // error
}
Inner().foo() // error: calling method on cold
Inner().foo()

val l1 = new L1(3)
println(l1.n)
Expand All @@ -35,7 +39,7 @@ class M(x: Int) {
val n: Int = 10
}

new A(new B(new D)) // error
new A(new B(new D))

trait T {
val m: Int = 10
Expand All @@ -52,6 +56,6 @@ class M(x: Int) {
}
}

class N extends M("hello") {
class N(d: D) extends M(d) {
val msg = "Scala"
}

0 comments on commit 7f91775

Please sign in to comment.