Skip to content

Commit 890896b

Browse files
committed
compiler: Improve merging of memo scopes that invalidate together
Improves merging of consecutive scopes so that we now merge two scopes if the dependencies of the second scope are a subset of the previous scope's output *and* that dependency has a type that will always produce a new value (array, object, jsx, function) if it is re-evaluated. To make this easier, we extend the set of builtin types to include ones for function expressions and JSX and to infer these types in InferTypes. This allows using the already inferred types in MergeReactiveScopesThatInvalidateTogether. ghstack-source-id: e9119fc Pull Request resolved: #29156
1 parent 82a0a5f commit 890896b

6 files changed

+48
-34
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ export type ObjectShape = {
188188
*/
189189
export type ShapeRegistry = Map<string, ObjectShape>;
190190
export const BuiltInArrayId = "BuiltInArray";
191+
export const BuiltInFunctionId = "BuiltInFunction";
192+
export const BuiltInJsxId = "BuiltInJsx";
191193
export const BuiltInObjectId = "BuiltInObject";
192194
export const BuiltInUseStateId = "BuiltInUseState";
193195
export const BuiltInSetStateId = "BuiltInSetState";

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

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,15 @@ import {
1919
ReactiveScopeDependencies,
2020
ReactiveScopeDependency,
2121
ReactiveStatement,
22+
Type,
2223
makeInstructionId,
2324
} from "../HIR";
25+
import {
26+
BuiltInArrayId,
27+
BuiltInFunctionId,
28+
BuiltInJsxId,
29+
BuiltInObjectId,
30+
} from "../HIR/ObjectShape";
2431
import { eachInstructionLValue } from "../HIR/visitors";
2532
import { assertExhaustive } from "../Utils/utils";
2633
import { printReactiveScopeSummary } from "./PrintReactiveFunction";
@@ -430,7 +437,13 @@ function canMergeScopes(
430437
}))
431438
),
432439
next.scope.dependencies
433-
)
440+
) ||
441+
(next.scope.dependencies.size !== 0 &&
442+
[...next.scope.dependencies].every(
443+
(dep) =>
444+
current.scope.declarations.has(dep.identifier.id) &&
445+
isAlwaysInvalidatingType(dep.identifier.type)
446+
))
434447
) {
435448
log(` outputs of prev are input to current`);
436449
return true;
@@ -441,6 +454,20 @@ function canMergeScopes(
441454
return false;
442455
}
443456

457+
function isAlwaysInvalidatingType(type: Type): boolean {
458+
if (type.kind === "Object") {
459+
switch (type.shapeId) {
460+
case BuiltInArrayId:
461+
case BuiltInObjectId:
462+
case BuiltInFunctionId:
463+
case BuiltInJsxId: {
464+
return true;
465+
}
466+
}
467+
}
468+
return false;
469+
}
470+
444471
function areEqualDependencies(
445472
a: Set<ReactiveScopeDependency>,
446473
b: Set<ReactiveScopeDependency>

compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import {
2020
} from "../HIR/HIR";
2121
import {
2222
BuiltInArrayId,
23+
BuiltInFunctionId,
24+
BuiltInJsxId,
2325
BuiltInObjectId,
2426
BuiltInUseRefId,
2527
} from "../HIR/ObjectShape";
@@ -313,6 +315,7 @@ function* generateInstructionTypes(
313315

314316
case "FunctionExpression": {
315317
yield* generate(value.loweredFunc.func);
318+
yield equation(left, { kind: "Object", shapeId: BuiltInFunctionId });
316319
break;
317320
}
318321

@@ -327,10 +330,13 @@ function* generateInstructionTypes(
327330
break;
328331
}
329332

333+
case "JsxExpression":
334+
case "JsxFragment": {
335+
yield equation(left, { kind: "Object", shapeId: BuiltInJsxId });
336+
break;
337+
}
330338
case "DeclareLocal":
331339
case "NewExpression":
332-
case "JsxExpression":
333-
case "JsxFragment":
334340
case "RegExpLiteral":
335341
case "PropertyStore":
336342
case "ComputedStore":

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-aliased-ref-in-callback-invoked-during-render-.expect.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ function Component(props) {
2222
7 | return <Foo item={item} current={current} />;
2323
8 | };
2424
> 9 | return <Items>{props.items.map((item) => renderItem(item))}</Items>;
25-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at mutate? $64[13:15] (9:9)
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at mutate? $64[13:15]:TObject<BuiltInFunction> (9:9)
2626
10 | }
2727
11 |
2828
```

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-ref-in-callback-invoked-during-render.expect.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function Component(props) {
2121
6 | return <Foo item={item} current={current} />;
2222
7 | };
2323
> 8 | return <Items>{props.items.map((item) => renderItem(item))}</Items>;
24-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at mutate? $60[14:16] (8:8)
24+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at mutate? $60[14:16]:TObject<BuiltInFunction> (8:8)
2525
9 | }
2626
10 |
2727
```

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/merge-consecutive-scopes-deps-subset-of-decls.expect.md

Lines changed: 8 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -33,44 +33,23 @@ import { c as _c } from "react/compiler-runtime";
3333
import { useState } from "react";
3434

3535
function Component() {
36-
const $ = _c(8);
36+
const $ = _c(2);
3737
const [count, setCount] = useState(0);
3838
let t0;
39-
let t1;
4039
if ($[0] !== count) {
41-
t0 = <button onClick={() => setCount(count - 1)}>Decrement</button>;
40+
t0 = (
41+
<div>
42+
<button onClick={() => setCount(count - 1)}>Decrement</button>
4243

43-
t1 = () => setCount(count + 1);
44+
<button onClick={() => setCount(count + 1)}>Increment</button>
45+
</div>
46+
);
4447
$[0] = count;
4548
$[1] = t0;
46-
$[2] = t1;
4749
} else {
4850
t0 = $[1];
49-
t1 = $[2];
50-
}
51-
let t2;
52-
if ($[3] !== t1) {
53-
t2 = <button onClick={t1}>Increment</button>;
54-
$[3] = t1;
55-
$[4] = t2;
56-
} else {
57-
t2 = $[4];
58-
}
59-
let t3;
60-
if ($[5] !== t0 || $[6] !== t2) {
61-
t3 = (
62-
<div>
63-
{t0}
64-
{t2}
65-
</div>
66-
);
67-
$[5] = t0;
68-
$[6] = t2;
69-
$[7] = t3;
70-
} else {
71-
t3 = $[7];
7251
}
73-
return t3;
52+
return t0;
7453
}
7554

7655
export const FIXTURE_ENTRYPOINT = {

0 commit comments

Comments
 (0)