Skip to content

Commit d33f183

Browse files
committed
[compiler] clean up retry pipeline: fireRetry flag -> compileMode
Removes `EnvironmentConfig.enableMinimalTransformsForRetry` in favor of `run` modes. This is a minimal difference but lets us explicitly opt certain compiler passes in/out of different modes. Retry flags don't really make sense to have in `EnvironmentConfig` anyways as the config is user-facing API, while retrying is a compiler implementation detail. (per @josephsavona's feedback #32164 (comment)) > Re the "hacky" framing of this in the PR title: I think this is fine. I can see having something like a compilation or output mode that we use when running the pipeline. Rather than changing environment settings when we re-run, various passes could take effect based on the combination of the mode + env flags. The modes might be: > > * Full: transform, validate, memoize. This is the default today. > * Transform: Along the lines of the backup mode in this PR. Only applies transforms that do not require following the rules of React, like `fire()`. > * Validate: This could be used for ESLint.
1 parent 443b7ff commit d33f183

File tree

5 files changed

+86
-57
lines changed

5 files changed

+86
-57
lines changed

compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
pruneUnusedLabelsHIR,
2525
} from '../HIR';
2626
import {
27+
CompilerMode,
2728
Environment,
2829
EnvironmentConfig,
2930
ReactFunctionType,
@@ -100,6 +101,7 @@ import {outlineJSX} from '../Optimization/OutlineJsx';
100101
import {optimizePropsMethodCalls} from '../Optimization/OptimizePropsMethodCalls';
101102
import {transformFire} from '../Transform';
102103
import {validateNoImpureFunctionsInRender} from '../Validation/ValiateNoImpureFunctionsInRender';
104+
import {CompilerError} from '..';
103105

104106
export type CompilerPipelineValue =
105107
| {kind: 'ast'; name: string; value: CodegenFunction}
@@ -113,6 +115,7 @@ function run(
113115
>,
114116
config: EnvironmentConfig,
115117
fnType: ReactFunctionType,
118+
mode: CompilerMode,
116119
useMemoCacheIdentifier: string,
117120
logger: Logger | null,
118121
filename: string | null,
@@ -122,6 +125,7 @@ function run(
122125
const env = new Environment(
123126
func.scope,
124127
fnType,
128+
mode,
125129
config,
126130
contextIdentifiers,
127131
logger,
@@ -160,10 +164,10 @@ function runWithEnvironment(
160164
validateUseMemo(hir);
161165

162166
if (
167+
env.isInferredMemoEnabled &&
163168
!env.config.enablePreserveExistingManualUseMemo &&
164169
!env.config.disableMemoizationForDebugging &&
165-
!env.config.enableChangeDetectionForDebugging &&
166-
!env.config.enableMinimalTransformsForRetry
170+
!env.config.enableChangeDetectionForDebugging
167171
) {
168172
dropManualMemoization(hir);
169173
log({kind: 'hir', name: 'DropManualMemoization', value: hir});
@@ -196,19 +200,20 @@ function runWithEnvironment(
196200
inferTypes(hir);
197201
log({kind: 'hir', name: 'InferTypes', value: hir});
198202

199-
if (env.config.validateHooksUsage) {
200-
validateHooksUsage(hir);
203+
if (env.isInferredMemoEnabled) {
204+
if (env.config.validateHooksUsage) {
205+
validateHooksUsage(hir);
206+
}
207+
if (env.config.validateNoCapitalizedCalls) {
208+
validateNoCapitalizedCalls(hir);
209+
}
201210
}
202211

203212
if (env.config.enableFire) {
204213
transformFire(hir);
205214
log({kind: 'hir', name: 'TransformFire', value: hir});
206215
}
207216

208-
if (env.config.validateNoCapitalizedCalls) {
209-
validateNoCapitalizedCalls(hir);
210-
}
211-
212217
if (env.config.lowerContextAccess) {
213218
lowerContextAccess(hir, env.config.lowerContextAccess);
214219
}
@@ -219,7 +224,12 @@ function runWithEnvironment(
219224
analyseFunctions(hir);
220225
log({kind: 'hir', name: 'AnalyseFunctions', value: hir});
221226

222-
inferReferenceEffects(hir);
227+
const referenceEffectsResult = inferReferenceEffects(hir);
228+
if (env.isInferredMemoEnabled) {
229+
if (referenceEffectsResult.fnEffectErrors.length > 0) {
230+
CompilerError.throw(referenceEffectsResult.fnEffectErrors[0]);
231+
}
232+
}
223233
log({kind: 'hir', name: 'InferReferenceEffects', value: hir});
224234

225235
validateLocalsNotReassignedAfterRender(hir);
@@ -239,28 +249,30 @@ function runWithEnvironment(
239249
inferMutableRanges(hir);
240250
log({kind: 'hir', name: 'InferMutableRanges', value: hir});
241251

242-
if (env.config.assertValidMutableRanges) {
243-
assertValidMutableRanges(hir);
244-
}
252+
if (env.isInferredMemoEnabled) {
253+
if (env.config.assertValidMutableRanges) {
254+
assertValidMutableRanges(hir);
255+
}
245256

246-
if (env.config.validateRefAccessDuringRender) {
247-
validateNoRefAccessInRender(hir);
248-
}
257+
if (env.config.validateRefAccessDuringRender) {
258+
validateNoRefAccessInRender(hir);
259+
}
249260

250-
if (env.config.validateNoSetStateInRender) {
251-
validateNoSetStateInRender(hir);
252-
}
261+
if (env.config.validateNoSetStateInRender) {
262+
validateNoSetStateInRender(hir);
263+
}
253264

254-
if (env.config.validateNoSetStateInPassiveEffects) {
255-
validateNoSetStateInPassiveEffects(hir);
256-
}
265+
if (env.config.validateNoSetStateInPassiveEffects) {
266+
validateNoSetStateInPassiveEffects(hir);
267+
}
257268

258-
if (env.config.validateNoJSXInTryStatements) {
259-
validateNoJSXInTryStatement(hir);
260-
}
269+
if (env.config.validateNoJSXInTryStatements) {
270+
validateNoJSXInTryStatement(hir);
271+
}
261272

262-
if (env.config.validateNoImpureFunctionsInRender) {
263-
validateNoImpureFunctionsInRender(hir);
273+
if (env.config.validateNoImpureFunctionsInRender) {
274+
validateNoImpureFunctionsInRender(hir);
275+
}
264276
}
265277

266278
inferReactivePlaces(hir);
@@ -280,7 +292,12 @@ function runWithEnvironment(
280292
value: hir,
281293
});
282294

283-
if (!env.config.enableMinimalTransformsForRetry) {
295+
if (env.isInferredMemoEnabled) {
296+
/**
297+
* Only create reactive scopes (which directly map to generated memo blocks)
298+
* if inferred memoization is enabled. This makes all later passes which
299+
* transform reactive-scope labeled instructions no-ops.
300+
*/
284301
inferReactiveScopeVariables(hir);
285302
log({kind: 'hir', name: 'InferReactiveScopeVariables', value: hir});
286303
}
@@ -529,6 +546,7 @@ export function compileFn(
529546
>,
530547
config: EnvironmentConfig,
531548
fnType: ReactFunctionType,
549+
mode: CompilerMode,
532550
useMemoCacheIdentifier: string,
533551
logger: Logger | null,
534552
filename: string | null,
@@ -538,6 +556,7 @@ export function compileFn(
538556
func,
539557
config,
540558
fnType,
559+
mode,
541560
useMemoCacheIdentifier,
542561
logger,
543562
filename,

compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ import {
1616
EnvironmentConfig,
1717
ExternalFunction,
1818
ReactFunctionType,
19-
MINIMAL_RETRY_CONFIG,
2019
tryParseExternalFunction,
20+
CompilerMode,
2121
} from '../HIR/Environment';
2222
import {CodegenFunction} from '../ReactiveScopes';
2323
import {isComponentDeclaration} from '../Utils/ComponentDeclaration';
@@ -408,6 +408,7 @@ export function compileProgram(
408408
fn,
409409
environment,
410410
fnType,
411+
CompilerMode.AllFeatures,
411412
useMemoCacheIdentifier.name,
412413
pass.opts.logger,
413414
pass.filename,
@@ -425,11 +426,9 @@ export function compileProgram(
425426
kind: 'compile',
426427
compiledFn: compileFn(
427428
fn,
428-
{
429-
...environment,
430-
...MINIMAL_RETRY_CONFIG,
431-
},
429+
environment,
432430
fnType,
431+
CompilerMode.NoInferredMemo,
433432
useMemoCacheIdentifier.name,
434433
pass.opts.logger,
435434
pass.filename,

compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ export const MacroSchema = z.union([
9696
z.tuple([z.string(), z.array(MacroMethodSchema)]),
9797
]);
9898

99+
export enum CompilerMode {
100+
AllFeatures = 'all_features',
101+
NoInferredMemo = 'no_inferred_memo',
102+
}
103+
99104
export type Macro = z.infer<typeof MacroSchema>;
100105
export type MacroMethod = z.infer<typeof MacroMethodSchema>;
101106

@@ -550,8 +555,6 @@ const EnvironmentConfigSchema = z.object({
550555
*/
551556
disableMemoizationForDebugging: z.boolean().default(false),
552557

553-
enableMinimalTransformsForRetry: z.boolean().default(false),
554-
555558
/**
556559
* When true, rather using memoized values, the compiler will always re-compute
557560
* values, and then use a heuristic to compare the memoized value to the newly
@@ -626,17 +629,6 @@ const EnvironmentConfigSchema = z.object({
626629

627630
export type EnvironmentConfig = z.infer<typeof EnvironmentConfigSchema>;
628631

629-
export const MINIMAL_RETRY_CONFIG: PartialEnvironmentConfig = {
630-
validateHooksUsage: false,
631-
validateRefAccessDuringRender: false,
632-
validateNoSetStateInRender: false,
633-
validateNoSetStateInPassiveEffects: false,
634-
validateNoJSXInTryStatements: false,
635-
validateMemoizedEffectDependencies: false,
636-
validateNoCapitalizedCalls: null,
637-
validateBlocklistedImports: null,
638-
enableMinimalTransformsForRetry: true,
639-
};
640632
/**
641633
* For test fixtures and playground only.
642634
*
@@ -851,6 +843,7 @@ export class Environment {
851843
code: string | null;
852844
config: EnvironmentConfig;
853845
fnType: ReactFunctionType;
846+
compilerMode: CompilerMode;
854847
useMemoCacheIdentifier: string;
855848
hasLoweredContextAccess: boolean;
856849
hasFireRewrite: boolean;
@@ -861,6 +854,7 @@ export class Environment {
861854
constructor(
862855
scope: BabelScope,
863856
fnType: ReactFunctionType,
857+
compilerMode: CompilerMode,
864858
config: EnvironmentConfig,
865859
contextIdentifiers: Set<t.Identifier>,
866860
logger: Logger | null,
@@ -870,6 +864,7 @@ export class Environment {
870864
) {
871865
this.#scope = scope;
872866
this.fnType = fnType;
867+
this.compilerMode = compilerMode;
873868
this.config = config;
874869
this.filename = filename;
875870
this.code = code;
@@ -924,6 +919,10 @@ export class Environment {
924919
this.#hoistedIdentifiers = new Set();
925920
}
926921

922+
get isInferredMemoEnabled(): boolean {
923+
return this.compilerMode !== CompilerMode.NoInferredMemo;
924+
}
925+
927926
get nextIdentifierId(): IdentifierId {
928927
return makeIdentifierId(this.#nextIdentifer++);
929928
}

compiler/packages/babel-plugin-react-compiler/src/Inference/InferFunctionEffects.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import {CompilerError, ErrorSeverity, ValueKind} from '..';
8+
import {
9+
CompilerError,
10+
CompilerErrorDetailOptions,
11+
ErrorSeverity,
12+
ValueKind,
13+
} from '..';
914
import {
1015
AbstractValue,
1116
BasicBlock,
@@ -290,21 +295,21 @@ export function inferTerminalFunctionEffects(
290295
return functionEffects;
291296
}
292297

293-
export function raiseFunctionEffectErrors(
298+
export function transformFunctionEffectErrors(
294299
functionEffects: Array<FunctionEffect>,
295-
): void {
296-
functionEffects.forEach(eff => {
300+
): Array<CompilerErrorDetailOptions> {
301+
return functionEffects.map(eff => {
297302
switch (eff.kind) {
298303
case 'ReactMutation':
299304
case 'GlobalMutation': {
300-
CompilerError.throw(eff.error);
305+
return eff.error;
301306
}
302307
case 'ContextMutation': {
303-
CompilerError.throw({
308+
return {
304309
severity: ErrorSeverity.Invariant,
305310
reason: `Unexpected ContextMutation in top-level function effects`,
306311
loc: eff.loc,
307-
});
312+
};
308313
}
309314
default:
310315
assertExhaustive(

compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import {CompilerError} from '../CompilerError';
8+
import {CompilerError, CompilerErrorDetailOptions} from '../CompilerError';
99
import {Environment} from '../HIR';
1010
import {
1111
AbstractValue,
@@ -49,7 +49,7 @@ import {assertExhaustive} from '../Utils/utils';
4949
import {
5050
inferTerminalFunctionEffects,
5151
inferInstructionFunctionEffects,
52-
raiseFunctionEffectErrors,
52+
transformFunctionEffectErrors,
5353
} from './InferFunctionEffects';
5454

5555
const UndefinedValue: InstructionValue = {
@@ -103,7 +103,9 @@ const UndefinedValue: InstructionValue = {
103103
export default function inferReferenceEffects(
104104
fn: HIRFunction,
105105
options: {isFunctionExpression: boolean} = {isFunctionExpression: false},
106-
): void {
106+
): {
107+
fnEffectErrors: Array<CompilerErrorDetailOptions>;
108+
} {
107109
/*
108110
* Initial state contains function params
109111
* TODO: include module declarations here as well
@@ -241,8 +243,13 @@ export default function inferReferenceEffects(
241243

242244
if (options.isFunctionExpression) {
243245
fn.effects = functionEffects;
244-
} else if (!fn.env.config.enableMinimalTransformsForRetry) {
245-
raiseFunctionEffectErrors(functionEffects);
246+
return {
247+
fnEffectErrors: [],
248+
};
249+
} else {
250+
return {
251+
fnEffectErrors: transformFunctionEffectErrors(functionEffects),
252+
};
246253
}
247254
}
248255

0 commit comments

Comments
 (0)