[effects] Allow capture-store effects in rvals
--- Some typed functions need to annotate callees or arguments as `Effect.Store`. This PR modifies alias analysis (`InferAliasForStores`) to account for this
Mofei Zhang committed
May 10, 2023 at 21:55 UTC
5e80821aa247362a5a9cf3e97754183cc0884e01
7 files changed
+147
-2
compiler/forget/src/HIR/ObjectShape.ts
+1
-1
@@ -159,7 +159,7 @@ addObject(BUILTIN_SHAPES, BuiltInArrayId, [
159
positionalParams: [],
160
restParam: Effect.Capture,
161
returnType: PRIMITIVE_TYPE,
162
- calleeEffect: Effect.Mutate,
162
+ calleeEffect: Effect.Store,
163
}),
164
],
165
// TODO: rest of Array properties
compiler/forget/src/Inference/InferAliasForStores.ts
+9
-1
@@ -25,7 +25,15 @@ export function inferAliasForStores(
25
for (const [_, block] of func.body.blocks) {
26
for (const instr of block.instructions) {
27
const { value, lvalue } = instr;
28
- if (lvalue.effect !== Effect.Store) {
28
+ const isStore =
29
+ lvalue.effect === Effect.Store ||
30
+ // Some typed functions annotate callees or arguments
31
+ // as Effect.Store.
32
+ ![...eachInstructionValueOperand(value)].every(
33
+ (operand) => operand.effect !== Effect.Store
34
+ );
35
+
36
+ if (!isStore) {
37
continue;
38
}
39
for (const operand of eachInstructionLValue(instr)) {
compiler/forget/src/Inference/InferReferenceEffects.ts
+14
@@ -323,6 +323,20 @@ class InferenceState {
323
break;
324
}
325
case Effect.Store: {
326
+ if (
327
+ valueKind !== ValueKind.Mutable &&
328
+ valueKind !== ValueKind.Context &&
329
+ shouldError
330
+ ) {
331
+ CompilerError.invalidInput(
332
+ `InferReferenceEffects: inferred mutation of known immutable value`,
333
+ place.loc,
334
+ `Found mutation of ${printIdentifier(place.identifier)}${printType(
335
+ place.identifier.type
336
+ )} (${valueKind})`
337
+ );
338
+ }
339
+
340
// TODO(gsn): This should be bailout once we add bailout infra.
341
//
342
// invariant(
compiler/forget/src/__tests__/fixtures/compiler/alias-capture-in-method-receiver-and-mutate.expect.md
new
+51
@@ -0,0 +1,51 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component() {
6
+ // a's mutable range should be the same as x's mutable range,
7
+ // since a is captured into x (which gets mutated later)
8
+ let a = someObj();
9
+
10
+ let x = [];
11
+ x.push(a);
12
+
13
+ mutate(x);
14
+ return [x, a];
15
+}
16
+
17
+```
18
+
19
+## Code
20
+
21
+```javascript
22
+import { unstable_useMemoCache as useMemoCache } from "react";
23
+function Component() {
24
+ const $ = useMemoCache(3);
25
+ let x;
26
+ let a;
27
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
28
+ a = someObj();
29
+
30
+ x = [];
31
+ x.push(a);
32
+
33
+ mutate(x);
34
+ $[0] = x;
35
+ $[1] = a;
36
+ } else {
37
+ x = $[0];
38
+ a = $[1];
39
+ }
40
+ let t0;
41
+ if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
42
+ t0 = [x, a];
43
+ $[2] = t0;
44
+ } else {
45
+ t0 = $[2];
46
+ }
47
+ return t0;
48
+}
49
+
50
+```
51
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/alias-capture-in-method-receiver-and-mutate.js
new
+11
@@ -0,0 +1,11 @@
1
+function Component() {
2
+ // a's mutable range should be the same as x's mutable range,
3
+ // since a is captured into x (which gets mutated later)
4
+ let a = someObj();
5
+
6
+ let x = [];
7
+ x.push(a);
8
+
9
+ mutate(x);
10
+ return [x, a];
11
+}
compiler/forget/src/__tests__/fixtures/compiler/alias-capture-in-method-receiver.expect.md
new
+51
@@ -0,0 +1,51 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component() {
6
+ // a's mutable range should be limited
7
+ // the following line
8
+ let a = someObj();
9
+
10
+ let x = [];
11
+ x.push(a);
12
+
13
+ return [x, a];
14
+}
15
+
16
+```
17
+
18
+## Code
19
+
20
+```javascript
21
+import { unstable_useMemoCache as useMemoCache } from "react";
22
+function Component() {
23
+ const $ = useMemoCache(3);
24
+ let t0;
25
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
26
+ t0 = someObj();
27
+ $[0] = t0;
28
+ } else {
29
+ t0 = $[0];
30
+ }
31
+ const a = t0;
32
+ let x;
33
+ if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
34
+ x = [];
35
+ x.push(a);
36
+ $[1] = x;
37
+ } else {
38
+ x = $[1];
39
+ }
40
+ let t1;
41
+ if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
42
+ t1 = [x, a];
43
+ $[2] = t1;
44
+ } else {
45
+ t1 = $[2];
46
+ }
47
+ return t1;
48
+}
49
+
50
+```
51
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/alias-capture-in-method-receiver.js
new
+10
@@ -0,0 +1,10 @@
1
+function Component() {
2
+ // a's mutable range should be limited
3
+ // the following line
4
+ let a = someObj();
5
+
6
+ let x = [];
7
+ x.push(a);
8
+
9
+ return [x, a];
10
+}