@@ -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