@samitouri / QOS-React-2 / commits / 5e80821aa2

[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 +}