Skip to content

Commit fc76549

Browse files
committed
Make isPopulatingParams a flag in Warm
1 parent ccbb355 commit fc76549

File tree

1 file changed

+71
-73
lines changed

1 file changed

+71
-73
lines changed

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

Lines changed: 71 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,7 @@ object Semantic {
7474

7575
def ensureObjectExists()(using Heap): this.type =
7676
if heap.contains(this) then this
77-
else {
78-
val obj = Objekt(this.klass, fields = Map.empty, outers = Map(this.klass -> this.outer))
79-
heap.update(this, obj)
80-
this
81-
}
77+
else ensureFresh()
8278

8379
def ensureFresh()(using Heap): this.type =
8480
val obj = Objekt(this.klass, fields = Map.empty, outers = Map(this.klass -> this.outer))
@@ -107,31 +103,40 @@ object Semantic {
107103
}
108104

109105
/** A reference to the object under initialization pointed by `this` */
110-
case class ThisRef(klass: ClassSymbol, outer: Value, ctor: Symbol, args: List[Value])(using @constructorOnly h: Heap) extends Ref {
111-
ensureObjectExists()
112-
}
106+
case class ThisRef(klass: ClassSymbol, outer: Value, ctor: Symbol, args: List[Value]) extends Ref
113107

114108
/** An object with all fields initialized but reaches objects under initialization
115109
*
116110
* We need to restrict nesting levels of `outer` to finitize the domain.
117111
*/
118-
case class Warm(klass: ClassSymbol, outer: Value, ctor: Symbol, args: List[Value])(using @constructorOnly h: Heap) extends Ref {
119-
ensureObjectExists()
112+
case class Warm(klass: ClassSymbol, outer: Value, ctor: Symbol, args: List[Value]) extends Ref {
113+
114+
/** If a warm value is in the process of populating parameters, class bodies are not executed. */
115+
private var populatingParams: Boolean = false
116+
117+
def isPopulatingParams = populatingParams
120118

121119
/** Ensure that outers and class parameters are initialized.
122120
*
123121
* Fields in class body are not initialized.
124122
*/
125-
def ensureInit(): Contextual[this.type] =
123+
def populateParams(): Contextual[this.type] = log("populating parameters", printer, (_: Warm).objekt.toString) {
124+
assert(!populatingParams, "the object is already populating parameters")
126125
// Somehow Dotty uses the one in the class parameters
126+
populatingParams = true
127127
given Heap = state.heap
128-
if objekt.outers.size <= 1 then
129-
state.populateWarm {
130-
val tpl = klass.defTree.asInstanceOf[TypeDef].rhs.asInstanceOf[Template]
131-
this.callConstructor(ctor, args.map(arg => ArgInfo(arg, EmptyTree)), tpl)
132-
}
133-
end if
128+
val tpl = klass.defTree.asInstanceOf[TypeDef].rhs.asInstanceOf[Template]
129+
this.callConstructor(ctor, args.map(arg => ArgInfo(arg, EmptyTree)), tpl)
130+
populatingParams = false
134131
this
132+
}
133+
134+
def ensureObjectExistsAndPopulated(): Contextual[this.type] =
135+
if heap.contains(this) then this
136+
else ensureFresh().populateParams()
137+
138+
def ensureObjectFreshAndPopulated(): Contextual[this.type] =
139+
ensureFresh().populateParams()
135140
}
136141

137142
/** A function value */
@@ -187,15 +192,20 @@ object Semantic {
187192
*/
188193
def prepare(heapBefore: Heap)(using State, Context) =
189194
this.map.keys.foreach {
190-
case warm: Warm if !heapBefore.contains(warm) =>
191-
given Env = Env.empty
192-
given Trace = Trace.empty
193-
given Promoted = Promoted.empty
194-
warm.ensureFresh().ensureInit()
195+
case warm: Warm =>
196+
if heapBefore.contains(warm) then
197+
assert(heapBefore(warm) == this(warm))
198+
else
199+
// We cannot simply remove the object, as the values in the
200+
// updated cache may refer to the warm object.
201+
given Env = Env.empty
202+
given Trace = Trace.empty
203+
given Promoted = Promoted.empty
204+
warm.ensureObjectFreshAndPopulated()
195205
case _ =>
196206
}
197207

198-
// ThisRef might be used in `ensureInit`
208+
// ThisRef might be used in `populateParams`
199209
this.map.keys.foreach {
200210
case thisRef: ThisRef =>
201211
this.map = this.map - thisRef
@@ -336,10 +346,7 @@ object Semantic {
336346
def assume(value: Value, expr: Tree, cacheResult: Boolean)(fun: => Result): Contextual[Result] =
337347
val assumeValue: Value =
338348
if last.contains(value, expr) then
339-
// The object corresponding to ThisRef may not exist in the heap. See `Heap.prepare`.
340-
last.get(value, expr) match
341-
case ref: ThisRef => ref.ensureObjectExists()
342-
case v => v
349+
last.get(value, expr)
343350
else
344351
last.put(value, expr, Hot)
345352
Hot
@@ -428,16 +435,7 @@ object Semantic {
428435

429436
// ----- State --------------------------------------------
430437
/** Global state of the checker */
431-
class State(val cache: Cache, val heap: Heap, val workList: WorkList, var isPopulatingWarm: Boolean = false) {
432-
// TODO: problematic for nested `init`.
433-
def populateWarm[T](fun: State ?=> T) = {
434-
val last = isPopulatingWarm
435-
isPopulatingWarm = true
436-
val res = fun(using this)
437-
isPopulatingWarm = last
438-
res
439-
}
440-
}
438+
class State(val cache: Cache, val heap: Heap, val workList: WorkList)
441439

442440
given (using s: State): Heap = s.heap
443441
given (using s: State): Cache = s.cache
@@ -619,44 +617,44 @@ object Semantic {
619617
report.error("unexpected constructor call, meth = " + ctor + ", value = " + value, source)
620618
Result(Hot, Nil)
621619

622-
case ref: Warm if state.isPopulatingWarm =>
623-
val trace1 = trace.add(source)
624-
if ctor.hasSource then
625-
given Trace = trace1
626-
val cls = ctor.owner.enclosingClass.asClass
627-
val ddef = ctor.defTree.asInstanceOf[DefDef]
628-
given Env = Env(ddef, args.map(_.value).widenArgs)
629-
if ctor.isPrimaryConstructor then
630-
val tpl = cls.defTree.asInstanceOf[TypeDef].rhs.asInstanceOf[Template]
631-
init(tpl, ref, cls)
632-
else
633-
val initCall = ddef.rhs match
634-
case Block(call :: _, _) => call
635-
case call => call
636-
eval(initCall, ref, cls)
637-
end if
620+
case ref: Warm if ref.isPopulatingParams =>
621+
val trace1 = trace.add(source)
622+
if ctor.hasSource then
623+
given Trace = trace1
624+
val cls = ctor.owner.enclosingClass.asClass
625+
val ddef = ctor.defTree.asInstanceOf[DefDef]
626+
given Env = Env(ddef, args.map(_.value).widenArgs)
627+
if ctor.isPrimaryConstructor then
628+
val tpl = cls.defTree.asInstanceOf[TypeDef].rhs.asInstanceOf[Template]
629+
init(tpl, ref, cls)
638630
else
639-
Result(Hot, Nil)
631+
val initCall = ddef.rhs match
632+
case Block(call :: _, _) => call
633+
case call => call
634+
eval(initCall, ref, cls)
635+
end if
636+
else
637+
Result(Hot, Nil)
640638

641639
case ref: Ref =>
642-
val trace1 = trace.add(source)
643-
if ctor.hasSource then
644-
given Trace = trace1
645-
val cls = ctor.owner.enclosingClass.asClass
646-
val ddef = ctor.defTree.asInstanceOf[DefDef]
647-
given Env= Env(ddef, args.map(_.value).widenArgs)
648-
if ctor.isPrimaryConstructor then
649-
val tpl = cls.defTree.asInstanceOf[TypeDef].rhs.asInstanceOf[Template]
650-
val res = withTrace(trace.add(cls.defTree)) { eval(tpl, ref, cls, cacheResult = true) }
651-
Result(ref, res.errors)
652-
else
653-
eval(ddef.rhs, ref, cls, cacheResult = true)
654-
else if ref.canIgnoreMethodCall(ctor) then
655-
Result(Hot, Nil)
640+
val trace1 = trace.add(source)
641+
if ctor.hasSource then
642+
given Trace = trace1
643+
val cls = ctor.owner.enclosingClass.asClass
644+
val ddef = ctor.defTree.asInstanceOf[DefDef]
645+
given Env= Env(ddef, args.map(_.value).widenArgs)
646+
if ctor.isPrimaryConstructor then
647+
val tpl = cls.defTree.asInstanceOf[TypeDef].rhs.asInstanceOf[Template]
648+
val res = withTrace(trace.add(cls.defTree)) { eval(tpl, ref, cls, cacheResult = true) }
649+
Result(ref, res.errors)
656650
else
657-
// no source code available
658-
val error = CallUnknown(ctor, source, trace.toVector)
659-
Result(Hot, error :: Nil)
651+
eval(ddef.rhs, ref, cls, cacheResult = true)
652+
else if ref.canIgnoreMethodCall(ctor) then
653+
Result(Hot, Nil)
654+
else
655+
// no source code available
656+
val error = CallUnknown(ctor, source, trace.toVector)
657+
Result(Hot, error :: Nil)
660658
}
661659

662660
}
@@ -694,7 +692,7 @@ object Semantic {
694692
val outer = ref match
695693
case warm @ Warm(_, _: Warm, _, _) =>
696694
// the widened warm object might not exist in the heap
697-
warm.copy(outer = Cold).ensureObjectExists().ensureInit()
695+
warm.copy(outer = Cold).ensureObjectExistsAndPopulated()
698696
case _ => ref
699697

700698
val argsWidened = args.map(_.value).widenArgs
@@ -1397,7 +1395,7 @@ object Semantic {
13971395
var fieldsChanged = true
13981396

13991397
// class body
1400-
if !state.isPopulatingWarm then tpl.body.foreach {
1398+
if !thisV.isWarm || !thisV.asInstanceOf[Warm].isPopulatingParams then tpl.body.foreach {
14011399
case vdef : ValDef if !vdef.symbol.is(Flags.Lazy) && !vdef.rhs.isEmpty =>
14021400
given Env = Env.empty
14031401
val res = eval(vdef.rhs, thisV, klass)

0 commit comments

Comments
 (0)