mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[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:
@@ -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() {}
|
||||
Reference in New Issue
Block a user