main
md 276 lines 10.1 KB
Rendered Raw
1 # React Compiler Knowledge Base
2
3 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.
4
5 ## Project Structure
6
7 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.
8
9 - `packages/babel-plugin-react-compiler/` - Main compiler package
10 - `src/HIR/` - High-level Intermediate Representation types and utilities
11 - `src/Inference/` - Effect inference passes (aliasing, mutation, etc.)
12 - `src/Validation/` - Validation passes that check for errors
13 - `src/Entrypoint/Pipeline.ts` - Main compilation pipeline with pass ordering
14 - `src/__tests__/fixtures/compiler/` - Test fixtures
15 - `error.todo-*.js` - Unsupported feature, correctly throws Todo error (graceful bailout)
16 - `error.bug-*.js` - Known bug, throws wrong error type or incorrect behavior
17 - `*.expect.md` - Expected output for each fixture
18
19 ## Running Tests
20
21 ```bash
22 # Run all tests
23 yarn snap
24
25 # Run tests matching a pattern
26 # Example: yarn snap -p 'error.*'
27 yarn snap -p <pattern>
28
29 # Run a single fixture in debug mode. Use the path relative to the __tests__/fixtures/compiler directory
30 # For each step of compilation, outputs the step name and state of the compiled program
31 # Example: yarn snap -p simple.js -d
32 yarn snap -p <file-basename> -d
33
34 # Update fixture outputs (also works with -p)
35 yarn snap -u
36 ```
37
38 ## Linting
39
40 ```bash
41 # Run lint on the compiler source
42 yarn workspace babel-plugin-react-compiler lint
43 ```
44
45 ## Formatting
46
47 ```bash
48 # Run prettier on all files (from the react root directory, not compiler/)
49 yarn prettier-all
50 ```
51
52 ## Compiling Arbitrary Files
53
54 Use `yarn snap compile` to compile any file (not just fixtures) with the React Compiler:
55
56 ```bash
57 # Compile a file and see the output
58 yarn snap compile <path>
59
60 # Compile with debug logging to see the state after each compiler pass
61 # This is an alternative to `yarn snap -d -p <pattern>` when you don't have a fixture file yet
62 yarn snap compile --debug <path>
63 ```
64
65 ## Minimizing Test Cases
66
67 Use `yarn snap minimize` to automatically reduce a failing test case to its minimal reproduction:
68
69 ```bash
70 # Minimize a file that causes a compiler error
71 yarn snap minimize <path>
72
73 # Minimize and update the file in-place with the minimized version
74 yarn snap minimize --update <path>
75 ```
76
77 ## Version Control
78
79 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.
80
81 ```bash
82 # Check status
83 sl status
84
85 # Add new files, remove deleted files
86 sl addremove
87
88 # Commit all changes
89 sl commit -m "Your commit message"
90
91 # Commit with multi-line message using heredoc
92 sl commit -m "$(cat <<'EOF'
93 Summary line
94
95 Detailed description here
96 EOF
97 )"
98 ```
99
100 ## Key Concepts
101
102 ### HIR (High-level Intermediate Representation)
103
104 The compiler converts source code to HIR for analysis. Key types in `src/HIR/HIR.ts`:
105
106 - **HIRFunction** - A function being compiled
107 - `body.blocks` - Map of BasicBlocks
108 - `context` - Captured variables from outer scope
109 - `params` - Function parameters
110 - `returns` - The function's return place
111 - `aliasingEffects` - Effects that describe the function's behavior when called
112
113 - **Instruction** - A single operation
114 - `lvalue` - The place being assigned to
115 - `value` - The instruction kind (CallExpression, FunctionExpression, LoadLocal, etc.)
116 - `effects` - Array of AliasingEffects for this instruction
117
118 - **Terminal** - Block terminators (return, branch, etc.)
119 - `effects` - Array of AliasingEffects
120
121 - **Place** - A reference to a value
122 - `identifier.id` - Unique IdentifierId
123
124 - **Phi nodes** - Join points for values from different control flow paths
125 - Located at `block.phis`
126 - `phi.place` - The result place
127 - `phi.operands` - Map of predecessor block to source place
128
129 ### AliasingEffects System
130
131 Effects describe data flow and operations. Defined in `src/Inference/AliasingEffects.ts`:
132
133 **Data Flow Effects:**
134 - `Impure` - Marks a place as containing an impure value (e.g., Date.now() result, ref.current)
135 - `Capture a -> b` - Value from `a` is captured into `b` (mutable capture)
136 - `Alias a -> b` - `b` aliases `a`
137 - `ImmutableCapture a -> b` - Immutable capture (like Capture but read-only)
138 - `Assign a -> b` - Direct assignment
139 - `MaybeAlias a -> b` - Possible aliasing
140 - `CreateFrom a -> b` - Created from source
141
142 **Mutation Effects:**
143 - `Mutate value` - Value is mutated
144 - `MutateTransitive value` - Value and transitive captures are mutated
145 - `MutateConditionally value` - May mutate
146 - `MutateTransitiveConditionally value` - May mutate transitively
147
148 **Other Effects:**
149 - `Render place` - Place is used in render context (JSX props, component return)
150 - `Freeze place` - Place is frozen (made immutable)
151 - `Create place` - New value created
152 - `CreateFunction` - Function expression created, includes `captures` array
153 - `Apply` - Function application with receiver, function, args, and result
154
155 ### Hook Aliasing Signatures
156
157 Located in `src/HIR/Globals.ts`, hooks can define custom aliasing signatures to control how data flows through them.
158
159 **Structure:**
160 ```typescript
161 aliasing: {
162 receiver: '@receiver', // The hook function itself
163 params: ['@param0'], // Named positional parameters
164 rest: '@rest', // Rest parameters (or null)
165 returns: '@returns', // Return value
166 temporaries: [], // Temporary values during execution
167 effects: [ // Array of effects to apply when hook is called
168 {kind: 'Freeze', value: '@param0', reason: ValueReason.HookCaptured},
169 {kind: 'Assign', from: '@param0', into: '@returns'},
170 ],
171 }
172 ```
173
174 **Common patterns:**
175
176 1. **RenderHookAliasing** (useState, useContext, useMemo, useCallback):
177 - Freezes arguments (`Freeze @rest`)
178 - Marks arguments as render-time (`Render @rest`)
179 - Creates frozen return value
180 - Aliases arguments to return
181
182 2. **EffectHookAliasing** (useEffect, useLayoutEffect, useInsertionEffect):
183 - Freezes function and deps
184 - Creates internal effect object
185 - Captures function and deps into effect
186 - Returns undefined
187
188 3. **Event handler hooks** (useEffectEvent):
189 - Freezes callback (`Freeze @fn`)
190 - Aliases input to return (`Assign @fn -> @returns`)
191 - NO Render effect (callback not called during render)
192
193 **Example: useEffectEvent**
194 ```typescript
195 const UseEffectEventHook = addHook(
196 DEFAULT_SHAPES,
197 {
198 positionalParams: [Effect.Freeze], // Takes one positional param
199 restParam: null,
200 returnType: {kind: 'Function', ...},
201 calleeEffect: Effect.Read,
202 hookKind: 'useEffectEvent',
203 returnValueKind: ValueKind.Frozen,
204 aliasing: {
205 receiver: '@receiver',
206 params: ['@fn'], // Name for the callback parameter
207 rest: null,
208 returns: '@returns',
209 temporaries: [],
210 effects: [
211 {kind: 'Freeze', value: '@fn', reason: ValueReason.HookCaptured},
212 {kind: 'Assign', from: '@fn', into: '@returns'},
213 // Note: NO Render effect - callback is not called during render
214 ],
215 },
216 },
217 BuiltInUseEffectEventId,
218 );
219
220 // Add as both names for compatibility
221 ['useEffectEvent', UseEffectEventHook],
222 ['experimental_useEffectEvent', UseEffectEventHook],
223 ```
224
225 **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.
226
227 ## Feature Flags
228
229 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:
230
231 ```javascript
232 // enableJsxOutlining @enableNameAnonymousFunctions:false
233
234 ...code...
235 ```
236
237 Would enable the `enableJsxOutlining` feature and disable the `enableNameAnonymousFunctions` feature.
238
239 ## Rust Port (Active)
240
241 Work is tracked in `compiler/docs/rust-port/` with numbered plan docs.
242 Rust crates live in `compiler/crates/`.
243
244 ### Before implementing from a plan:
245 - Run `git log --oneline --grep="<plan-name>"` to see what's already done
246 - Read the plan doc's Remaining Work / Status section
247 - Only implement what's actually remaining
248
249 ### After implementing:
250 - Update the plan doc's status
251 - Run `/compiler-verify`
252 - Ensure `compiler/scripts/test-babel-ast.sh` passes
253
254 ## Debugging Tips
255
256 1. Run `yarn snap -p <fixture>` to see full HIR output with effects
257 2. Look for `@aliasingEffects=` on FunctionExpressions
258 3. Look for `Impure`, `Render`, `Capture` effects on instructions
259 4. Check the pass ordering in Pipeline.ts to understand when effects are populated vs validated
260
261 ## Error Handling and Fault Tolerance
262
263 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.
264
265 **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()`.
266
267 **`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.
268
269 **Error categories:**
270 - `CompilerError.throwTodo()` — Unsupported but known pattern. Graceful bailout. Can be caught by `tryRecord()`.
271 - `CompilerError.invariant()` — Truly unexpected/invalid state. Always throws immediately, never caught by `tryRecord()`.
272 - Non-`CompilerError` exceptions — Always re-thrown.
273
274 **Key files:** `Environment.ts` (`recordError`, `tryRecord`, `hasErrors`, `aggregateErrors`), `Pipeline.ts` (pass orchestration), `Program.ts` (`tryCompileFunction` handles the `Result`).
275
276 **Test fixtures:** `__tests__/fixtures/compiler/fault-tolerance/` contains multi-error fixtures verifying all errors are reported.