@samitouri / QOS-React-2 / commits / 6b67f597a6

Capturing a frozen value is a Read

If a value is known to be frozen (or potentially frozen), then it doesn't need to be considered 'captured' since no mutation can occur via aliasing.

Joe Savona committed Feb 14, 2023 at 09:33 UTC 6b67f597a6bc286fdf716c6840ece0c56997aba1
5 files changed +329 -1
compiler/forget/src/Inference/InferReferenceEffects.ts
+8 -1
@@ -308,7 +308,14 @@ class Environment {
308 break;
309 }
310 case Effect.Capture: {
311 - effect = Effect.Capture;
311 + if (
312 + valueKind === ValueKind.Frozen ||
313 + valueKind === ValueKind.MaybeFrozen
314 + ) {
315 + effect = Effect.Read;
316 + } else {
317 + effect = Effect.Capture;
318 + }
319 break;
320 }
321 case Effect.Read: {
compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare-maybe-frozen.expect.md new
+127
@@ -0,0 +1,127 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// note: comments are for the ideal scopes, not what is currently
6 +// emitted
7 +function foo(props) {
8 + // scope 0: deps=[props.a] decl=[x] reassign=none
9 + let x = [];
10 + x.push(props.a);
11 +
12 + // scope 1: deps=[x] decl=[header] reassign=none
13 + const header = props.showHeader ? <div>{x}</div> : null;
14 +
15 + // scope 2:
16 + // deps=[x, props.b, props.c]
17 + // decl=none
18 + // reassign=[x]
19 + const y = [x]; // y depends on the earlier x
20 + x = []; // x reassigned
21 + y.push(props.b); // interleaved mutation of x/y
22 + x.push(props.c); // interleaved mutation
23 +
24 + // scope 3 ...
25 + const content = (
26 + <div>
27 + {x}
28 + {y}
29 + </div>
30 + );
31 +
32 + // scope 4 ...
33 + return (
34 + <>
35 + {header}
36 + {content}
37 + </>
38 + );
39 +}
40 +
41 +```
42 +
43 +## Code
44 +
45 +```javascript
46 +// note: comments are for the ideal scopes, not what is currently
47 +// emitted
48 +function foo(props) {
49 + const $ = React.unstable_useMemoCache();
50 + const c_0 = $[0] !== props.a;
51 + let x;
52 + if (c_0) {
53 + x = [];
54 + x.push(props.a);
55 + $[0] = props.a;
56 + $[1] = x;
57 + } else {
58 + x = $[1];
59 + }
60 + const c_2 = $[2] !== props;
61 + const c_3 = $[3] !== x;
62 + let t0;
63 + if (c_2 || c_3) {
64 + t0 = props.showHeader ? <div>{x}</div> : null;
65 + $[2] = props;
66 + $[3] = x;
67 + $[4] = t0;
68 + } else {
69 + t0 = $[4];
70 + }
71 + const header = t0;
72 + const c_5 = $[5] !== x;
73 + const c_6 = $[6] !== props.b;
74 + const c_7 = $[7] !== props.c;
75 + let x$0;
76 + let y;
77 + if (c_5 || c_6 || c_7) {
78 + y = [x];
79 + x$0 = [];
80 + y.push(props.b);
81 + x$0.push(props.c);
82 + $[5] = x;
83 + $[6] = props.b;
84 + $[7] = props.c;
85 + $[8] = x$0;
86 + $[9] = y;
87 + } else {
88 + x$0 = $[8];
89 + y = $[9];
90 + }
91 + const c_10 = $[10] !== x$0;
92 + const c_11 = $[11] !== y;
93 + let content;
94 + if (c_10 || c_11) {
95 + content = (
96 + <div>
97 + {x$0}
98 + {y}
99 + </div>
100 + );
101 + $[10] = x$0;
102 + $[11] = y;
103 + $[12] = content;
104 + } else {
105 + content = $[12];
106 + }
107 + const c_13 = $[13] !== header;
108 + const c_14 = $[14] !== content;
109 + let t1;
110 + if (c_13 || c_14) {
111 + t1 = (
112 + <>
113 + {header}
114 + {content}
115 + </>
116 + );
117 + $[13] = header;
118 + $[14] = content;
119 + $[15] = t1;
120 + } else {
121 + t1 = $[15];
122 + }
123 + return t1;
124 +}
125 +
126 +```
127 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare-maybe-frozen.js new
+35
@@ -0,0 +1,35 @@
1 +// note: comments are for the ideal scopes, not what is currently
2 +// emitted
3 +function foo(props) {
4 + // scope 0: deps=[props.a] decl=[x] reassign=none
5 + let x = [];
6 + x.push(props.a);
7 +
8 + // scope 1: deps=[x] decl=[header] reassign=none
9 + const header = props.showHeader ? <div>{x}</div> : null;
10 +
11 + // scope 2:
12 + // deps=[x, props.b, props.c]
13 + // decl=none
14 + // reassign=[x]
15 + const y = [x]; // y depends on the earlier x
16 + x = []; // x reassigned
17 + y.push(props.b); // interleaved mutation of x/y
18 + x.push(props.c); // interleaved mutation
19 +
20 + // scope 3 ...
21 + const content = (
22 + <div>
23 + {x}
24 + {y}
25 + </div>
26 + );
27 +
28 + // scope 4 ...
29 + return (
30 + <>
31 + {header}
32 + {content}
33 + </>
34 + );
35 +}
compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare.expect.md new
+124
@@ -0,0 +1,124 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// note: comments are for the ideal scopes, not what is currently
6 +// emitted
7 +function foo(props) {
8 + // scope 0: deps=[props.a] decl=[x] reassign=none
9 + let x = [];
10 + x.push(props.a);
11 +
12 + // scope 1: deps=[x] decl=[header] reassign=none
13 + const header = <div>{x}</div>;
14 +
15 + // scope 2:
16 + // deps=[x, props.b, props.c]
17 + // decl=none
18 + // reassign=[x]
19 + const y = [x]; // y depends on the earlier x
20 + x = []; // x reassigned
21 + y.push(props.b); // interleaved mutation of x/y
22 + x.push(props.c); // interleaved mutation
23 +
24 + // scope 3 ...
25 + const content = (
26 + <div>
27 + {x}
28 + {y}
29 + </div>
30 + );
31 +
32 + // scope 4 ...
33 + return (
34 + <>
35 + {header}
36 + {content}
37 + </>
38 + );
39 +}
40 +
41 +```
42 +
43 +## Code
44 +
45 +```javascript
46 +// note: comments are for the ideal scopes, not what is currently
47 +// emitted
48 +function foo(props) {
49 + const $ = React.unstable_useMemoCache();
50 + const c_0 = $[0] !== props.a;
51 + let x;
52 + if (c_0) {
53 + x = [];
54 + x.push(props.a);
55 + $[0] = props.a;
56 + $[1] = x;
57 + } else {
58 + x = $[1];
59 + }
60 + const c_2 = $[2] !== x;
61 + let header;
62 + if (c_2) {
63 + header = <div>{x}</div>;
64 + $[2] = x;
65 + $[3] = header;
66 + } else {
67 + header = $[3];
68 + }
69 + const c_4 = $[4] !== x;
70 + const c_5 = $[5] !== props.b;
71 + const c_6 = $[6] !== props.c;
72 + let x$0;
73 + let y;
74 + if (c_4 || c_5 || c_6) {
75 + y = [x];
76 + x$0 = [];
77 + y.push(props.b);
78 + x$0.push(props.c);
79 + $[4] = x;
80 + $[5] = props.b;
81 + $[6] = props.c;
82 + $[7] = x$0;
83 + $[8] = y;
84 + } else {
85 + x$0 = $[7];
86 + y = $[8];
87 + }
88 + const c_9 = $[9] !== x$0;
89 + const c_10 = $[10] !== y;
90 + let content;
91 + if (c_9 || c_10) {
92 + content = (
93 + <div>
94 + {x$0}
95 + {y}
96 + </div>
97 + );
98 + $[9] = x$0;
99 + $[10] = y;
100 + $[11] = content;
101 + } else {
102 + content = $[11];
103 + }
104 + const c_12 = $[12] !== header;
105 + const c_13 = $[13] !== content;
106 + let t0;
107 + if (c_12 || c_13) {
108 + t0 = (
109 + <>
110 + {header}
111 + {content}
112 + </>
113 + );
114 + $[12] = header;
115 + $[13] = content;
116 + $[14] = t0;
117 + } else {
118 + t0 = $[14];
119 + }
120 + return t0;
121 +}
122 +
123 +```
124 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare.js new
+35
@@ -0,0 +1,35 @@
1 +// note: comments are for the ideal scopes, not what is currently
2 +// emitted
3 +function foo(props) {
4 + // scope 0: deps=[props.a] decl=[x] reassign=none
5 + let x = [];
6 + x.push(props.a);
7 +
8 + // scope 1: deps=[x] decl=[header] reassign=none
9 + const header = <div>{x}</div>;
10 +
11 + // scope 2:
12 + // deps=[x, props.b, props.c]
13 + // decl=none
14 + // reassign=[x]
15 + const y = [x]; // y depends on the earlier x
16 + x = []; // x reassigned
17 + y.push(props.b); // interleaved mutation of x/y
18 + x.push(props.c); // interleaved mutation
19 +
20 + // scope 3 ...
21 + const content = (
22 + <div>
23 + {x}
24 + {y}
25 + </div>
26 + );
27 +
28 + // scope 4 ...
29 + return (
30 + <>
31 + {header}
32 + {content}
33 + </>
34 + );
35 +}