Skip to content

Commit b5c2ffb

Browse files
committed
More docs
1 parent 1736fb9 commit b5c2ffb

File tree

1 file changed

+40
-28
lines changed

1 file changed

+40
-28
lines changed

compiler/src/dotty/tools/dotc/transform/init/Semantic.scala

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)