# React Compiler Knowledge Base This document contains knowledge about the React Compiler gathered during development sessions. It serves as a reference for understanding the codebase architecture and key concepts. ## Project Structure When modifying the compiler, you MUST read the documentation about that pass in `compiler/packages/babel-plugin-react-compiler/docs/passes/` to learn more about the role of that pass within the compiler. - `packages/babel-plugin-react-compiler/` - Main compiler package - `src/HIR/` - High-level Intermediate Representation types and utilities - `src/Inference/` - Effect inference passes (aliasing, mutation, etc.) - `src/Validation/` - Validation passes that check for errors - `src/Entrypoint/Pipeline.ts` - Main compilation pipeline with pass ordering - `src/__tests__/fixtures/compiler/` - Test fixtures - `error.todo-*.js` - Unsupported feature, correctly throws Todo error (graceful bailout) - `error.bug-*.js` - Known bug, throws wrong error type or incorrect behavior - `*.expect.md` - Expected output for each fixture ## Running Tests ```bash # Run all tests yarn snap # Run tests matching a pattern # Example: yarn snap -p 'error.*' yarn snap -p # Run a single fixture in debug mode. Use the path relative to the __tests__/fixtures/compiler directory # For each step of compilation, outputs the step name and state of the compiled program # Example: yarn snap -p simple.js -d yarn snap -p -d # Update fixture outputs (also works with -p) yarn snap -u ``` ## Linting ```bash # Run lint on the compiler source yarn workspace babel-plugin-react-compiler lint ``` ## Formatting ```bash # Run prettier on all files (from the react root directory, not compiler/) yarn prettier-all ``` ## Compiling Arbitrary Files Use `yarn snap compile` to compile any file (not just fixtures) with the React Compiler: ```bash # Compile a file and see the output yarn snap compile # Compile with debug logging to see the state after each compiler pass # This is an alternative to `yarn snap -d -p ` when you don't have a fixture file yet yarn snap compile --debug ``` ## Minimizing Test Cases Use `yarn snap minimize` to automatically reduce a failing test case to its minimal reproduction: ```bash # Minimize a file that causes a compiler error yarn snap minimize # Minimize and update the file in-place with the minimized version yarn snap minimize --update ``` ## Version Control This repository uses Sapling (`sl`) for version control. Sapling is similar to Mercurial: there is not staging area, but new/deleted files must be explicitly added/removed. ```bash # Check status sl status # Add new files, remove deleted files sl addremove # Commit all changes sl commit -m "Your commit message" # Commit with multi-line message using heredoc sl commit -m "$(cat <<'EOF' Summary line Detailed description here EOF )" ``` ## Key Concepts ### HIR (High-level Intermediate Representation) The compiler converts source code to HIR for analysis. Key types in `src/HIR/HIR.ts`: - **HIRFunction** - A function being compiled - `body.blocks` - Map of BasicBlocks - `context` - Captured variables from outer scope - `params` - Function parameters - `returns` - The function's return place - `aliasingEffects` - Effects that describe the function's behavior when called - **Instruction** - A single operation - `lvalue` - The place being assigned to - `value` - The instruction kind (CallExpression, FunctionExpression, LoadLocal, etc.) - `effects` - Array of AliasingEffects for this instruction - **Terminal** - Block terminators (return, branch, etc.) - `effects` - Array of AliasingEffects - **Place** - A reference to a value - `identifier.id` - Unique IdentifierId - **Phi nodes** - Join points for values from different control flow paths - Located at `block.phis` - `phi.place` - The result place - `phi.operands` - Map of predecessor block to source place ### AliasingEffects System Effects describe data flow and operations. Defined in `src/Inference/AliasingEffects.ts`: **Data Flow Effects:** - `Impure` - Marks a place as containing an impure value (e.g., Date.now() result, ref.current) - `Capture a -> b` - Value from `a` is captured into `b` (mutable capture) - `Alias a -> b` - `b` aliases `a` - `ImmutableCapture a -> b` - Immutable capture (like Capture but read-only) - `Assign a -> b` - Direct assignment - `MaybeAlias a -> b` - Possible aliasing - `CreateFrom a -> b` - Created from source **Mutation Effects:** - `Mutate value` - Value is mutated - `MutateTransitive value` - Value and transitive captures are mutated - `MutateConditionally value` - May mutate - `MutateTransitiveConditionally value` - May mutate transitively **Other Effects:** - `Render place` - Place is used in render context (JSX props, component return) - `Freeze place` - Place is frozen (made immutable) - `Create place` - New value created - `CreateFunction` - Function expression created, includes `captures` array - `Apply` - Function application with receiver, function, args, and result ### Hook Aliasing Signatures Located in `src/HIR/Globals.ts`, hooks can define custom aliasing signatures to control how data flows through them. **Structure:** ```typescript aliasing: { receiver: '@receiver', // The hook function itself params: ['@param0'], // Named positional parameters rest: '@rest', // Rest parameters (or null) returns: '@returns', // Return value temporaries: [], // Temporary values during execution effects: [ // Array of effects to apply when hook is called {kind: 'Freeze', value: '@param0', reason: ValueReason.HookCaptured}, {kind: 'Assign', from: '@param0', into: '@returns'}, ], } ``` **Common patterns:** 1. **RenderHookAliasing** (useState, useContext, useMemo, useCallback): - Freezes arguments (`Freeze @rest`) - Marks arguments as render-time (`Render @rest`) - Creates frozen return value - Aliases arguments to return 2. **EffectHookAliasing** (useEffect, useLayoutEffect, useInsertionEffect): - Freezes function and deps - Creates internal effect object - Captures function and deps into effect - Returns undefined 3. **Event handler hooks** (useEffectEvent): - Freezes callback (`Freeze @fn`) - Aliases input to return (`Assign @fn -> @returns`) - NO Render effect (callback not called during render) **Example: useEffectEvent** ```typescript const UseEffectEventHook = addHook( DEFAULT_SHAPES, { positionalParams: [Effect.Freeze], // Takes one positional param restParam: null, returnType: {kind: 'Function', ...}, calleeEffect: Effect.Read, hookKind: 'useEffectEvent', returnValueKind: ValueKind.Frozen, aliasing: { receiver: '@receiver', params: ['@fn'], // Name for the callback parameter rest: null, returns: '@returns', temporaries: [], effects: [ {kind: 'Freeze', value: '@fn', reason: ValueReason.HookCaptured}, {kind: 'Assign', from: '@fn', into: '@returns'}, // Note: NO Render effect - callback is not called during render ], }, }, BuiltInUseEffectEventId, ); // Add as both names for compatibility ['useEffectEvent', UseEffectEventHook], ['experimental_useEffectEvent', UseEffectEventHook], ``` **Key insight:** If a hook is missing an `aliasing` config, it falls back to `DefaultNonmutatingHook` which includes a `Render` effect on all arguments. This can cause false positives for hooks like `useEffectEvent` whose callbacks are not called during render. ## Feature Flags Feature flags are configured in `src/HIR/Environment.ts`, for example `enableJsxOutlining`. Test fixtures can override the active feature flags used for that fixture via a comment pragma on the first line of the fixture input, for example: ```javascript // enableJsxOutlining @enableNameAnonymousFunctions:false ...code... ``` Would enable the `enableJsxOutlining` feature and disable the `enableNameAnonymousFunctions` feature. ## Debugging Tips 1. Run `yarn snap -p ` to see full HIR output with effects 2. Look for `@aliasingEffects=` on FunctionExpressions 3. Look for `Impure`, `Render`, `Capture` effects on instructions 4. Check the pass ordering in Pipeline.ts to understand when effects are populated vs validated ## Error Handling and Fault Tolerance The compiler is fault-tolerant: it runs all passes and accumulates errors on the `Environment` rather than throwing on the first error. This lets users see all compilation errors at once. **Recording errors** — Passes record errors via `env.recordError(diagnostic)`. Errors are accumulated on `Environment.#errors` and checked at the end of the pipeline via `env.hasErrors()` / `env.aggregateErrors()`. **`tryRecord()` wrapper** — In Pipeline.ts, validation passes are wrapped in `env.tryRecord(() => pass(hir))` which catches thrown `CompilerError`s (non-invariant) and records them. Infrastructure/transformation passes are NOT wrapped in `tryRecord()` because later passes depend on their output being structurally valid. **Error categories:** - `CompilerError.throwTodo()` — Unsupported but known pattern. Graceful bailout. Can be caught by `tryRecord()`. - `CompilerError.invariant()` — Truly unexpected/invalid state. Always throws immediately, never caught by `tryRecord()`. - Non-`CompilerError` exceptions — Always re-thrown. **Key files:** `Environment.ts` (`recordError`, `tryRecord`, `hasErrors`, `aggregateErrors`), `Pipeline.ts` (pass orchestration), `Program.ts` (`tryCompileFunction` handles the `Result`). **Test fixtures:** `__tests__/fixtures/compiler/fault-tolerance/` contains multi-error fixtures verifying all errors are reported.