Skip to content

Commit 4386afc

Browse files
committed
Update
[ghstack-poisoned]
2 parents a43d068 + 9ce4824 commit 4386afc

File tree

99 files changed

+2202
-790
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

99 files changed

+2202
-790
lines changed

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

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -439,8 +439,11 @@ function lowerStatement(
439439
loc: id.parentPath.node.loc ?? GeneratedSource,
440440
});
441441
continue;
442-
} else if (binding.kind !== 'const' && binding.kind !== 'var') {
443-
// Avoid double errors on var declarations, which we do not plan to support anyways
442+
} else if (
443+
binding.kind !== 'const' &&
444+
binding.kind !== 'var' &&
445+
binding.kind !== 'let'
446+
) {
444447
builder.errors.push({
445448
severity: ErrorSeverity.Todo,
446449
reason: 'Handle non-const declarations for hoisting',
@@ -463,10 +466,17 @@ function lowerStatement(
463466
reactive: false,
464467
loc: id.node.loc ?? GeneratedSource,
465468
};
469+
const kind =
470+
// Avoid double errors on var declarations, which we do not plan to support anyways
471+
binding.kind === 'const' || binding.kind === 'var'
472+
? InstructionKind.HoistedConst
473+
: binding.kind === 'let'
474+
? InstructionKind.HoistedLet
475+
: assertExhaustive(binding.kind, 'Unexpected binding kind');
466476
lowerValueToTemporary(builder, {
467477
kind: 'DeclareContext',
468478
lvalue: {
469-
kind: InstructionKind.HoistedConst,
479+
kind,
470480
place,
471481
},
472482
loc: id.node.loc ?? GeneratedSource,
@@ -2837,6 +2847,21 @@ function isReorderableExpression(
28372847
allowLocalIdentifiers,
28382848
);
28392849
}
2850+
case 'LogicalExpression': {
2851+
const logical = expr as NodePath<t.LogicalExpression>;
2852+
return (
2853+
isReorderableExpression(
2854+
builder,
2855+
logical.get('left'),
2856+
allowLocalIdentifiers,
2857+
) &&
2858+
isReorderableExpression(
2859+
builder,
2860+
logical.get('right'),
2861+
allowLocalIdentifiers,
2862+
)
2863+
);
2864+
}
28402865
case 'ConditionalExpression': {
28412866
const conditional = expr as NodePath<t.ConditionalExpression>;
28422867
return (

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
BuiltInUseReducerId,
1919
BuiltInUseRefId,
2020
BuiltInUseStateId,
21+
BuiltInUseTransitionId,
2122
ShapeRegistry,
2223
addFunction,
2324
addHook,
@@ -425,6 +426,17 @@ const REACT_APIS: Array<[string, BuiltInType]> = [
425426
BuiltInUseInsertionEffectHookId,
426427
),
427428
],
429+
[
430+
'useTransition',
431+
addHook(DEFAULT_SHAPES, {
432+
positionalParams: [],
433+
restParam: null,
434+
returnType: {kind: 'Object', shapeId: BuiltInUseTransitionId},
435+
calleeEffect: Effect.Read,
436+
hookKind: 'useTransition',
437+
returnValueKind: ValueKind.Frozen,
438+
}),
439+
],
428440
[
429441
'use',
430442
addFunction(

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,9 @@ export enum InstructionKind {
741741

742742
// hoisted const declarations
743743
HoistedConst = 'HoistedConst',
744+
745+
// hoisted const declarations
746+
HoistedLet = 'HoistedLet',
744747
}
745748

746749
function _staticInvariantInstructionValueHasLocation(
@@ -858,7 +861,10 @@ export type InstructionValue =
858861
| {
859862
kind: 'DeclareContext';
860863
lvalue: {
861-
kind: InstructionKind.Let | InstructionKind.HoistedConst;
864+
kind:
865+
| InstructionKind.Let
866+
| InstructionKind.HoistedConst
867+
| InstructionKind.HoistedLet;
862868
place: Place;
863869
};
864870
loc: SourceLocation;
@@ -1595,6 +1601,12 @@ export function isUseActionStateType(id: Identifier): boolean {
15951601
);
15961602
}
15971603

1604+
export function isStartTransitionType(id: Identifier): boolean {
1605+
return (
1606+
id.type.kind === 'Function' && id.type.shapeId === 'BuiltInStartTransition'
1607+
);
1608+
}
1609+
15981610
export function isSetActionStateType(id: Identifier): boolean {
15991611
return (
16001612
id.type.kind === 'Function' && id.type.shapeId === 'BuiltInSetActionState'
@@ -1610,7 +1622,13 @@ export function isDispatcherType(id: Identifier): boolean {
16101622
}
16111623

16121624
export function isStableType(id: Identifier): boolean {
1613-
return isSetStateType(id) || isSetActionStateType(id) || isDispatcherType(id);
1625+
return (
1626+
isSetStateType(id) ||
1627+
isSetActionStateType(id) ||
1628+
isDispatcherType(id) ||
1629+
isUseRefType(id) ||
1630+
isStartTransitionType(id)
1631+
);
16141632
}
16151633

16161634
export function isUseEffectHookType(id: Identifier): boolean {

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ export type HookKind =
126126
| 'useInsertionEffect'
127127
| 'useMemo'
128128
| 'useCallback'
129+
| 'useTransition'
129130
| 'Custom';
130131

131132
/*
@@ -209,6 +210,8 @@ export const BuiltInUseOperatorId = 'BuiltInUseOperator';
209210
export const BuiltInUseReducerId = 'BuiltInUseReducer';
210211
export const BuiltInDispatchId = 'BuiltInDispatch';
211212
export const BuiltInUseContextHookId = 'BuiltInUseContextHook';
213+
export const BuiltInUseTransitionId = 'BuiltInUseTransition';
214+
export const BuiltInStartTransitionId = 'BuiltInStartTransition';
212215

213216
// ShapeRegistry with default definitions for built-ins.
214217
export const BUILTIN_SHAPES: ShapeRegistry = new Map();
@@ -444,6 +447,25 @@ addObject(BUILTIN_SHAPES, BuiltInUseStateId, [
444447
],
445448
]);
446449

450+
addObject(BUILTIN_SHAPES, BuiltInUseTransitionId, [
451+
['0', {kind: 'Primitive'}],
452+
[
453+
'1',
454+
addFunction(
455+
BUILTIN_SHAPES,
456+
[],
457+
{
458+
positionalParams: [],
459+
restParam: null,
460+
returnType: PRIMITIVE_TYPE,
461+
calleeEffect: Effect.Read,
462+
returnValueKind: ValueKind.Primitive,
463+
},
464+
BuiltInStartTransitionId,
465+
),
466+
],
467+
]);
468+
447469
addObject(BUILTIN_SHAPES, BuiltInUseActionStateId, [
448470
['0', {kind: 'Poly'}],
449471
[

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,9 @@ export function printLValue(lval: LValue): string {
760760
case InstructionKind.HoistedConst: {
761761
return `HoistedConst ${lvalue}$`;
762762
}
763+
case InstructionKind.HoistedLet: {
764+
return `HoistedLet ${lvalue}$`;
765+
}
763766
default: {
764767
assertExhaustive(lval.kind, `Unexpected lvalue kind \`${lval.kind}\``);
765768
}

compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -994,6 +994,13 @@ function codegenTerminal(
994994
loc: iterableItem.loc,
995995
suggestions: null,
996996
});
997+
case InstructionKind.HoistedLet:
998+
CompilerError.invariant(false, {
999+
reason: 'Unexpected HoistedLet variable in for..in collection',
1000+
description: null,
1001+
loc: iterableItem.loc,
1002+
suggestions: null,
1003+
});
9971004
default:
9981005
assertExhaustive(
9991006
iterableItem.value.lvalue.kind,
@@ -1089,6 +1096,13 @@ function codegenTerminal(
10891096
loc: iterableItem.loc,
10901097
suggestions: null,
10911098
});
1099+
case InstructionKind.HoistedLet:
1100+
CompilerError.invariant(false, {
1101+
reason: 'Unexpected HoistedLet variable in for..of collection',
1102+
description: null,
1103+
loc: iterableItem.loc,
1104+
suggestions: null,
1105+
});
10921106
default:
10931107
assertExhaustive(
10941108
iterableItem.value.lvalue.kind,
@@ -1289,6 +1303,15 @@ function codegenInstructionNullable(
12891303
case InstructionKind.Catch: {
12901304
return t.emptyStatement();
12911305
}
1306+
case InstructionKind.HoistedLet: {
1307+
CompilerError.invariant(false, {
1308+
reason:
1309+
'Expected HoistedLet to have been pruned in PruneHoistedContexts',
1310+
description: null,
1311+
loc: instr.loc,
1312+
suggestions: null,
1313+
});
1314+
}
12921315
case InstructionKind.HoistedConst: {
12931316
CompilerError.invariant(false, {
12941317
reason:

compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PruneHoistedContexts.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ import {
2323
* original instruction kind.
2424
*/
2525
export function pruneHoistedContexts(fn: ReactiveFunction): void {
26-
const hoistedIdentifiers: HoistedIdentifiers = new Set();
26+
const hoistedIdentifiers: HoistedIdentifiers = new Map();
2727
visitReactiveFunction(fn, new Visitor(), hoistedIdentifiers);
2828
}
2929

30-
type HoistedIdentifiers = Set<DeclarationId>;
30+
type HoistedIdentifiers = Map<DeclarationId, InstructionKind>;
3131

3232
class Visitor extends ReactiveFunctionTransform<HoistedIdentifiers> {
3333
override transformInstruction(
@@ -39,14 +39,31 @@ class Visitor extends ReactiveFunctionTransform<HoistedIdentifiers> {
3939
instruction.value.kind === 'DeclareContext' &&
4040
instruction.value.lvalue.kind === 'HoistedConst'
4141
) {
42-
state.add(instruction.value.lvalue.place.identifier.declarationId);
42+
state.set(
43+
instruction.value.lvalue.place.identifier.declarationId,
44+
InstructionKind.Const,
45+
);
46+
return {kind: 'remove'};
47+
}
48+
49+
if (
50+
instruction.value.kind === 'DeclareContext' &&
51+
instruction.value.lvalue.kind === 'HoistedLet'
52+
) {
53+
state.set(
54+
instruction.value.lvalue.place.identifier.declarationId,
55+
InstructionKind.Let,
56+
);
4357
return {kind: 'remove'};
4458
}
4559

4660
if (
4761
instruction.value.kind === 'StoreContext' &&
4862
state.has(instruction.value.lvalue.place.identifier.declarationId)
4963
) {
64+
const kind = state.get(
65+
instruction.value.lvalue.place.identifier.declarationId,
66+
)!;
5067
return {
5168
kind: 'replace',
5269
value: {
@@ -57,7 +74,7 @@ class Visitor extends ReactiveFunctionTransform<HoistedIdentifiers> {
5774
...instruction.value,
5875
lvalue: {
5976
...instruction.value.lvalue,
60-
kind: InstructionKind.Const,
77+
kind,
6178
},
6279
type: null,
6380
kind: 'StoreLocal',

compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateLocalsNotReassignedAfterRender.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,16 @@ function getContextReassignment(
130130
*/
131131
contextVariables.add(value.lvalue.place.identifier.id);
132132
}
133+
const reassignment = reassigningFunctions.get(
134+
value.value.identifier.id,
135+
);
136+
if (reassignment !== undefined) {
137+
reassigningFunctions.set(
138+
value.lvalue.place.identifier.id,
139+
reassignment,
140+
);
141+
reassigningFunctions.set(lvalue.identifier.id, reassignment);
142+
}
133143
break;
134144
}
135145
default: {

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-mutating-ref-in-callback-passed-to-jsx-indirect.expect.md

Lines changed: 14 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -40,62 +40,37 @@ import { c as _c } from "react/compiler-runtime"; // @validateRefAccessDuringRen
4040
import { useRef } from "react";
4141

4242
function Component() {
43-
const $ = _c(10);
43+
const $ = _c(2);
4444
const ref = useRef(null);
4545
let t0;
4646
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
47-
t0 = () => {
47+
const setRef = () => {
4848
if (ref.current !== null) {
4949
ref.current = "";
5050
}
5151
};
52+
53+
t0 = () => {
54+
setRef();
55+
};
5256
$[0] = t0;
5357
} else {
5458
t0 = $[0];
5559
}
56-
const setRef = t0;
60+
const onClick = t0;
5761
let t1;
58-
if ($[1] !== setRef) {
59-
t1 = () => {
60-
setRef();
61-
};
62-
$[1] = setRef;
63-
$[2] = t1;
64-
} else {
65-
t1 = $[2];
66-
}
67-
const onClick = t1;
68-
let t2;
69-
if ($[3] !== ref) {
70-
t2 = <input ref={ref} />;
71-
$[3] = ref;
72-
$[4] = t2;
73-
} else {
74-
t2 = $[4];
75-
}
76-
let t3;
77-
if ($[5] !== onClick) {
78-
t3 = <button onClick={onClick} />;
79-
$[5] = onClick;
80-
$[6] = t3;
81-
} else {
82-
t3 = $[6];
83-
}
84-
let t4;
85-
if ($[7] !== t2 || $[8] !== t3) {
86-
t4 = (
62+
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
63+
t1 = (
8764
<>
88-
{t2}
89-
{t3}
65+
<input ref={ref} />
66+
<button onClick={onClick} />
9067
</>
9168
);
92-
$[7] = t2;
93-
$[8] = t3;
94-
$[9] = t4;
69+
$[1] = t1;
9570
} else {
96-
t4 = $[9];
71+
t1 = $[1];
9772
}
98-
return t4;
73+
return t1;
9974
}
10075

10176
export const FIXTURE_ENTRYPOINT = {

0 commit comments

Comments
 (0)