[compiler] Repro for invalid Array.map type (#32094)
See test fixture --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32094). * #32099 * #32104 * #32098 * #32097 * #32096 * #32095 * __->__ #32094 * #32093
mofeiZ committed
Jan 22, 2025 at 14:58 UTC
deba48a72795d1332fe1df1159fc6b73566667fe
3 files changed
+220
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-invalid-mixedreadonly-map-shape.expect.md
new
+163
@@ -0,0 +1,163 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+import {
6
+ arrayPush,
7
+ identity,
8
+ makeArray,
9
+ Stringify,
10
+ useFragment,
11
+} from 'shared-runtime';
12
+
13
+/**
14
+ * Bug repro showing why it's invalid for function references to be annotated
15
+ * with a `Read` effect when that reference might lead to the function being
16
+ * invoked.
17
+ *
18
+ * Note that currently, `Array.map` is annotated to have `Read` effects on its
19
+ * operands. This is incorrect as function effects must be replayed when `map`
20
+ * is called
21
+ * - Read: non-aliasing data dependency
22
+ * - Capture: maybe-aliasing data dependency
23
+ * - ConditionallyMutate: maybe-aliasing data dependency; maybe-write / invoke
24
+ * but only if the value is mutable
25
+ *
26
+ * Invalid evaluator result: Found differences in evaluator results Non-forget
27
+ * (expected): (kind: ok)
28
+ * <div>{"x":[2,2,2],"count":3}</div><div>{"item":1}</div>
29
+ * <div>{"x":[2,2,2],"count":4}</div><div>{"item":1}</div>
30
+ * Forget:
31
+ * (kind: ok)
32
+ * <div>{"x":[2,2,2],"count":3}</div><div>{"item":1}</div>
33
+ * <div>{"x":[2,2,2,2,2,2],"count":4}</div><div>{"item":1}</div>
34
+ */
35
+
36
+function Component({extraJsx}) {
37
+ const x = makeArray();
38
+ const items = useFragment();
39
+ const jsx = items.a.map((item, i) => {
40
+ arrayPush(x, 2);
41
+ return <Stringify item={item} key={i} />;
42
+ });
43
+ const offset = jsx.length;
44
+ for (let i = 0; i < extraJsx; i++) {
45
+ jsx.push(<Stringify item={0} key={i + offset} />);
46
+ }
47
+ const count = jsx.length;
48
+ identity(count);
49
+ return (
50
+ <>
51
+ <Stringify x={x} count={count} />
52
+ {jsx[0]}
53
+ </>
54
+ );
55
+}
56
+export const FIXTURE_ENTRYPOINT = {
57
+ fn: Component,
58
+ params: [{extraJsx: 0}],
59
+ sequentialRenders: [{extraJsx: 0}, {extraJsx: 1}],
60
+};
61
+
62
+```
63
+
64
+## Code
65
+
66
+```javascript
67
+import { c as _c } from "react/compiler-runtime";
68
+import {
69
+ arrayPush,
70
+ identity,
71
+ makeArray,
72
+ Stringify,
73
+ useFragment,
74
+} from "shared-runtime";
75
+
76
+/**
77
+ * Bug repro showing why it's invalid for function references to be annotated
78
+ * with a `Read` effect when that reference might lead to the function being
79
+ * invoked.
80
+ *
81
+ * Note that currently, `Array.map` is annotated to have `Read` effects on its
82
+ * operands. This is incorrect as function effects must be replayed when `map`
83
+ * is called
84
+ * - Read: non-aliasing data dependency
85
+ * - Capture: maybe-aliasing data dependency
86
+ * - ConditionallyMutate: maybe-aliasing data dependency; maybe-write / invoke
87
+ * but only if the value is mutable
88
+ *
89
+ * Invalid evaluator result: Found differences in evaluator results Non-forget
90
+ * (expected): (kind: ok)
91
+ * <div>{"x":[2,2,2],"count":3}</div><div>{"item":1}</div>
92
+ * <div>{"x":[2,2,2],"count":4}</div><div>{"item":1}</div>
93
+ * Forget:
94
+ * (kind: ok)
95
+ * <div>{"x":[2,2,2],"count":3}</div><div>{"item":1}</div>
96
+ * <div>{"x":[2,2,2,2,2,2],"count":4}</div><div>{"item":1}</div>
97
+ */
98
+
99
+function Component(t0) {
100
+ const $ = _c(9);
101
+ const { extraJsx } = t0;
102
+ let t1;
103
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
104
+ t1 = makeArray();
105
+ $[0] = t1;
106
+ } else {
107
+ t1 = $[0];
108
+ }
109
+ const x = t1;
110
+ const items = useFragment();
111
+ let jsx;
112
+ if ($[1] !== extraJsx || $[2] !== items.a) {
113
+ jsx = items.a.map((item, i) => {
114
+ arrayPush(x, 2);
115
+ return <Stringify item={item} key={i} />;
116
+ });
117
+ const offset = jsx.length;
118
+ for (let i_0 = 0; i_0 < extraJsx; i_0++) {
119
+ jsx.push(<Stringify item={0} key={i_0 + offset} />);
120
+ }
121
+ $[1] = extraJsx;
122
+ $[2] = items.a;
123
+ $[3] = jsx;
124
+ } else {
125
+ jsx = $[3];
126
+ }
127
+
128
+ const count = jsx.length;
129
+ identity(count);
130
+ let t2;
131
+ if ($[4] !== count) {
132
+ t2 = <Stringify x={x} count={count} />;
133
+ $[4] = count;
134
+ $[5] = t2;
135
+ } else {
136
+ t2 = $[5];
137
+ }
138
+ const t3 = jsx[0];
139
+ let t4;
140
+ if ($[6] !== t2 || $[7] !== t3) {
141
+ t4 = (
142
+ <>
143
+ {t2}
144
+ {t3}
145
+ </>
146
+ );
147
+ $[6] = t2;
148
+ $[7] = t3;
149
+ $[8] = t4;
150
+ } else {
151
+ t4 = $[8];
152
+ }
153
+ return t4;
154
+}
155
+
156
+export const FIXTURE_ENTRYPOINT = {
157
+ fn: Component,
158
+ params: [{ extraJsx: 0 }],
159
+ sequentialRenders: [{ extraJsx: 0 }, { extraJsx: 1 }],
160
+};
161
+
162
+```
163
+
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-invalid-mixedreadonly-map-shape.js
new
+56
@@ -0,0 +1,56 @@
1
+import {
2
+ arrayPush,
3
+ identity,
4
+ makeArray,
5
+ Stringify,
6
+ useFragment,
7
+} from 'shared-runtime';
8
+
9
+/**
10
+ * Bug repro showing why it's invalid for function references to be annotated
11
+ * with a `Read` effect when that reference might lead to the function being
12
+ * invoked.
13
+ *
14
+ * Note that currently, `Array.map` is annotated to have `Read` effects on its
15
+ * operands. This is incorrect as function effects must be replayed when `map`
16
+ * is called
17
+ * - Read: non-aliasing data dependency
18
+ * - Capture: maybe-aliasing data dependency
19
+ * - ConditionallyMutate: maybe-aliasing data dependency; maybe-write / invoke
20
+ * but only if the value is mutable
21
+ *
22
+ * Invalid evaluator result: Found differences in evaluator results Non-forget
23
+ * (expected): (kind: ok)
24
+ * <div>{"x":[2,2,2],"count":3}</div><div>{"item":1}</div>
25
+ * <div>{"x":[2,2,2],"count":4}</div><div>{"item":1}</div>
26
+ * Forget:
27
+ * (kind: ok)
28
+ * <div>{"x":[2,2,2],"count":3}</div><div>{"item":1}</div>
29
+ * <div>{"x":[2,2,2,2,2,2],"count":4}</div><div>{"item":1}</div>
30
+ */
31
+
32
+function Component({extraJsx}) {
33
+ const x = makeArray();
34
+ const items = useFragment();
35
+ const jsx = items.a.map((item, i) => {
36
+ arrayPush(x, 2);
37
+ return <Stringify item={item} key={i} />;
38
+ });
39
+ const offset = jsx.length;
40
+ for (let i = 0; i < extraJsx; i++) {
41
+ jsx.push(<Stringify item={0} key={i + offset} />);
42
+ }
43
+ const count = jsx.length;
44
+ identity(count);
45
+ return (
46
+ <>
47
+ <Stringify x={x} count={count} />
48
+ {jsx[0]}
49
+ </>
50
+ );
51
+}
52
+export const FIXTURE_ENTRYPOINT = {
53
+ fn: Component,
54
+ params: [{extraJsx: 0}],
55
+ sequentialRenders: [{extraJsx: 0}, {extraJsx: 1}],
56
+};
compiler/packages/snap/src/SproutTodoFilter.ts
+1
@@ -486,6 +486,7 @@ const skipFilter = new Set([
486
'bug-aliased-capture-mutate',
487
'bug-functiondecl-hoisting',
488
'bug-try-catch-maybe-null-dependency',
489
+ 'bug-invalid-mixedreadonly-map-shape',
490
'bug-type-inference-control-flow',
491
'reduce-reactive-deps/bug-infer-function-cond-access-not-hoisted',
492
'bug-invalid-phi-as-dependency',