| 1 | # React Compiler Passes Documentation |
| 2 | |
| 3 | This directory contains detailed documentation for each pass in the React Compiler pipeline. The compiler transforms React components and hooks to add automatic memoization. |
| 4 | |
| 5 | ## High-Level Architecture |
| 6 | |
| 7 | ``` |
| 8 | ┌─────────────────────────────────────────────────────────────┐ |
| 9 | │ COMPILATION PIPELINE │ |
| 10 | └─────────────────────────────────────────────────────────────┘ |
| 11 | │ |
| 12 | ▼ |
| 13 | ┌─────────────────────────────────────────────────────────────────────────────────────┐ |
| 14 | │ PHASE 1: HIR CONSTRUCTION │ |
| 15 | │ ┌─────────┐ │ |
| 16 | │ │ Babel │──▶ lower ──▶ enterSSA ──▶ eliminateRedundantPhi │ |
| 17 | │ │ AST │ │ │ |
| 18 | │ └─────────┘ ▼ │ |
| 19 | │ ┌──────────┐ │ |
| 20 | │ │ HIR │ (Control Flow Graph in SSA Form) │ |
| 21 | │ └──────────┘ │ |
| 22 | └─────────────────────────────────────────────────────────────────────────────────────┘ |
| 23 | │ |
| 24 | ▼ |
| 25 | ┌─────────────────────────────────────────────────────────────────────────────────────┐ |
| 26 | │ PHASE 2: OPTIMIZATION │ |
| 27 | │ │ |
| 28 | │ constantPropagation ──▶ deadCodeElimination │ |
| 29 | │ │ |
| 30 | └─────────────────────────────────────────────────────────────────────────────────────┘ |
| 31 | │ |
| 32 | ▼ |
| 33 | ┌─────────────────────────────────────────────────────────────────────────────────────┐ |
| 34 | │ PHASE 3: TYPE & EFFECT INFERENCE │ |
| 35 | │ │ |
| 36 | │ inferTypes ──▶ analyseFunctions ──▶ inferMutationAliasingEffects │ |
| 37 | │ │ │ |
| 38 | │ ▼ │ |
| 39 | │ inferMutationAliasingRanges ──▶ inferReactivePlaces │ |
| 40 | │ │ |
| 41 | └─────────────────────────────────────────────────────────────────────────────────────┘ |
| 42 | │ |
| 43 | ▼ |
| 44 | ┌─────────────────────────────────────────────────────────────────────────────────────┐ |
| 45 | │ PHASE 4: REACTIVE SCOPE CONSTRUCTION │ |
| 46 | │ │ |
| 47 | │ inferReactiveScopeVariables ──▶ alignMethodCallScopes ──▶ alignObjectMethodScopes │ |
| 48 | │ │ │ |
| 49 | │ ▼ │ |
| 50 | │ alignReactiveScopesToBlockScopesHIR ──▶ mergeOverlappingReactiveScopesHIR │ |
| 51 | │ │ │ |
| 52 | │ ▼ │ |
| 53 | │ buildReactiveScopeTerminalsHIR ──▶ flattenReactiveLoopsHIR │ |
| 54 | │ │ │ |
| 55 | │ ▼ │ |
| 56 | │ flattenScopesWithHooksOrUseHIR ──▶ propagateScopeDependenciesHIR │ |
| 57 | │ │ |
| 58 | └─────────────────────────────────────────────────────────────────────────────────────┘ |
| 59 | │ |
| 60 | ▼ |
| 61 | ┌─────────────────────────────────────────────────────────────────────────────────────┐ |
| 62 | │ PHASE 5: HIR → REACTIVE FUNCTION │ |
| 63 | │ │ |
| 64 | │ buildReactiveFunction │ |
| 65 | │ │ │ |
| 66 | │ ▼ │ |
| 67 | │ ┌───────────────────┐ │ |
| 68 | │ │ ReactiveFunction │ (Tree Structure) │ |
| 69 | │ └───────────────────┘ │ |
| 70 | │ │ |
| 71 | └─────────────────────────────────────────────────────────────────────────────────────┘ |
| 72 | │ |
| 73 | ▼ |
| 74 | ┌─────────────────────────────────────────────────────────────────────────────────────┐ |
| 75 | │ PHASE 6: REACTIVE FUNCTION OPTIMIZATION │ |
| 76 | │ │ |
| 77 | │ pruneUnusedLabels ──▶ pruneNonEscapingScopes ──▶ pruneNonReactiveDependencies │ |
| 78 | │ │ │ |
| 79 | │ ▼ │ |
| 80 | │ pruneUnusedScopes ──▶ mergeReactiveScopesThatInvalidateTogether │ |
| 81 | │ │ │ |
| 82 | │ ▼ │ |
| 83 | │ pruneAlwaysInvalidatingScopes ──▶ propagateEarlyReturns ──▶ promoteUsedTemporaries │ |
| 84 | │ │ |
| 85 | └─────────────────────────────────────────────────────────────────────────────────────┘ |
| 86 | │ |
| 87 | ▼ |
| 88 | ┌─────────────────────────────────────────────────────────────────────────────────────┐ |
| 89 | │ PHASE 7: CODE GENERATION │ |
| 90 | │ │ |
| 91 | │ renameVariables ──▶ codegenReactiveFunction │ |
| 92 | │ │ │ |
| 93 | │ ▼ │ |
| 94 | │ ┌─────────────┐ │ |
| 95 | │ │ Babel AST │ (With Memoization) │ |
| 96 | │ └─────────────┘ │ |
| 97 | │ │ |
| 98 | └─────────────────────────────────────────────────────────────────────────────────────┘ |
| 99 | ``` |
| 100 | |
| 101 | ## Pass Categories |
| 102 | |
| 103 | ### HIR Construction & SSA (1-3) |
| 104 | |
| 105 | | # | Pass | File | Description | |
| 106 | |---|------|------|-------------| |
| 107 | | 1 | [lower](01-lower.md) | `HIR/BuildHIR.ts` | Convert Babel AST to HIR control-flow graph | |
| 108 | | 2 | [enterSSA](02-enterSSA.md) | `SSA/EnterSSA.ts` | Convert to Static Single Assignment form | |
| 109 | | 3 | [eliminateRedundantPhi](03-eliminateRedundantPhi.md) | `SSA/EliminateRedundantPhi.ts` | Remove unnecessary phi nodes | |
| 110 | |
| 111 | ### Optimization (4-5) |
| 112 | |
| 113 | | # | Pass | File | Description | |
| 114 | |---|------|------|-------------| |
| 115 | | 4 | [constantPropagation](04-constantPropagation.md) | `Optimization/ConstantPropagation.ts` | Sparse conditional constant propagation | |
| 116 | | 5 | [deadCodeElimination](05-deadCodeElimination.md) | `Optimization/DeadCodeElimination.ts` | Remove unreferenced instructions | |
| 117 | |
| 118 | ### Type Inference (6) |
| 119 | |
| 120 | | # | Pass | File | Description | |
| 121 | |---|------|------|-------------| |
| 122 | | 6 | [inferTypes](06-inferTypes.md) | `TypeInference/InferTypes.ts` | Constraint-based type unification | |
| 123 | |
| 124 | ### Mutation/Aliasing Inference (7-10) |
| 125 | |
| 126 | | # | Pass | File | Description | |
| 127 | |---|------|------|-------------| |
| 128 | | 7 | [analyseFunctions](07-analyseFunctions.md) | `Inference/AnalyseFunctions.ts` | Analyze nested function effects | |
| 129 | | 8 | [inferMutationAliasingEffects](08-inferMutationAliasingEffects.md) | `Inference/InferMutationAliasingEffects.ts` | Infer mutation/aliasing via abstract interpretation | |
| 130 | | 9 | [inferMutationAliasingRanges](09-inferMutationAliasingRanges.md) | `Inference/InferMutationAliasingRanges.ts` | Compute mutable ranges from effects | |
| 131 | | 10 | [inferReactivePlaces](10-inferReactivePlaces.md) | `Inference/InferReactivePlaces.ts` | Mark reactive places (props, hooks, derived) | |
| 132 | |
| 133 | ### Reactive Scope Variables (11-12) |
| 134 | |
| 135 | | # | Pass | File | Description | |
| 136 | |---|------|------|-------------| |
| 137 | | 11 | [inferReactiveScopeVariables](11-inferReactiveScopeVariables.md) | `ReactiveScopes/InferReactiveScopeVariables.ts` | Group co-mutating variables into scopes | |
| 138 | | 12 | [rewriteInstructionKindsBasedOnReassignment](12-rewriteInstructionKindsBasedOnReassignment.md) | `SSA/RewriteInstructionKindsBasedOnReassignment.ts` | Convert SSA loads to context loads for reassigned vars | |
| 139 | |
| 140 | ### Scope Alignment (13-15) |
| 141 | |
| 142 | | # | Pass | File | Description | |
| 143 | |---|------|------|-------------| |
| 144 | | 13 | [alignMethodCallScopes](13-alignMethodCallScopes.md) | `ReactiveScopes/AlignMethodCallScopes.ts` | Align method call scopes with receivers | |
| 145 | | 14 | [alignObjectMethodScopes](14-alignObjectMethodScopes.md) | `ReactiveScopes/AlignObjectMethodScopes.ts` | Align object method scopes | |
| 146 | | 15 | [alignReactiveScopesToBlockScopesHIR](15-alignReactiveScopesToBlockScopesHIR.md) | `ReactiveScopes/AlignReactiveScopesToBlockScopesHIR.ts` | Align to control-flow block boundaries | |
| 147 | |
| 148 | ### Scope Construction (16-18) |
| 149 | |
| 150 | | # | Pass | File | Description | |
| 151 | |---|------|------|-------------| |
| 152 | | 16 | [mergeOverlappingReactiveScopesHIR](16-mergeOverlappingReactiveScopesHIR.md) | `HIR/MergeOverlappingReactiveScopesHIR.ts` | Merge overlapping scopes | |
| 153 | | 17 | [buildReactiveScopeTerminalsHIR](17-buildReactiveScopeTerminalsHIR.md) | `HIR/BuildReactiveScopeTerminalsHIR.ts` | Insert scope terminals into CFG | |
| 154 | | 18 | [flattenReactiveLoopsHIR](18-flattenReactiveLoopsHIR.md) | `ReactiveScopes/FlattenReactiveLoopsHIR.ts` | Prune scopes inside loops | |
| 155 | |
| 156 | ### Scope Flattening & Dependencies (19-20) |
| 157 | |
| 158 | | # | Pass | File | Description | |
| 159 | |---|------|------|-------------| |
| 160 | | 19 | [flattenScopesWithHooksOrUseHIR](19-flattenScopesWithHooksOrUseHIR.md) | `ReactiveScopes/FlattenScopesWithHooksOrUseHIR.ts` | Prune scopes containing hooks | |
| 161 | | 20 | [propagateScopeDependenciesHIR](20-propagateScopeDependenciesHIR.md) | `HIR/PropagateScopeDependenciesHIR.ts` | Derive minimal scope dependencies | |
| 162 | |
| 163 | ### HIR → Reactive Conversion (21) |
| 164 | |
| 165 | | # | Pass | File | Description | |
| 166 | |---|------|------|-------------| |
| 167 | | 21 | [buildReactiveFunction](21-buildReactiveFunction.md) | `ReactiveScopes/BuildReactiveFunction.ts` | Convert CFG to tree structure | |
| 168 | |
| 169 | ### Reactive Function Pruning (22-25) |
| 170 | |
| 171 | | # | Pass | File | Description | |
| 172 | |---|------|------|-------------| |
| 173 | | 22 | [pruneUnusedLabels](22-pruneUnusedLabels.md) | `ReactiveScopes/PruneUnusedLabels.ts` | Remove unused labels | |
| 174 | | 23 | [pruneNonEscapingScopes](23-pruneNonEscapingScopes.md) | `ReactiveScopes/PruneNonEscapingScopes.ts` | Remove non-escaping scopes | |
| 175 | | 24 | [pruneNonReactiveDependencies](24-pruneNonReactiveDependencies.md) | `ReactiveScopes/PruneNonReactiveDependencies.ts` | Remove non-reactive dependencies | |
| 176 | | 25 | [pruneUnusedScopes](25-pruneUnusedScopes.md) | `ReactiveScopes/PruneUnusedScopes.ts` | Remove empty scopes | |
| 177 | |
| 178 | ### Scope Optimization (26-28) |
| 179 | |
| 180 | | # | Pass | File | Description | |
| 181 | |---|------|------|-------------| |
| 182 | | 26 | [mergeReactiveScopesThatInvalidateTogether](26-mergeReactiveScopesThatInvalidateTogether.md) | `ReactiveScopes/MergeReactiveScopesThatInvalidateTogether.ts` | Merge co-invalidating scopes | |
| 183 | | 27 | [pruneAlwaysInvalidatingScopes](27-pruneAlwaysInvalidatingScopes.md) | `ReactiveScopes/PruneAlwaysInvalidatingScopes.ts` | Prune always-invalidating scopes | |
| 184 | | 28 | [propagateEarlyReturns](28-propagateEarlyReturns.md) | `ReactiveScopes/PropagateEarlyReturns.ts` | Handle early returns in scopes | |
| 185 | |
| 186 | ### Codegen Preparation (29-31) |
| 187 | |
| 188 | | # | Pass | File | Description | |
| 189 | |---|------|------|-------------| |
| 190 | | 29 | [promoteUsedTemporaries](29-promoteUsedTemporaries.md) | `ReactiveScopes/PromoteUsedTemporaries.ts` | Promote temps to named vars | |
| 191 | | 30 | [renameVariables](30-renameVariables.md) | `ReactiveScopes/RenameVariables.ts` | Ensure unique variable names | |
| 192 | | 31 | [codegenReactiveFunction](31-codegenReactiveFunction.md) | `ReactiveScopes/CodegenReactiveFunction.ts` | Generate final Babel AST | |
| 193 | |
| 194 | ### Transformations (32-38) |
| 195 | |
| 196 | | # | Pass | File | Description | |
| 197 | |---|------|------|-------------| |
| 198 | | 34 | [optimizePropsMethodCalls](34-optimizePropsMethodCalls.md) | `Optimization/OptimizePropsMethodCalls.ts` | Normalize props method calls | |
| 199 | | 35 | [optimizeForSSR](35-optimizeForSSR.md) | `Optimization/OptimizeForSSR.ts` | SSR-specific optimizations | |
| 200 | | 36 | [outlineJSX](36-outlineJSX.md) | `Optimization/OutlineJsx.ts` | Outline JSX to components | |
| 201 | | 37 | [outlineFunctions](37-outlineFunctions.md) | `Optimization/OutlineFunctions.ts` | Outline pure functions | |
| 202 | | 38 | [memoizeFbtAndMacroOperandsInSameScope](38-memoizeFbtAndMacroOperandsInSameScope.md) | `ReactiveScopes/MemoizeFbtAndMacroOperandsInSameScope.ts` | Keep FBT operands together | |
| 203 | |
| 204 | ### Validation (39-55) |
| 205 | |
| 206 | | # | Pass | File | Description | |
| 207 | |---|------|------|-------------| |
| 208 | | 39 | [validateContextVariableLValues](39-validateContextVariableLValues.md) | `Validation/ValidateContextVariableLValues.ts` | Variable reference consistency | |
| 209 | | 40 | [validateUseMemo](40-validateUseMemo.md) | `Validation/ValidateUseMemo.ts` | useMemo callback requirements | |
| 210 | | 41 | [validateHooksUsage](41-validateHooksUsage.md) | `Validation/ValidateHooksUsage.ts` | Rules of Hooks | |
| 211 | | 42 | [validateNoCapitalizedCalls](42-validateNoCapitalizedCalls.md) | `Validation/ValidateNoCapitalizedCalls.ts` | Component vs function calls | |
| 212 | | 43 | [validateLocalsNotReassignedAfterRender](43-validateLocalsNotReassignedAfterRender.md) | `Validation/ValidateLocalsNotReassignedAfterRender.ts` | Variable mutation safety | |
| 213 | | 44 | [validateNoSetStateInRender](44-validateNoSetStateInRender.md) | `Validation/ValidateNoSetStateInRender.ts` | No setState during render | |
| 214 | | 45 | [validateNoDerivedComputationsInEffects](45-validateNoDerivedComputationsInEffects.md) | `Validation/ValidateNoDerivedComputationsInEffects.ts` | Effect optimization hints | |
| 215 | | 46 | [validateNoSetStateInEffects](46-validateNoSetStateInEffects.md) | `Validation/ValidateNoSetStateInEffects.ts` | Effect performance | |
| 216 | | 47 | [validateNoJSXInTryStatement](47-validateNoJSXInTryStatement.md) | `Validation/ValidateNoJSXInTryStatement.ts` | Error boundary usage | |
| 217 | | 48 | [validateNoImpureValuesInRender](48-validateNoImpureValuesInRender.md) | `Validation/ValidateNoImpureValuesInRender.ts` | Impure value isolation | |
| 218 | | 49 | [validateNoRefAccessInRender](49-validateNoRefAccessInRender.md) | `Validation/ValidateNoRefAccessInRender.ts` | Ref access constraints | |
| 219 | | 50 | [validateNoFreezingKnownMutableFunctions](50-validateNoFreezingKnownMutableFunctions.md) | `Validation/ValidateNoFreezingKnownMutableFunctions.ts` | Mutable function isolation | |
| 220 | | 51 | [validateExhaustiveDependencies](51-validateExhaustiveDependencies.md) | `Validation/ValidateExhaustiveDependencies.ts` | Dependency array completeness | |
| 221 | | 53 | [validatePreservedManualMemoization](53-validatePreservedManualMemoization.md) | `Validation/ValidatePreservedManualMemoization.ts` | Manual memo preservation | |
| 222 | | 54 | [validateStaticComponents](54-validateStaticComponents.md) | `Validation/ValidateStaticComponents.ts` | Component identity stability | |
| 223 | | 55 | [validateSourceLocations](55-validateSourceLocations.md) | `Validation/ValidateSourceLocations.ts` | Source location preservation | |
| 224 | |
| 225 | ## Key Data Structures |
| 226 | |
| 227 | ### HIR (High-level Intermediate Representation) |
| 228 | |
| 229 | The compiler converts source code to HIR for analysis. Key types: |
| 230 | |
| 231 | - **HIRFunction**: A function being compiled |
| 232 | - `body.blocks`: Map of BasicBlocks (control flow graph) |
| 233 | - `context`: Captured variables from outer scope |
| 234 | - `params`: Function parameters |
| 235 | - `returns`: The function's return place |
| 236 | |
| 237 | - **BasicBlock**: A sequence of instructions with a terminal |
| 238 | - `instructions`: Array of Instructions |
| 239 | - `terminal`: Control flow (return, branch, etc.) |
| 240 | - `phis`: Phi nodes for SSA |
| 241 | |
| 242 | - **Instruction**: A single operation |
| 243 | - `lvalue`: The place being assigned to |
| 244 | - `value`: The instruction kind (CallExpression, FunctionExpression, etc.) |
| 245 | - `effects`: Array of AliasingEffects |
| 246 | |
| 247 | - **Place**: A reference to a value |
| 248 | - `identifier.id`: Unique IdentifierId |
| 249 | - `effect`: How the place is used (read, mutate, etc.) |
| 250 | |
| 251 | ### ReactiveFunction |
| 252 | |
| 253 | After HIR is analyzed, it's converted to ReactiveFunction: |
| 254 | |
| 255 | - Tree structure instead of CFG |
| 256 | - Contains ReactiveScopes that define memoization boundaries |
| 257 | - Each scope has dependencies and declarations |
| 258 | |
| 259 | ### AliasingEffects |
| 260 | |
| 261 | Effects describe data flow and operations: |
| 262 | |
| 263 | - **Capture/Alias**: Value relationships |
| 264 | - **Mutate/MutateTransitive**: Mutation tracking |
| 265 | - **Freeze**: Immutability marking |
| 266 | - **Render**: JSX usage context |
| 267 | - **Create/CreateFunction**: Value creation |
| 268 | |
| 269 | ## Feature Flags |
| 270 | |
| 271 | Many passes are controlled by feature flags in `Environment.ts`: |
| 272 | |
| 273 | | Flag | Enables Pass | |
| 274 | |------|--------------| |
| 275 | | `enableJsxOutlining` | outlineJSX | |
| 276 | | `enableFunctionOutlining` | outlineFunctions | |
| 277 | | `validateNoSetStateInRender` | validateNoSetStateInRender | |
| 278 | | `enableUseMemoCacheInterop` | Preserves manual memoization | |
| 279 | |
| 280 | ## Running Tests |
| 281 | |
| 282 | ```bash |
| 283 | # Run all tests |
| 284 | yarn snap |
| 285 | |
| 286 | # Run specific fixture |
| 287 | yarn snap -p <fixture-name> |
| 288 | |
| 289 | # Run with debug output (shows all passes) |
| 290 | yarn snap -p <fixture-name> -d |
| 291 | |
| 292 | # Compile any file (not just fixtures) and see output |
| 293 | yarn snap compile <path> |
| 294 | |
| 295 | # Compile any file with debug output (alternative to yarn snap -d -p when you don't have a fixture) |
| 296 | yarn snap compile --debug <path> |
| 297 | |
| 298 | # Minimize a failing test case to its minimal reproduction |
| 299 | yarn snap minimize <path> |
| 300 | |
| 301 | # Update expected outputs |
| 302 | yarn snap -u |
| 303 | ``` |
| 304 | |
| 305 | ## Fault Tolerance |
| 306 | |
| 307 | The pipeline is fault-tolerant: all passes run to completion, accumulating errors on `Environment` rather than aborting on the first error. |
| 308 | |
| 309 | - **Validation passes** are wrapped in `env.tryRecord()` in Pipeline.ts, which catches non-invariant `CompilerError`s and records them. If a validation pass throws, compilation continues. |
| 310 | - **Infrastructure/transformation passes** (enterSSA, eliminateRedundantPhi, inferMutationAliasingEffects, codegen, etc.) are NOT wrapped in `tryRecord()` because subsequent passes depend on their output being structurally valid. If they fail, compilation aborts. |
| 311 | - **`lower()` (BuildHIR)** always produces an `HIRFunction`, recording errors on `env` instead of returning `Err`. Unsupported constructs (e.g., `var`) are lowered best-effort. |
| 312 | - At the end of the pipeline, `env.hasErrors()` determines whether to return `Ok(codegen)` or `Err(aggregatedErrors)`. |
| 313 | |
| 314 | ## Further Reading |
| 315 | |
| 316 | - [MUTABILITY_ALIASING_MODEL.md](../../src/Inference/MUTABILITY_ALIASING_MODEL.md): Detailed aliasing model docs |
| 317 | - [Pipeline.ts](../../src/Entrypoint/Pipeline.ts): Pass ordering and orchestration |
| 318 | - [HIR.ts](../../src/HIR/HIR.ts): Core data structure definitions |