diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts
index 206bfc0bca1a4..90a352620ce35 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts
+++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts
@@ -246,7 +246,7 @@ export const EnvironmentConfigSchema = z.object({
/**
* Enable a new model for mutability and aliasing inference
*/
- enableNewMutationAliasingModel: z.boolean().default(false),
+ enableNewMutationAliasingModel: z.boolean().default(true),
/**
* Enables inference of optional dependency chains. Without this flag
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 fff913210347d..c8726f4e4b7bb 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/Inference/InferMutationAliasingEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts
index 19f0d84b9a8ce..f0ecb6754607a 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts
+++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts
@@ -901,11 +901,36 @@ function applyEffect(
console.log(prettyFormat(state.debugAbstractValue(value)));
}
- const reason = getWriteErrorReason({
- kind: value.kind,
- reason: value.reason,
- context: new Set(),
- });
+ let reason: string;
+ let description: string | null = null;
+
+ if (
+ mutationKind === 'mutate-frozen' &&
+ context.hoistedContextDeclarations.has(
+ effect.value.identifier.declarationId,
+ )
+ ) {
+ reason = `This variable is accessed before it is declared, which prevents the earlier access from updating when this value changes over time`;
+ if (
+ effect.value.identifier.name !== null &&
+ effect.value.identifier.name.kind === 'named'
+ ) {
+ description = `Move the declaration of \`${effect.value.identifier.name.value}\` to before it is first referenced`;
+ }
+ } else {
+ reason = getWriteErrorReason({
+ kind: value.kind,
+ reason: value.reason,
+ context: new Set(),
+ });
+ if (
+ effect.value.identifier.name !== null &&
+ effect.value.identifier.name.kind === 'named'
+ ) {
+ description = `Found mutation of \`${effect.value.identifier.name.value}\``;
+ }
+ }
+
effects.push({
kind:
value.kind === ValueKind.Frozen ? 'MutateFrozen' : 'MutateGlobal',
@@ -913,11 +938,7 @@ function applyEffect(
error: {
severity: ErrorSeverity.InvalidReact,
reason,
- description:
- effect.value.identifier.name !== null &&
- effect.value.identifier.name.kind === 'named'
- ? `Found mutation of \`${effect.value.identifier.name.value}\``
- : null,
+ description,
loc: effect.value.loc,
suggestions: null,
},
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-truncated-dep.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-truncated-dep.expect.md
index 933fafff5f1ba..12c7b4d5eab93 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-truncated-dep.expect.md
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/aliased-nested-scope-truncated-dep.expect.md
@@ -175,21 +175,14 @@ import {
* and mutability.
*/
function Component(t0) {
- const $ = _c(4);
+ const $ = _c(2);
const { prop } = t0;
let t1;
if ($[0] !== prop) {
const obj = shallowCopy(prop);
const aliasedObj = identity(obj);
- let t2;
- if ($[2] !== obj) {
- t2 = [obj.id];
- $[2] = obj;
- $[3] = t2;
- } else {
- t2 = $[3];
- }
- const id = t2;
+
+ const id = [obj.id];
mutate(aliasedObj);
setPropertyByKey(aliasedObj, "id", prop.id + 1);
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-2-iife.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-2-iife.expect.md
index 2afc5fd25dbac..50480f1b2515e 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-2-iife.expect.md
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-2-iife.expect.md
@@ -25,17 +25,25 @@ export const FIXTURE_ENTRYPOINT = {
```javascript
import { c as _c } from "react/compiler-runtime";
function bar(a) {
- const $ = _c(2);
- let y;
+ const $ = _c(4);
+ let t0;
if ($[0] !== a) {
- const x = [a];
+ t0 = [a];
+ $[0] = a;
+ $[1] = t0;
+ } else {
+ t0 = $[1];
+ }
+ const x = t0;
+ let y;
+ if ($[2] !== x[0][1]) {
y = {};
y = x[0][1];
- $[0] = a;
- $[1] = y;
+ $[2] = x[0][1];
+ $[3] = y;
} else {
- y = $[1];
+ y = $[3];
}
return y;
}
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-3-iife.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-3-iife.expect.md
index f0267c3309f5b..9678918b3d27f 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-3-iife.expect.md
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-3-iife.expect.md
@@ -29,20 +29,29 @@ export const FIXTURE_ENTRYPOINT = {
```javascript
import { c as _c } from "react/compiler-runtime";
function bar(a, b) {
- const $ = _c(3);
- let y;
+ const $ = _c(6);
+ let t0;
if ($[0] !== a || $[1] !== b) {
- const x = [a, b];
+ t0 = [a, b];
+ $[0] = a;
+ $[1] = b;
+ $[2] = t0;
+ } else {
+ t0 = $[2];
+ }
+ const x = t0;
+ let y;
+ if ($[3] !== x[0][1] || $[4] !== x[1][0]) {
y = {};
let t = {};
y = x[0][1];
t = x[1][0];
- $[0] = a;
- $[1] = b;
- $[2] = y;
+ $[3] = x[0][1];
+ $[4] = x[1][0];
+ $[5] = y;
} else {
- y = $[2];
+ y = $[5];
}
return y;
}
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-4-iife.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-4-iife.expect.md
index 22728aaf4323d..edddf3715a453 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-4-iife.expect.md
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-4-iife.expect.md
@@ -25,17 +25,25 @@ export const FIXTURE_ENTRYPOINT = {
```javascript
import { c as _c } from "react/compiler-runtime";
function bar(a) {
- const $ = _c(2);
- let y;
+ const $ = _c(4);
+ let t0;
if ($[0] !== a) {
- const x = [a];
+ t0 = [a];
+ $[0] = a;
+ $[1] = t0;
+ } else {
+ t0 = $[1];
+ }
+ const x = t0;
+ let y;
+ if ($[2] !== x[0].a[1]) {
y = {};
y = x[0].a[1];
- $[0] = a;
- $[1] = y;
+ $[2] = x[0].a[1];
+ $[3] = y;
} else {
- y = $[1];
+ y = $[3];
}
return y;
}
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-iife.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-iife.expect.md
index 60f829cdc4d66..c9ce6dda9f627 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-iife.expect.md
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capturing-function-alias-computed-load-iife.expect.md
@@ -24,17 +24,25 @@ export const FIXTURE_ENTRYPOINT = {
```javascript
import { c as _c } from "react/compiler-runtime";
function bar(a) {
- const $ = _c(2);
- let y;
+ const $ = _c(4);
+ let t0;
if ($[0] !== a) {
- const x = [a];
+ t0 = [a];
+ $[0] = a;
+ $[1] = t0;
+ } else {
+ t0 = $[1];
+ }
+ const x = t0;
+ let y;
+ if ($[2] !== x[0]) {
y = {};
y = x[0];
- $[0] = a;
- $[1] = y;
+ $[2] = x[0];
+ $[3] = y;
} else {
- y = $[1];
+ y = $[3];
}
return y;
}
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-hoisting-setstate.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-hoisting-setstate.expect.md
index fcd5dcc698e2b..3fcc84c9a48d3 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-hoisting-setstate.expect.md
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-hoisting-setstate.expect.md
@@ -41,7 +41,7 @@ export const FIXTURE_ENTRYPOINT = {
19 | useEffect(() => setState(2), []);
20 |
> 21 | const [state, setState] = useState(0);
- | ^^^^^^^^ InvalidReact: Updating a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the mutation before calling useEffect(). Found mutation of `setState` (21:21)
+ | ^^^^^^^^ InvalidReact: This variable is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. Move the declaration of `setState` to before it is first referenced (21:21)
22 | return ;
23 | }
24 |
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-impure-functions-in-render.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-impure-functions-in-render.expect.md
index a67d467df8cfd..0fb17a8f6eab4 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-impure-functions-in-render.expect.md
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-impure-functions-in-render.expect.md
@@ -20,7 +20,7 @@ function Component() {
2 |
3 | function Component() {
> 4 | const date = Date.now();
- | ^^^^^^^^ InvalidReact: Calling an impure function can produce unstable results. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent). `Date.now` is an impure function whose results may change on every call (4:4)
+ | ^^^^^^^^^^ InvalidReact: Calling an impure function can produce unstable results. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent). `Date.now` is an impure function whose results may change on every call (4:4)
InvalidReact: Calling an impure function can produce unstable results. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent). `performance.now` is an impure function whose results may change on every call (5:5)
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-non-imported-reanimated-shared-value-writes.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-non-imported-reanimated-shared-value-writes.expect.md
index f1399a41b6fec..d3bb7f413622b 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-non-imported-reanimated-shared-value-writes.expect.md
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-non-imported-reanimated-shared-value-writes.expect.md
@@ -27,7 +27,7 @@ function SomeComponent() {
9 | return (
10 |