diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/AnalyseFunctions.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/AnalyseFunctions.ts index fff9132103..c8726f4e4b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/AnalyseFunctions.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/AnalyseFunctions.ts @@ -42,8 +42,16 @@ export default function analyseFunctions(func: HIRFunction): void { * Reset mutable range for outer inferReferenceEffects */ for (const operand of instr.value.loweredFunc.func.context) { - operand.identifier.mutableRange.start = makeInstructionId(0); - operand.identifier.mutableRange.end = makeInstructionId(0); + /** + * NOTE: inferReactiveScopeVariables makes identifiers in the scope + * point to the *same* mutableRange instance. Resetting start/end + * here is insufficient, because a later mutation of the range + * for any one identifier could affect the range for other identifiers. + */ + operand.identifier.mutableRange = { + start: makeInstructionId(0), + end: makeInstructionId(0), + }; operand.identifier.scope = null; } break; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/repro-internal-compiler-shared-mutablerange-bug.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/repro-internal-compiler-shared-mutablerange-bug.expect.md new file mode 100644 index 0000000000..9a0c82a3cc --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/repro-internal-compiler-shared-mutablerange-bug.expect.md @@ -0,0 +1,80 @@ + +## Input + +```javascript +//@flow @validatePreserveExistingMemoizationGuarantees @enableNewMutationAliasingModel +component Component( + onAsyncSubmit?: (() => void) => void, + onClose: (isConfirmed: boolean) => void +) { + // When running inferReactiveScopeVariables, + // onAsyncSubmit and onClose update to share + // a mutableRange instance. + const onSubmit = useCallback(() => { + if (onAsyncSubmit) { + onAsyncSubmit(() => { + onClose(true); + }); + return; + } + }, [onAsyncSubmit, onClose]); + // When running inferReactiveScopeVariables here, + // first the existing range gets updated (affecting + // onAsyncSubmit) and then onClose gets assigned a + // different mutable range instance, which is the + // one reset after AnalyzeFunctions. + // The fix is to fully reset mutable ranges *instances* + // after AnalyzeFunctions visit a function expression + return onClose(false)} />; +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +function Component(t0) { + const $ = _c(8); + const { onAsyncSubmit, onClose } = t0; + let t1; + if ($[0] !== onAsyncSubmit || $[1] !== onClose) { + t1 = () => { + if (onAsyncSubmit) { + onAsyncSubmit(() => { + onClose(true); + }); + return; + } + }; + $[0] = onAsyncSubmit; + $[1] = onClose; + $[2] = t1; + } else { + t1 = $[2]; + } + const onSubmit = t1; + let t2; + if ($[3] !== onClose) { + t2 = () => onClose(false); + $[3] = onClose; + $[4] = t2; + } else { + t2 = $[4]; + } + let t3; + if ($[5] !== onSubmit || $[6] !== t2) { + t3 = ; + $[5] = onSubmit; + $[6] = t2; + $[7] = t3; + } else { + t3 = $[7]; + } + return t3; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/repro-internal-compiler-shared-mutablerange-bug.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/repro-internal-compiler-shared-mutablerange-bug.js new file mode 100644 index 0000000000..20cad06e97 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/repro-internal-compiler-shared-mutablerange-bug.js @@ -0,0 +1,25 @@ +//@flow @validatePreserveExistingMemoizationGuarantees @enableNewMutationAliasingModel +component Component( + onAsyncSubmit?: (() => void) => void, + onClose: (isConfirmed: boolean) => void +) { + // When running inferReactiveScopeVariables, + // onAsyncSubmit and onClose update to share + // a mutableRange instance. + const onSubmit = useCallback(() => { + if (onAsyncSubmit) { + onAsyncSubmit(() => { + onClose(true); + }); + return; + } + }, [onAsyncSubmit, onClose]); + // When running inferReactiveScopeVariables here, + // first the existing range gets updated (affecting + // onAsyncSubmit) and then onClose gets assigned a + // different mutable range instance, which is the + // one reset after AnalyzeFunctions. + // The fix is to fully reset mutable ranges *instances* + // after AnalyzeFunctions visit a function expression + return onClose(false)} />; +}