[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.
This commit is contained in:
Sathya Gunasekaran
2023-07-26 12:03:19 +01:00
parent dcb6549dda
commit 25527b3c0a
15 changed files with 171 additions and 20 deletions
@@ -17,6 +17,7 @@ import { Err, Ok, Result } from "../Utils/Result";
import { assertExhaustive } from "../Utils/utils";
import { Environment } from "./Environment";
import {
ArrayExpression,
ArrayPattern,
BlockId,
BranchTerminal,
@@ -1064,14 +1065,11 @@ function lowerExpression(
}
case "ArrayExpression": {
const expr = exprPath as NodePath<t.ArrayExpression>;
let elements: Array<Place | SpreadPattern> = [];
let elements: ArrayExpression["elements"] = [];
for (const element of expr.get("elements")) {
if (element.node == null) {
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Handle ${element.type} elements in ArrayExpression`,
severity: ErrorSeverity.Todo,
loc: null,
suggestions: null,
elements.push({
kind: "Hole",
});
continue;
} else if (element.isExpression()) {
@@ -2696,6 +2694,9 @@ function lowerAssignment(
for (let i = 0; i < elements.length; i++) {
const element = elements[i];
if (element.node == null) {
items.push({
kind: "Hole",
});
continue;
}
if (element.isRestElement()) {
@@ -472,8 +472,18 @@ export type LValuePattern = {
kind: InstructionKind;
};
export type ArrayExpression = {
kind: "ArrayExpression";
elements: Array<Place | SpreadPattern | Hole>;
loc: SourceLocation;
};
export type Pattern = ArrayPattern | ObjectPattern;
export type Hole = {
kind: "Hole";
};
export type SpreadPattern = {
kind: "Spread";
place: Place;
@@ -481,7 +491,7 @@ export type SpreadPattern = {
export type ArrayPattern = {
kind: "ArrayPattern";
items: Array<Place | SpreadPattern>;
items: Array<Place | SpreadPattern | Hole>;
};
export type ObjectPattern = {
@@ -648,11 +658,7 @@ export type InstructionValue =
properties: Array<ObjectProperty | SpreadPattern>;
loc: SourceLocation;
}
| {
kind: "ArrayExpression";
elements: Array<Place | SpreadPattern>;
loc: SourceLocation;
}
| ArrayExpression
| { kind: "JsxFragment"; children: Array<Place>; loc: SourceLocation }
| {
kind: "RegExpLiteral";
@@ -239,6 +239,10 @@ export function printTerminal(terminal: Terminal): Array<string> | string {
return value;
}
function printHole(): string {
return "<hole>";
}
export function printInstructionValue(instrValue: ReactiveValue): string {
let value = "";
switch (instrValue.kind) {
@@ -247,6 +251,8 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
.map((element) => {
if (element.kind === "Identifier") {
return printPlace(element);
} else if (element.kind === "Hole") {
return printHole();
} else {
return `...${printPlace(element.place)}`;
}
@@ -554,7 +560,16 @@ export function printPattern(pattern: Pattern | Place | SpreadPattern): string {
switch (pattern.kind) {
case "ArrayPattern": {
return (
"[ " + pattern.items.map((item) => printPattern(item)).join(", ") + " ]"
"[ " +
pattern.items
.map((item) => {
if (item.kind === "Hole") {
return "<hole>";
}
return printPattern(item);
})
.join(", ") +
" ]"
);
}
case "ObjectPattern": {
@@ -158,7 +158,7 @@ export function* eachInstructionValueOperand(
for (const element of instrValue.elements) {
if (element.kind === "Identifier") {
yield element;
} else {
} else if (element.kind === "Spread") {
yield element.place;
}
}
@@ -253,6 +253,8 @@ export function* eachPatternOperand(pattern: Pattern): Iterable<Place> {
yield item;
} else if (item.kind === "Spread") {
yield item.place;
} else if (item.kind === "Hole") {
continue;
} else {
assertExhaustive(
item,
@@ -424,9 +426,11 @@ export function mapInstructionOperands(
instrValue.elements = instrValue.elements.map((element) => {
if (element.kind === "Identifier") {
return fn(element);
} else {
} else if (element.kind === "Spread") {
element.place = fn(element.place);
return element;
} else {
return element;
}
});
break;
@@ -496,9 +500,11 @@ export function mapPatternOperands(
pattern.items = pattern.items.map((item) => {
if (item.kind === "Identifier") {
return fn(item);
} else {
} else if (item.kind === "Spread") {
item.place = fn(item.place);
return item;
} else {
return item;
}
});
break;
@@ -114,7 +114,7 @@ function visitInstruction(instr: Instruction, state: State): void {
nextItems = originalItems.slice(0, i + 1);
break;
}
} else {
} else if (item.kind === "Spread") {
if (state.used(item.place.identifier)) {
nextItems = originalItems.slice(0, i + 1);
break;
@@ -716,8 +716,10 @@ function codegenInstructionValue(
const elements = instrValue.elements.map((element) => {
if (element.kind === "Identifier") {
return codegenPlace(cx, element);
} else {
} else if (element.kind === "Spread") {
return t.spreadElement(codegenPlace(cx, element.place));
} else {
return null;
}
});
value = t.arrayExpression(elements);
@@ -1245,7 +1247,14 @@ function codegenLValue(
): t.ArrayPattern | t.ObjectPattern | t.RestElement | t.Identifier {
switch (pattern.kind) {
case "ArrayPattern": {
return t.arrayPattern(pattern.items.map((item) => codegenLValue(item)));
return t.arrayPattern(
pattern.items.map((item) => {
if (item.kind === "Hole") {
return null;
}
return codegenLValue(item);
})
);
}
case "ObjectPattern": {
return t.objectPattern(
@@ -628,7 +628,7 @@ function computePatternLValues(pattern: Pattern): Array<LValueMemoization> {
for (const item of pattern.items) {
if (item.kind === "Identifier") {
lvalues.push({ place: item, level: MemoizationLevel.Conditional });
} else {
} else if (item.kind === "Spread") {
lvalues.push({ place: item.place, level: MemoizationLevel.Memoized });
}
}
@@ -0,0 +1,32 @@
## Input
```javascript
function t(props) {
let x = [, foo, props];
return x;
}
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function t(props) {
const $ = useMemoCache(2);
const c_0 = $[0] !== props;
let t0;
if (c_0) {
t0 = [, foo, props];
$[0] = props;
$[1] = t0;
} else {
t0 = $[1];
}
const x = t0;
return x;
}
```
@@ -0,0 +1,4 @@
function t(props) {
let x = [, foo, props];
return x;
}
@@ -0,0 +1,21 @@
## Input
```javascript
function t(props) {
let [foo, bar, ,] = props;
return foo;
}
```
## Code
```javascript
function t(props) {
const [foo] = props;
return foo;
}
```
@@ -0,0 +1,4 @@
function t(props) {
let [foo, bar, ,] = props;
return foo;
}
@@ -0,0 +1,21 @@
## Input
```javascript
function t(props) {
let [, foo, bar] = props;
return foo;
}
```
## Code
```javascript
function t(props) {
const [, foo] = props;
return foo;
}
```
@@ -0,0 +1,4 @@
function t(props) {
let [, foo, bar] = props;
return foo;
}
@@ -0,0 +1,23 @@
## Input
```javascript
function t(props) {
let [, setstate] = useState();
setstate(1);
return props.foo;
}
```
## Code
```javascript
function t(props) {
const [, setstate] = useState();
setstate(1);
return props.foo;
}
```
@@ -0,0 +1,5 @@
function t(props) {
let [, setstate] = useState();
setstate(1);
return props.foo;
}