Skip to content

Commit a974753

Browse files
committed
[compiler] Use aliasing effects for impurity inference
1 parent 2c4a3b9 commit a974753

File tree

29 files changed

+490
-579
lines changed

29 files changed

+490
-579
lines changed

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

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,7 @@ const RenderHookAliasing: (
664664

665665
const EffectHookAliasing: AliasingSignatureConfig = {
666666
receiver: '@receiver',
667-
params: [],
667+
params: ['@fn', '@deps'],
668668
rest: '@rest',
669669
returns: '@returns',
670670
temporaries: ['@effect'],
@@ -675,6 +675,21 @@ const EffectHookAliasing: AliasingSignatureConfig = {
675675
value: '@rest',
676676
reason: ValueReason.Effect,
677677
},
678+
{
679+
kind: 'Freeze',
680+
value: '@fn',
681+
reason: ValueReason.Effect,
682+
},
683+
{
684+
kind: 'Freeze',
685+
value: '@deps',
686+
reason: ValueReason.Effect,
687+
},
688+
// Deps are accessed during render
689+
{
690+
kind: 'Render',
691+
place: '@deps',
692+
},
678693
// Internally creates an effect object that captures the function and deps
679694
{
680695
kind: 'Create',
@@ -688,6 +703,11 @@ const EffectHookAliasing: AliasingSignatureConfig = {
688703
from: '@rest',
689704
into: '@effect',
690705
},
706+
{
707+
kind: 'Capture',
708+
from: '@fn',
709+
into: '@effect',
710+
},
691711
// Returns undefined
692712
{
693713
kind: 'Create',
@@ -703,6 +723,39 @@ const EffectHookAliasing: AliasingSignatureConfig = {
703723
* now that FeatureFlag `enableTreatHooksAsFunctions` is removed we can
704724
* use positional params too (?)
705725
*/
726+
const useEffectEvent = addHook(
727+
DEFAULT_SHAPES,
728+
{
729+
positionalParams: [],
730+
restParam: Effect.Freeze,
731+
returnType: {
732+
kind: 'Function',
733+
return: {kind: 'Poly'},
734+
shapeId: BuiltInEffectEventId,
735+
isConstructor: false,
736+
},
737+
calleeEffect: Effect.Read,
738+
hookKind: 'useEffectEvent',
739+
// Frozen because it should not mutate any locally-bound values
740+
returnValueKind: ValueKind.Frozen,
741+
aliasing: {
742+
receiver: '@receiver',
743+
params: ['@value'],
744+
rest: null,
745+
returns: '@return',
746+
temporaries: [],
747+
effects: [
748+
{kind: 'Assign', from: '@value', into: '@return'},
749+
{
750+
kind: 'Freeze',
751+
value: '@value',
752+
reason: ValueReason.HookCaptured,
753+
},
754+
],
755+
},
756+
},
757+
BuiltInUseEffectEventId,
758+
);
706759
const REACT_APIS: Array<[string, BuiltInType]> = [
707760
[
708761
'useContext',
@@ -915,27 +968,8 @@ const REACT_APIS: Array<[string, BuiltInType]> = [
915968
BuiltInFireId,
916969
),
917970
],
918-
[
919-
'useEffectEvent',
920-
addHook(
921-
DEFAULT_SHAPES,
922-
{
923-
positionalParams: [],
924-
restParam: Effect.Freeze,
925-
returnType: {
926-
kind: 'Function',
927-
return: {kind: 'Poly'},
928-
shapeId: BuiltInEffectEventId,
929-
isConstructor: false,
930-
},
931-
calleeEffect: Effect.Read,
932-
hookKind: 'useEffectEvent',
933-
// Frozen because it should not mutate any locally-bound values
934-
returnValueKind: ValueKind.Frozen,
935-
},
936-
BuiltInUseEffectEventId,
937-
),
938-
],
971+
['useEffectEvent', useEffectEvent],
972+
['experimental_useEffectEvent', useEffectEvent],
939973
['AUTODEPS', addObject(DEFAULT_SHAPES, BuiltInAutodepsId, [])],
940974
];
941975

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1879,7 +1879,15 @@ export function isRefValueType(id: Identifier): boolean {
18791879
}
18801880

18811881
export function isUseRefType(id: Identifier): boolean {
1882-
return id.type.kind === 'Object' && id.type.shapeId === 'BuiltInUseRefId';
1882+
return isUseRefType_(id.type);
1883+
}
1884+
1885+
export function isUseRefType_(type: Type): boolean {
1886+
return (
1887+
(type.kind === 'Object' && type.shapeId === 'BuiltInUseRefId') ||
1888+
(type.kind === 'Phi' &&
1889+
type.operands.some(operand => isUseRefType_(operand)))
1890+
);
18831891
}
18841892

18851893
export function isUseStateType(id: Identifier): boolean {

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

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
isArrayType,
3030
isJsxOrJsxUnionType,
3131
isMapType,
32+
isMutableEffect,
3233
isPrimitiveType,
3334
isRefOrRefValue,
3435
isSetType,
@@ -587,6 +588,17 @@ function inferBlock(
587588
}),
588589
);
589590
}
591+
if (
592+
context.fn.fnType === 'Component' ||
593+
isJsxOrJsxUnionType(context.fn.returns.identifier.type)
594+
) {
595+
terminal.effects.push(
596+
context.internEffect({
597+
kind: 'Render',
598+
place: terminal.value,
599+
}),
600+
);
601+
}
590602
}
591603
}
592604

@@ -758,17 +770,7 @@ function applyEffect(
758770
break;
759771
}
760772
case 'ImmutableCapture': {
761-
const kind = state.kind(effect.from).kind;
762-
switch (kind) {
763-
case ValueKind.Global:
764-
case ValueKind.Primitive: {
765-
// no-op: we don't need to track data flow for copy types
766-
break;
767-
}
768-
default: {
769-
effects.push(effect);
770-
}
771-
}
773+
effects.push(effect);
772774
break;
773775
}
774776
case 'CreateFrom': {
@@ -1070,6 +1072,17 @@ function applyEffect(
10701072
reason: new Set(fromValue.reason),
10711073
});
10721074
state.define(effect.into, value);
1075+
applyEffect(
1076+
context,
1077+
state,
1078+
{
1079+
kind: 'ImmutableCapture',
1080+
from: effect.from,
1081+
into: effect.into,
1082+
},
1083+
initialized,
1084+
effects,
1085+
);
10731086
break;
10741087
}
10751088
default: {
@@ -1975,6 +1988,11 @@ function computeSignatureForInstruction(
19751988
value: ValueKind.Primitive,
19761989
reason: ValueReason.Other,
19771990
});
1991+
effects.push({
1992+
kind: 'ImmutableCapture',
1993+
from: value.object,
1994+
into: lvalue,
1995+
});
19781996
} else {
19791997
effects.push({
19801998
kind: 'CreateFrom',
@@ -2180,6 +2198,9 @@ function computeSignatureForInstruction(
21802198
for (const prop of value.props) {
21812199
const place =
21822200
prop.kind === 'JsxAttribute' ? prop.place : prop.argument;
2201+
if (isUseRefType(place.identifier)) {
2202+
continue;
2203+
}
21832204
if (place.identifier.type.kind === 'Function') {
21842205
if (isJsxOrJsxUnionType(place.identifier.type.return)) {
21852206
effects.push({
@@ -2226,6 +2247,11 @@ function computeSignatureForInstruction(
22262247
value: ValueKind.Primitive,
22272248
reason: ValueReason.Other,
22282249
});
2250+
effects.push({
2251+
kind: 'ImmutableCapture',
2252+
from: value.value,
2253+
into: place,
2254+
});
22292255
} else if (patternItem.kind === 'Identifier') {
22302256
effects.push({
22312257
kind: 'CreateFrom',
@@ -2407,15 +2433,46 @@ function computeSignatureForInstruction(
24072433
});
24082434
break;
24092435
}
2436+
case 'BinaryExpression': {
2437+
effects.push({
2438+
kind: 'Create',
2439+
into: lvalue,
2440+
value: ValueKind.Primitive,
2441+
reason: ValueReason.Other,
2442+
});
2443+
effects.push({
2444+
kind: 'ImmutableCapture',
2445+
into: lvalue,
2446+
from: value.left,
2447+
});
2448+
effects.push({
2449+
kind: 'ImmutableCapture',
2450+
into: lvalue,
2451+
from: value.right,
2452+
});
2453+
break;
2454+
}
2455+
case 'UnaryExpression': {
2456+
effects.push({
2457+
kind: 'Create',
2458+
into: lvalue,
2459+
value: ValueKind.Primitive,
2460+
reason: ValueReason.Other,
2461+
});
2462+
effects.push({
2463+
kind: 'ImmutableCapture',
2464+
into: lvalue,
2465+
from: value.value,
2466+
});
2467+
break;
2468+
}
24102469
case 'TaggedTemplateExpression':
2411-
case 'BinaryExpression':
24122470
case 'Debugger':
24132471
case 'JSXText':
24142472
case 'MetaProperty':
24152473
case 'Primitive':
24162474
case 'RegExpLiteral':
24172475
case 'TemplateLiteral':
2418-
case 'UnaryExpression':
24192476
case 'UnsupportedNode': {
24202477
effects.push({
24212478
kind: 'Create',

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

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -224,17 +224,20 @@ export function inferMutationAliasingRanges(
224224
if (effect.kind === 'Alias') {
225225
state.assign(index++, effect.from, effect.into);
226226
} else {
227-
CompilerError.invariant(effect.kind === 'Freeze', {
228-
reason: `Unexpected '${effect.kind}' effect for MaybeThrow terminal`,
229-
description: null,
230-
details: [
231-
{
232-
kind: 'error',
233-
loc: block.terminal.loc,
234-
message: null,
235-
},
236-
],
237-
});
227+
CompilerError.invariant(
228+
effect.kind === 'Freeze' || effect.kind === 'Render',
229+
{
230+
reason: `Unexpected '${effect.kind}' effect for MaybeThrow terminal`,
231+
description: null,
232+
details: [
233+
{
234+
kind: 'error',
235+
loc: block.terminal.loc,
236+
message: null,
237+
},
238+
],
239+
},
240+
);
238241
}
239242
}
240243
}

compiler/packages/babel-plugin-react-compiler/src/Utils/utils.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,14 @@ export function Set_filter<T>(
167167
return result;
168168
}
169169

170+
export function Set_subtract<T>(
171+
source: ReadonlySet<T>,
172+
other: Iterable<T>,
173+
): Set<T> {
174+
const otherSet = other instanceof Set ? other : new Set(other);
175+
return Set_filter(source, item => !otherSet.has(item));
176+
}
177+
170178
export function hasNode<T>(
171179
input: NodePath<T | null | undefined>,
172180
): input is NodePath<NonNullable<T>> {

0 commit comments

Comments
 (0)