Skip to content

Commit da7356c

Browse files
committed
Address review
1 parent 8b00725 commit da7356c

File tree

3 files changed

+105
-53
lines changed

3 files changed

+105
-53
lines changed

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,7 @@ class Checker extends MiniPhase {
4848
if (instantiable && cls.enclosingPackageClass != defn.StdLibPatchesPackage.moduleClass) {
4949
import semantic._
5050
val tpl = tree.rhs.asInstanceOf[Template]
51-
val thisRef = ThisRef(cls)
52-
53-
val obj = Objekt(cls, fields = mutable.Map.empty, outers = mutable.Map(cls -> Hot))
54-
heap.update(thisRef, obj)
51+
val thisRef = ThisRef(cls).ensureExists
5552

5653
val paramValues = tpl.constr.termParamss.flatten.map(param => param.symbol -> Hot).toMap
5754

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

Lines changed: 65 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,14 @@ class Semantic {
6767

6868
sealed abstract class Addr extends Value {
6969
def klass: ClassSymbol
70+
def outer: Value
7071
}
7172

7273
/** A reference to the object under initialization pointed by `this`
7374
*/
74-
case class ThisRef(klass: ClassSymbol) extends Addr
75+
case class ThisRef(klass: ClassSymbol) extends Addr {
76+
val outer = Hot
77+
}
7578

7679
/** An object with all fields initialized but reaches objects under initialization
7780
*
@@ -183,6 +186,7 @@ class Semantic {
183186

184187
type Env = Env.Env
185188
def env(using env: Env) = env
189+
inline def withEnv[T](env: Env)(op: Env ?=> T): T = op(using env)
186190

187191
import Env._
188192

@@ -261,8 +265,8 @@ class Semantic {
261265
def call(meth: Symbol, args: List[ArgInfo], superType: Type, source: Tree): Contextual[Result] =
262266
value.call(meth, args, superType, source) ++ errors
263267

264-
def instantiate(klass: ClassSymbol, ctor: Symbol, args: List[ArgInfo], source: Tree): Contextual[Result] =
265-
value.instantiate(klass, ctor, args, source) ++ errors
268+
def instantiate(klass: ClassSymbol, ctor: Symbol, args: List[ArgInfo], source: Tree, inside: ClassSymbol): Contextual[Result] =
269+
value.instantiate(klass, ctor, args, source, inside) ++ errors
266270
}
267271

268272
/** The state that threads through the interpreter */
@@ -284,6 +288,7 @@ class Semantic {
284288

285289
import Trace._
286290
def trace(using t: Trace): Trace = t
291+
inline def withTrace[T](t: Trace)(op: Trace ?=> T): T = op(using t)
287292

288293
// ----- Operations on domains -----------------------------
289294
extension (a: Value)
@@ -305,10 +310,11 @@ class Semantic {
305310

306311
case (RefSet(refs1), RefSet(refs2)) => RefSet(refs1 ++ refs2)
307312

308-
def widen: Value =
313+
/** Conservatively approximate the value with `Cold` or `Hot` */
314+
def widenArg: Value =
309315
a match
310316
case _: Addr | _: Fun => Cold
311-
case RefSet(refs) => refs.map(_.widen).join
317+
case RefSet(refs) => refs.map(_.widenArg).join
312318
case _ => a
313319

314320

@@ -317,7 +323,7 @@ class Semantic {
317323
if values.isEmpty then Hot
318324
else values.reduce { (v1, v2) => v1.join(v2) }
319325

320-
def widen: List[Value] = values.map(_.widen).toList
326+
def widenArgs: List[Value] = values.map(_.widenArg).toList
321327

322328
extension (value: Value)
323329
def select(field: Symbol, source: Tree, needResolve: Boolean = true): Contextual[Result] = log("select " + field.show, printer, res => res.asInstanceOf[Result].show) {
@@ -384,7 +390,7 @@ class Semantic {
384390
Result(Hot, error :: checkArgs)
385391

386392
case addr: Addr =>
387-
val isLocal = meth.owner.isClass
393+
val isLocal = !meth.owner.isClass
388394
val target =
389395
if !needResolve then
390396
meth
@@ -399,25 +405,28 @@ class Semantic {
399405
given Trace = trace1
400406
val cls = target.owner.enclosingClass.asClass
401407
val ddef = target.defTree.asInstanceOf[DefDef]
402-
val env2 = Env(ddef, args.map(_.value).widen)
408+
val env2 = Env(ddef, args.map(_.value).widenArgs)
403409
if target.isPrimaryConstructor then
404410
given Env = env2
405411
val tpl = cls.defTree.asInstanceOf[TypeDef].rhs.asInstanceOf[Template]
406-
val res = use(trace.add(cls.defTree)) { eval(tpl, addr, cls, cacheResult = true) }
412+
val res = withTrace(trace.add(cls.defTree)) { eval(tpl, addr, cls, cacheResult = true) }
407413
Result(addr, res.errors)
408414
else if target.isConstructor then
409415
given Env = env2
410416
eval(ddef.rhs, addr, cls, cacheResult = true)
411417
else
412-
use(if isLocal then env else Env.empty) {
418+
// normal method call
419+
withEnv(if isLocal then env else Env.empty) {
413420
eval(ddef.rhs, addr, cls, cacheResult = true) ++ checkArgs
414421
}
415422
else if addr.canIgnoreMethodCall(target) then
416423
Result(Hot, Nil)
417424
else
425+
// no source code available
418426
val error = CallUnknown(target, source, trace.toVector)
419427
Result(Hot, error :: checkArgs)
420428
else
429+
// method call resolves to a field
421430
val obj = heap(addr)
422431
if obj.fields.contains(target) then
423432
Result(obj.fields(target), Nil)
@@ -428,7 +437,7 @@ class Semantic {
428437
// meth == NoSymbol for poly functions
429438
if meth.name.toString == "tupled" then Result(value, Nil) // a call like `fun.tupled`
430439
else
431-
use(env) {
440+
withEnv(env) {
432441
eval(body, thisV, klass, cacheResult = true) ++ checkArgs
433442
}
434443

@@ -441,7 +450,7 @@ class Semantic {
441450
}
442451

443452
/** Handle a new expression `new p.C` where `p` is abstracted by `value` */
444-
def instantiate(klass: ClassSymbol, ctor: Symbol, args: List[ArgInfo], source: Tree): Contextual[Result] = log("instantiating " + klass.show + ", args = " + args, printer, res => res.asInstanceOf[Result].show) {
453+
def instantiate(klass: ClassSymbol, ctor: Symbol, args: List[ArgInfo], source: Tree, inside: ClassSymbol): Contextual[Result] = log("instantiating " + klass.show + ", value = " + value + ", args = " + args, printer, res => res.asInstanceOf[Result].show) {
445454
val trace1 = trace.add(source)
446455
if promoted.isCurrentObjectPromoted then Result(Hot, Nil)
447456
else value match {
@@ -451,15 +460,12 @@ class Semantic {
451460
val errors = arg.promote
452461
buffer ++= errors
453462
if errors.isEmpty then Hot
454-
else arg.value.widen
463+
else arg.value.widenArg
455464
}
456465

457466
if buffer.isEmpty then Result(Hot, Errors.empty)
458467
else
459-
val value = Warm(klass, Hot, ctor, args2)
460-
if !heap.contains(value) then
461-
val obj = Objekt(klass, fields = mutable.Map.empty, outers = mutable.Map(klass -> Hot))
462-
heap.update(value, obj)
468+
val value = Warm(klass, Hot, ctor, args2).ensureExists
463469
val res = value.call(ctor, args, superType = NoType, source)
464470
Result(value, res.errors)
465471

@@ -475,23 +481,23 @@ class Semantic {
475481
case Warm(_, _: Warm, _, _) => Cold
476482
case _ => addr
477483

478-
val value = Warm(klass, outer, ctor, args.map(_.value).widen)
479-
if !heap.contains(value) then
480-
val obj = Objekt(klass, fields = mutable.Map.empty, outers = mutable.Map(klass -> outer))
481-
heap.update(value, obj)
484+
val value = Warm(klass, outer, ctor, args.map(_.value).widenArgs).ensureExists
482485
val res = value.call(ctor, args, superType = NoType, source)
483486

484-
// Abstract instances of local classes inside 2nd constructor as Cold
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.
485491
// This way, we avoid complicating the domain for Warm unnecessarily
486-
if !env.isHot then Result(Cold, res.errors)
492+
if klass.isContainedIn(inside) && inSecondaryConstructor(klass.owner) then Result(Cold, res.errors)
487493
else Result(value, res.errors)
488494

489495
case Fun(body, thisV, klass, env) =>
490496
report.error("unexpected tree in instantiating a function, fun = " + body.show, source)
491497
Result(Hot, Nil)
492498

493499
case RefSet(refs) =>
494-
val resList = refs.map(_.instantiate(klass, ctor, args, source))
500+
val resList = refs.map(_.instantiate(klass, ctor, args, source, inside))
495501
val value2 = resList.map(_.value).join
496502
val errors = resList.flatMap(_.errors)
497503
Result(value2, errors)
@@ -526,15 +532,25 @@ class Semantic {
526532
}
527533
}
528534

535+
/** Ensure the corresponding object exists in the heap */
536+
def ensureExists: addr.type =
537+
if !heap.contains(addr) then
538+
val obj = Objekt(addr.klass, fields = mutable.Map.empty, outers = mutable.Map(addr.klass -> addr.outer))
539+
heap.update(addr, obj)
540+
addr
541+
542+
end extension
543+
529544
extension (thisRef: ThisRef)
530545
def tryPromoteCurrentObject: Contextual[Boolean] = log("tryPromoteCurrentObject ", printer) {
531-
promoted.isCurrentObjectPromoted || {
546+
if promoted.isCurrentObjectPromoted then
547+
true
548+
else if thisRef.isFullyFilled then
532549
// If we have all fields initialized, then we can promote This to hot.
533-
thisRef.isFullyFilled && {
534-
promoted.promoteCurrent(thisRef)
535-
true
536-
}
537-
}
550+
promoted.promoteCurrent(thisRef)
551+
true
552+
else
553+
false
538554
}
539555

540556
extension (value: Value)
@@ -563,7 +579,7 @@ class Semantic {
563579
case fun @ Fun(body, thisV, klass, env) =>
564580
if promoted.contains(fun) then Nil
565581
else
566-
val res = eval(body, thisV, klass)
582+
val res = withEnv(env) { eval(body, thisV, klass) }
567583
val errors2 = res.value.promote(msg, source)
568584
if (res.errors.nonEmpty || errors2.nonEmpty)
569585
UnsafePromotion(msg, source, trace.toVector, res.errors ++ errors2) :: Nil
@@ -727,7 +743,7 @@ class Semantic {
727743
val trace2 = trace.add(expr)
728744
locally {
729745
given Trace = trace2
730-
(res ++ errors).instantiate(cls, ctor, args, expr)
746+
(res ++ errors).instantiate(cls, ctor, args, source = expr, inside = klass)
731747
}
732748

733749
case Call(ref, argss) =>
@@ -879,16 +895,13 @@ class Semantic {
879895

880896
def default() = Result(Hot, Nil)
881897

882-
if sym.is(Flags.Param) then
883-
if sym.owner.isConstructor then
884-
// instances of local classes inside secondary constructors cannot
885-
// reach here, as those values are abstracted by Cold instead of Warm.
886-
// This enables us to simplify the domain without sacrificing
887-
// expressiveness nor soundess, as local classes inside secondary
888-
// constructors are uncommon.
889-
Result(env.lookup(sym), Nil)
890-
else
891-
default()
898+
if sym.is(Flags.Param) && sym.owner.isConstructor then
899+
// instances of local classes inside secondary constructors cannot
900+
// reach here, as those values are abstracted by Cold instead of Warm.
901+
// This enables us to simplify the domain without sacrificing
902+
// expressiveness nor soundess, as local classes inside secondary
903+
// constructors are uncommon.
904+
Result(env.lookup(sym), Nil)
892905
else
893906
default()
894907

@@ -897,7 +910,9 @@ class Semantic {
897910

898911
case tp @ ThisType(tref) =>
899912
val cls = tref.classSymbol.asClass
900-
if cls.isStaticOwner && !klass.isContainedIn(cls) then Result(Hot, Nil)
913+
if cls.isStaticOwner && !klass.isContainedIn(cls) then
914+
// O.this outside the body of the object O
915+
Result(Hot, Nil)
901916
else
902917
val value = resolveThis(cls, thisV, klass, source)
903918
Result(value, Errors.empty)
@@ -962,6 +977,8 @@ class Semantic {
962977
printer.println(acc.show + " initialized with " + value)
963978
}
964979

980+
// Handler is used to schedule super constructor calls.
981+
// Super constructor calls are delayed until all outers are set.
965982
type Handler = (() => Unit) => Unit
966983
def superCall(tref: TypeRef, ctor: Symbol, args: List[ArgInfo], source: Tree, handler: Handler)(using Env): Unit =
967984
val cls = tref.classSymbol.asClass
@@ -982,13 +999,13 @@ class Semantic {
982999
def initParent(parent: Tree, handler: Handler)(using Env) = parent match {
9831000
case tree @ Block(stats, NewExpr(tref, New(tpt), ctor, argss)) => // can happen
9841001
eval(stats, thisV, klass).foreach { res => errorBuffer ++= res.errors }
985-
val (erros, args) = evalArgs(argss.flatten, thisV, klass)
986-
errorBuffer ++= erros
1002+
val (errors, args) = evalArgs(argss.flatten, thisV, klass)
1003+
errorBuffer ++= errors
9871004
superCall(tref, ctor, args, tree, handler)
9881005

9891006
case tree @ NewExpr(tref, New(tpt), ctor, argss) => // extends A(args)
990-
val (erros, args) = evalArgs(argss.flatten, thisV, klass)
991-
errorBuffer ++= erros
1007+
val (errors, args) = evalArgs(argss.flatten, thisV, klass)
1008+
errorBuffer ++= errors
9921009
superCall(tref, ctor, args, tree, handler)
9931010

9941011
case _ => // extends A or extends A[T]
@@ -1087,14 +1104,13 @@ class Semantic {
10871104
object Semantic {
10881105

10891106
// ----- Utility methods and extractors --------------------------------
1090-
inline def use[T, R](v: T)(inline op: T ?=> R): R = op(using v)
10911107

10921108
def typeRefOf(tp: Type)(using Context): TypeRef = tp.dealias.typeConstructor match {
10931109
case tref: TypeRef => tref
10941110
case hklambda: HKTypeLambda => typeRefOf(hklambda.resType)
10951111
}
10961112

1097-
type Arg = Tree | ByNameArg
1113+
opaque type Arg = Tree | ByNameArg
10981114
case class ByNameArg(tree: Tree)
10991115

11001116
extension (arg: Arg)

tests/init/neg/secondary-ctor3.scala

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

0 commit comments

Comments
 (0)