@samitouri / QOS-React-2 / commits / bbba752e98

[typer] Add type inference for Phis

Sathya Gunasekaran committed Jan 11, 2023 at 11:30 UTC bbba752e98738c259e862ce0088dc8dda36cbec0
3 files changed +97 -4
compiler/forget/src/TypeInference/InferTypes.ts
+37 -4
@@ -3,7 +3,6 @@ import invariant from "invariant";
3 import {
4 HIRFunction,
5 Instruction,
6 - Place,
6 Type,
7 typeEquals,
8 TypeId,
@@ -46,6 +45,9 @@ export default function (func: HIRFunction) {
45
46 function apply(func: HIRFunction, unifier: Unifier) {
47 for (const [_, block] of func.body.blocks) {
48 + for (const phi of block.phis) {
49 + phi.type = unifier.get(phi.type);
50 + }
51 for (const instr of block.instructions) {
52 for (const place of eachInstructionOperand(instr)) {
53 place.identifier.type = unifier.get(place.identifier.type);
@@ -61,8 +63,20 @@ type TypeEquation = {
63 right: Type;
64 };
65
64 -function* generate(func: HIRFunction) {
66 +function* generate(
67 + func: HIRFunction
68 +): Generator<TypeEquation, void, undefined> {
69 for (const [_, block] of func.body.blocks) {
70 + for (const phi of block.phis) {
71 + yield {
72 + left: phi.type,
73 + right: {
74 + kind: "Phi",
75 + operands: [...phi.operands.values()].map((id) => id.type),
76 + },
77 + };
78 + }
79 +
80 for (const instr of block.instructions) {
81 yield* generateTypeEquation(instr);
82 }
@@ -148,6 +162,19 @@ class Unifier {
162 return;
163 }
164
165 + if (type.kind === "Phi") {
166 + const operands = new Set(type.operands.map((i) => this.get(i).kind));
167 +
168 + invariant(operands.size > 0, "there should be at least one operand");
169 + const kind = operands.values().next().value;
170 +
171 + // there's only one unique type and it's not a type var
172 + if (operands.size === 1 && kind !== "Type") {
173 + this.unify(v, type.operands[0]);
174 + return;
175 + }
176 + }
177 +
178 if (this.occursCheck(v, type)) {
179 throw new Error("cycle detected");
180 }
@@ -162,6 +189,10 @@ class Unifier {
189 return this.occursCheck(v, this.substitutions.get(type.id)!);
190 }
191
192 + if (type.kind === "Phi") {
193 + return type.operands.some((o) => this.occursCheck(v, o));
194 + }
195 +
196 return false;
197 }
198
@@ -169,11 +200,13 @@ class Unifier {
200 if (type.kind === "Type") {
201 if (this.substitutions.has(type.id)) {
202 return this.get(this.substitutions.get(type.id)!);
172 - } else {
173 - return type;
203 }
204 }
205
206 + if (type.kind === "Phi") {
207 + return { kind: "Phi", operands: type.operands.map((o) => this.get(o)) };
208 + }
209 +
210 return type;
211 }
212 }
compiler/forget/src/__tests__/fixtures/hir/infer-phi-primitive.expect.md new
+49
@@ -0,0 +1,49 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function foo(a, b) {
6 + let x;
7 + if (a) {
8 + x = 1;
9 + } else {
10 + x = 2;
11 + }
12 +
13 + let y = x;
14 + return y;
15 +}
16 +
17 +```
18 +
19 +## Code
20 +
21 +```javascript
22 +function foo(a, b) {
23 + const $ = React.useMemoCache();
24 + const x = undefined;
25 + const c_0 = $[0] !== a;
26 + let x$0;
27 + if (c_0) {
28 + x$0 = undefined;
29 +
30 + if (a) {
31 + const x$1 = 1;
32 + x$0 = x$1;
33 + } else {
34 + const x$2 = 2;
35 + x$0 = x$2;
36 + }
37 +
38 + $[0] = a;
39 + $[1] = x$0;
40 + } else {
41 + x$0 = $[1];
42 + }
43 +
44 + const y = x$0;
45 + return y;
46 +}
47 +
48 +```
49 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/infer-phi-primitive.js new
+11
@@ -0,0 +1,11 @@
1 +function foo(a, b) {
2 + let x;
3 + if (a) {
4 + x = 1;
5 + } else {
6 + x = 2;
7 + }
8 +
9 + let y = x;
10 + return y;
11 +}