[hir] Add support for holey arrays
This PR adds a Hole kind that can be present in both ArrayExpression and ArrayPatterns. This Hole type is not interesting for our inference passes and is skipped over for all of the pipeline.
Sathya Gunasekaran committed
Jul 26, 2023 at 12:03 UTC
25527b3c0a7e90f740ae09f439ba6a4ae7efc4b0
15 files changed
+171
-20
compiler/forget/packages/babel-plugin-react-forget/src/HIR/BuildHIR.ts
+7
-6
@@ -17,6 +17,7 @@ import { Err, Ok, Result } from "../Utils/Result";
17
import { assertExhaustive } from "../Utils/utils";
18
import { Environment } from "./Environment";
19
import {
20
+ ArrayExpression,
21
ArrayPattern,
22
BlockId,
23
BranchTerminal,
@@ -1064,14 +1065,11 @@ function lowerExpression(
1065
}
1066
case "ArrayExpression": {
1067
const expr = exprPath as NodePath<t.ArrayExpression>;
1067
- let elements: Array<Place | SpreadPattern> = [];
1068
+ let elements: ArrayExpression["elements"] = [];
1069
for (const element of expr.get("elements")) {
1070
if (element.node == null) {
1070
- builder.errors.push({
1071
- reason: `(BuildHIR::lowerExpression) Handle ${element.type} elements in ArrayExpression`,
1072
- severity: ErrorSeverity.Todo,
1073
- loc: null,
1074
- suggestions: null,
1071
+ elements.push({
1072
+ kind: "Hole",
1073
});
1074
continue;
1075
} else if (element.isExpression()) {
@@ -2696,6 +2694,9 @@ function lowerAssignment(
2694
for (let i = 0; i < elements.length; i++) {
2695
const element = elements[i];
2696
if (element.node == null) {
2697
+ items.push({
2698
+ kind: "Hole",
2699
+ });
2700
continue;
2701
}
2702
if (element.isRestElement()) {
compiler/forget/packages/babel-plugin-react-forget/src/HIR/HIR.ts
+12
-6
@@ -472,8 +472,18 @@ export type LValuePattern = {
472
kind: InstructionKind;
473
};
474
475
+export type ArrayExpression = {
476
+ kind: "ArrayExpression";
477
+ elements: Array<Place | SpreadPattern | Hole>;
478
+ loc: SourceLocation;
479
+};
480
+
481
export type Pattern = ArrayPattern | ObjectPattern;
482
483
+export type Hole = {
484
+ kind: "Hole";
485
+};
486
+
487
export type SpreadPattern = {
488
kind: "Spread";
489
place: Place;
@@ -481,7 +491,7 @@ export type SpreadPattern = {
491
492
export type ArrayPattern = {
493
kind: "ArrayPattern";
484
- items: Array<Place | SpreadPattern>;
494
+ items: Array<Place | SpreadPattern | Hole>;
495
};
496
497
export type ObjectPattern = {
@@ -648,11 +658,7 @@ export type InstructionValue =
658
properties: Array<ObjectProperty | SpreadPattern>;
659
loc: SourceLocation;
660
}
651
- | {
652
- kind: "ArrayExpression";
653
- elements: Array<Place | SpreadPattern>;
654
- loc: SourceLocation;
655
- }
661
+ | ArrayExpression
662
| { kind: "JsxFragment"; children: Array<Place>; loc: SourceLocation }
663
| {
664
kind: "RegExpLiteral";
compiler/forget/packages/babel-plugin-react-forget/src/HIR/PrintHIR.ts
+16
-1
@@ -239,6 +239,10 @@ export function printTerminal(terminal: Terminal): Array<string> | string {
239
return value;
240
}
241
242
+function printHole(): string {
243
+ return "<hole>";
244
+}
245
+
246
export function printInstructionValue(instrValue: ReactiveValue): string {
247
let value = "";
248
switch (instrValue.kind) {
@@ -247,6 +251,8 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
251
.map((element) => {
252
if (element.kind === "Identifier") {
253
return printPlace(element);
254
+ } else if (element.kind === "Hole") {
255
+ return printHole();
256
} else {
257
return `...${printPlace(element.place)}`;
258
}
@@ -554,7 +560,16 @@ export function printPattern(pattern: Pattern | Place | SpreadPattern): string {
560
switch (pattern.kind) {
561
case "ArrayPattern": {
562
return (
557
- "[ " + pattern.items.map((item) => printPattern(item)).join(", ") + " ]"
563
+ "[ " +
564
+ pattern.items
565
+ .map((item) => {
566
+ if (item.kind === "Hole") {
567
+ return "<hole>";
568
+ }
569
+ return printPattern(item);
570
+ })
571
+ .join(", ") +
572
+ " ]"
573
);
574
}
575
case "ObjectPattern": {
compiler/forget/packages/babel-plugin-react-forget/src/HIR/visitors.ts
+9
-3
@@ -158,7 +158,7 @@ export function* eachInstructionValueOperand(
158
for (const element of instrValue.elements) {
159
if (element.kind === "Identifier") {
160
yield element;
161
- } else {
161
+ } else if (element.kind === "Spread") {
162
yield element.place;
163
}
164
}
@@ -253,6 +253,8 @@ export function* eachPatternOperand(pattern: Pattern): Iterable<Place> {
253
yield item;
254
} else if (item.kind === "Spread") {
255
yield item.place;
256
+ } else if (item.kind === "Hole") {
257
+ continue;
258
} else {
259
assertExhaustive(
260
item,
@@ -424,9 +426,11 @@ export function mapInstructionOperands(
426
instrValue.elements = instrValue.elements.map((element) => {
427
if (element.kind === "Identifier") {
428
return fn(element);
427
- } else {
429
+ } else if (element.kind === "Spread") {
430
element.place = fn(element.place);
431
return element;
432
+ } else {
433
+ return element;
434
}
435
});
436
break;
@@ -496,9 +500,11 @@ export function mapPatternOperands(
500
pattern.items = pattern.items.map((item) => {
501
if (item.kind === "Identifier") {
502
return fn(item);
499
- } else {
503
+ } else if (item.kind === "Spread") {
504
item.place = fn(item.place);
505
return item;
506
+ } else {
507
+ return item;
508
}
509
});
510
break;
compiler/forget/packages/babel-plugin-react-forget/src/Optimization/DeadCodeElimination.ts
+1
-1
@@ -114,7 +114,7 @@ function visitInstruction(instr: Instruction, state: State): void {
114
nextItems = originalItems.slice(0, i + 1);
115
break;
116
}
117
- } else {
117
+ } else if (item.kind === "Spread") {
118
if (state.used(item.place.identifier)) {
119
nextItems = originalItems.slice(0, i + 1);
120
break;
compiler/forget/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+11
-2
@@ -716,8 +716,10 @@ function codegenInstructionValue(
716
const elements = instrValue.elements.map((element) => {
717
if (element.kind === "Identifier") {
718
return codegenPlace(cx, element);
719
- } else {
719
+ } else if (element.kind === "Spread") {
720
return t.spreadElement(codegenPlace(cx, element.place));
721
+ } else {
722
+ return null;
723
}
724
});
725
value = t.arrayExpression(elements);
@@ -1245,7 +1247,14 @@ function codegenLValue(
1247
): t.ArrayPattern | t.ObjectPattern | t.RestElement | t.Identifier {
1248
switch (pattern.kind) {
1249
case "ArrayPattern": {
1248
- return t.arrayPattern(pattern.items.map((item) => codegenLValue(item)));
1250
+ return t.arrayPattern(
1251
+ pattern.items.map((item) => {
1252
+ if (item.kind === "Hole") {
1253
+ return null;
1254
+ }
1255
+ return codegenLValue(item);
1256
+ })
1257
+ );
1258
}
1259
case "ObjectPattern": {
1260
return t.objectPattern(
compiler/forget/packages/babel-plugin-react-forget/src/ReactiveScopes/PruneNonEscapingScopes.ts
+1
-1
@@ -628,7 +628,7 @@ function computePatternLValues(pattern: Pattern): Array<LValueMemoization> {
628
for (const item of pattern.items) {
629
if (item.kind === "Identifier") {
630
lvalues.push({ place: item, level: MemoizationLevel.Conditional });
631
- } else {
631
+ } else if (item.kind === "Spread") {
632
lvalues.push({ place: item.place, level: MemoizationLevel.Memoized });
633
}
634
}
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/holey-array-expr.expect.md
new
+32
@@ -0,0 +1,32 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function t(props) {
6
+ let x = [, foo, props];
7
+ return x;
8
+}
9
+
10
+```
11
+
12
+## Code
13
+
14
+```javascript
15
+import { unstable_useMemoCache as useMemoCache } from "react";
16
+function t(props) {
17
+ const $ = useMemoCache(2);
18
+ const c_0 = $[0] !== props;
19
+ let t0;
20
+ if (c_0) {
21
+ t0 = [, foo, props];
22
+ $[0] = props;
23
+ $[1] = t0;
24
+ } else {
25
+ t0 = $[1];
26
+ }
27
+ const x = t0;
28
+ return x;
29
+}
30
+
31
+```
32
+
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/holey-array-expr.js
new
+4
@@ -0,0 +1,4 @@
1
+function t(props) {
2
+ let x = [, foo, props];
3
+ return x;
4
+}
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/holey-array-pattern-dce-2.expect.md
new
+21
@@ -0,0 +1,21 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function t(props) {
6
+ let [foo, bar, ,] = props;
7
+ return foo;
8
+}
9
+
10
+```
11
+
12
+## Code
13
+
14
+```javascript
15
+function t(props) {
16
+ const [foo] = props;
17
+ return foo;
18
+}
19
+
20
+```
21
+
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/holey-array-pattern-dce-2.js
new
+4
@@ -0,0 +1,4 @@
1
+function t(props) {
2
+ let [foo, bar, ,] = props;
3
+ return foo;
4
+}
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/holey-array-pattern-dce.expect.md
new
+21
@@ -0,0 +1,21 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function t(props) {
6
+ let [, foo, bar] = props;
7
+ return foo;
8
+}
9
+
10
+```
11
+
12
+## Code
13
+
14
+```javascript
15
+function t(props) {
16
+ const [, foo] = props;
17
+ return foo;
18
+}
19
+
20
+```
21
+
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/holey-array-pattern-dce.js
new
+4
@@ -0,0 +1,4 @@
1
+function t(props) {
2
+ let [, foo, bar] = props;
3
+ return foo;
4
+}
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/holey-array.expect.md
new
+23
@@ -0,0 +1,23 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function t(props) {
6
+ let [, setstate] = useState();
7
+ setstate(1);
8
+ return props.foo;
9
+}
10
+
11
+```
12
+
13
+## Code
14
+
15
+```javascript
16
+function t(props) {
17
+ const [, setstate] = useState();
18
+ setstate(1);
19
+ return props.foo;
20
+}
21
+
22
+```
23
+
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/holey-array.js
new
+5
@@ -0,0 +1,5 @@
1
+function t(props) {
2
+ let [, setstate] = useState();
3
+ setstate(1);
4
+ return props.foo;
5
+}