diff --git a/compiler/forget/src/HIR/BuildHIR.ts b/compiler/forget/src/HIR/BuildHIR.ts index 4c72f7e8e8..ce3debfab4 100644 --- a/compiler/forget/src/HIR/BuildHIR.ts +++ b/compiler/forget/src/HIR/BuildHIR.ts @@ -811,8 +811,55 @@ function lowerExpression( ); } case "??": { - // test should be roughly the equivalent of ` != null` - todo("Handle logical ??"); + // generate the equivalent of + // const tmp = ; + // tmp != null ? tmp : + 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( diff --git a/compiler/forget/src/HIR/InferReferenceEffects.ts b/compiler/forget/src/HIR/InferReferenceEffects.ts index 7b8a126f3a..e93bf4e4d0 100644 --- a/compiler/forget/src/HIR/InferReferenceEffects.ts +++ b/compiler/forget/src/HIR/InferReferenceEffects.ts @@ -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)); } diff --git a/compiler/forget/src/__tests__/fixtures/hir/logical-expression.expect.md b/compiler/forget/src/__tests__/fixtures/hir/logical-expression.expect.md new file mode 100644 index 0000000000..212e23f61f --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/logical-expression.expect.md @@ -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()) { + ("<>"); + } else { + ("<>"); + } + 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()) { + ("<>"); + } else { + ("<>"); + } + 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) { + ("<>"); + } else { + ("<>"); + } + return t14; +} + +``` +## HIR + +``` +bb0: + Return +``` + +## Code + +```javascript +function f$0() { + return; +} + +``` +## HIR + +``` +bb0: + Return +``` + +## Code + +```javascript +function g$0() { + return; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/logical-expression.js b/compiler/forget/src/__tests__/fixtures/hir/logical-expression.js new file mode 100644 index 0000000000..f5ad160196 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/logical-expression.js @@ -0,0 +1,14 @@ +function And() { + return f() && g(); +} + +function Or() { + return f() || g(); +} + +function QuestionQuestion(props) { + return f() ?? g(); +} + +function f() {} +function g() {}