@@ -38,13 +38,8 @@ class Semantic {
38
38
case object Cold extends Value
39
39
40
40
/** Object referred by `this` which stores abstract values for all fields
41
- *
42
- * Note: the mutable `fields` plays the role of heap. Thanks to monotonicity
43
- * of the heap, we may handle it in a simple way.
44
41
*/
45
- case class ThisRef (klass : ClassSymbol )(val fields : mutable.Map [Symbol , Value ]) extends Value {
46
- var allFieldsInitialized : Boolean = false
47
- }
42
+ case class ThisRef (klass : ClassSymbol ) extends Value
48
43
49
44
/** An object with all fields initialized but reaches objects under initialization
50
45
*
@@ -61,6 +56,21 @@ class Semantic {
61
56
*/
62
57
case class RefSet (refs : List [Warm | Fun | ThisRef ]) extends Value
63
58
59
+ /** The current object under initialization
60
+ */
61
+ case class Objekt (val fields : mutable.Map [Symbol , Value ]) {
62
+ var allFieldsInitialized : Boolean = false
63
+ }
64
+
65
+ /** Abstract heap stores abstract objects
66
+ *
67
+ * As in the OOPSLA paper, the abstract heap is monotonistic.
68
+ *
69
+ * This is only one object we need to care about, hence it's just `Objekt`.
70
+ */
71
+ type Heap = Objekt
72
+ def heap (using h : Heap ): Heap = h
73
+
64
74
/** Interpreter configuration
65
75
*
66
76
* The (abstract) interpreter can be seen as a push-down automaton
@@ -103,28 +113,30 @@ class Semantic {
103
113
104
114
def + (error : Error ): Result = this .copy(errors = this .errors :+ error)
105
115
106
- def ensureHot (msg : String , source : Tree )( using Context , Trace ) : Result =
116
+ def ensureHot (msg : String , source : Tree ): Contextual [ Result ] =
107
117
this ++ value.promote(msg, source)
108
118
109
- def select (f : Symbol , source : Tree )( using Context , Trace ) : Result =
119
+ def select (f : Symbol , source : Tree ): Contextual [ Result ] =
110
120
value.select(f, source) ++ errors
111
121
112
- def call (meth : Symbol , superType : Type , source : Tree )( using Context , Trace ) : Result =
122
+ def call (meth : Symbol , superType : Type , source : Tree ): Contextual [ Result ] =
113
123
value.call(meth, superType, source) ++ errors
114
124
115
- def instantiate (klass : ClassSymbol , ctor : Symbol , source : Tree )( using Context , Trace ) : Result =
125
+ def instantiate (klass : ClassSymbol , ctor : Symbol , source : Tree ): Contextual [ Result ] =
116
126
value.instantiate(klass, ctor, source) ++ errors
117
127
}
118
128
129
+ /** The state that threads through the interpreter */
130
+ type Contextual [T ] = (Heap , Context , Trace ) ?=> T
131
+
119
132
// ----- Error Handling -----------------------------------
120
133
type Trace = Vector [Tree ]
121
-
122
- val noErrors = Nil
134
+ def trace (using t : Trace ): Trace = t
123
135
124
136
extension (trace : Trace )
125
137
def add (node : Tree ): Trace = trace :+ node
126
138
127
- def trace ( using t : Trace ) : Trace = t
139
+ val noErrors = Nil
128
140
129
141
// ----- Operations on domains -----------------------------
130
142
extension (a : Value )
@@ -149,7 +161,7 @@ class Semantic {
149
161
else values.reduce { (v1, v2) => v1.join(v2) }
150
162
151
163
extension (value : Value )
152
- def select (field : Symbol , source : Tree )( using Context , Trace ) : Result =
164
+ def select (field : Symbol , source : Tree ): Contextual [ Result ] =
153
165
value match {
154
166
case Hot =>
155
167
Result (Hot , noErrors)
@@ -161,8 +173,8 @@ class Semantic {
161
173
case thisRef : ThisRef =>
162
174
val target = resolve(thisRef.klass, field)
163
175
if target.is(Flags .Lazy ) then value.call(target, superType = NoType , source)
164
- else if thisRef .fields.contains(target) then
165
- Result (thisRef .fields(target), Nil )
176
+ else if heap .fields.contains(target) then
177
+ Result (heap .fields(target), Nil )
166
178
else
167
179
val error = AccessNonInit (target, trace.add(source))
168
180
Result (Hot , error :: Nil )
@@ -186,7 +198,7 @@ class Semantic {
186
198
Result (value2, errors)
187
199
}
188
200
189
- def call (meth : Symbol , superType : Type , source : Tree )( using Context , Trace ) : Result =
201
+ def call (meth : Symbol , superType : Type , source : Tree ): Contextual [ Result ] =
190
202
value match {
191
203
case Hot =>
192
204
Result (Hot , noErrors)
@@ -216,8 +228,8 @@ class Semantic {
216
228
else
217
229
val error = CallUnknown (target, source, trace)
218
230
Result (Hot , error :: Nil )
219
- else if thisRef .fields.contains(target) then
220
- Result (thisRef .fields(target), Nil )
231
+ else if heap .fields.contains(target) then
232
+ Result (heap .fields(target), Nil )
221
233
else
222
234
val error = AccessNonInit (target, trace.add(source))
223
235
Result (Hot , error :: Nil )
@@ -257,7 +269,7 @@ class Semantic {
257
269
Result (value2, errors)
258
270
}
259
271
260
- def instantiate (klass : ClassSymbol , ctor : Symbol , source : Tree )( using Context , Trace ) : Result =
272
+ def instantiate (klass : ClassSymbol , ctor : Symbol , source : Tree ): Contextual [ Result ] =
261
273
value match {
262
274
case Hot =>
263
275
Result (Hot , noErrors)
@@ -289,8 +301,17 @@ class Semantic {
289
301
}
290
302
end extension
291
303
304
+ extension (ref : ThisRef | Warm )
305
+ def updateField (field : Symbol , value : Value ): Contextual [Unit ] =
306
+ ref match
307
+ case thisRef : ThisRef => heap.fields(field) = value
308
+ case warm : Warm => // ignore
309
+ end extension
310
+
311
+ // ----- Promotion ----------------------------------------------------
312
+
292
313
extension (value : Value )
293
- def canDirectlyPromote (using Context ): Boolean =
314
+ def canDirectlyPromote (using Heap , Context ): Boolean =
294
315
value match
295
316
case Hot => true
296
317
case Cold => false
@@ -299,13 +320,13 @@ class Semantic {
299
320
warm.outer.canDirectlyPromote
300
321
301
322
case thisRef : ThisRef =>
302
- thisRef .allFieldsInitialized || {
323
+ heap .allFieldsInitialized || {
303
324
// If we have all fields initialized, then we can promote This to hot.
304
- thisRef .allFieldsInitialized = thisRef.klass.appliedRef.fields.forall { denot =>
325
+ heap .allFieldsInitialized = thisRef.klass.appliedRef.fields.forall { denot =>
305
326
val sym = denot.symbol
306
- sym.isOneOf(Flags .Lazy | Flags .Deferred ) || thisRef .fields.contains(sym)
327
+ sym.isOneOf(Flags .Lazy | Flags .Deferred ) || heap .fields.contains(sym)
307
328
}
308
- thisRef .allFieldsInitialized
329
+ heap .allFieldsInitialized
309
330
}
310
331
311
332
case fun : Fun => false
@@ -316,7 +337,7 @@ class Semantic {
316
337
end canDirectlyPromote
317
338
318
339
/** Promotion of values to hot */
319
- def promote (msg : String , source : Tree )( using Context , Trace ) : List [Error ] =
340
+ def promote (msg : String , source : Tree ): Contextual [ List [Error ] ] =
320
341
value match
321
342
case Hot => Nil
322
343
@@ -354,7 +375,7 @@ class Semantic {
354
375
* promote the field value
355
376
*
356
377
*/
357
- def tryPromote (msg : String , source : Tree )( using Context , Trace ) : List [Error ] =
378
+ def tryPromote (msg : String , source : Tree ): Contextual [ List [Error ]] = log( " promote " + warm.show, printer) {
358
379
val classRef = warm.klass.appliedRef
359
380
if classRef.memberClasses.nonEmpty then
360
381
return PromoteWarm (source, trace) :: Nil
@@ -381,16 +402,10 @@ class Semantic {
381
402
382
403
if buffer.isEmpty then Nil
383
404
else UnsafePromotion (source, trace, buffer.toList) :: Nil
405
+ }
384
406
385
407
end extension
386
408
387
- extension (ref : ThisRef | Warm )
388
- def updateField (field : Symbol , value : Value ): Unit =
389
- ref match
390
- case thisRef : ThisRef => thisRef.fields(field) = value
391
- case warm : Warm => // ignore
392
- end extension
393
-
394
409
// ----- Policies ------------------------------------------------------
395
410
extension (value : Warm | ThisRef )
396
411
/** Can the method call on `value` be ignored?
@@ -409,7 +424,7 @@ class Semantic {
409
424
*
410
425
* This method only handles cache logic and delegates the work to `cases`.
411
426
*/
412
- def eval (expr : Tree , thisV : Value , klass : ClassSymbol , cacheResult : Boolean = false )( using Context , Trace ) : Result = log(" evaluating " + expr.show + " , this = " + thisV.show, printer, res => res.asInstanceOf [Result ].show) {
427
+ def eval (expr : Tree , thisV : Value , klass : ClassSymbol , cacheResult : Boolean = false ): Contextual [ Result ] = log(" evaluating " + expr.show + " , this = " + thisV.show, printer, res => res.asInstanceOf [Result ].show) {
413
428
val innerMap = cache.getOrElseUpdate(thisV, new EqHashMap [Tree , Value ])
414
429
if (innerMap.contains(expr)) Result (innerMap(expr), noErrors)
415
430
else {
@@ -425,11 +440,11 @@ class Semantic {
425
440
}
426
441
427
442
/** Evaluate a list of expressions */
428
- def eval (exprs : List [Tree ], thisV : Value , klass : ClassSymbol )( using Context , Trace ) : List [Result ] =
443
+ def eval (exprs : List [Tree ], thisV : Value , klass : ClassSymbol ): Contextual [ List [Result ] ] =
429
444
exprs.map { expr => eval(expr, thisV, klass) }
430
445
431
446
/** Evaluate arguments of methods */
432
- def evalArgs (args : List [Arg ], thisV : Value , klass : ClassSymbol )( using Context , Trace ) : List [Error ] =
447
+ def evalArgs (args : List [Arg ], thisV : Value , klass : ClassSymbol ): Contextual [ List [Error ] ] =
433
448
val ress = args.map { arg =>
434
449
val res =
435
450
if arg.isByName then
@@ -449,7 +464,7 @@ class Semantic {
449
464
*
450
465
* Note: Recursive call should go to `eval` instead of `cases`.
451
466
*/
452
- def cases (expr : Tree , thisV : Value , klass : ClassSymbol )( using Context , Trace ) : Result =
467
+ def cases (expr : Tree , thisV : Value , klass : ClassSymbol ): Contextual [ Result ] =
453
468
expr match {
454
469
case Ident (nme.WILDCARD ) =>
455
470
// TODO: disallow `var x: T = _`
@@ -471,15 +486,17 @@ class Semantic {
471
486
// check args
472
487
val errors = evalArgs(argss.flatten, thisV, klass)
473
488
489
+ val trace2 : Trace = trace.add(expr)
490
+
474
491
ref match
475
492
case Select (supert : Super , _) =>
476
493
val SuperType (thisTp, superTp) = supert.tpe
477
494
val thisValue2 = resolveThis(thisTp.classSymbol.asClass, thisV, klass)
478
- Result (thisValue2, errors).call(ref.symbol, superTp, expr)(using ctx, trace.add(expr) )
495
+ Result (thisValue2, errors).call(ref.symbol, superTp, expr)(using heap, ctx, trace2 )
479
496
480
497
case Select (qual, _) =>
481
498
val res = eval(qual, thisV, klass) ++ errors
482
- res.call(ref.symbol, superType = NoType , source = expr)(using ctx, trace.add(expr) )
499
+ res.call(ref.symbol, superType = NoType , source = expr)(using heap, ctx, trace2 )
483
500
484
501
case id : Ident =>
485
502
id.tpe match
@@ -491,10 +508,10 @@ class Semantic {
491
508
case Hot => Result (Hot , errors)
492
509
case _ =>
493
510
val rhs = id.symbol.defTree.asInstanceOf [DefDef ].rhs
494
- eval(rhs, thisValue2, enclosingClass, cacheResult = true )(using ctx, trace.add(expr) )
511
+ eval(rhs, thisValue2, enclosingClass, cacheResult = true )(using heap, ctx, trace2 )
495
512
case TermRef (prefix, _) =>
496
513
val res = cases(prefix, thisV, klass, id) ++ errors
497
- res.call(id.symbol, superType = NoType , source = expr)(using ctx, trace.add(expr) )
514
+ res.call(id.symbol, superType = NoType , source = expr)(using heap, ctx, trace2 )
498
515
499
516
case Select (qualifier, name) =>
500
517
eval(qualifier, thisV, klass).select(expr.symbol, expr)
@@ -611,7 +628,7 @@ class Semantic {
611
628
}
612
629
613
630
/** Handle semantics of leaf nodes */
614
- def cases (tp : Type , thisV : Value , klass : ClassSymbol , source : Tree )( using Context , Trace ) : Result = log(" evaluating " + tp.show, printer, res => res.asInstanceOf [Result ].show) {
631
+ def cases (tp : Type , thisV : Value , klass : ClassSymbol , source : Tree ): Contextual [ Result ] = log(" evaluating " + tp.show, printer, res => res.asInstanceOf [Result ].show) {
615
632
tp match {
616
633
case _ : ConstantType =>
617
634
Result (Hot , noErrors)
@@ -638,7 +655,7 @@ class Semantic {
638
655
}
639
656
640
657
/** Resolve C.this that appear in `klass` */
641
- def resolveThis (target : ClassSymbol , thisV : Value , klass : ClassSymbol )( using Context , Trace ) : Value = log(" resolving " + target.show + " , this = " + thisV.show + " in " + klass.show, printer, res => res.asInstanceOf [Value ].show) {
658
+ def resolveThis (target : ClassSymbol , thisV : Value , klass : ClassSymbol ): Contextual [ Value ] = log(" resolving " + target.show + " , this = " + thisV.show + " in " + klass.show, printer, res => res.asInstanceOf [Value ].show) {
642
659
if target == klass then thisV
643
660
else
644
661
thisV match
@@ -660,7 +677,7 @@ class Semantic {
660
677
}
661
678
662
679
/** Compute the outer value that correspond to `tref.prefix` */
663
- def outerValue (tref : TypeRef , thisV : Value , klass : ClassSymbol , source : Tree )( using Context , Trace ) : Result =
680
+ def outerValue (tref : TypeRef , thisV : Value , klass : ClassSymbol , source : Tree ): Contextual [ Result ] =
664
681
val cls = tref.classSymbol.asClass
665
682
if tref.prefix == NoPrefix then
666
683
val enclosing = cls.owner.lexicallyEnclosingClass.asClass
@@ -670,7 +687,7 @@ class Semantic {
670
687
cases(tref.prefix, thisV, klass, source)
671
688
672
689
/** Initialize part of an abstract object in `klass` of the inheritance chain */
673
- def init (tpl : Template , thisV : ThisRef | Warm , klass : ClassSymbol )( using Context , Trace ) : Result = log(" init " + klass.show, printer, res => res.asInstanceOf [Result ].show) {
690
+ def init (tpl : Template , thisV : ThisRef | Warm , klass : ClassSymbol ): Contextual [ Result ] = log(" init " + klass.show, printer, res => res.asInstanceOf [Result ].show) {
674
691
val errorBuffer = new mutable.ArrayBuffer [Error ]
675
692
676
693
// init param fields
@@ -748,7 +765,7 @@ class Semantic {
748
765
*
749
766
* This is intended to avoid type soundness issues in Dotty.
750
767
*/
751
- def checkTermUsage (tpt : Tree , thisV : Value , klass : ClassSymbol )( using Context , Trace ) : List [Error ] =
768
+ def checkTermUsage (tpt : Tree , thisV : Value , klass : ClassSymbol ): Contextual [ List [Error ] ] =
752
769
val buf = new mutable.ArrayBuffer [Error ]
753
770
val traverser = new TypeTraverser {
754
771
def traverse (tp : Type ): Unit = tp match {
0 commit comments