MutableIfOperandsAreMutable flag handles mutation via capturing
There was one missing piece to the optimization from the previous PR: Array#map can return an alias to the receiver in its output, which means that mutations of the result have to be treated as mutations of the receiver. This means we need to use a Capture effect on the receiver. If that doesn't get downgraded to a Read bc the value was immutable, we then also need to make the lvalue effect a Store (so that InferMutableRanges actually looks at it for aliasing).
Joe Savona committed
Nov 29, 2023 at 10:46 UTC
75c7fdcbd0e73a846899e69b13062c82f0fac468
3 files changed
+70
-3
compiler/packages/babel-plugin-react-forget/src/Inference/InferReferenceEffects.ts
+6
-3
@@ -851,16 +851,19 @@ function inferBlock(
851
) {
852
/*
853
* None of the args are mutable or mutate their params, we can downgrade to
854
- * treating as all reads
854
+ * treating as all reads (except that the receiver may be captured)
855
*/
856
for (const arg of instrValue.args) {
857
const place = arg.kind === "Identifier" ? arg : arg.place;
858
state.reference(place, Effect.Read);
859
}
860
- state.reference(instrValue.receiver, Effect.Read);
860
+ state.reference(instrValue.receiver, Effect.Capture);
861
state.initialize(instrValue, signature.returnValueKind);
862
state.define(instr.lvalue, instrValue);
863
- instr.lvalue.effect = Effect.ConditionallyMutate;
863
+ instr.lvalue.effect =
864
+ instrValue.receiver.effect === Effect.Capture
865
+ ? Effect.Store
866
+ : Effect.ConditionallyMutate;
867
continue;
868
}
869
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/array-map-mutable-array-non-mutating-lambda-mutated-result.expect.md
new
+50
@@ -0,0 +1,50 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component(props) {
6
+ const x = [{}];
7
+ const y = x.map((item) => {
8
+ return item;
9
+ });
10
+ y[0].flag = true;
11
+ return [x, y];
12
+}
13
+
14
+export const FIXTURE_ENTRYPOINT = {
15
+ fn: Component,
16
+ params: [{}],
17
+ isComponent: false,
18
+};
19
+
20
+```
21
+
22
+## Code
23
+
24
+```javascript
25
+import { unstable_useMemoCache as useMemoCache } from "react";
26
+function Component(props) {
27
+ const $ = useMemoCache(1);
28
+ let t0;
29
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
30
+ const x = [{}];
31
+ const y = x.map((item) => item);
32
+ y[0].flag = true;
33
+ t0 = [x, y];
34
+ $[0] = t0;
35
+ } else {
36
+ t0 = $[0];
37
+ }
38
+ return t0;
39
+}
40
+
41
+export const FIXTURE_ENTRYPOINT = {
42
+ fn: Component,
43
+ params: [{}],
44
+ isComponent: false,
45
+};
46
+
47
+```
48
+
49
+### Eval output
50
+(kind: ok) [[{"flag":true}],["[[ cyclic ref *2 ]]"]]
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/array-map-mutable-array-non-mutating-lambda-mutated-result.js
new
+14
@@ -0,0 +1,14 @@
1
+function Component(props) {
2
+ const x = [{}];
3
+ const y = x.map((item) => {
4
+ return item;
5
+ });
6
+ y[0].flag = true;
7
+ return [x, y];
8
+}
9
+
10
+export const FIXTURE_ENTRYPOINT = {
11
+ fn: Component,
12
+ params: [{}],
13
+ isComponent: false,
14
+};