@@ -9,7 +9,7 @@ import Types._
99import StdNames ._
1010
1111import ast .tpd ._
12- import util .SourcePosition
12+ import util .EqHashMap
1313import config .Printers .init as printer
1414import reporting .trace as log
1515
@@ -22,9 +22,6 @@ class Semantic {
2222
2323// ----- Domain definitions --------------------------------
2424
25- /** Locations are finite for any givn program */
26- type Loc = SourcePosition
27-
2825 /** Abstract values
2926 *
3027 * Value = Hot | Cold | Warm | ThisRef | Fun | RefSet
@@ -79,16 +76,23 @@ class Semantic {
7976 * Which also avoid computing fix-point on the cache, as the cache is
8077 * immutable.
8178 */
82- case class Config (thisV : Value , loc : Loc )
79+ case class Config (thisV : Value , expr : Tree )
8380
8481 /** Cache used to terminate the analysis
8582 *
8683 * A finitary configuration is not enough for the analysis to
8784 * terminate. We need to use cache to let the interpreter "know"
8885 * that it can terminate.
86+ *
87+ * For performance reasons we use curried key.
88+ *
89+ * Note: It's tempting to use location of trees as key. That should
90+ * be avoided as a template may have the same location as its single
91+ * statement body. Macros may also create incorrect locations.
92+ *
8993 */
90- type Cache = mutable.Map [Config , Value ]
91- val cache : Cache = mutable.Map .empty[Config , Value ]
94+ type Cache = mutable.Map [Value , EqHashMap [ Tree , Value ] ]
95+ val cache : Cache = mutable.Map .empty[Value , EqHashMap [ Tree , Value ] ]
9296
9397 /** Result of abstract interpretation */
9498 case class Result (value : Value , errors : Seq [Error ]) {
@@ -346,16 +350,16 @@ class Semantic {
346350 * This method only handles cache logic and delegates the work to `cases`.
347351 */
348352 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) {
349- val cfg = Config (thisV, expr.sourcePos )
350- if (cache .contains(cfg )) Result (cache(cfg ), noErrors)
353+ val innerMap = cache.getOrElseUpdate (thisV, new EqHashMap [ Tree , Value ] )
354+ if (innerMap .contains(expr )) Result (innerMap(expr ), noErrors)
351355 else {
352356 // no need to compute fix-point, because
353357 // 1. the result is decided by `cfg` for a legal program
354358 // (heap change is irrelevant thanks to monotonicity)
355359 // 2. errors will have been reported for an illegal program
356- if cacheResult then cache(cfg ) = Hot
360+ innerMap(expr ) = Hot
357361 val res = cases(expr, thisV, klass)
358- if cacheResult then cache(cfg ) = res.value
362+ if cacheResult then innerMap(expr ) = res.value else innerMap.remove(expr)
359363 res
360364 }
361365 }
0 commit comments