[compiler] remove invariant to account for backedges (#32417)
Fixes https://github.com/facebook/react/issues/32269, see comments for details. Added test fixture for repro
mofeiZ committed
Feb 19, 2025 at 14:22 UTC
fcb4e0f137db6efa6e180ad12dcfaabd0c590219
3 files changed
+101
-11
compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts
+7
-11
@@ -1598,18 +1598,14 @@ function inferBlock(
1598
break;
1599
}
1600
case 'LoadLocal': {
1601
+ /**
1602
+ * Due to backedges in the CFG, we may revisit LoadLocal lvalues
1603
+ * multiple times. Unlike StoreLocal which may reassign to existing
1604
+ * identifiers, LoadLocal always evaluates to store a new temporary.
1605
+ * This means that we should always model LoadLocal as a Capture effect
1606
+ * on the rvalue.
1607
+ */
1608
const lvalue = instr.lvalue;
1602
- CompilerError.invariant(
1603
- !(
1604
- state.isDefined(lvalue) &&
1605
- state.kind(lvalue).kind === ValueKind.Context
1606
- ),
1607
- {
1608
- reason:
1609
- '[InferReferenceEffects] Unexpected LoadLocal with context kind',
1610
- loc: lvalue.loc,
1611
- },
1612
- );
1609
state.referenceAndRecordEffects(
1610
freezeActions,
1611
instrValue.place,
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-backedge-reference-effect.expect.md
new
+71
@@ -0,0 +1,71 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+import {Stringify} from 'shared-runtime';
6
+
7
+function Foo({userIds}) {
8
+ return (
9
+ <Stringify
10
+ fn={() => {
11
+ const arr = [];
12
+
13
+ for (const selectedUser of userIds) {
14
+ arr.push(selectedUser);
15
+ }
16
+ return arr;
17
+ }}
18
+ shouldInvokeFns={true}
19
+ />
20
+ );
21
+}
22
+
23
+export const FIXTURE_ENTRYPOINT = {
24
+ fn: Foo,
25
+ params: [{userIds: [1, 2, 3]}],
26
+ sequentialRenders: [{userIds: [1, 2, 4]}],
27
+};
28
+
29
+```
30
+
31
+## Code
32
+
33
+```javascript
34
+import { c as _c } from "react/compiler-runtime";
35
+import { Stringify } from "shared-runtime";
36
+
37
+function Foo(t0) {
38
+ const $ = _c(2);
39
+ const { userIds } = t0;
40
+ let t1;
41
+ if ($[0] !== userIds) {
42
+ t1 = (
43
+ <Stringify
44
+ fn={() => {
45
+ const arr = [];
46
+ for (const selectedUser of userIds) {
47
+ arr.push(selectedUser);
48
+ }
49
+ return arr;
50
+ }}
51
+ shouldInvokeFns={true}
52
+ />
53
+ );
54
+ $[0] = userIds;
55
+ $[1] = t1;
56
+ } else {
57
+ t1 = $[1];
58
+ }
59
+ return t1;
60
+}
61
+
62
+export const FIXTURE_ENTRYPOINT = {
63
+ fn: Foo,
64
+ params: [{ userIds: [1, 2, 3] }],
65
+ sequentialRenders: [{ userIds: [1, 2, 4] }],
66
+};
67
+
68
+```
69
+
70
+### Eval output
71
+(kind: ok) <div>{"fn":{"kind":"Function","result":[1,2,4]},"shouldInvokeFns":true}</div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-backedge-reference-effect.js
new
+23
@@ -0,0 +1,23 @@
1
+import {Stringify} from 'shared-runtime';
2
+
3
+function Foo({userIds}) {
4
+ return (
5
+ <Stringify
6
+ fn={() => {
7
+ const arr = [];
8
+
9
+ for (const selectedUser of userIds) {
10
+ arr.push(selectedUser);
11
+ }
12
+ return arr;
13
+ }}
14
+ shouldInvokeFns={true}
15
+ />
16
+ );
17
+}
18
+
19
+export const FIXTURE_ENTRYPOINT = {
20
+ fn: Foo,
21
+ params: [{userIds: [1, 2, 3]}],
22
+ sequentialRenders: [{userIds: [1, 2, 4]}],
23
+};