@samitouri / QOS-React-2 / commits / 45d884bb6b

Fix LeaveSSA missing param declarations

Nice find from @mofeiz, we weren't adding declarations for function params in LeaveSSA. This caused us to hit an invariant for update expressions that updated params which assumed that all named variables had a declaration. This is why it's helpful to add invariants, it helps you find places where you violate them :-)

Joe Savona committed Aug 14, 2023 at 15:48 UTC 45d884bb6b443bc8c34633e32916791236d34e7b
4 files changed +97 -2
compiler/forget/packages/babel-plugin-react-forget/src/ReactiveScopes/PruneNonEscapingScopes.ts
+14 -2
@@ -430,8 +430,6 @@ function computeMemoizationInputs(
430 rvalues: value.children,
431 };
432 }
433 - case "PrefixUpdate":
434 - case "PostfixUpdate":
433 case "Debugger":
434 case "ComputedDelete":
435 case "PropertyDelete":
@@ -507,6 +505,20 @@ function computeMemoizationInputs(
505 rvalues: [],
506 };
507 }
508 + case "PrefixUpdate":
509 + case "PostfixUpdate": {
510 + const lvalues = [
511 + { place: value.lvalue, level: MemoizationLevel.Conditional },
512 + ];
513 + if (lvalue !== null) {
514 + lvalues.push({ place: lvalue, level: MemoizationLevel.Conditional });
515 + }
516 + return {
517 + // Indirection for the inner value, memoized if the value is
518 + lvalues,
519 + rvalues: [value.value],
520 + };
521 + }
522 case "StoreLocal": {
523 const lvalues = [
524 { place: value.lvalue.place, level: MemoizationLevel.Conditional },
compiler/forget/packages/babel-plugin-react-forget/src/SSA/LeaveSSA.ts
+12
@@ -99,6 +99,18 @@ export function leaveSSA(fn: HIRFunction): void {
99 { lvalue: LValue | LValuePattern; place: Place }
100 > = new Map();
101
102 + for (const param of fn.params) {
103 + if (param.identifier.name !== null) {
104 + declarations.set(param.identifier.name, {
105 + lvalue: {
106 + kind: InstructionKind.Let,
107 + place: param,
108 + },
109 + place: param,
110 + });
111 + }
112 + }
113 +
114 // For non-memoizable phis, this maps original identifiers to the identifier they should be
115 // *rewritten* to. The keys are the original identifiers, and the value will be _either_ the
116 // phi id or, more typically, the operand that was defined prior to the phi.
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/update-expression-on-function-parameter.expect.md new
+61
@@ -0,0 +1,61 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @debug
6 +function Component(a, [b], { c }) {
7 + let d = a++;
8 + let e = ++a;
9 + let f = b--;
10 + let g = --b;
11 + let h = c++;
12 + let i = --c;
13 + return [a, b, c, d, e, f, g, h, i];
14 +}
15 +
16 +```
17 +
18 +## Code
19 +
20 +```javascript
21 +import { unstable_useMemoCache as useMemoCache } from "react"; // @debug
22 +function Component(a, t37, t38) {
23 + const $ = useMemoCache(10);
24 + let [b] = t37;
25 + let { c } = t38;
26 + const d = a++;
27 + const e = ++a;
28 + const f = b--;
29 + const g = --b;
30 + const h = c++;
31 + const i = --c;
32 + const c_0 = $[0] !== a;
33 + const c_1 = $[1] !== b;
34 + const c_2 = $[2] !== c;
35 + const c_3 = $[3] !== d;
36 + const c_4 = $[4] !== e;
37 + const c_5 = $[5] !== f;
38 + const c_6 = $[6] !== g;
39 + const c_7 = $[7] !== h;
40 + const c_8 = $[8] !== i;
41 + let t0;
42 + if (c_0 || c_1 || c_2 || c_3 || c_4 || c_5 || c_6 || c_7 || c_8) {
43 + t0 = [a, b, c, d, e, f, g, h, i];
44 + $[0] = a;
45 + $[1] = b;
46 + $[2] = c;
47 + $[3] = d;
48 + $[4] = e;
49 + $[5] = f;
50 + $[6] = g;
51 + $[7] = h;
52 + $[8] = i;
53 + $[9] = t0;
54 + } else {
55 + t0 = $[9];
56 + }
57 + return t0;
58 +}
59 +
60 +```
61 +
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/update-expression-on-function-parameter.js new
+10
@@ -0,0 +1,10 @@
1 +// @debug
2 +function Component(a, [b], { c }) {
3 + let d = a++;
4 + let e = ++a;
5 + let f = b--;
6 + let g = --b;
7 + let h = c++;
8 + let i = --c;
9 + return [a, b, c, d, e, f, g, h, i];
10 +}