diff --git a/compiler/packages/babel-plugin-react-forget/src/HIR/ObjectShape.ts b/compiler/packages/babel-plugin-react-forget/src/HIR/ObjectShape.ts index 5910f7327a..05ae9fe108 100644 --- a/compiler/packages/babel-plugin-react-forget/src/HIR/ObjectShape.ts +++ b/compiler/packages/babel-plugin-react-forget/src/HIR/ObjectShape.ts @@ -203,7 +203,7 @@ addObject(BUILTIN_SHAPES, BuiltInArrayId, [ positionalParams: [Effect.Read], restParam: null, returnType: { kind: "Poly" }, - calleeEffect: Effect.Read, + calleeEffect: Effect.Capture, returnValueKind: ValueKind.Mutable, }), ], diff --git a/compiler/packages/babel-plugin-react-forget/src/Inference/InferReferenceEffects.ts b/compiler/packages/babel-plugin-react-forget/src/Inference/InferReferenceEffects.ts index 974e6b83a3..a439d4f663 100644 --- a/compiler/packages/babel-plugin-react-forget/src/Inference/InferReferenceEffects.ts +++ b/compiler/packages/babel-plugin-react-forget/src/Inference/InferReferenceEffects.ts @@ -809,6 +809,7 @@ function inferBlock( signature !== null ? getFunctionEffects(instrValue, signature) : null; const returnValueKind = signature !== null ? signature.returnValueKind : ValueKind.Mutable; + let hasCaptureArgument = false; for (let i = 0; i < instrValue.args.length; i++) { const arg = instrValue.args[i]; const place = arg.kind === "Identifier" ? arg : arg.place; @@ -817,16 +818,20 @@ function inferBlock( } else { state.reference(place, Effect.ConditionallyMutate); } + hasCaptureArgument ||= place.effect === Effect.Capture; } if (signature !== null) { state.reference(instrValue.callee, signature.calleeEffect); } else { state.reference(instrValue.callee, Effect.ConditionallyMutate); } + hasCaptureArgument ||= instrValue.callee.effect === Effect.Capture; state.initialize(instrValue, returnValueKind); state.define(instr.lvalue, instrValue); - instr.lvalue.effect = Effect.ConditionallyMutate; + instr.lvalue.effect = hasCaptureArgument + ? Effect.Store + : Effect.ConditionallyMutate; continue; } case "MethodCall": { @@ -871,6 +876,7 @@ function inferBlock( signature !== null ? getFunctionEffects(instrValue, signature) : null; const returnValueKind = signature !== null ? signature.returnValueKind : ValueKind.Mutable; + let hasCaptureArgument = false; for (let i = 0; i < instrValue.args.length; i++) { const arg = instrValue.args[i]; const place = arg.kind === "Identifier" ? arg : arg.place; @@ -883,16 +889,20 @@ function inferBlock( } else { state.reference(place, Effect.ConditionallyMutate); } + hasCaptureArgument ||= place.effect === Effect.Capture; } if (signature !== null) { state.reference(instrValue.receiver, signature.calleeEffect); } else { state.reference(instrValue.receiver, Effect.ConditionallyMutate); } + hasCaptureArgument ||= instrValue.receiver.effect === Effect.Capture; state.initialize(instrValue, returnValueKind); state.define(instr.lvalue, instrValue); - instr.lvalue.effect = Effect.ConditionallyMutate; + instr.lvalue.effect = hasCaptureArgument + ? Effect.Store + : Effect.ConditionallyMutate; continue; } case "PropertyStore": { diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/array-at-mutate-after-capture.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/array-at-mutate-after-capture.expect.md index 121d085be1..c0ef5e5a03 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/array-at-mutate-after-capture.expect.md +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/array-at-mutate-after-capture.expect.md @@ -21,18 +21,18 @@ function Component(props) { import { unstable_useMemoCache as useMemoCache } from "react"; // x's mutable range should extend to `mutate(y)` function Component(props) { - const $ = useMemoCache(1); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = [42, {}]; - $[0] = t0; + const $ = useMemoCache(2); + let x; + if ($[0] !== props.b) { + x = [42, {}]; + const idx = foo(props.b); + const y = x.at(idx); + mutate(y); + $[0] = props.b; + $[1] = x; } else { - t0 = $[0]; + x = $[1]; } - const x = t0; - const idx = foo(props.b); - const y = x.at(idx); - mutate(y); return x; } diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/assignment-variations-complex-lvalue-array.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/assignment-variations-complex-lvalue-array.expect.md index 451f29f572..7d045bc764 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/assignment-variations-complex-lvalue-array.expect.md +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/assignment-variations-complex-lvalue-array.expect.md @@ -23,16 +23,15 @@ export const FIXTURE_ENTRYPOINT = { import { unstable_useMemoCache as useMemoCache } from "react"; function foo() { const $ = useMemoCache(1); - let t0; + let a; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = [[1]]; - $[0] = t0; + a = [[1]]; + const first = a.at(0); + first.set(0, 2); + $[0] = a; } else { - t0 = $[0]; + a = $[0]; } - const a = t0; - const first = a.at(0); - first.set(0, 2); return a; }