[compiler] Fix <ValidateMemoization> (#33547)
By accident we were only ever checking the compiled output, but the intention was in general to be able to compare memoization with/without forget. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33547). * #33571 * #33558 * __->__ #33547
Joseph Savona committed
Jun 18, 2025 at 16:00 UTC
3a2ff8b51b5ab54c22f55a5f826c53419f718887
21 files changed
+141
-74
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/todo-control-flow-sensitive-mutation.expect.md
+4
-9
@@ -23,14 +23,9 @@ function Component({a, b, c}: {a: number; b: number; c: number}) {
23
24
return (
25
<>
26
- <ValidateMemoization inputs={[a, b, c]} output={x} alwaysCheck={true} />;
26
+ <ValidateMemoization inputs={[a, b, c]} output={x} />;
27
{/* TODO: should only depend on c */}
28
- <ValidateMemoization
29
- inputs={[a, b, c]}
30
- output={x[0]}
31
- alwaysCheck={true}
32
- />
33
- ;
28
+ <ValidateMemoization inputs={[a, b, c]} output={x[0]} />;
29
</>
30
);
31
}
@@ -98,7 +93,7 @@ function Component(t0) {
93
}
94
let t3;
95
if ($[9] !== t2 || $[10] !== x) {
101
- t3 = <ValidateMemoization inputs={t2} output={x} alwaysCheck={true} />;
96
+ t3 = <ValidateMemoization inputs={t2} output={x} />;
97
$[9] = t2;
98
$[10] = x;
99
$[11] = t3;
@@ -117,7 +112,7 @@ function Component(t0) {
112
}
113
let t5;
114
if ($[16] !== t4 || $[17] !== x[0]) {
120
- t5 = <ValidateMemoization inputs={t4} output={x[0]} alwaysCheck={true} />;
115
+ t5 = <ValidateMemoization inputs={t4} output={x[0]} />;
116
$[16] = t4;
117
$[17] = x[0];
118
$[18] = t5;
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/todo-control-flow-sensitive-mutation.tsx
+2
-7
@@ -19,14 +19,9 @@ function Component({a, b, c}: {a: number; b: number; c: number}) {
19
20
return (
21
<>
22
- <ValidateMemoization inputs={[a, b, c]} output={x} alwaysCheck={true} />;
22
+ <ValidateMemoization inputs={[a, b, c]} output={x} />;
23
{/* TODO: should only depend on c */}
24
- <ValidateMemoization
25
- inputs={[a, b, c]}
26
- output={x[0]}
27
- alwaysCheck={true}
28
- />
29
- ;
24
+ <ValidateMemoization inputs={[a, b, c]} output={x[0]} />;
25
</>
26
);
27
}
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/todo-transitivity-createfrom-capture-lambda.expect.md
+2
-2
@@ -22,7 +22,7 @@ function Component({a, b}) {
22
typedMutate(z, b);
23
24
// TODO: this *should* only depend on `a`
25
- return <ValidateMemoization inputs={[a, b]} output={x} alwaysCheck={true} />;
25
+ return <ValidateMemoization inputs={[a, b]} output={x} />;
26
}
27
28
export const FIXTURE_ENTRYPOINT = {
@@ -86,7 +86,7 @@ function Component(t0) {
86
}
87
let t3;
88
if ($[7] !== t2 || $[8] !== x) {
89
- t3 = <ValidateMemoization inputs={t2} output={x} alwaysCheck={true} />;
89
+ t3 = <ValidateMemoization inputs={t2} output={x} />;
90
$[7] = t2;
91
$[8] = x;
92
$[9] = t3;
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/todo-transitivity-createfrom-capture-lambda.tsx
+1
-1
@@ -18,7 +18,7 @@ function Component({a, b}) {
18
typedMutate(z, b);
19
20
// TODO: this *should* only depend on `a`
21
- return <ValidateMemoization inputs={[a, b]} output={x} alwaysCheck={true} />;
21
+ return <ValidateMemoization inputs={[a, b]} output={x} />;
22
}
23
24
export const FIXTURE_ENTRYPOINT = {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/transitivity-add-captured-array-to-itself.expect.md
+4
-4
@@ -20,8 +20,8 @@ function Component({a, b}) {
20
21
return (
22
<>
23
- <ValidateMemoization inputs={[a]} output={o} alwaysCheck={true} />;
24
- <ValidateMemoization inputs={[a, b]} output={x} alwaysCheck={true} />;
23
+ <ValidateMemoization inputs={[a]} output={o} />;
24
+ <ValidateMemoization inputs={[a, b]} output={x} />;
25
</>
26
);
27
}
@@ -92,7 +92,7 @@ function Component(t0) {
92
}
93
let t5;
94
if ($[8] !== o || $[9] !== t4) {
95
- t5 = <ValidateMemoization inputs={t4} output={o} alwaysCheck={true} />;
95
+ t5 = <ValidateMemoization inputs={t4} output={o} />;
96
$[8] = o;
97
$[9] = t4;
98
$[10] = t5;
@@ -110,7 +110,7 @@ function Component(t0) {
110
}
111
let t7;
112
if ($[14] !== t6 || $[15] !== x) {
113
- t7 = <ValidateMemoization inputs={t6} output={x} alwaysCheck={true} />;
113
+ t7 = <ValidateMemoization inputs={t6} output={x} />;
114
$[14] = t6;
115
$[15] = x;
116
$[16] = t7;
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/transitivity-add-captured-array-to-itself.tsx
+2
-2
@@ -16,8 +16,8 @@ function Component({a, b}) {
16
17
return (
18
<>
19
- <ValidateMemoization inputs={[a]} output={o} alwaysCheck={true} />;
20
- <ValidateMemoization inputs={[a, b]} output={x} alwaysCheck={true} />;
19
+ <ValidateMemoization inputs={[a]} output={o} />;
20
+ <ValidateMemoization inputs={[a, b]} output={x} />;
21
</>
22
);
23
}
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/transitivity-capture-createfrom-lambda.expect.md
+2
-2
@@ -21,7 +21,7 @@ function Component({a, b}: {a: number; b: number}) {
21
// mutates x
22
typedMutate(z, b);
23
24
- return <ValidateMemoization inputs={[a, b]} output={x} alwaysCheck={true} />;
24
+ return <ValidateMemoization inputs={[a, b]} output={x} />;
25
}
26
27
export const FIXTURE_ENTRYPOINT = {
@@ -85,7 +85,7 @@ function Component(t0) {
85
}
86
let t3;
87
if ($[7] !== t2 || $[8] !== x) {
88
- t3 = <ValidateMemoization inputs={t2} output={x} alwaysCheck={true} />;
88
+ t3 = <ValidateMemoization inputs={t2} output={x} />;
89
$[7] = t2;
90
$[8] = x;
91
$[9] = t3;
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/transitivity-capture-createfrom-lambda.tsx
+1
-1
@@ -17,7 +17,7 @@ function Component({a, b}: {a: number; b: number}) {
17
// mutates x
18
typedMutate(z, b);
19
20
- return <ValidateMemoization inputs={[a, b]} output={x} alwaysCheck={true} />;
20
+ return <ValidateMemoization inputs={[a, b]} output={x} />;
21
}
22
23
export const FIXTURE_ENTRYPOINT = {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/transitivity-capture-createfrom.expect.md
+2
-2
@@ -17,7 +17,7 @@ function Component({a, b}: {a: number; b: number}) {
17
// mutates x
18
typedMutate(z, b);
19
20
- return <ValidateMemoization inputs={[a, b]} output={x} alwaysCheck={true} />;
20
+ return <ValidateMemoization inputs={[a, b]} output={x} />;
21
}
22
23
export const FIXTURE_ENTRYPOINT = {
@@ -76,7 +76,7 @@ function Component(t0) {
76
}
77
let t3;
78
if ($[7] !== t2 || $[8] !== x) {
79
- t3 = <ValidateMemoization inputs={t2} output={x} alwaysCheck={true} />;
79
+ t3 = <ValidateMemoization inputs={t2} output={x} />;
80
$[7] = t2;
81
$[8] = x;
82
$[9] = t3;
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/transitivity-capture-createfrom.tsx
+1
-1
@@ -13,7 +13,7 @@ function Component({a, b}: {a: number; b: number}) {
13
// mutates x
14
typedMutate(z, b);
15
16
- return <ValidateMemoization inputs={[a, b]} output={x} alwaysCheck={true} />;
16
+ return <ValidateMemoization inputs={[a, b]} output={x} />;
17
}
18
19
export const FIXTURE_ENTRYPOINT = {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/transitivity-createfrom-capture.expect.md
+2
-2
@@ -17,7 +17,7 @@ function Component({a, b}) {
17
// does not mutate x, so x should not depend on b
18
typedMutate(z, b);
19
20
- return <ValidateMemoization inputs={[a]} output={x} alwaysCheck={true} />;
20
+ return <ValidateMemoization inputs={[a]} output={x} />;
21
}
22
23
export const FIXTURE_ENTRYPOINT = {
@@ -73,7 +73,7 @@ function Component(t0) {
73
}
74
let t4;
75
if ($[4] !== t3 || $[5] !== x) {
76
- t4 = <ValidateMemoization inputs={t3} output={x} alwaysCheck={true} />;
76
+ t4 = <ValidateMemoization inputs={t3} output={x} />;
77
$[4] = t3;
78
$[5] = x;
79
$[6] = t4;
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/transitivity-createfrom-capture.tsx
+1
-1
@@ -13,7 +13,7 @@ function Component({a, b}) {
13
// does not mutate x, so x should not depend on b
14
typedMutate(z, b);
15
16
- return <ValidateMemoization inputs={[a]} output={x} alwaysCheck={true} />;
16
+ return <ValidateMemoization inputs={[a]} output={x} />;
17
}
18
19
export const FIXTURE_ENTRYPOINT = {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/transitivity-phi-assign-or-capture.expect.md
+2
-2
@@ -21,7 +21,7 @@ function Component({a, b}) {
21
// could mutate x
22
typedMutate(z, b);
23
24
- return <ValidateMemoization inputs={[a, b]} output={x} alwaysCheck={true} />;
24
+ return <ValidateMemoization inputs={[a, b]} output={x} />;
25
}
26
27
export const FIXTURE_ENTRYPOINT = {
@@ -92,7 +92,7 @@ function Component(t0) {
92
}
93
let t4;
94
if ($[9] !== t3 || $[10] !== x) {
95
- t4 = <ValidateMemoization inputs={t3} output={x} alwaysCheck={true} />;
95
+ t4 = <ValidateMemoization inputs={t3} output={x} />;
96
$[9] = t3;
97
$[10] = x;
98
$[11] = t4;
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/transitivity-phi-assign-or-capture.tsx
+1
-1
@@ -17,7 +17,7 @@ function Component({a, b}) {
17
// could mutate x
18
typedMutate(z, b);
19
20
- return <ValidateMemoization inputs={[a, b]} output={x} alwaysCheck={true} />;
20
+ return <ValidateMemoization inputs={[a, b]} output={x} />;
21
}
22
23
export const FIXTURE_ENTRYPOINT = {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/typed-identity-function-frozen-input.expect.md
+19
-15
@@ -4,6 +4,7 @@
4
```javascript
5
// @enableNewMutationAliasingModel
6
7
+import {useMemo} from 'react';
8
import {
9
identity,
10
makeObject_Primitives,
@@ -14,7 +15,7 @@ import {
15
16
function Component({a, b}) {
17
// create a mutable value with input `a`
17
- const x = makeObject_Primitives(a);
18
+ const x = useMemo(() => makeObject_Primitives(a), [a]);
19
20
// freeze the value
21
useIdentity(x);
@@ -49,6 +50,7 @@ export const FIXTURE_ENTRYPOINT = {
50
```javascript
51
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel
52
53
+import { useMemo } from "react";
54
import {
55
identity,
56
makeObject_Primitives,
@@ -61,13 +63,15 @@ function Component(t0) {
63
const $ = _c(7);
64
const { a, b } = t0;
65
let t1;
66
+ let t2;
67
if ($[0] !== a) {
65
- t1 = makeObject_Primitives(a);
68
+ t2 = makeObject_Primitives(a);
69
$[0] = a;
67
- $[1] = t1;
70
+ $[1] = t2;
71
} else {
69
- t1 = $[1];
72
+ t2 = $[1];
73
}
74
+ t1 = t2;
75
const x = t1;
76
77
useIdentity(x);
@@ -75,24 +79,24 @@ function Component(t0) {
79
const x2 = typedIdentity(x);
80
81
identity(x2, b);
78
- let t2;
82
+ let t3;
83
if ($[2] !== a) {
80
- t2 = [a];
84
+ t3 = [a];
85
$[2] = a;
82
- $[3] = t2;
86
+ $[3] = t3;
87
} else {
84
- t2 = $[3];
88
+ t3 = $[3];
89
}
86
- let t3;
87
- if ($[4] !== t2 || $[5] !== x) {
88
- t3 = <ValidateMemoization inputs={t2} output={x} />;
89
- $[4] = t2;
90
+ let t4;
91
+ if ($[4] !== t3 || $[5] !== x) {
92
+ t4 = <ValidateMemoization inputs={t3} output={x} />;
93
+ $[4] = t3;
94
$[5] = x;
91
- $[6] = t3;
95
+ $[6] = t4;
96
} else {
93
- t3 = $[6];
97
+ t4 = $[6];
98
}
95
- return t3;
99
+ return t4;
100
}
101
102
export const FIXTURE_ENTRYPOINT = {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/typed-identity-function-frozen-input.js
+2
-1
@@ -1,5 +1,6 @@
1
// @enableNewMutationAliasingModel
2
3
+import {useMemo} from 'react';
4
import {
5
identity,
6
makeObject_Primitives,
@@ -10,7 +11,7 @@ import {
11
12
function Component({a, b}) {
13
// create a mutable value with input `a`
13
- const x = makeObject_Primitives(a);
14
+ const x = useMemo(() => makeObject_Primitives(a), [a]);
15
16
// freeze the value
17
useIdentity(x);
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/weakmap-constructor.expect.md
+30
-6
@@ -2,6 +2,7 @@
2
## Input
3
4
```javascript
5
+import {useMemo} from 'react';
6
import {ValidateMemoization} from 'shared-runtime';
7
8
function Component({a, b, c}) {
@@ -13,9 +14,21 @@ function Component({a, b, c}) {
14
15
return (
16
<>
16
- <ValidateMemoization inputs={[a, c]} output={map} />
17
- <ValidateMemoization inputs={[a, c]} output={mapAlias} />
18
- <ValidateMemoization inputs={[b]} output={[hasB]} />
17
+ <ValidateMemoization
18
+ inputs={[a, c]}
19
+ output={map}
20
+ onlyCheckCompiled={true}
21
+ />
22
+ <ValidateMemoization
23
+ inputs={[a, c]}
24
+ output={mapAlias}
25
+ onlyCheckCompiled={true}
26
+ />
27
+ <ValidateMemoization
28
+ inputs={[b]}
29
+ output={[hasB]}
30
+ onlyCheckCompiled={true}
31
+ />
32
</>
33
);
34
}
@@ -44,6 +57,7 @@ export const FIXTURE_ENTRYPOINT = {
57
58
```javascript
59
import { c as _c } from "react/compiler-runtime";
60
+import { useMemo } from "react";
61
import { ValidateMemoization } from "shared-runtime";
62
63
function Component(t0) {
@@ -76,7 +90,9 @@ function Component(t0) {
90
}
91
let t2;
92
if ($[7] !== map || $[8] !== t1) {
79
- t2 = <ValidateMemoization inputs={t1} output={map} />;
93
+ t2 = (
94
+ <ValidateMemoization inputs={t1} output={map} onlyCheckCompiled={true} />
95
+ );
96
$[7] = map;
97
$[8] = t1;
98
$[9] = t2;
@@ -94,7 +110,13 @@ function Component(t0) {
110
}
111
let t4;
112
if ($[13] !== mapAlias || $[14] !== t3) {
97
- t4 = <ValidateMemoization inputs={t3} output={mapAlias} />;
113
+ t4 = (
114
+ <ValidateMemoization
115
+ inputs={t3}
116
+ output={mapAlias}
117
+ onlyCheckCompiled={true}
118
+ />
119
+ );
120
$[13] = mapAlias;
121
$[14] = t3;
122
$[15] = t4;
@@ -119,7 +141,9 @@ function Component(t0) {
141
}
142
let t7;
143
if ($[20] !== t5 || $[21] !== t6) {
122
- t7 = <ValidateMemoization inputs={t5} output={t6} />;
144
+ t7 = (
145
+ <ValidateMemoization inputs={t5} output={t6} onlyCheckCompiled={true} />
146
+ );
147
$[20] = t5;
148
$[21] = t6;
149
$[22] = t7;
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/weakmap-constructor.js
+16
-3
@@ -1,3 +1,4 @@
1
+import {useMemo} from 'react';
2
import {ValidateMemoization} from 'shared-runtime';
3
4
function Component({a, b, c}) {
@@ -9,9 +10,21 @@ function Component({a, b, c}) {
10
11
return (
12
<>
12
- <ValidateMemoization inputs={[a, c]} output={map} />
13
- <ValidateMemoization inputs={[a, c]} output={mapAlias} />
14
- <ValidateMemoization inputs={[b]} output={[hasB]} />
13
+ <ValidateMemoization
14
+ inputs={[a, c]}
15
+ output={map}
16
+ onlyCheckCompiled={true}
17
+ />
18
+ <ValidateMemoization
19
+ inputs={[a, c]}
20
+ output={mapAlias}
21
+ onlyCheckCompiled={true}
22
+ />
23
+ <ValidateMemoization
24
+ inputs={[b]}
25
+ output={[hasB]}
26
+ onlyCheckCompiled={true}
27
+ />
28
</>
29
);
30
}
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/weakset-constructor.expect.md
+30
-6
@@ -2,6 +2,7 @@
2
## Input
3
4
```javascript
5
+import {useMemo} from 'react';
6
import {ValidateMemoization} from 'shared-runtime';
7
8
function Component({a, b, c}) {
@@ -13,9 +14,21 @@ function Component({a, b, c}) {
14
15
return (
16
<>
16
- <ValidateMemoization inputs={[a, c]} output={set} />
17
- <ValidateMemoization inputs={[a, c]} output={setAlias} />
18
- <ValidateMemoization inputs={[b]} output={[hasB]} />
17
+ <ValidateMemoization
18
+ inputs={[a, c]}
19
+ output={set}
20
+ onlyCheckCompiled={true}
21
+ />
22
+ <ValidateMemoization
23
+ inputs={[a, c]}
24
+ output={setAlias}
25
+ onlyCheckCompiled={true}
26
+ />
27
+ <ValidateMemoization
28
+ inputs={[b]}
29
+ output={[hasB]}
30
+ onlyCheckCompiled={true}
31
+ />
32
</>
33
);
34
}
@@ -44,6 +57,7 @@ export const FIXTURE_ENTRYPOINT = {
57
58
```javascript
59
import { c as _c } from "react/compiler-runtime";
60
+import { useMemo } from "react";
61
import { ValidateMemoization } from "shared-runtime";
62
63
function Component(t0) {
@@ -76,7 +90,9 @@ function Component(t0) {
90
}
91
let t2;
92
if ($[7] !== set || $[8] !== t1) {
79
- t2 = <ValidateMemoization inputs={t1} output={set} />;
93
+ t2 = (
94
+ <ValidateMemoization inputs={t1} output={set} onlyCheckCompiled={true} />
95
+ );
96
$[7] = set;
97
$[8] = t1;
98
$[9] = t2;
@@ -94,7 +110,13 @@ function Component(t0) {
110
}
111
let t4;
112
if ($[13] !== setAlias || $[14] !== t3) {
97
- t4 = <ValidateMemoization inputs={t3} output={setAlias} />;
113
+ t4 = (
114
+ <ValidateMemoization
115
+ inputs={t3}
116
+ output={setAlias}
117
+ onlyCheckCompiled={true}
118
+ />
119
+ );
120
$[13] = setAlias;
121
$[14] = t3;
122
$[15] = t4;
@@ -119,7 +141,9 @@ function Component(t0) {
141
}
142
let t7;
143
if ($[20] !== t5 || $[21] !== t6) {
122
- t7 = <ValidateMemoization inputs={t5} output={t6} />;
144
+ t7 = (
145
+ <ValidateMemoization inputs={t5} output={t6} onlyCheckCompiled={true} />
146
+ );
147
$[20] = t5;
148
$[21] = t6;
149
$[22] = t7;
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/weakset-constructor.js
+16
-3
@@ -1,3 +1,4 @@
1
+import {useMemo} from 'react';
2
import {ValidateMemoization} from 'shared-runtime';
3
4
function Component({a, b, c}) {
@@ -9,9 +10,21 @@ function Component({a, b, c}) {
10
11
return (
12
<>
12
- <ValidateMemoization inputs={[a, c]} output={set} />
13
- <ValidateMemoization inputs={[a, c]} output={setAlias} />
14
- <ValidateMemoization inputs={[b]} output={[hasB]} />
13
+ <ValidateMemoization
14
+ inputs={[a, c]}
15
+ output={set}
16
+ onlyCheckCompiled={true}
17
+ />
18
+ <ValidateMemoization
19
+ inputs={[a, c]}
20
+ output={setAlias}
21
+ onlyCheckCompiled={true}
22
+ />
23
+ <ValidateMemoization
24
+ inputs={[b]}
25
+ output={[hasB]}
26
+ onlyCheckCompiled={true}
27
+ />
28
</>
29
);
30
}
compiler/packages/snap/src/sprout/shared-runtime.ts
+1
-3
@@ -269,12 +269,10 @@ export function ValidateMemoization({
269
inputs,
270
output: rawOutput,
271
onlyCheckCompiled = false,
272
- alwaysCheck = false,
272
}: {
273
inputs: Array<any>;
274
output: any;
275
onlyCheckCompiled?: boolean;
277
- alwaysCheck?: boolean;
276
}): React.ReactElement {
277
'use no forget';
278
// Wrap rawOutput as it might be a function, which useState would invoke.
@@ -282,7 +280,7 @@ export function ValidateMemoization({
280
const [previousInputs, setPreviousInputs] = React.useState(inputs);
281
const [previousOutput, setPreviousOutput] = React.useState(output);
282
if (
285
- alwaysCheck ||
283
+ !onlyCheckCompiled ||
284
(onlyCheckCompiled &&
285
(globalThis as any).__SNAP_EVALUATOR_MODE === 'forget')
286
) {