compiler/packages/babel-plugin-react-compiler/docs/passes/39-validateContextVariableLValues.md MARKDOWN 193 lines View on github.com → Search inside
1# validateContextVariableLValues23## File4`src/Validation/ValidateContextVariableLValues.ts`56## Purpose7This validation pass ensures that all load/store references to a given named identifier are consistent with the "kind" of that variable (normal local variable or context variable). Context variables are variables that are captured by closures and require special handling for correct closure semantics.89The pass prevents mixing context variable operations (`DeclareContext`, `StoreContext`, `LoadContext`) with local variable operations (`DeclareLocal`, `StoreLocal`, `LoadLocal`, `Destructure`) on the same identifier.1011## Input Invariants12- The function has been lowered to HIR13- All instructions have been categorized by kind14- Nested function expressions have been lowered1516## Validation Rules1718### Rule 1: Consistent Variable Kind19All references to the same identifier must use consistent load/store operations:20- Context variables must only use `DeclareContext`, `StoreContext`, `LoadContext`21- Local variables must only use `DeclareLocal`, `StoreLocal`, `LoadLocal`2223**Error (Invariant violation):**24```25Expected all references to a variable to be consistently local or context references26Identifier [place] is referenced as a [kind] variable, but was previously referenced as a [prev.kind] variable27```2829### Rule 2: No Destructuring of Context Variables30Context variables cannot be destructured using the `Destructure` instruction.3132**Error (Todo):**33```34Support destructuring of context variables35```3637### Rule 3: Unhandled Instruction Variants38If an instruction has lvalues that the pass does not handle, it throws a Todo error.3940**Error (Todo):**41```42ValidateContextVariableLValues: unhandled instruction variant43Handle '[kind]' lvalues44```4546## Algorithm4748### Phase 1: Initialize Tracking49```typescript50const identifierKinds: Map<IdentifierId, {place: Place, kind: 'local' | 'context' | 'destructure'}> = new Map();51```5253### Phase 2: Visit All Instructions54The pass iterates through all blocks and instructions, categorizing each based on its kind:5556```typescript57for (const [, block] of fn.body.blocks) {58  for (const instr of block.instructions) {59    switch (value.kind) {60      case 'DeclareContext':61      case 'StoreContext':62        visit(identifierKinds, value.lvalue.place, 'context');63        break;64      case 'LoadContext':65        visit(identifierKinds, value.place, 'context');66        break;67      case 'StoreLocal':68      case 'DeclareLocal':69        visit(identifierKinds, value.lvalue.place, 'local');70        break;71      case 'LoadLocal':72        visit(identifierKinds, value.place, 'local');73        break;74      case 'PostfixUpdate':75      case 'PrefixUpdate':76        visit(identifierKinds, value.lvalue, 'local');77        break;78      case 'Destructure':79        for (const lvalue of eachPatternOperand(value.lvalue.pattern)) {80          visit(identifierKinds, lvalue, 'destructure');81        }82        break;83      case 'ObjectMethod':84      case 'FunctionExpression':85        // Recursively validate nested functions86        validateContextVariableLValuesImpl(value.loweredFunc.func, identifierKinds);87        break;88    }89  }90}91```9293### Phase 3: Check Consistency94For each place visited, the `visit` function checks if the identifier was previously seen with a different kind:9596```typescript97function visit(identifiers, place, kind) {98  const prev = identifiers.get(place.identifier.id);99  if (prev !== undefined) {100    const wasContext = prev.kind === 'context';101    const isContext = kind === 'context';102    if (wasContext !== isContext) {103      // Check for destructuring of context variable104      if (prev.kind === 'destructure' || kind === 'destructure') {105        CompilerError.throwTodo({106          reason: `Support destructuring of context variables`,107          ...108        });109      }110      // Invariant violation: inconsistent variable kinds111      CompilerError.invariant(false, {112        reason: 'Expected all references to be consistently local or context references',113        ...114      });115    }116  }117  identifiers.set(place.identifier.id, {place, kind});118}119```120121## Edge Cases122123### Nested Function Expressions124The validation recursively processes nested function expressions and object methods, sharing the same `identifierKinds` map. This ensures that a variable captured by a nested function is consistently treated as a context variable throughout the entire function hierarchy.125126### Destructuring Patterns127Each operand in a destructure pattern is visited individually, marked as 'destructure' kind. If the same identifier was previously used as a context variable, a Todo error is thrown since destructuring of context variables is not yet supported.128129### Update Expressions130Both `PostfixUpdate` (e.g., `x++`) and `PrefixUpdate` (e.g., `++x`) are treated as local variable operations.131132## TODOs1331341. **Destructuring of context variables** - Currently not supported:135   ```typescript136   CompilerError.throwTodo({137     reason: `Support destructuring of context variables`,138     ...139   });140   ```1411422. **Unhandled instruction variants** - Some instruction types with lvalues may not be handled:143   ```typescript144   CompilerError.throwTodo({145     reason: 'ValidateContextVariableLValues: unhandled instruction variant',146     description: `Handle '${value.kind}' lvalues`,147     ...148   });149   ```150151## Example152153### Fixture: `error.todo-for-of-loop-with-context-variable-iterator.js`154155**Input:**156```javascript157import {useHook} from 'shared-runtime';158159function Component(props) {160  const data = useHook();161  const items = [];162  // NOTE: `item` is a context variable because it's reassigned and also referenced163  // within a closure, the `onClick` handler of each item164  for (let item of props.data) {165    item = item ?? {}; // reassignment to force a context variable166    items.push(167      <div key={item.id} onClick={() => data.set(item)}>168        {item.id}169      </div>170    );171  }172  return <div>{items}</div>;173}174```175176**Error:**177```178Todo: Support non-trivial for..of inits179180error.todo-for-of-loop-with-context-variable-iterator.ts:8:2181   6 |   // NOTE: `item` is a context variable because it's reassigned and also referenced182   7 |   // within a closure, the `onClick` handler of each item183>  8 |   for (let item of props.data) {184     |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^185>  9 |     item = item ?? {}; // reassignment to force a context variable186     | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^187...188> 15 |   }189     | ^^^^ Support non-trivial for..of inits190```191192Note: This particular error comes from an earlier pass (lowering), but demonstrates the kind of context variable scenarios that this validation is designed to catch.

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.