@samitouri / QOS-React-2 / commits / 25527b3c0a

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