Repository: facebook/react
Pull request: [compiler] remove invariant to account for backedges


Fixes https://github.com/facebook/react/issues/32269, see comments for details.

Added test fixture for repro


Changed files:
filename: compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts
patch: @@ -1598,18 +1598,14 @@ function inferBlock(
         break;
       }
       case 'LoadLocal': {
+        /**
+         * Due to backedges in the CFG, we may revisit LoadLocal lvalues
+         * multiple times. Unlike StoreLocal which may reassign to existing
+         * identifiers, LoadLocal always evaluates to store a new temporary.
+         * This means that we should always model LoadLocal as a Capture effect
+         * on the rvalue.
+         */
         const lvalue = instr.lvalue;
-        CompilerError.invariant(
-          !(
-            state.isDefined(lvalue) &&
-            state.kind(lvalue).kind === ValueKind.Context
-          ),
-          {
-            reason:
-              '[InferReferenceEffects] Unexpected LoadLocal with context kind',
-            loc: lvalue.loc,
-          },
-        );
         state.referenceAndRecordEffects(
           freezeActions,
           instrValue.place,

filename: compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-backedge-reference-effect.expect.md
patch: @@ -0,0 +1,71 @@
+
+## Input
+
+```javascript
+import {Stringify} from 'shared-runtime';
+
+function Foo({userIds}) {
+  return (
+    <Stringify
+      fn={() => {
+        const arr = [];
+
+        for (const selectedUser of userIds) {
+          arr.push(selectedUser);
+        }
+        return arr;
+      }}
+      shouldInvokeFns={true}
+    />
+  );
+}
+
+export const FIXTURE_ENTRYPOINT = {
+  fn: Foo,
+  params: [{userIds: [1, 2, 3]}],
+  sequentialRenders: [{userIds: [1, 2, 4]}],
+};
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime";
+import { Stringify } from "shared-runtime";
+
+function Foo(t0) {
+  const $ = _c(2);
+  const { userIds } = t0;
+  let t1;
+  if ($[0] !== userIds) {
+    t1 = (
+      <Stringify
+        fn={() => {
+          const arr = [];
+          for (const selectedUser of userIds) {
+            arr.push(selectedUser);
+          }
+          return arr;
+        }}
+        shouldInvokeFns={true}
+      />
+    );
+    $[0] = userIds;
+    $[1] = t1;
+  } else {
+    t1 = $[1];
+  }
+  return t1;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+  fn: Foo,
+  params: [{ userIds: [1, 2, 3] }],
+  sequentialRenders: [{ userIds: [1, 2, 4] }],
+};
+
+```
+      
+### Eval output
+(kind: ok) <div>{"fn":{"kind":"Function","result":[1,2,4]},"shouldInvokeFns":true}</div>
\ No newline at end of file

filename: compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-backedge-reference-effect.js
patch: @@ -0,0 +1,23 @@
+import {Stringify} from 'shared-runtime';
+
+function Foo({userIds}) {
+  return (
+    <Stringify
+      fn={() => {
+        const arr = [];
+
+        for (const selectedUser of userIds) {
+          arr.push(selectedUser);
+        }
+        return arr;
+      }}
+      shouldInvokeFns={true}
+    />
+  );
+}
+
+export const FIXTURE_ENTRYPOINT = {
+  fn: Foo,
+  params: [{userIds: [1, 2, 3]}],
+  sequentialRenders: [{userIds: [1, 2, 4]}],
+};