main
md 84 lines 4.6 KB
Rendered Raw
1 # analyseFunctions
2
3 ## File
4 `src/Inference/AnalyseFunctions.ts`
5
6 ## Purpose
7 Recursively analyzes all nested function expressions and object methods in a function to infer their aliasing effect signatures, which describe how the function affects its captured variables when invoked.
8
9 ## Input Invariants
10 - The HIR has been through SSA conversion and type inference
11 - FunctionExpression and ObjectMethod instructions have an empty `aliasingEffects` array (`@aliasingEffects=[]`)
12 - Context variables (captured variables from outer scope) exist on `fn.context` but do not have their effect populated
13
14 ## Output Guarantees
15 - Every FunctionExpression and ObjectMethod has its `aliasingEffects` array populated with the effects the function performs when called (mutations, captures, aliasing to return value, etc.)
16 - Each context variable's `effect` property is set to either `Effect.Capture` (if the variable is captured or mutated by the inner function) or `Effect.Read` (if only read)
17 - Context variable mutable ranges are reset to `{start: 0, end: 0}` and scopes are set to `null` to prepare for the outer function's subsequent `inferMutationAliasingRanges` pass
18
19 ## Algorithm
20 1. **Recursive traversal**: Iterates through all blocks and instructions looking for `FunctionExpression` or `ObjectMethod` instructions
21 2. **Depth-first processing**: For each function expression found, calls `lowerWithMutationAliasing()` which:
22 - Recursively calls `analyseFunctions()` on the inner function (handles nested functions)
23 - Runs `inferMutationAliasingEffects()` on the inner function to determine effects
24 - Runs `deadCodeElimination()` to clean up
25 - Runs `inferMutationAliasingRanges()` to compute mutable ranges and extract externally-visible effects
26 - Runs `rewriteInstructionKindsBasedOnReassignment()` and `inferReactiveScopeVariables()`
27 - Stores the computed effects in `fn.aliasingEffects`
28 3. **Context variable effect classification**: Scans the computed effects to determine which context variables are captured/mutated vs only read:
29 - Effects like `Capture`, `Alias`, `Assign`, `MaybeAlias`, `CreateFrom` mark the source as captured
30 - Mutation effects (`Mutate`, `MutateTransitive`, etc.) mark the target as captured
31 - Sets `operand.effect = Effect.Capture` or `Effect.Read` accordingly
32 4. **Range reset**: Resets mutable ranges and scopes on context variables to prepare for outer function analysis
33
34 ## Key Data Structures
35 - **HIRFunction.aliasingEffects**: Array of `AliasingEffect` storing the externally-visible behavior of a function when called
36 - **Place.effect**: Effect enum value (`Capture` or `Read`) describing how a context variable is used
37 - **AliasingEffect**: Union type describing data flow (Capture, Alias, Assign, etc.) and mutations (Mutate, MutateTransitive, etc.)
38 - **FunctionExpression/ObjectMethod.loweredFunc.func**: The inner HIRFunction to analyze
39
40 ## Edge Cases
41 - **Nested functions**: Handled via recursive call to `analyseFunctions()` before processing the current function - innermost functions are analyzed first
42 - **ObjectMethod**: Treated identically to FunctionExpression
43 - **Apply effects invariant**: The pass asserts that no `Apply` effects remain in the function's signature - these should have been resolved to more precise effects by `inferMutationAliasingRanges()`
44 - **Conditional mutations**: Effects like `MutateTransitiveConditionally` are tracked - a function that conditionally mutates a captured variable will have that effect in its signature
45 - **Immutable captures**: `ImmutableCapture`, `Freeze`, `Create`, `Impure`, `Render` effects do not contribute to marking context variables as `Capture`
46
47 ## TODOs
48 - No TODO comments in the pass itself
49
50 ## Example
51 Consider a function that captures and conditionally mutates a variable:
52
53 ```javascript
54 function useHook(a, b) {
55 let z = {a};
56 let y = b;
57 let x = function () {
58 if (y) {
59 maybeMutate(z); // Unknown function, may mutate z
60 }
61 };
62 return x;
63 }
64 ```
65
66 **Before AnalyseFunctions:**
67 ```
68 Function @context[y$28, z$25] @aliasingEffects=[]
69 ```
70
71 **After AnalyseFunctions:**
72 ```
73 Function @context[read y$28, capture z$25] @aliasingEffects=[
74 MutateTransitiveConditionally z$25,
75 Create $14 = primitive
76 ]
77 ```
78
79 The pass infers:
80 - `y` is only read (used in the condition)
81 - `z` is captured into the function and conditionally mutated transitively (because `maybeMutate()` is unknown)
82 - The inner function's signature includes `MutateTransitiveConditionally z$25` to indicate this potential mutation
83
84 This signature is then used by `InferMutationAliasingEffects` on the outer function to understand that creating this function captures `z`, and calling the function may mutate `z`.