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

Fix bug with scope output analysis for nested scopes

When collecting the output of each scope I was checking to see whether each operand was being used after the end of the _current_ scope. What we really want to be checking is whether the operand is used after _the scope in which it's defined_. Those are the same thing when there is no nesting, involved, so the previous logic worked for most examples. It isn't super easy to tell when if the operand's scope has ended, because we don't always know what the "current" InstructionId is inside a ReactiveFunction. And that in turn isn't quite so easy to change, because of some edge cases like break statements that we synthesize. The solution here is to track the set of active scopes, and if an operand is used and its scope is not active then voila, it's scope must have completed and its an output.

Joe Savona committed Dec 18, 2022 at 12:14 UTC 5db523a98a617842bb45500ff82491184d8ecacd
12 files changed +158 -148
compiler/forget/src/ReactiveScopes/PropagateScopeDependencies.ts
+36 -70
@@ -12,10 +12,10 @@ import {
12 InstructionKind,
13 InstructionValue,
14 makeInstructionId,
15 - MutableRange,
15 Place,
16 ReactiveBlock,
17 ReactiveFunction,
18 + ReactiveScope,
19 ReactiveValueBlock,
20 } from "../HIR/HIR";
21 import { eachInstructionValueOperand } from "../HIR/visitors";
@@ -39,7 +39,7 @@ export function propagateScopeDependencies(fn: ReactiveFunction): void {
39 id: makeInstructionId(0),
40 });
41 }
42 - visit(fn.body, dependencies, declarations, null);
42 + visit(fn.body, dependencies, declarations, []);
43 }
44
45 enum DeclKind {
@@ -49,11 +49,13 @@ enum DeclKind {
49
50 type DeclMap = Map<Identifier, { kind: DeclKind; id: InstructionId }>;
51
52 +type Scopes = Array<ReactiveScope>;
53 +
54 function visit(
55 block: ReactiveBlock,
56 dependencies: Set<Place>,
57 declarations: DeclMap,
56 - scopeRange: MutableRange | null
58 + scopes: Scopes
59 ): void {
60 for (const item of block) {
61 switch (item.kind) {
@@ -61,19 +63,16 @@ function visit(
63 const scopeDependencies: Set<Place> = new Set();
64 // TODO: it would be sufficient to use a single mapping of declarations
65 const scopeDeclarations: DeclMap = new Map(declarations);
64 - visit(
65 - item.instructions,
66 - scopeDependencies,
67 - scopeDeclarations,
68 - item.scope.range
69 - );
66 + scopes.push(item.scope);
67 + visit(item.instructions, scopeDependencies, scopeDeclarations, scopes);
68 + scopes.pop();
69 item.scope.dependencies = scopeDependencies;
70 for (const dep of scopeDependencies) {
71 // propagate dependencies upward using the same rules as
72 // normal dependency collection. child scopes may have dependencies
73 // on values created within the outer scope, which necessarily cannot
74 // be dependencies of the outer scope
76 - visitOperand(dep, dependencies, declarations, scopeRange);
75 + visitOperand(dep, dependencies, declarations, scopes);
76 }
77 for (const [ident, kind] of scopeDeclarations) {
78 declarations.set(ident, kind);
@@ -81,12 +80,7 @@ function visit(
80 break;
81 }
82 case "instruction": {
84 - visitInstruction(
85 - item.instruction,
86 - dependencies,
87 - declarations,
88 - scopeRange
89 - );
83 + visitInstruction(item.instruction, dependencies, declarations, scopes);
84 break;
85 }
86 case "terminal": {
@@ -98,69 +92,44 @@ function visit(
92 }
93 case "return": {
94 if (terminal.value !== null) {
101 - visitOperand(
102 - terminal.value,
103 - dependencies,
104 - declarations,
105 - scopeRange
106 - );
95 + visitOperand(terminal.value, dependencies, declarations, scopes);
96 }
97 break;
98 }
99 case "throw": {
111 - visitOperand(
112 - terminal.value,
113 - dependencies,
114 - declarations,
115 - scopeRange
116 - );
100 + visitOperand(terminal.value, dependencies, declarations, scopes);
101 break;
102 }
103 case "for": {
120 - visitValueBlock(
121 - terminal.init,
122 - dependencies,
123 - declarations,
124 - scopeRange
125 - );
126 - visitValueBlock(
127 - terminal.test,
128 - dependencies,
129 - declarations,
130 - scopeRange
131 - );
104 + visitValueBlock(terminal.init, dependencies, declarations, scopes);
105 + visitValueBlock(terminal.test, dependencies, declarations, scopes);
106 visitValueBlock(
107 terminal.update,
108 dependencies,
109 declarations,
136 - scopeRange
110 + scopes
111 );
138 - visit(terminal.loop, dependencies, declarations, scopeRange);
112 + visit(terminal.loop, dependencies, declarations, scopes);
113 break;
114 }
115 case "while": {
142 - visitValueBlock(
143 - terminal.test,
144 - dependencies,
145 - declarations,
146 - scopeRange
147 - );
148 - visit(terminal.loop, dependencies, declarations, scopeRange);
116 + visitValueBlock(terminal.test, dependencies, declarations, scopes);
117 + visit(terminal.loop, dependencies, declarations, scopes);
118 break;
119 }
120 case "if": {
152 - visitOperand(terminal.test, dependencies, declarations, scopeRange);
153 - visit(terminal.consequent, dependencies, declarations, scopeRange);
121 + visitOperand(terminal.test, dependencies, declarations, scopes);
122 + visit(terminal.consequent, dependencies, declarations, scopes);
123 if (terminal.alternate !== null) {
155 - visit(terminal.alternate, dependencies, declarations, scopeRange);
124 + visit(terminal.alternate, dependencies, declarations, scopes);
125 }
126 break;
127 }
128 case "switch": {
160 - visitOperand(terminal.test, dependencies, declarations, scopeRange);
129 + visitOperand(terminal.test, dependencies, declarations, scopes);
130 for (const case_ of terminal.cases) {
131 if (case_.block !== undefined) {
163 - visit(case_.block, dependencies, declarations, scopeRange);
132 + visit(case_.block, dependencies, declarations, scopes);
133 }
134 }
135 break;
@@ -185,7 +154,7 @@ function visitValueBlock(
154 block: ReactiveValueBlock,
155 dependencies: Set<Place>,
156 declarations: DeclMap,
188 - scopeRange: MutableRange | null
157 + scopes: Scopes
158 ): void {
159 for (const initItem of block.instructions) {
160 if (initItem.kind === "instruction") {
@@ -193,12 +162,12 @@ function visitValueBlock(
162 initItem.instruction,
163 dependencies,
164 declarations,
196 - scopeRange
165 + scopes
166 );
167 }
168 }
169 if (block.value !== null) {
201 - visitInstructionValue(block.value, dependencies, declarations, scopeRange);
170 + visitInstructionValue(block.value, dependencies, declarations, scopes);
171 }
172 }
173
@@ -206,7 +175,7 @@ function visitOperand(
175 maybeDependency: Place,
176 dependencies: Set<Place>,
177 declarations: DeclMap,
209 - scopeRange: MutableRange | null
178 + scopes: Scopes
179 ): void {
180 const decl = declarations.get(maybeDependency.identifier);
181
@@ -215,22 +184,19 @@ function visitOperand(
184 // some later code needs access to the value.
185 if (decl !== undefined) {
186 const operandScope = maybeDependency.identifier.scope;
218 - if (
219 - operandScope !== null &&
220 - ((scopeRange !== null && operandScope.range.end <= scopeRange.start) ||
221 - scopeRange === null)
222 - ) {
187 + if (operandScope !== null && scopes.indexOf(operandScope) === -1) {
188 operandScope.outputs.add(maybeDependency.identifier);
189 }
190 }
191
192 // If this operand is used in a scope, has a dynamic value, and was defined
193 // before this scope, then its a dependency of the scope.
194 + const currentScope = scopes.at(-1);
195 if (
196 decl !== undefined &&
197 decl.kind !== DeclKind.Const &&
232 - scopeRange !== null &&
233 - decl.id < scopeRange.start
198 + currentScope !== undefined &&
199 + decl.id < currentScope.range.start
200 ) {
201 // Check if there is an existing dependency that describes this operand
202 for (const dep of dependencies) {
@@ -272,7 +238,7 @@ function visitInstructionValue(
238 value: InstructionValue,
239 dependencies: Set<Place>,
240 declarations: DeclMap,
275 - scopeRange: MutableRange | null
241 + scopes: Scopes
242 ): void {
243 for (const operand of eachInstructionValueOperand(value)) {
244 // check for method invocation, we want to depend on the callee, not the method
@@ -285,9 +251,9 @@ function visitInstructionValue(
251 ...operand,
252 memberPath: operand.memberPath.slice(0, -1),
253 };
288 - visitOperand(callee, dependencies, declarations, scopeRange);
254 + visitOperand(callee, dependencies, declarations, scopes);
255 } else {
290 - visitOperand(operand, dependencies, declarations, scopeRange);
256 + visitOperand(operand, dependencies, declarations, scopes);
257 }
258 }
259 }
@@ -296,9 +262,9 @@ function visitInstruction(
262 instr: Instruction,
263 dependencies: Set<Place>,
264 declarations: DeclMap,
299 - scopeRange: MutableRange | null
265 + scopes: Scopes
266 ): void {
301 - visitInstructionValue(instr.value, dependencies, declarations, scopeRange);
267 + visitInstructionValue(instr.value, dependencies, declarations, scopes);
268 const { lvalue } = instr;
269 if (
270 lvalue !== null &&
compiler/forget/src/__tests__/fixtures/hir/_bug_independently-memoize-object-property.expect.md
+17 -14
@@ -27,8 +27,8 @@ bb0:
27 [3] Const mutate y$12_@1:TObject[3:6] = Object { a: read a$8 }
28 [4] Const mutate z$13_@2 = Array [read b$9, read c$10]
29 [5] Reassign mutate y$12_@1.y[3:6] = read z$13_@2
30 - [6] Const mutate t12$14_@3 = Array [read x$11_@0:TObject, read y$12_@1:TObject]
31 - [7] Return freeze t12$14_@3
30 + [6] Const mutate t13$14_@3 = Array [read x$11_@0:TObject, read y$12_@1:TObject]
31 + [7] Return freeze t13$14_@3
32 ```
33
34 ## Reactive Scopes
@@ -45,7 +45,7 @@ function foo(
45 }
46 scope @1 [3:6] deps=[read a$8, read b$9, read c$10] out=[y$12_@1] {
47 [3] Const mutate y$12_@1:TObject[3:6] = Object { a: read a$8 }
48 - scope @2 [4:5] deps=[read b$9, read c$10] out=[] {
48 + scope @2 [4:5] deps=[read b$9, read c$10] out=[z$13_@2] {
49 [4] Const mutate z$13_@2 = Array [read b$9, read c$10]
50 }
51 [5] Reassign mutate y$12_@1.y[3:6] = read z$13_@2
@@ -91,12 +91,15 @@ function foo$0(a$8, b$9, c$10) {
91 };
92 const c_8 = $[8] !== b$9;
93 const c_9 = $[9] !== c$10;
94 + let z$13;
95
96 if (c_8 || c_9) {
96 - const z$13 = [b$9, c$10];
97 + z$13 = [b$9, c$10];
98 $[8] = b$9;
99 $[9] = c$10;
100 + $[10] = z$13;
101 } else {
102 + z$13 = $[10];
103 }
104
105 y$12.y = z$13;
@@ -108,20 +111,20 @@ function foo$0(a$8, b$9, c$10) {
111 y$12 = $[7];
112 }
113
111 - const c_10 = $[10] !== x$11;
112 - const c_11 = $[11] !== y$12;
113 - let t12$14;
114 + const c_11 = $[11] !== x$11;
115 + const c_12 = $[12] !== y$12;
116 + let t13$14;
117
115 - if (c_10 || c_11) {
116 - t12$14 = [x$11, y$12];
117 - $[10] = x$11;
118 - $[11] = y$12;
119 - $[12] = t12$14;
118 + if (c_11 || c_12) {
119 + t13$14 = [x$11, y$12];
120 + $[11] = x$11;
121 + $[12] = y$12;
122 + $[13] = t13$14;
123 } else {
121 - t12$14 = $[12];
124 + t13$14 = $[13];
125 }
126
124 - return t12$14;
127 + return t13$14;
128 }
129
130 ```
compiler/forget/src/__tests__/fixtures/hir/component.expect.md
+26 -23
@@ -95,11 +95,11 @@ bb2:
95 [35] Const mutate $68 = "\n "
96 [36] Const mutate $69:TPrimitive = "h1"
97 [37] Const mutate $70 = " Items"
98 - [38] Const mutate t6$71_@4 = JSX <read $69:TPrimitive>{freeze count$66:TProp}{read $70}</read $69:TPrimitive>
98 + [38] Const mutate t7$71_@4 = JSX <read $69:TPrimitive>{freeze count$66:TProp}{read $70}</read $69:TPrimitive>
99 [39] Const mutate $72 = "\n "
100 [40] Const mutate $73 = "\n "
101 - [41] Const mutate t9$74_@5 = JSX <read $67:TPrimitive>{read $68}{read t6$71_@4}{read $72}{freeze renderedItems$32_@0:TFunction}{read $73}</read $67:TPrimitive>
102 - [42] Return read t9$74_@5
101 + [41] Const mutate t10$74_@5 = JSX <read $67:TPrimitive>{read $68}{read t7$71_@4}{read $72}{freeze renderedItems$32_@0:TFunction}{read $73}</read $67:TPrimitive>
102 + [42] Return read t10$74_@5
103 ```
104
105 ## Reactive Scopes
@@ -114,7 +114,7 @@ function Component(
114 [3] Const mutate renderedItems$32_@0:TFunction[3:33] = Array []
115 [4] Const mutate seen$33_@0:TFunction[3:33] = New mutate Set$6()
116 [5] Const mutate $34:TPrimitive = 0
117 - scope @2 [6:7] deps=[read maxItems$31:TProp] out=[] {
117 + scope @2 [6:7] deps=[read maxItems$31:TProp] out=[max$35_@2] {
118 [6] Const mutate max$35_@2:TPrimitive = Call mutate Math$8.max(read $34:TPrimitive, read maxItems$31:TProp)
119 }
120 for (
@@ -182,11 +182,14 @@ function Component$0(props$29) {
182 renderedItems$32 = [];
183 const seen$33 = new Set$6();
184 const c_4 = $[4] !== maxItems$31;
185 + let max$35;
186
187 if (c_4) {
187 - const max$35 = Math$8.max(0, maxItems$31);
188 + max$35 = Math$8.max(0, maxItems$31);
189 $[4] = maxItems$31;
190 + $[5] = max$35;
191 } else {
192 + max$35 = $[5];
193 }
194
195 for (let i$36 = 0; i$36 < items$30.length; i$36 = i$36 + 1, i$36) {
@@ -217,36 +220,36 @@ function Component$0(props$29) {
220 }
221
222 const count$66 = renderedItems$32.length;
220 - const c_5 = $[5] !== count$66;
221 - let t6$71;
223 + const c_6 = $[6] !== count$66;
224 + let t7$71;
225
223 - if (c_5) {
224 - t6$71 = <h1>{count$66} Items</h1>;
225 - $[5] = count$66;
226 - $[6] = t6$71;
226 + if (c_6) {
227 + t7$71 = <h1>{count$66} Items</h1>;
228 + $[6] = count$66;
229 + $[7] = t7$71;
230 } else {
228 - t6$71 = $[6];
231 + t7$71 = $[7];
232 }
233
231 - const c_7 = $[7] !== t6$71;
232 - const c_8 = $[8] !== renderedItems$32;
233 - let t9$74;
234 + const c_8 = $[8] !== t7$71;
235 + const c_9 = $[9] !== renderedItems$32;
236 + let t10$74;
237
235 - if (c_7 || c_8) {
236 - t9$74 = (
238 + if (c_8 || c_9) {
239 + t10$74 = (
240 <div>
238 - {t6$71}
241 + {t7$71}
242 {renderedItems$32}
243 </div>
244 );
242 - $[7] = t6$71;
243 - $[8] = renderedItems$32;
244 - $[9] = t9$74;
245 + $[8] = t7$71;
246 + $[9] = renderedItems$32;
247 + $[10] = t10$74;
248 } else {
246 - t9$74 = $[9];
249 + t10$74 = $[10];
250 }
251
249 - return t9$74;
252 + return t10$74;
253 }
254
255 ```
compiler/forget/src/__tests__/fixtures/hir/mutable-lifetime-with-aliasing.expect.md
+5 -2
@@ -102,7 +102,7 @@ function Component(
102 }
103 scope @1 [2:15] deps=[read a$11_@0:TObject] out=[] {
104 [2] Const mutate b$12_@1[2:15] = Array [read a$11_@0:TObject]
105 - scope @2 [3:4] deps=[] out=[] {
105 + scope @2 [3:4] deps=[] out=[c$13_@2] {
106 [3] Const mutate c$13_@2:TObject = Object { }
107 }
108 [4] Const mutate d$14_@1:TObject[2:15] = Object { c: read c$13_@2:TObject }
@@ -144,10 +144,13 @@ function Component$0(props$10) {
144
145 if (c_1) {
146 const b$12 = [a$11];
147 + let c$13;
148
149 if (true) {
149 - const c$13 = {};
150 + c$13 = {};
151 + $[2] = c$13;
152 } else {
153 + c$13 = $[2];
154 }
155
156 const d$14 = {
compiler/forget/src/__tests__/fixtures/hir/mutable-liverange-loop.expect.md
+7 -2
@@ -129,7 +129,7 @@ function Component(
129 scope @0 [1:18] deps=[] out=[] {
130 [1] Const mutate a$12_@0:TObject[1:18] = Object { }
131 [2] Const mutate b$13_@0:TObject[1:18] = Object { }
132 - scope @1 [3:4] deps=[] out=[] {
132 + scope @1 [3:4] deps=[] out=[c$14_@1] {
133 [3] Const mutate c$14_@1:TObject = Object { }
134 }
135 [4] Const mutate d$15_@0:TObject[1:18] = Object { }
@@ -163,12 +163,17 @@ function Component(
163
164 ```javascript
165 function Component$0(props$11) {
166 + const $ = React.useMemoCache();
167 if (true) {
168 const a$12 = {};
169 const b$13 = {};
170 + let c$14;
171 +
172 if (true) {
170 - const c$14 = {};
173 + c$14 = {};
174 + $[0] = c$14;
175 } else {
176 + c$14 = $[0];
177 }
178
179 const d$15 = {};
compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-shadowing-within-block.expect.md
+8 -4
@@ -34,8 +34,8 @@ bb4:
34 bb3:
35 predecessor blocks: bb4 bb2
36 [7] Const mutate $13:TPrimitive = "div"
37 - [8] Const mutate $15_@2 = JSX <read $13:TPrimitive>{freeze y$12_@1:TFunction}</read $13:TPrimitive>
38 - [9] Call mutate x$11_@0.push(read $15_@2)
37 + [8] Const mutate t8$15_@2 = JSX <read $13:TPrimitive>{freeze y$12_@1:TFunction}</read $13:TPrimitive>
38 + [9] Call mutate x$11_@0.push(read t8$15_@2)
39 [10] Goto bb1
40 bb1:
41 predecessor blocks: bb3 bb0
@@ -60,7 +60,7 @@ function foo(
60 }
61 }
62 [7] Const mutate $13:TPrimitive = "div"
63 - scope @2 [8:9] deps=[freeze y$12_@1:TFunction] out=[] {
63 + scope @2 [8:9] deps=[freeze y$12_@1:TFunction] out=[$15_@2] {
64 [8] Const mutate $15_@2 = JSX <read $13:TPrimitive>{freeze y$12_@1:TFunction}</read $13:TPrimitive>
65 }
66 [9] Call mutate x$11_@0.push(read $15_@2)
@@ -103,13 +103,17 @@ function foo$0(a$8, b$9, c$10) {
103 }
104
105 const c_7 = $[7] !== y$12;
106 + let t8$15;
107
108 if (c_7) {
109 + t8$15 = <div>{y$12}</div>;
110 $[7] = y$12;
111 + $[8] = t8$15;
112 } else {
113 + t8$15 = $[8];
114 }
115
112 - x$11.push(<div>{y$12}</div>);
116 + x$11.push(t8$15);
117 }
118
119 $[0] = a$8;
compiler/forget/src/__tests__/fixtures/hir/reactive-scope-grouping.expect.md
+5 -2
@@ -33,7 +33,7 @@ function foo(
33 ) {
34 scope @0 [1:6] deps=[] out=[x$4_@0] {
35 [1] Const mutate x$4_@0:TObject[1:6] = Object { }
36 - scope @1 [2:5] deps=[] out=[] {
36 + scope @1 [2:5] deps=[] out=[y$5_@1] {
37 [2] Const mutate y$5_@1:TFunction[2:5] = Array []
38 [3] Const mutate z$6_@1:TObject[2:5] = Object { }
39 [4] Call mutate y$5_@1.push(mutate z$6_@1:TObject)
@@ -53,12 +53,15 @@ function foo$0() {
53 let x$4;
54 if (true) {
55 x$4 = {};
56 + let y$5;
57
58 if (true) {
58 - const y$5 = [];
59 + y$5 = [];
60 const z$6 = {};
61 y$5.push(z$6);
62 + $[1] = y$5;
63 } else {
64 + y$5 = $[1];
65 }
66
67 x$4.y = y$5;
compiler/forget/src/__tests__/fixtures/hir/reactive-scopes-if.expect.md
+8 -4
@@ -27,8 +27,8 @@ bb2:
27 [3] Const mutate y$12_@1:TFunction[3:5] = Array []
28 [4] Call mutate y$12_@1.push(read b$9)
29 [5] Const mutate $13:TPrimitive = "div"
30 - [6] Const mutate $14_@2 = JSX <read $13:TPrimitive>{freeze y$12_@1:TFunction}</read $13:TPrimitive>
31 - [7] Call mutate x$11_@0.push(read $14_@2)
30 + [6] Const mutate t7$14_@2 = JSX <read $13:TPrimitive>{freeze y$12_@1:TFunction}</read $13:TPrimitive>
31 + [7] Call mutate x$11_@0.push(read t7$14_@2)
32 [8] Goto bb1
33 bb3:
34 predecessor blocks: bb0
@@ -55,7 +55,7 @@ function foo(
55 [4] Call mutate y$12_@1.push(read b$9)
56 }
57 [5] Const mutate $13:TPrimitive = "div"
58 - scope @2 [6:7] deps=[freeze y$12_@1:TFunction] out=[] {
58 + scope @2 [6:7] deps=[freeze y$12_@1:TFunction] out=[$14_@2] {
59 [6] Const mutate $14_@2 = JSX <read $13:TPrimitive>{freeze y$12_@1:TFunction}</read $13:TPrimitive>
60 }
61 [7] Call mutate x$11_@0.push(read $14_@2)
@@ -94,13 +94,17 @@ function foo$0(a$8, b$9, c$10) {
94 }
95
96 const c_6 = $[6] !== y$12;
97 + let t7$14;
98
99 if (c_6) {
100 + t7$14 = <div>{y$12}</div>;
101 $[6] = y$12;
102 + $[7] = t7$14;
103 } else {
104 + t7$14 = $[7];
105 }
106
103 - x$11.push(<div>{y$12}</div>);
107 + x$11.push(t7$14);
108 } else {
109 x$11.push(c$10);
110 }
compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx-2.expect.md
+23 -19
@@ -48,8 +48,8 @@ bb0:
48 [1] Const mutate a$11_@0[1:10] = Array []
49 [2] Const mutate b$12_@0:TObject[1:10] = Object { }
50 [3] Call mutate foo$4:TFunction(mutate a$11_@0, mutate b$12_@0:TObject)
51 - [4] Const mutate $13_@1 = Call mutate foo$4:TFunction()
52 - [5] If (read $13_@1) then:bb2 else:bb1 fallthrough=bb1
51 + [4] Const mutate t2$13_@1 = Call mutate foo$4:TFunction()
52 + [5] If (read t2$13_@1) then:bb2 else:bb1 fallthrough=bb1
53 bb2:
54 predecessor blocks: bb0
55 [6] Const mutate $14:TPrimitive = "div"
@@ -59,8 +59,8 @@ bb1:
59 predecessor blocks: bb2 bb0
60 [9] Call mutate foo$4:TFunction(read a$11_@0, mutate b$12_@0:TObject)
61 [10] Const mutate $19:TPrimitive = "div"
62 - [11] Const mutate t5$20_@3 = JSX <read $19:TPrimitive a={freeze a$11_@0} b={freeze b$12_@0:TObject} ></read $19:TPrimitive>
63 - [12] Return read t5$20_@3
62 + [11] Const mutate t6$20_@3 = JSX <read $19:TPrimitive a={freeze a$11_@0} b={freeze b$12_@0:TObject} ></read $19:TPrimitive>
63 + [12] Return read t6$20_@3
64 ```
65
66 ## Reactive Scopes
@@ -73,7 +73,7 @@ function Component(
73 [1] Const mutate a$11_@0[1:10] = Array []
74 [2] Const mutate b$12_@0:TObject[1:10] = Object { }
75 [3] Call mutate foo$4:TFunction(mutate a$11_@0, mutate b$12_@0:TObject)
76 - scope @1 [4:5] deps=[] out=[] {
76 + scope @1 [4:5] deps=[] out=[$13_@1] {
77 [4] Const mutate $13_@1 = Call mutate foo$4:TFunction()
78 }
79 if (read $13_@1) {
@@ -104,18 +104,22 @@ function Component$0(props$10) {
104 a$11 = [];
105 b$12 = {};
106 foo$4(a$11, b$12);
107 + let t2$13;
108
109 if (true) {
110 + t2$13 = foo$4();
111 + $[2] = t2$13;
112 } else {
113 + t2$13 = $[2];
114 }
115
112 - if (foo$4()) {
113 - const c_2 = $[2] !== a$11;
116 + if (t2$13) {
117 + const c_3 = $[3] !== a$11;
118
115 - if (c_2) {
119 + if (c_3) {
120 const _$15 = <div a={a$11}></div>;
121
118 - $[2] = a$11;
122 + $[3] = a$11;
123 } else {
124 }
125 }
@@ -128,20 +132,20 @@ function Component$0(props$10) {
132 b$12 = $[1];
133 }
134
131 - const c_3 = $[3] !== a$11;
132 - const c_4 = $[4] !== b$12;
133 - let t5$20;
135 + const c_4 = $[4] !== a$11;
136 + const c_5 = $[5] !== b$12;
137 + let t6$20;
138
135 - if (c_3 || c_4) {
136 - t5$20 = <div a={a$11} b={b$12}></div>;
137 - $[3] = a$11;
138 - $[4] = b$12;
139 - $[5] = t5$20;
139 + if (c_4 || c_5) {
140 + t6$20 = <div a={a$11} b={b$12}></div>;
141 + $[4] = a$11;
142 + $[5] = b$12;
143 + $[6] = t6$20;
144 } else {
141 - t5$20 = $[5];
145 + t6$20 = $[6];
146 }
147
144 - return t5$20;
148 + return t6$20;
149 }
150
151 ```
compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-if.expect.md
+12 -4
@@ -46,12 +46,12 @@ function foo(
46 scope @0 [1:9] deps=[read a$5] out=[x$6_@0] {
47 [1] Const mutate x$6_@0:TObject[1:9] = Object { }
48 if (read a$5) {
49 - scope @1 [3:4] deps=[] out=[] {
49 + scope @1 [3:4] deps=[] out=[y$7_@1] {
50 [3] Const mutate y$7_@1:TObject = Object { }
51 }
52 [4] Reassign mutate x$6_@0.y[1:9] = read y$7_@1:TObject
53 } else {
54 - scope @2 [6:7] deps=[] out=[] {
54 + scope @2 [6:7] deps=[] out=[z$8_@2] {
55 [6] Const mutate z$8_@2:TObject = Object { }
56 }
57 [7] Reassign mutate x$6_@0.z[1:9] = read z$8_@2:TObject
@@ -73,16 +73,24 @@ function foo$0(a$5) {
73 x$6 = {};
74
75 if (a$5) {
76 + let y$7;
77 +
78 if (true) {
77 - const y$7 = {};
79 + y$7 = {};
80 + $[2] = y$7;
81 } else {
82 + y$7 = $[2];
83 }
84
85 x$6.y = y$7;
86 } else {
87 + let z$8;
88 +
89 if (true) {
84 - const z$8 = {};
90 + z$8 = {};
91 + $[3] = z$8;
92 } else {
93 + z$8 = $[3];
94 }
95
96 x$6.z = z$8;
compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-mutate-inside-if.expect.md
+6 -2
@@ -52,7 +52,7 @@ function foo(
52 [4] Reassign mutate x$7_@0.y[1:10] = read y$8_@0:TObject
53 [5] Call mutate mutate$4:TFunction(mutate y$8_@0:TObject)
54 } else {
55 - scope @1 [7:8] deps=[] out=[] {
55 + scope @1 [7:8] deps=[] out=[z$9_@1] {
56 [7] Const mutate z$9_@1:TObject = Object { }
57 }
58 [8] Reassign mutate x$7_@0.z[1:10] = read z$9_@1:TObject
@@ -78,9 +78,13 @@ function foo$0(a$6) {
78 x$7.y = y$8;
79 mutate$4(y$8);
80 } else {
81 + let z$9;
82 +
83 if (true) {
82 - const z$9 = {};
84 + z$9 = {};
85 + $[2] = z$9;
86 } else {
87 + z$9 = $[2];
88 }
89
90 x$7.z = z$9;
compiler/forget/src/__tests__/fixtures/hir/type-test-field-store.expect.md
+5 -2
@@ -29,7 +29,7 @@ function component(
29 ) {
30 scope @0 [1:4] deps=[] out=[x$4_@0] {
31 [1] Const mutate x$4_@0:TObject[1:4] = Object { }
32 - scope @1 [2:3] deps=[] out=[] {
32 + scope @1 [2:3] deps=[] out=[q$5_@1] {
33 [2] Const mutate q$5_@1:TObject = Object { }
34 }
35 [3] Reassign mutate x$4_@0.t[1:4] = read q$5_@1:TObject
@@ -48,10 +48,13 @@ function component$0() {
48 let x$4;
49 if (true) {
50 x$4 = {};
51 + let q$5;
52
53 if (true) {
53 - const q$5 = {};
54 + q$5 = {};
55 + $[1] = q$5;
56 } else {
57 + q$5 = $[1];
58 }
59
60 x$4.t = q$5;