[typer] Add type inference for Phis

This commit is contained in:
Sathya Gunasekaran
2023-01-11 11:30:58 +00:00
parent 1910ede500
commit bbba752e98
3 changed files with 97 additions and 4 deletions
@@ -3,7 +3,6 @@ import invariant from "invariant";
import {
HIRFunction,
Instruction,
Place,
Type,
typeEquals,
TypeId,
@@ -46,6 +45,9 @@ export default function (func: HIRFunction) {
function apply(func: HIRFunction, unifier: Unifier) {
for (const [_, block] of func.body.blocks) {
for (const phi of block.phis) {
phi.type = unifier.get(phi.type);
}
for (const instr of block.instructions) {
for (const place of eachInstructionOperand(instr)) {
place.identifier.type = unifier.get(place.identifier.type);
@@ -61,8 +63,20 @@ type TypeEquation = {
right: Type;
};
function* generate(func: HIRFunction) {
function* generate(
func: HIRFunction
): Generator<TypeEquation, void, undefined> {
for (const [_, block] of func.body.blocks) {
for (const phi of block.phis) {
yield {
left: phi.type,
right: {
kind: "Phi",
operands: [...phi.operands.values()].map((id) => id.type),
},
};
}
for (const instr of block.instructions) {
yield* generateTypeEquation(instr);
}
@@ -148,6 +162,19 @@ class Unifier {
return;
}
if (type.kind === "Phi") {
const operands = new Set(type.operands.map((i) => this.get(i).kind));
invariant(operands.size > 0, "there should be at least one operand");
const kind = operands.values().next().value;
// there's only one unique type and it's not a type var
if (operands.size === 1 && kind !== "Type") {
this.unify(v, type.operands[0]);
return;
}
}
if (this.occursCheck(v, type)) {
throw new Error("cycle detected");
}
@@ -162,6 +189,10 @@ class Unifier {
return this.occursCheck(v, this.substitutions.get(type.id)!);
}
if (type.kind === "Phi") {
return type.operands.some((o) => this.occursCheck(v, o));
}
return false;
}
@@ -169,11 +200,13 @@ class Unifier {
if (type.kind === "Type") {
if (this.substitutions.has(type.id)) {
return this.get(this.substitutions.get(type.id)!);
} else {
return type;
}
}
if (type.kind === "Phi") {
return { kind: "Phi", operands: type.operands.map((o) => this.get(o)) };
}
return type;
}
}
@@ -0,0 +1,49 @@
## Input
```javascript
function foo(a, b) {
let x;
if (a) {
x = 1;
} else {
x = 2;
}
let y = x;
return y;
}
```
## Code
```javascript
function foo(a, b) {
const $ = React.useMemoCache();
const x = undefined;
const c_0 = $[0] !== a;
let x$0;
if (c_0) {
x$0 = undefined;
if (a) {
const x$1 = 1;
x$0 = x$1;
} else {
const x$2 = 2;
x$0 = x$2;
}
$[0] = a;
$[1] = x$0;
} else {
x$0 = $[1];
}
const y = x$0;
return y;
}
```
@@ -0,0 +1,11 @@
function foo(a, b) {
let x;
if (a) {
x = 1;
} else {
x = 2;
}
let y = x;
return y;
}