[typer] Remove type inference of fields

Given that our type inference needs to be very conservative, there's not a lot 
of benefit to having such fine grained type inference. 

In the future, we can use type information from flow/ts for inference.
This commit is contained in:
Sathya Gunasekaran
2022-12-21 21:13:57 +00:00
parent b59f9dd561
commit ff29264050
9 changed files with 93 additions and 255 deletions
+2 -30
View File
@@ -451,15 +451,13 @@ export type Type =
| PrimitiveType
| FunctionType
| ObjectType
| PropType
| PolyType
| TypeVar;
export type PrimitiveType = { kind: "Primitive" };
export type FunctionType = {
kind: "Function";
};
export type ObjectType = { kind: "Object"; properties: Map<string, Type> };
export type PropType = { kind: "Prop"; objectType: Type; name: string };
export type ObjectType = { kind: "Object" };
export type TypeVar = {
kind: "Type";
id: TypeId;
@@ -497,7 +495,6 @@ export function typeEquals(tA: Type, tB: Type): boolean {
typeVarEquals(tA, tB) ||
funcTypeEquals(tA, tB) ||
objectTypeEquals(tA, tB) ||
propTypeEquals(tA, tB) ||
primitiveTypeEquals(tA, tB) ||
polyTypeEquals(tA, tB)
);
@@ -519,32 +516,7 @@ function polyTypeEquals(tA: Type, tB: Type): boolean {
}
function objectTypeEquals(tA: Type, tB: Type): boolean {
if (tA.kind === "Object" && tB.kind === "Object") {
if (tA.properties.size !== tB.properties.size) {
return false;
}
for (const key of tA.properties.keys()) {
if (!typeEquals(tA.properties.get(key)!, tB.properties.get(key)!)) {
return false;
}
}
return true;
}
return false;
}
function propTypeEquals(tA: Type, tB: Type): boolean {
if (tA.kind === "Prop" && tB.kind === "Prop") {
if (tA.name !== tB.name) {
return false;
}
return objectTypeEquals(tA.objectType, tB.objectType);
}
return false;
return tA.kind === "Object" && tB.kind === "Object";
}
function funcTypeEquals(tA: Type, tB: Type): boolean {
+23 -157
View File
@@ -3,10 +3,7 @@ import invariant from "invariant";
import {
HIRFunction,
Instruction,
LValue,
makeType,
Place,
PropType,
Type,
typeEquals,
TypeId,
@@ -85,130 +82,60 @@ function generateTypeEquation(
unifier: Unifier
): Array<TypeEquation> {
const equations: Array<TypeEquation> = [];
function add(left: Type | null, right: Type | null) {
if (left === null || right === null) return;
equations.push({
left,
right,
});
}
const { lvalue, value } = instr;
const left: Type | null = assignTypeForLvalue(lvalue, equations, unifier);
const left = assignType(lvalue?.place);
switch (value.kind) {
case "Primitive": {
if (left !== null) {
equations.push({
left,
right: { kind: "Primitive" },
});
}
add(left, { kind: "Primitive" });
break;
}
case "Identifier": {
if (left !== null) {
let right: Type = assignTypeForPlace(value, equations);
equations.push({
left,
right,
});
}
add(left, assignType(value));
break;
}
case "BinaryExpression": {
if (isPrimitiveBinaryOp(value.operator)) {
equations.push({
left: value.left.identifier.type,
right: { kind: "Primitive" },
});
equations.push({
left: value.right.identifier.type,
right: { kind: "Primitive" },
});
add(assignType(value.left), { kind: "Primitive" });
add(assignType(value.right), { kind: "Primitive" });
}
if (left !== null) {
equations.push({
left,
right: { kind: "Primitive" },
});
}
add(left, { kind: "Primitive" });
break;
}
case "CallExpression": {
const argTypes = value.args.map((a) => a.identifier.type);
// TODO(gsn): Handle method calls separately
if (value.callee.memberPath !== null) {
break;
}
equations.push({
left: value.callee.identifier.type,
right: { kind: "Function" },
});
add(assignType(value.callee), { kind: "Function" });
break;
}
case "ObjectExpression": {
invariant(left !== null, "invald object expression");
const properties = new Map(
[...(value.properties?.entries() ?? [])].map(([prop, place]) => [
prop,
place.identifier.type,
])
);
equations.push({
left,
right: {
kind: "Object",
properties,
},
});
add(left, { kind: "Object" });
break;
}
}
return equations;
}
function assignTypeForPlace(
value: Place,
equations: Array<TypeEquation>
): Type {
if (value.memberPath === null) {
return value.identifier.type;
function assignType(place: Place | undefined): Type | null {
// We type only top level identifiers. Typing objects is not very useful
// when we have to be so conservative.
if (place?.memberPath !== null) {
return null;
}
if (value.memberPath.length > 1) {
// TODO(gsn): Lower nested memberPaths in HIR
return makeType();
}
const propName = value.memberPath[0];
const propType = makeType();
equations.push({
left: propType,
right: {
kind: "Prop",
objectType: value.identifier.type,
name: propName,
},
});
return propType;
}
function assignTypeForLvalue(
lvalue: LValue | null,
equations: Array<TypeEquation>,
unifier: Unifier
): Type | null {
if (lvalue === null) return null;
return (
unifier.generalize(lvalue.place.identifier.type, lvalue.place.memberPath) ??
assignTypeForPlace(lvalue.place, equations)
);
return place.identifier.type;
}
type Substitution = Map<TypeId, Type>;
@@ -242,11 +169,6 @@ class Unifier {
return;
}
if (type.kind === "Prop") {
this.bindToProp(v, type);
return;
}
if (this.occursCheck(v, type)) {
throw new Error("cycle detected");
}
@@ -254,26 +176,6 @@ class Unifier {
this.substitutions.set(v.id, type);
}
bindToProp(type: TypeVar, prop: PropType) {
let object = prop.objectType;
if (object.kind === "Type" && this.substitutions.has(object.id)) {
object = this.substitutions.get(object.id)!;
}
if (object.kind === "Object") {
if (!object.properties.has(prop.name)) {
object.properties.set(prop.name, type);
return;
}
this.unify(object.properties.get(prop.name)!, type);
return;
}
this.substitutions.set(type.id, prop);
}
occursCheck(v: TypeVar, type: Type): boolean {
if (typeEquals(v, type)) return true;
@@ -281,22 +183,10 @@ class Unifier {
return this.occursCheck(v, this.substitutions.get(type.id)!);
}
if (type.kind === "Object") {
return [...type.properties.values()].some((p) => this.occursCheck(v, p));
}
if (type.kind === "Prop") {
return this.occursCheck(v, type.objectType);
}
return false;
}
get(type: Type): Type {
if (type.kind === "Primitive") {
return type;
}
if (type.kind === "Type") {
if (this.substitutions.has(type.id)) {
return this.get(this.substitutions.get(type.id)!);
@@ -307,28 +197,4 @@ class Unifier {
return type;
}
generalize(objectType: Type, name: Array<string> | null): Type | null {
if (objectType.kind === "Type" && this.substitutions.has(objectType.id)) {
objectType = this.substitutions.get(objectType.id)!;
}
if (
name !== null &&
objectType.kind === "Object" &&
objectType.properties.has(name[0])
) {
let type = objectType.properties.get(name[0])!;
if (type.kind === "Poly") {
return type;
}
type = { kind: "Poly" };
objectType.properties.set(name[0], type);
return type;
}
return null;
}
}
@@ -35,12 +35,12 @@ function Component(props) {
```
bb0:
[1] Const mutate items$30:TProp = read props$29.items
[2] Const mutate maxItems$31:TProp = read props$29.maxItems
[3] Const mutate renderedItems$32_@0:TPrimitive[3:33] = Array []
[1] Const mutate items$30 = read props$29.items
[2] Const mutate maxItems$31 = read props$29.maxItems
[3] Const mutate renderedItems$32_@0[3:33] = Array []
[4] Const mutate seen$33_@0[3:33] = New mutate Set$5()
[5] Const mutate $34:TPrimitive = 0
[6] Const mutate max$35_@2:TPrimitive = Call mutate Math$7.max(read $34:TPrimitive, read maxItems$31:TProp)
[6] Const mutate max$35_@2:TPrimitive = Call mutate Math$7.max(read $34:TPrimitive, read maxItems$31)
[7] For init=bb3 test=bb1 loop=bb5 update=bb4 fallthrough=bb2
bb3:
predecessor blocks: bb0
@@ -92,15 +92,15 @@ bb4:
[32] Goto bb1
bb2:
predecessor blocks: bb6 bb1
[33] Const mutate count$66:TProp = read renderedItems$32_@0.length
[33] Const mutate count$66 = read renderedItems$32_@0.length
[34] Const mutate $67:TPrimitive = "div"
[35] Const mutate $68 = "\n "
[36] Const mutate $69:TPrimitive = "h1"
[37] Const mutate $70 = " Items"
[38] Const mutate t7$71_@5 = JSX <read $69:TPrimitive>{freeze count$66:TProp}{read $70}</read $69:TPrimitive>
[38] Const mutate t7$71_@5 = JSX <read $69:TPrimitive>{freeze count$66}{read $70}</read $69:TPrimitive>
[39] Const mutate $72 = "\n "
[40] Const mutate $73 = "\n "
[41] Const mutate t10$74_@6 = JSX <read $67:TPrimitive>{read $68}{read t7$71_@5}{read $72}{freeze renderedItems$32_@0:TPrimitive}{read $73}</read $67:TPrimitive>
[41] Const mutate t10$74_@6 = JSX <read $67:TPrimitive>{read $68}{read t7$71_@5}{read $72}{freeze renderedItems$32_@0}{read $73}</read $67:TPrimitive>
[42] Return read t10$74_@6
```
@@ -110,14 +110,14 @@ bb2:
function Component(
props,
) {
[1] Const mutate items$30:TProp = read props$29.items
[2] Const mutate maxItems$31:TProp = read props$29.maxItems
scope @0 [3:33] deps=[read maxItems$31:TProp, read items$30.length, read items$30] out=[renderedItems$32_@0] {
[3] Const mutate renderedItems$32_@0:TPrimitive[3:33] = Array []
[1] Const mutate items$30 = read props$29.items
[2] Const mutate maxItems$31 = read props$29.maxItems
scope @0 [3:33] deps=[read maxItems$31, read items$30.length, read items$30] out=[renderedItems$32_@0] {
[3] Const mutate renderedItems$32_@0[3:33] = Array []
[4] Const mutate seen$33_@0[3:33] = New mutate Set$5()
[5] Const mutate $34:TPrimitive = 0
scope @2 [6:7] deps=[read maxItems$31:TProp] out=[max$35_@2] {
[6] Const mutate max$35_@2:TPrimitive = Call mutate Math$7.max(read $34:TPrimitive, read maxItems$31:TProp)
scope @2 [6:7] deps=[read maxItems$31] out=[max$35_@2] {
[6] Const mutate max$35_@2:TPrimitive = Call mutate Math$7.max(read $34:TPrimitive, read maxItems$31)
}
for (
[8] Let mutate i$36_@0:TPrimitive[3:33] = 0
@@ -153,18 +153,18 @@ function Component(
}
}
}
[33] Const mutate count$66:TProp = read renderedItems$32_@0.length
[33] Const mutate count$66 = read renderedItems$32_@0.length
[34] Const mutate $67:TPrimitive = "div"
[35] Const mutate $68 = "\n "
[36] Const mutate $69:TPrimitive = "h1"
[37] Const mutate $70 = " Items"
scope @5 [38:39] deps=[freeze count$66:TProp] out=[$71_@5] {
[38] Const mutate $71_@5 = JSX <read $69:TPrimitive>{freeze count$66:TProp}{read $70}</read $69:TPrimitive>
scope @5 [38:39] deps=[freeze count$66] out=[$71_@5] {
[38] Const mutate $71_@5 = JSX <read $69:TPrimitive>{freeze count$66}{read $70}</read $69:TPrimitive>
}
[39] Const mutate $72 = "\n "
[40] Const mutate $73 = "\n "
scope @6 [41:42] deps=[read $71_@5, freeze renderedItems$32_@0:TPrimitive] out=[$74_@6] {
[41] Const mutate $74_@6 = JSX <read $67:TPrimitive>{read $68}{read $71_@5}{read $72}{freeze renderedItems$32_@0:TPrimitive}{read $73}</read $67:TPrimitive>
scope @6 [41:42] deps=[read $71_@5, freeze renderedItems$32_@0] out=[$74_@6] {
[41] Const mutate $74_@6 = JSX <read $67:TPrimitive>{read $68}{read $71_@5}{read $72}{freeze renderedItems$32_@0}{read $73}</read $67:TPrimitive>
}
return read $74_@6
}
@@ -26,15 +26,15 @@ function call(x) {}
```
bb0:
[1] Const mutate cond$8:TProp = read props$7.cond
[2] Const mutate x$9:TProp = read props$7.x
[1] Const mutate cond$8 = read props$7.cond
[2] Const mutate x$9 = read props$7.x
[3] Const mutate a$10:TPrimitive = undefined
[4] Let mutate a$14_@0[4:9] = undefined
[4] If (read cond$8:TProp) then:bb2 else:bb3 fallthrough=bb1
[4] If (read cond$8) then:bb2 else:bb3 fallthrough=bb1
bb2:
predecessor blocks: bb0
[5] Const mutate a$11:TProp = read x$9:TProp
[6] Reassign mutate a$14_@0[4:9] = read a$11:TProp
[5] Const mutate a$11 = read x$9
[6] Reassign mutate a$14_@0[4:9] = read a$11
[6] Goto bb1
bb3:
predecessor blocks: bb0
@@ -55,14 +55,14 @@ bb1:
function Component(
props,
) {
[1] Const mutate cond$8:TProp = read props$7.cond
[2] Const mutate x$9:TProp = read props$7.x
[1] Const mutate cond$8 = read props$7.cond
[2] Const mutate x$9 = read props$7.x
[3] Const mutate a$10:TPrimitive = undefined
scope @0 [4:9] deps=[read cond$8:TProp, read x$9:TProp] out=[a$14_@0] {
scope @0 [4:9] deps=[read cond$8, read x$9] out=[a$14_@0] {
[4] Let mutate a$14_@0[4:9] = undefined
if (read cond$8:TProp) {
[5] Const mutate a$11:TProp = read x$9:TProp
[6] Reassign mutate a$14_@0[4:9] = read a$11:TProp
if (read cond$8) {
[5] Const mutate a$11 = read x$9
[6] Reassign mutate a$14_@0[4:9] = read a$11
} else {
scope @1 [7:8] deps=[] out=[a$12_@1] {
[7] Const mutate a$12_@1 = Array []
@@ -52,31 +52,31 @@ bb2:
Fallthrough: bb1
bb8:
predecessor blocks: bb2
[7] Const mutate x$11:TProp = read props$6.v0
[8] Reassign mutate x$16_@0[2:18] = read x$11:TProp
[7] Const mutate x$11 = read props$6.v0
[8] Reassign mutate x$16_@0[2:18] = read x$11
[8] Goto bb1
bb6:
predecessor blocks: bb2
[9] Const mutate x$12:TProp = read props$6.v1
[10] Reassign mutate x$16_@0[2:18] = read x$12:TProp
[9] Const mutate x$12 = read props$6.v1
[10] Reassign mutate x$16_@0[2:18] = read x$12
[10] Goto bb1
bb4:
predecessor blocks: bb2
[11] Const mutate x$13:TProp = read props$6.v2
[12] Reassign mutate x$16_@0[2:18] = read x$13:TProp
[11] Const mutate x$13 = read props$6.v2
[12] Reassign mutate x$16_@0[2:18] = read x$13
[12] Goto bb1
bb10:
predecessor blocks: bb0
[13] If (read props$6.cond2) then:bb12 else:bb13 fallthrough=bb1
bb12:
predecessor blocks: bb10
[14] Const mutate x$14:TProp = read props$6.b
[15] Reassign mutate x$16_@0[2:18] = read x$14:TProp
[14] Const mutate x$14 = read props$6.b
[15] Reassign mutate x$16_@0[2:18] = read x$14
[15] Goto bb1
bb13:
predecessor blocks: bb10
[16] Const mutate x$15:TProp = read props$6.c
[17] Reassign mutate x$16_@0[2:18] = read x$15:TProp
[16] Const mutate x$15 = read props$6.c
[17] Reassign mutate x$16_@0[2:18] = read x$15
[17] Goto bb1
bb1:
predecessor blocks: bb8 bb6 bb4 bb12 bb13
@@ -99,29 +99,29 @@ function Component(
[5] Const mutate $10:TPrimitive = 0
switch (read props$6.test) {
case read $10:TPrimitive: {
[7] Const mutate x$11:TProp = read props$6.v0
[8] Reassign mutate x$16_@0[2:18] = read x$11:TProp
[7] Const mutate x$11 = read props$6.v0
[8] Reassign mutate x$16_@0[2:18] = read x$11
break bb1
}
case read $9:TPrimitive: {
[9] Const mutate x$12:TProp = read props$6.v1
[10] Reassign mutate x$16_@0[2:18] = read x$12:TProp
[9] Const mutate x$12 = read props$6.v1
[10] Reassign mutate x$16_@0[2:18] = read x$12
break bb1
}
case read $8:TPrimitive: {
}
default: {
[11] Const mutate x$13:TProp = read props$6.v2
[12] Reassign mutate x$16_@0[2:18] = read x$13:TProp
[11] Const mutate x$13 = read props$6.v2
[12] Reassign mutate x$16_@0[2:18] = read x$13
}
}
} else {
if (read props$6.cond2) {
[14] Const mutate x$14:TProp = read props$6.b
[15] Reassign mutate x$16_@0[2:18] = read x$14:TProp
[14] Const mutate x$14 = read props$6.b
[15] Reassign mutate x$16_@0[2:18] = read x$14
} else {
[16] Const mutate x$15:TProp = read props$6.c
[17] Reassign mutate x$16_@0[2:18] = read x$15:TProp
[16] Const mutate x$15 = read props$6.c
[17] Reassign mutate x$16_@0[2:18] = read x$15
}
}
}
@@ -15,7 +15,7 @@ function component() {
bb0:
[1] Const mutate $4:TPrimitive = 1
[2] Const mutate x$5_@0:TObject = Object { t: read $4:TPrimitive }
[3] Const mutate p$6:TPrimitive = read x$5_@0.t
[3] Const mutate p$6 = read x$5_@0.t
[4] Return
```
@@ -28,7 +28,7 @@ function component(
scope @0 [2:3] deps=[] out=[x$5_@0] {
[2] Const mutate x$5_@0:TObject = Object { t: read $4:TPrimitive }
}
[3] Const mutate p$6:TPrimitive = read x$5_@0.t
[3] Const mutate p$6 = read x$5_@0.t
return
}
@@ -19,17 +19,17 @@ function component() {
```
bb0:
[1] Const mutate t0$10_@0:TPrimitive = Call mutate makeSomePrimitive$1:TFunction()
[2] Const mutate t1$11_@1:TPrimitive = Call mutate makeSomePrimitive$1:TFunction()
[3] Const mutate x$12_@2:TObject = Object { u: read t0$10_@0:TPrimitive, v: read t1$11_@1:TPrimitive }
[1] Const mutate t0$10_@0 = Call mutate makeSomePrimitive$1:TFunction()
[2] Const mutate t1$11_@1 = Call mutate makeSomePrimitive$1:TFunction()
[3] Const mutate x$12_@2:TObject = Object { u: read t0$10_@0, v: read t1$11_@1 }
[4] Const mutate u$13:TPrimitive = read x$12_@2.u
[5] Const mutate v$14:TPrimitive = read x$12_@2.v
[6] Const mutate $15:TPrimitive = Binary read u$13:TPrimitive > read v$14:TPrimitive
[7] If (read $15:TPrimitive) then:bb1 else:bb1 fallthrough=bb1
bb1:
predecessor blocks: bb0
[8] Const mutate y$16:TPrimitive = read x$12_@2.u
[9] Const mutate z$17:TPrimitive = read x$12_@2.v
[8] Const mutate y$16 = read x$12_@2.u
[9] Const mutate z$17 = read x$12_@2.v
[10] Return
```
@@ -39,21 +39,21 @@ bb1:
function component(
) {
scope @0 [1:2] deps=[] out=[$10_@0] {
[1] Const mutate $10_@0:TPrimitive = Call mutate makeSomePrimitive$1:TFunction()
[1] Const mutate $10_@0 = Call mutate makeSomePrimitive$1:TFunction()
}
scope @1 [2:3] deps=[] out=[$11_@1] {
[2] Const mutate $11_@1:TPrimitive = Call mutate makeSomePrimitive$1:TFunction()
[2] Const mutate $11_@1 = Call mutate makeSomePrimitive$1:TFunction()
}
scope @2 [3:4] deps=[read $10_@0:TPrimitive, read $11_@1:TPrimitive] out=[x$12_@2] {
[3] Const mutate x$12_@2:TObject = Object { u: read $10_@0:TPrimitive, v: read $11_@1:TPrimitive }
scope @2 [3:4] deps=[read $10_@0, read $11_@1] out=[x$12_@2] {
[3] Const mutate x$12_@2:TObject = Object { u: read $10_@0, v: read $11_@1 }
}
[4] Const mutate u$13:TPrimitive = read x$12_@2.u
[5] Const mutate v$14:TPrimitive = read x$12_@2.v
[6] Const mutate $15:TPrimitive = Binary read u$13:TPrimitive > read v$14:TPrimitive
if (read $15:TPrimitive) {
}
[8] Const mutate y$16:TPrimitive = read x$12_@2.u
[9] Const mutate z$17:TPrimitive = read x$12_@2.v
[8] Const mutate y$16 = read x$12_@2.u
[9] Const mutate z$17 = read x$12_@2.v
return
}
@@ -18,7 +18,7 @@ bb0:
[1] Const mutate x$4_@0:TObject[1:4] = Object { }
[2] Const mutate q$5_@1:TObject = Object { }
[3] Reassign store x$4_@0.t[1:4] = read q$5_@1:TObject
[4] Const mutate z$6:TObject = read x$4_@0.t
[4] Const mutate z$6 = read x$4_@0.t
[5] Return
```
@@ -34,7 +34,7 @@ function component(
}
[3] Reassign store x$4_@0.t[1:4] = read q$5_@1:TObject
}
[4] Const mutate z$6:TObject = read x$4_@0.t
[4] Const mutate z$6 = read x$4_@0.t
return
}
@@ -27,9 +27,9 @@ bb0:
[3] Const mutate o$8_@1:TObject = Object { }
[4] Const mutate x$9_@2:TObject[4:8] = Object { }
[5] Reassign store x$9_@2.t[4:8] = read p$7_@0:TPrimitive
[6] Const mutate z$10_@2:TPrimitive[4:8] = read x$9_@2.t
[6] Const mutate z$10_@2[4:8] = read x$9_@2.t
[7] Reassign store x$9_@2.t[4:8] = read o$8_@1:TObject
[8] Const mutate y$11:TPoly = read x$9_@2.t
[8] Const mutate y$11 = read x$9_@2.t
[9] Return
```
@@ -48,10 +48,10 @@ function component(
scope @2 [4:8] deps=[read p$7_@0:TPrimitive, read o$8_@1:TObject] out=[x$9_@2] {
[4] Const mutate x$9_@2:TObject[4:8] = Object { }
[5] Reassign store x$9_@2.t[4:8] = read p$7_@0:TPrimitive
[6] Const mutate z$10_@2:TPrimitive[4:8] = read x$9_@2.t
[6] Const mutate z$10_@2[4:8] = read x$9_@2.t
[7] Reassign store x$9_@2.t[4:8] = read o$8_@1:TObject
}
[8] Const mutate y$11:TPoly = read x$9_@2.t
[8] Const mutate y$11 = read x$9_@2.t
return
}