@samitouri / QOS-React-2 / commits / 189b22171f

[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?

Jan Kassens committed Nov 3, 2022 at 10:28 UTC 189b22171f1ffb93fa2570d1dac01c99731bba40
4 files changed +218 -5
compiler/forget/src/HIR/BuildHIR.ts
+49 -2
@@ -811,8 +811,55 @@ function lowerExpression(
811 );
812 }
813 case "??": {
814 - // test should be roughly the equivalent of `<left> != null`
815 - todo("Handle logical ??");
814 + // generate the equivalent of
815 + // const tmp = <left>;
816 + // tmp != null ? tmp : <right>
817 + const left = lowerExpressionToPlace(builder, leftPath);
818 +
819 + const nullPlace: Place = {
820 + kind: "Identifier",
821 + identifier: builder.makeTemporary(),
822 + memberPath: null,
823 + effect: Effect.Unknown,
824 + path: null as any,
825 + };
826 + builder.push({
827 + value: {
828 + kind: "Primitive",
829 + value: null,
830 + path: null as any,
831 + },
832 + path: exprPath,
833 + lvalue: { place: { ...nullPlace }, kind: InstructionKind.Const },
834 + });
835 +
836 + const condPlace: Place = {
837 + kind: "Identifier",
838 + identifier: builder.makeTemporary(),
839 + memberPath: null,
840 + effect: Effect.Unknown,
841 + path: null as any,
842 + };
843 + builder.push({
844 + lvalue: {
845 + place: { ...condPlace },
846 + kind: InstructionKind.Const,
847 + },
848 + value: {
849 + kind: "BinaryExpression",
850 + operator: "!=",
851 + left,
852 + right: nullPlace,
853 + path: null as any,
854 + },
855 + path: null as any,
856 + });
857 + return lowerConditional(
858 + builder,
859 + condPlace,
860 + () => left,
861 + () => lowerExpression(builder, expr.get("right"))
862 + );
863 }
864 default: {
865 assertExhaustive(
compiler/forget/src/HIR/InferReferenceEffects.ts
+2 -3
@@ -210,9 +210,8 @@ class Environment {
210 const values = this.#variables.get(value.identifier.id);
211 invariant(
212 values != null,
213 - `Expected value to be populated at '${String(value.path)}' in '${String(
214 - value.path.parentPath
215 - )}'`
213 + "Expected value for identifier `%s` to be initialized.",
214 + value.identifier.id
215 );
216 this.#variables.set(place.identifier.id, new Set(values));
217 }
compiler/forget/src/__tests__/fixtures/hir/logical-expression.expect.md new
+153
@@ -0,0 +1,153 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function And() {
6 + return f() && g();
7 +}
8 +
9 +function Or() {
10 + return f() || g();
11 +}
12 +
13 +function QuestionQuestion(props) {
14 + return f() ?? g();
15 +}
16 +
17 +function f() {}
18 +function g() {}
19 +
20 +```
21 +
22 +## HIR
23 +
24 +```
25 +bb0:
26 + Const mutate $5 = Call mutate f$1()
27 + If (read $5) then:bb2 else:bb3
28 +bb2:
29 + predecessor blocks: bb0
30 + Const mutate $6 = Call mutate g$4()
31 + Goto bb1
32 +bb3:
33 + predecessor blocks: bb0
34 + Const mutate $7 = read $5
35 + Goto bb1
36 +bb1:
37 + predecessor blocks: bb3 bb2
38 + $8: phi(bb3: $7, bb2: $6)
39 + Return freeze $8
40 +```
41 +
42 +## Code
43 +
44 +```javascript
45 +function And$0() {
46 + if (f$1()) {
47 + ("<<TODO: handle complex control flow in codegen>>");
48 + } else {
49 + ("<<TODO: handle complex control flow in codegen>>");
50 + }
51 + return t8;
52 +}
53 +
54 +```
55 +## HIR
56 +
57 +```
58 +bb0:
59 + Const mutate $5 = Call mutate f$1()
60 + If (read $5) then:bb2 else:bb3
61 +bb2:
62 + predecessor blocks: bb0
63 + Const mutate $6 = read $5
64 + Goto bb1
65 +bb3:
66 + predecessor blocks: bb0
67 + Const mutate $7 = Call mutate g$4()
68 + Goto bb1
69 +bb1:
70 + predecessor blocks: bb3 bb2
71 + $8: phi(bb3: $7, bb2: $6)
72 + Return freeze $8
73 +```
74 +
75 +## Code
76 +
77 +```javascript
78 +function Or$0() {
79 + if (f$1()) {
80 + ("<<TODO: handle complex control flow in codegen>>");
81 + } else {
82 + ("<<TODO: handle complex control flow in codegen>>");
83 + }
84 + return t8;
85 +}
86 +
87 +```
88 +## HIR
89 +
90 +```
91 +bb0:
92 + Const mutate $9 = Call mutate f$2()
93 + Const mutate $10 = null
94 + Const mutate $11 = Binary read $9 != read $10
95 + If (read $11) then:bb2 else:bb3
96 +bb2:
97 + predecessor blocks: bb0
98 + Const mutate $12 = read $9
99 + Goto bb1
100 +bb3:
101 + predecessor blocks: bb0
102 + Const mutate $13 = Call mutate g$7()
103 + Goto bb1
104 +bb1:
105 + predecessor blocks: bb3 bb2
106 + $14: phi(bb3: $13, bb2: $12)
107 + Return freeze $14
108 +```
109 +
110 +## Code
111 +
112 +```javascript
113 +function QuestionQuestion$0(props$8) {
114 + if (f$2() != null) {
115 + ("<<TODO: handle complex control flow in codegen>>");
116 + } else {
117 + ("<<TODO: handle complex control flow in codegen>>");
118 + }
119 + return t14;
120 +}
121 +
122 +```
123 +## HIR
124 +
125 +```
126 +bb0:
127 + Return
128 +```
129 +
130 +## Code
131 +
132 +```javascript
133 +function f$0() {
134 + return;
135 +}
136 +
137 +```
138 +## HIR
139 +
140 +```
141 +bb0:
142 + Return
143 +```
144 +
145 +## Code
146 +
147 +```javascript
148 +function g$0() {
149 + return;
150 +}
151 +
152 +```
153 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/logical-expression.js new
+14
@@ -0,0 +1,14 @@
1 +function And() {
2 + return f() && g();
3 +}
4 +
5 +function Or() {
6 + return f() || g();
7 +}
8 +
9 +function QuestionQuestion(props) {
10 + return f() ?? g();
11 +}
12 +
13 +function f() {}
14 +function g() {}