[hir] implement nullish coalescing operator

This currently basically lowers the code into the equivalent of 

``` 

const vLeft = <left>; 

const vNull = null; 

const vCond = vLeft != vNull; 

vCond ? vLeft : <right> 

``` 

I created a temporary `Place` to hold the `null` constant value because the 
binary operator in HIR accepts only `Place`s. Not sure if this is the preferred 
approach. Alternatives I could think of: 

-  Allow constants as an alternative to Place? 

- A `NotNull` operator for `<x> != null` 

- Some other extension to the HIR?
This commit is contained in:
Jan Kassens
2022-11-03 10:28:26 -04:00
parent 515c33d2a6
commit 189b22171f
4 changed files with 218 additions and 5 deletions
+49 -2
View File
@@ -811,8 +811,55 @@ function lowerExpression(
);
}
case "??": {
// test should be roughly the equivalent of `<left> != null`
todo("Handle logical ??");
// generate the equivalent of
// const tmp = <left>;
// tmp != null ? tmp : <right>
const left = lowerExpressionToPlace(builder, leftPath);
const nullPlace: Place = {
kind: "Identifier",
identifier: builder.makeTemporary(),
memberPath: null,
effect: Effect.Unknown,
path: null as any,
};
builder.push({
value: {
kind: "Primitive",
value: null,
path: null as any,
},
path: exprPath,
lvalue: { place: { ...nullPlace }, kind: InstructionKind.Const },
});
const condPlace: Place = {
kind: "Identifier",
identifier: builder.makeTemporary(),
memberPath: null,
effect: Effect.Unknown,
path: null as any,
};
builder.push({
lvalue: {
place: { ...condPlace },
kind: InstructionKind.Const,
},
value: {
kind: "BinaryExpression",
operator: "!=",
left,
right: nullPlace,
path: null as any,
},
path: null as any,
});
return lowerConditional(
builder,
condPlace,
() => left,
() => lowerExpression(builder, expr.get("right"))
);
}
default: {
assertExhaustive(
@@ -210,9 +210,8 @@ class Environment {
const values = this.#variables.get(value.identifier.id);
invariant(
values != null,
`Expected value to be populated at '${String(value.path)}' in '${String(
value.path.parentPath
)}'`
"Expected value for identifier `%s` to be initialized.",
value.identifier.id
);
this.#variables.set(place.identifier.id, new Set(values));
}
@@ -0,0 +1,153 @@
## Input
```javascript
function And() {
return f() && g();
}
function Or() {
return f() || g();
}
function QuestionQuestion(props) {
return f() ?? g();
}
function f() {}
function g() {}
```
## HIR
```
bb0:
Const mutate $5 = Call mutate f$1()
If (read $5) then:bb2 else:bb3
bb2:
predecessor blocks: bb0
Const mutate $6 = Call mutate g$4()
Goto bb1
bb3:
predecessor blocks: bb0
Const mutate $7 = read $5
Goto bb1
bb1:
predecessor blocks: bb3 bb2
$8: phi(bb3: $7, bb2: $6)
Return freeze $8
```
## Code
```javascript
function And$0() {
if (f$1()) {
("<<TODO: handle complex control flow in codegen>>");
} else {
("<<TODO: handle complex control flow in codegen>>");
}
return t8;
}
```
## HIR
```
bb0:
Const mutate $5 = Call mutate f$1()
If (read $5) then:bb2 else:bb3
bb2:
predecessor blocks: bb0
Const mutate $6 = read $5
Goto bb1
bb3:
predecessor blocks: bb0
Const mutate $7 = Call mutate g$4()
Goto bb1
bb1:
predecessor blocks: bb3 bb2
$8: phi(bb3: $7, bb2: $6)
Return freeze $8
```
## Code
```javascript
function Or$0() {
if (f$1()) {
("<<TODO: handle complex control flow in codegen>>");
} else {
("<<TODO: handle complex control flow in codegen>>");
}
return t8;
}
```
## HIR
```
bb0:
Const mutate $9 = Call mutate f$2()
Const mutate $10 = null
Const mutate $11 = Binary read $9 != read $10
If (read $11) then:bb2 else:bb3
bb2:
predecessor blocks: bb0
Const mutate $12 = read $9
Goto bb1
bb3:
predecessor blocks: bb0
Const mutate $13 = Call mutate g$7()
Goto bb1
bb1:
predecessor blocks: bb3 bb2
$14: phi(bb3: $13, bb2: $12)
Return freeze $14
```
## Code
```javascript
function QuestionQuestion$0(props$8) {
if (f$2() != null) {
("<<TODO: handle complex control flow in codegen>>");
} else {
("<<TODO: handle complex control flow in codegen>>");
}
return t14;
}
```
## HIR
```
bb0:
Return
```
## Code
```javascript
function f$0() {
return;
}
```
## HIR
```
bb0:
Return
```
## Code
```javascript
function g$0() {
return;
}
```
@@ -0,0 +1,14 @@
function And() {
return f() && g();
}
function Or() {
return f() || g();
}
function QuestionQuestion(props) {
return f() ?? g();
}
function f() {}
function g() {}