analyseFunctions
File
src/Inference/AnalyseFunctions.ts
Purpose
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.
Input Invariants
- The HIR has been through SSA conversion and type inference
- FunctionExpression and ObjectMethod instructions have an empty
aliasingEffectsarray (@aliasingEffects=[]) - Context variables (captured variables from outer scope) exist on
fn.contextbut do not have their effect populated
Output Guarantees
- Every FunctionExpression and ObjectMethod has its
aliasingEffectsarray populated with the effects the function performs when called (mutations, captures, aliasing to return value, etc.) - Each context variable's
effectproperty is set to eitherEffect.Capture(if the variable is captured or mutated by the inner function) orEffect.Read(if only read) - Context variable mutable ranges are reset to
{start: 0, end: 0}and scopes are set tonullto prepare for the outer function's subsequentinferMutationAliasingRangespass
Algorithm
- Recursive traversal: Iterates through all blocks and instructions looking for
FunctionExpressionorObjectMethodinstructions - Depth-first processing: For each function expression found, calls
lowerWithMutationAliasing()which:- Recursively calls
analyseFunctions()on the inner function (handles nested functions) - Runs
inferMutationAliasingEffects()on the inner function to determine effects - Runs
deadCodeElimination()to clean up - Runs
inferMutationAliasingRanges()to compute mutable ranges and extract externally-visible effects - Runs
rewriteInstructionKindsBasedOnReassignment()andinferReactiveScopeVariables() - Stores the computed effects in
fn.aliasingEffects
- Recursively calls
- Context variable effect classification: Scans the computed effects to determine which context variables are captured/mutated vs only read:
- Effects like
Capture,Alias,Assign,MaybeAlias,CreateFrommark the source as captured - Mutation effects (
Mutate,MutateTransitive, etc.) mark the target as captured - Sets
operand.effect = Effect.CaptureorEffect.Readaccordingly
- Effects like
- Range reset: Resets mutable ranges and scopes on context variables to prepare for outer function analysis
Key Data Structures
- HIRFunction.aliasingEffects: Array of
AliasingEffectstoring the externally-visible behavior of a function when called - Place.effect: Effect enum value (
CaptureorRead) describing how a context variable is used - AliasingEffect: Union type describing data flow (Capture, Alias, Assign, etc.) and mutations (Mutate, MutateTransitive, etc.)
- FunctionExpression/ObjectMethod.loweredFunc.func: The inner HIRFunction to analyze
Edge Cases
- Nested functions: Handled via recursive call to
analyseFunctions()before processing the current function - innermost functions are analyzed first - ObjectMethod: Treated identically to FunctionExpression
- Apply effects invariant: The pass asserts that no
Applyeffects remain in the function's signature - these should have been resolved to more precise effects byinferMutationAliasingRanges() - Conditional mutations: Effects like
MutateTransitiveConditionallyare tracked - a function that conditionally mutates a captured variable will have that effect in its signature - Immutable captures:
ImmutableCapture,Freeze,Create,Impure,Rendereffects do not contribute to marking context variables asCapture
TODOs
- No TODO comments in the pass itself
Example
Consider a function that captures and conditionally mutates a variable:
function useHook(a, b) {
let z = {a};
let y = b;
let x = function () {
if (y) {
maybeMutate(z); // Unknown function, may mutate z
}
};
return x;
}
Before AnalyseFunctions:
Function @context[y$28, z$25] @aliasingEffects=[]
After AnalyseFunctions:
Function @context[read y$28, capture z$25] @aliasingEffects=[
MutateTransitiveConditionally z$25,
Create $14 = primitive
]
The pass infers:
- y is only read (used in the condition)
- z is captured into the function and conditionally mutated transitively (because maybeMutate() is unknown)
- The inner function's signature includes MutateTransitiveConditionally z$25 to indicate this potential mutation
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.