@@ -168,44 +168,56 @@ object Semantic:
168168 import Promoted .*
169169 inline def promoted (using p : Promoted ): Promoted = p
170170
171- /** Interpreter configuration
171+ /** Cache used in fixed point computation
172172 *
173- * The (abstract) interpreter can be seen as a push-down automaton
174- * that transits between the configurations where the stack is the
175- * implicit call stack of the meta-language.
173+ * The analysis computes the least fixed point for the cache (see doc for
174+ * `ExprValueCache`).
176175 *
177- * It's important that the configuration is finite for the analysis
178- * to terminate.
176+ * For the fixed point computation to terminate, we need to make sure that
177+ * the domain of the cache, i.e. the key pair (Ref, Tree) is finite. As the
178+ * code is finite, we only need to carefully design the abstract domain to
179+ * be finitary.
179180 *
180- * For soundness, we need to compute fixed point of the cache, which
181- * maps configuration to evaluation result.
181+ * We also need to make sure that the computing function (i.e. the abstract
182+ * interpreter) is monotone. Error handling breaks monotonicity of the
183+ * abstract interpreter, because when an error happens, we always return
184+ * the bottom value `Hot` for an expression. It is not a threat for
185+ * termination because when an error happens, we stop the fixed point
186+ * computation at the end of the iteration where the error happens.
182187 *
183- * Thanks to heap monotonicity, heap is not part of the configuration.
184- *
185- * This class is only used for the purpose of documentation.
186- */
187- case class Config (thisV : Value , expr : Tree )
188-
189- /** Cache used to terminate the analysis
190- *
191- * A finitary configuration is not enough for the analysis to
192- * terminate. We need to use cache to let the interpreter "know"
193- * that it can terminate.
194- *
195- * For performance reasons we use curried key.
196- *
197- * Note: It's tempting to use location of trees as key. That should
198- * be avoided as a template may have the same location as its single
199- * statement body. Macros may also create incorrect locations.
188+ * Note: It's tempting to use location of trees as key. That should
189+ * be avoided as a template may have the same location as its single
190+ * statement body. Macros may also create incorrect locations.
200191 */
201-
202192 object Cache :
203193 /** Cache for expressions
204194 *
205195 * Ref -> Tree -> Value
206196 *
207197 * The first key is the value of `this` for the expression. We do not need
208- * environment, because the environment is always hot.
198+ * environment in the key, because the environment is always hot.
199+ *
200+ * We do not need the heap in the key, because the value of an expression
201+ * is only determined by the value of `this`. The heap is immutable: the
202+ * abstract values for object fields never change within one iteration.
203+ * The initial abstraction of a field is always a safe over-approximation
204+ * thanks to monotonicity of initialization states.
205+ *
206+ * If the heap is unstable in an iteration, the cache should also be
207+ * unstable. This is because all values stored in the heap are also present
208+ * in the cache. Therefore, we only need to track whether the cache is
209+ * stable between two iterations.
210+ *
211+ * The heap is not part of the fixed point computation -- we throw the
212+ * unstable heap from last iteration away. In contrast, we use the unstable
213+ * output cache from the last iteration as input for the next iteration.
214+ * This is safe because the heap is determined by the cache -- it is a
215+ * "local" data to the computing function, conceptually. Local data is
216+ * always safe be discarded.
217+ *
218+ * Now, if a fixed point is reached, the local data contains stable data
219+ * that could be reused to check other classes. We employ this trick to
220+ * improve performance of the analysis.
209221 */
210222 private type ExprValueCache = Map [Value , Map [TreeWrapper , Value ]]
211223
@@ -323,7 +335,7 @@ object Semantic:
323335 *
324336 * It assumes the value is `Hot` if it doesn't exist in the last cache.
325337 *
326- * It update the current caches if the values change.
338+ * It updates the current caches if the values change.
327339 *
328340 * The two caches are required because we want to make sure in a new iteration, an expression is evaluated once.
329341 */
0 commit comments