@@ -74,11 +74,7 @@ object Semantic {
74
74
75
75
def ensureObjectExists ()(using Heap ): this .type =
76
76
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()
82
78
83
79
def ensureFresh ()(using Heap ): this .type =
84
80
val obj = Objekt (this .klass, fields = Map .empty, outers = Map (this .klass -> this .outer))
@@ -107,31 +103,40 @@ object Semantic {
107
103
}
108
104
109
105
/** 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
113
107
114
108
/** An object with all fields initialized but reaches objects under initialization
115
109
*
116
110
* We need to restrict nesting levels of `outer` to finitize the domain.
117
111
*/
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
120
118
121
119
/** Ensure that outers and class parameters are initialized.
122
120
*
123
121
* Fields in class body are not initialized.
124
122
*/
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" )
126
125
// Somehow Dotty uses the one in the class parameters
126
+ populatingParams = true
127
127
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
134
131
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()
135
140
}
136
141
137
142
/** A function value */
@@ -187,15 +192,20 @@ object Semantic {
187
192
*/
188
193
def prepare (heapBefore : Heap )(using State , Context ) =
189
194
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()
195
205
case _ =>
196
206
}
197
207
198
- // ThisRef might be used in `ensureInit `
208
+ // ThisRef might be used in `populateParams `
199
209
this .map.keys.foreach {
200
210
case thisRef : ThisRef =>
201
211
this .map = this .map - thisRef
@@ -336,10 +346,7 @@ object Semantic {
336
346
def assume (value : Value , expr : Tree , cacheResult : Boolean )(fun : => Result ): Contextual [Result ] =
337
347
val assumeValue : Value =
338
348
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)
343
350
else
344
351
last.put(value, expr, Hot )
345
352
Hot
@@ -428,16 +435,7 @@ object Semantic {
428
435
429
436
// ----- State --------------------------------------------
430
437
/** 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 )
441
439
442
440
given (using s : State ): Heap = s.heap
443
441
given (using s : State ): Cache = s.cache
@@ -619,44 +617,44 @@ object Semantic {
619
617
report.error(" unexpected constructor call, meth = " + ctor + " , value = " + value, source)
620
618
Result (Hot , Nil )
621
619
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)
638
630
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 )
640
638
641
639
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)
656
650
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 )
660
658
}
661
659
662
660
}
@@ -694,7 +692,7 @@ object Semantic {
694
692
val outer = ref match
695
693
case warm @ Warm (_, _ : Warm , _, _) =>
696
694
// the widened warm object might not exist in the heap
697
- warm.copy(outer = Cold ).ensureObjectExists().ensureInit ()
695
+ warm.copy(outer = Cold ).ensureObjectExistsAndPopulated ()
698
696
case _ => ref
699
697
700
698
val argsWidened = args.map(_.value).widenArgs
@@ -1397,7 +1395,7 @@ object Semantic {
1397
1395
var fieldsChanged = true
1398
1396
1399
1397
// class body
1400
- if ! state.isPopulatingWarm then tpl.body.foreach {
1398
+ if ! thisV.isWarm || ! thisV. asInstanceOf [ Warm ].isPopulatingParams then tpl.body.foreach {
1401
1399
case vdef : ValDef if ! vdef.symbol.is(Flags .Lazy ) && ! vdef.rhs.isEmpty =>
1402
1400
given Env = Env .empty
1403
1401
val res = eval(vdef.rhs, thisV, klass)
0 commit comments