@@ -267,8 +267,31 @@ object Semantic {
267
267
* statement body. Macros may also create incorrect locations.
268
268
*
269
269
*/
270
- type Cache = mutable.Map [Value , EqHashMap [Tree , Value ]]
271
- val cache : Cache = mutable.Map .empty[Value , EqHashMap [Tree , Value ]]
270
+
271
+ class Cache (val in : Cache .CacheIn , var out : Cache .CacheOut ) {
272
+ var changed : Boolean = false
273
+ }
274
+
275
+ object Cache {
276
+ opaque type CacheIn = mutable.Map [Value , EqHashMap [Tree , Value ]]
277
+ opaque type CacheOut = mutable.Map [Value , EqHashMap [Tree , Value ]]
278
+
279
+ val empty : Cache = new Cache (mutable.Map .empty, mutable.Map .empty)
280
+
281
+ extension (cache : CacheIn | CacheOut )
282
+ def contains (value : Value , expr : Tree ): Boolean = cache.contains(value) && cache(value).contains(expr)
283
+ def get (value : Value , expr : Tree ): Value = cache(value)(expr)
284
+ def put (value : Value , expr : Tree , result : Value ): Unit = {
285
+ val innerMap = cache.getOrElseUpdate(value, new EqHashMap [Tree , Value ])
286
+ innerMap(expr) = result
287
+ }
288
+ end extension
289
+ }
290
+
291
+ import Cache ._
292
+
293
+ inline def cache (using c : Cache ): Cache = c
294
+
272
295
273
296
/** Result of abstract interpretation */
274
297
case class Result (value : Value , errors : Seq [Error ]) {
@@ -293,9 +316,10 @@ object Semantic {
293
316
294
317
// ----- State --------------------------------------------
295
318
/** Global state of the checker */
296
- class State (val heap : Heap , val workList : WorkList )
319
+ class State (val cache : Cache , val heap : Heap , val workList : WorkList )
297
320
298
321
given (using s : State ): Heap = s.heap
322
+ given (using s : State ): Cache = s.cache
299
323
given (using s : State ): WorkList = s.workList
300
324
301
325
/** The state that threads through the interpreter */
@@ -368,7 +392,7 @@ object Semantic {
368
392
if target.is(Flags .Lazy ) then
369
393
given Trace = trace1
370
394
val rhs = target.defTree.asInstanceOf [ValDef ].rhs
371
- eval(rhs, ref, target.owner.asClass, cacheResult = true )
395
+ eval(rhs, ref, target.owner.asClass)
372
396
else
373
397
val obj = ref.objekt
374
398
if obj.hasField(target) then
@@ -382,7 +406,7 @@ object Semantic {
382
406
Result (Hot , Nil )
383
407
else if target.hasSource then
384
408
val rhs = target.defTree.asInstanceOf [ValOrDefDef ].rhs
385
- eval(rhs, ref, target.owner.asClass, cacheResult = true )
409
+ eval(rhs, ref, target.owner.asClass)
386
410
else
387
411
val error = CallUnknown (field, source, trace.toVector)
388
412
Result (Hot , error :: Nil )
@@ -434,7 +458,7 @@ object Semantic {
434
458
val env2 = Env (ddef, args.map(_.value).widenArgs)
435
459
// normal method call
436
460
withEnv(if isLocal then env else Env .empty) {
437
- eval(ddef.rhs, ref, cls, cacheResult = true ) ++ checkArgs
461
+ eval(ddef.rhs, ref, cls) ++ checkArgs
438
462
}
439
463
else if ref.canIgnoreMethodCall(target) then
440
464
Result (Hot , Nil )
@@ -455,7 +479,7 @@ object Semantic {
455
479
if meth.name.toString == " tupled" then Result (value, Nil ) // a call like `fun.tupled`
456
480
else
457
481
withEnv(env) {
458
- eval(body, thisV, klass, cacheResult = true ) ++ checkArgs
482
+ eval(body, thisV, klass) ++ checkArgs
459
483
}
460
484
461
485
case RefSet (refs) =>
@@ -482,11 +506,11 @@ object Semantic {
482
506
if ctor.isPrimaryConstructor then
483
507
given Env = env2
484
508
val tpl = cls.defTree.asInstanceOf [TypeDef ].rhs.asInstanceOf [Template ]
485
- val res = withTrace(trace.add(cls.defTree)) { eval(tpl, ref, cls, cacheResult = true ) }
509
+ val res = withTrace(trace.add(cls.defTree)) { eval(tpl, ref, cls) }
486
510
Result (ref, res.errors)
487
511
else
488
512
given Env = env2
489
- eval(ddef.rhs, ref, cls, cacheResult = true )
513
+ eval(ddef.rhs, ref, cls)
490
514
else if ref.canIgnoreMethodCall(ctor) then
491
515
Result (Hot , Nil )
492
516
else
@@ -792,7 +816,7 @@ object Semantic {
792
816
* }
793
817
*/
794
818
def withInitialState [T ](work : State ?=> T ): T = {
795
- val initialState = State (Heap .empty, new WorkList )
819
+ val initialState = State (Cache .empty, Heap .empty, new WorkList )
796
820
work(using initialState)
797
821
}
798
822
@@ -818,17 +842,15 @@ object Semantic {
818
842
*
819
843
* This method only handles cache logic and delegates the work to `cases`.
820
844
*/
821
- def eval (expr : Tree , thisV : Ref , klass : ClassSymbol , cacheResult : Boolean = false ): Contextual [Result ] = log(" evaluating " + expr.show + " , this = " + thisV.show, printer, res => res.asInstanceOf [Result ].show) {
822
- val innerMap = cache.getOrElseUpdate(thisV, new EqHashMap [Tree , Value ])
823
- if (innerMap.contains(expr)) Result (innerMap(expr), Errors .empty)
845
+ def eval (expr : Tree , thisV : Ref , klass : ClassSymbol ): Contextual [Result ] = log(" evaluating " + expr.show + " , this = " + thisV.show, printer, res => res.asInstanceOf [Result ].show) {
846
+ if (cache.out.contains(thisV, expr)) Result (cache.out.get(thisV, expr), Errors .empty)
824
847
else {
825
- // no need to compute fix-point, because
826
- // 1. the result is decided by `cfg` for a legal program
827
- // (heap change is irrelevant thanks to monotonicity)
828
- // 2. errors will have been reported for an illegal program
829
- innerMap(expr) = Hot
848
+ val assumeValue = if (cache.in.contains(thisV, expr)) cache.in.get(thisV, expr) else Hot
849
+ cache.out.put(thisV, expr, assumeValue)
830
850
val res = cases(expr, thisV, klass)
831
- if cacheResult then innerMap(expr) = res.value else innerMap.remove(expr)
851
+ if res.value != assumeValue then
852
+ cache.changed = true
853
+ cache.out.put(thisV, expr, res.value)
832
854
res
833
855
}
834
856
}
@@ -1005,7 +1027,7 @@ object Semantic {
1005
1027
case vdef : ValDef =>
1006
1028
// local val definition
1007
1029
// TODO: support explicit @cold annotation for local definitions
1008
- eval(vdef.rhs, thisV, klass, cacheResult = true )
1030
+ eval(vdef.rhs, thisV, klass)
1009
1031
1010
1032
case ddef : DefDef =>
1011
1033
// local method
@@ -1225,7 +1247,7 @@ object Semantic {
1225
1247
tpl.body.foreach {
1226
1248
case vdef : ValDef if ! vdef.symbol.is(Flags .Lazy ) && ! vdef.rhs.isEmpty =>
1227
1249
given Env = Env .empty
1228
- val res = eval(vdef.rhs, thisV, klass, cacheResult = true )
1250
+ val res = eval(vdef.rhs, thisV, klass)
1229
1251
errorBuffer ++= res.errors
1230
1252
thisV.updateField(vdef.symbol, res.value)
1231
1253
fieldsChanged = true
0 commit comments