compiler/packages/babel-plugin-react-compiler/docs/passes/09-inferMutationAliasingRanges.md MARKDOWN 150 lines View on github.com → Search inside
1# inferMutationAliasingRanges23## File4`src/Inference/InferMutationAliasingRanges.ts`56## Purpose7This pass builds an abstract model of the heap and interprets the effects of the given function to determine: (1) the mutable ranges of all identifiers, (2) the externally-visible effects of the function (mutations of params/context-vars, aliasing relationships), and (3) the legacy `Effect` annotation for each Place.89## Input Invariants10- InferMutationAliasingEffects must have already run, populating `instr.effects` on each instruction with aliasing/mutation effects11- SSA form must be established (identifiers are in SSA)12- Type inference has been run (InferTypes)13- Functions have been analyzed (AnalyseFunctions)14- Dead code elimination has been performed1516## Output Guarantees17- Every identifier has a populated `mutableRange` (start:end instruction IDs)18- Every Place has a legacy `Effect` annotation (Read, Capture, Store, Freeze, etc.)19- The function's `aliasingEffects` array is populated with externally-visible effects (mutations of params/context-vars, aliasing between params/context-vars/return)20- Validation errors are collected for invalid effects like `MutateFrozen` or `MutateGlobal`2122## Algorithm23The pass operates in three main phases:2425**Part 1: Build Data Flow Graph and Infer Mutable Ranges**261. Creates an `AliasingState` which maintains a `Node` for each identifier272. Iterates through all blocks and instructions, processing effects in program order283. For each effect:29   - `Create`/`CreateFunction`: Creates a new node in the graph30   - `CreateFrom`/`Assign`/`Alias`: Adds alias edges between nodes (with ordering index)31   - `MaybeAlias`: Adds conditional alias edges32   - `Capture`: Adds capture edges (for transitive mutations)33   - `Mutate*`: Queues mutations for later processing34   - `Render`: Queues render effects for later processing354. Phi node operands are connected once their predecessor blocks have been visited365. After the graph is built, mutations are processed:37   - Mutations propagate both forward (via edges) and backward (via aliases/captures)38   - Each mutation extends the `mutableRange.end` of affected identifiers39   - Transitive mutations also traverse capture edges backward40   - `MaybeAlias` edges downgrade mutations to `Conditional`416. Render effects are processed to mark values as rendered4243**Part 2: Populate Legacy Per-Place Effects**44- Sets legacy effects on lvalues and operands based on instruction effects and mutable ranges45- Fixes up mutable range start values for identifiers that are mutated after creation4647**Part 3: Infer Externally-Visible Function Effects**48- Creates a `Create` effect for the return value49- Simulates transitive mutations of each param/context-var/return to detect capture relationships50- Produces `Alias`/`Capture` effects showing data flow between params/context-vars/return5152## Key Data Structures5354### `AliasingState`55The main state class maintaining the data flow graph:56- `nodes: Map<Identifier, Node>` - Maps identifiers to their graph nodes5758### `Node`59Represents an identifier in the data flow graph:60```typescript61type Node = {62  id: Identifier;63  createdFrom: Map<Identifier, number>;   // CreateFrom edges (source -> index)64  captures: Map<Identifier, number>;       // Capture edges (source -> index)65  aliases: Map<Identifier, number>;        // Alias/Assign edges (source -> index)66  maybeAliases: Map<Identifier, number>;   // MaybeAlias edges (source -> index)67  edges: Array<{index, node, kind}>;       // Forward edges to other nodes68  transitive: {kind: MutationKind; loc} | null;  // Transitive mutation info69  local: {kind: MutationKind; loc} | null;       // Local mutation info70  lastMutated: number;                     // Index of last mutation affecting this node71  mutationReason: MutationReason | null;   // Reason for mutation72  value: {kind: 'Object'} | {kind: 'Phi'} | {kind: 'Function'; function: HIRFunction};73  render: Place | null;                    // Render context if used in JSX74};75```7677### `MutationKind`78Enum describing mutation certainty:79```typescript80enum MutationKind {81  None = 0,82  Conditional = 1,  // May mutate (e.g., via MaybeAlias or MutateConditionally)83  Definite = 2,     // Definitely mutates84}85```8687## Edge Cases8889### Phi Nodes90- Phi nodes are created as special `{kind: 'Phi'}` nodes91- Phi operands from predecessor blocks are processed with pending edges until the predecessor is visited92- When traversing "forwards" through edges and encountering a phi, backward traversal is stopped (prevents mutation from one phi input affecting other inputs)9394### Transitive vs Local Mutations95- Local mutations (`Mutate`) only affect alias/assign edges backward96- Transitive mutations (`MutateTransitive`) also affect capture edges backward97- Both affect all forward edges9899### MaybeAlias100- Mutations through MaybeAlias edges are downgraded to `Conditional`101- This prevents false positive errors when we cannot be certain about aliasing102103### Function Values104- Functions are tracked specially as `{kind: 'Function'}` nodes105- When a function is mutated (transitively), errors from the function body are propagated106- This handles cases where mutating a captured value in a function affects render safety107108### Render Effect Propagation109- Render effects traverse backward through alias/capture/createFrom edges110- Functions that have not been mutated are skipped during render traversal (except for JSX-returning functions)111- Ref types (`isUseRefType`) stop render traversal112113## TODOs1141. Assign effects should have an invariant that the node is not initialized yet. Currently `InferFunctionExpressionAliasingEffectSignatures` infers Assign effects that should be Alias, causing reinitialization.1151162. Phi place effects are not properly set today.1171183. Phi mutable range start calculation is imprecise - currently just sets it to the instruction before the block rather than computing the exact start.119120## Example121122Consider the following code:123```javascript124function foo() {125  let a = {};   // Create a (instruction 1)126  let b = {};   // Create b (instruction 3)127  a = b;        // Assign a <- b (instruction 8)128  mutate(a, b); // MutateTransitiveConditionally a, b (instruction 16)129  return a;130}131```132133The pass builds a graph:1341. Creates node for `{}` at instruction 1 (initially assigned to `a`)1352. Creates node for `{}` at instruction 3 (initially assigned to `b`)1363. At instruction 8, creates alias edge: `b -> a` with index 81374. At instruction 16, mutations are queued for `a` and `b`138139When processing the mutation of `a` at instruction 16:140- Extends `a`'s mutableRange.end to 17141- Traverses backward through alias edge to `b`, extends `b`'s mutableRange.end to 17142- Since `a = b`, both objects must be considered mutable until instruction 17143144The output shows identifiers with range annotations like `$25[3:17]` meaning:145- `$25` is the identifier146- `3` is the instruction where it was created147- `17` is the instruction after which it is no longer mutated148149For aliased values, the ranges are unified - all values that could be affected by a mutation have their ranges extended to include that mutation point.

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.