[hir][typer] infer polymorphic types from PropertyLoad and PropertyCall

--- 

Expand Hindley Milner type inference to infer dependent types. 

Say `t` is a typevar and `t'` is some type (a built-in type, phi node, or 
another typevar). 

Our type equations are as follows (please edit/correct notation 😅) 

- type substitution: `t = t'`, 

- ~~dependent~~ polymorphic property load: `t = t'.prop` 

- polymorphic function call `t = fnCall{returnType}` 

- ~~dependent property call: `t = t'.prop` (only if t'.prop is a function 
type)~~ 

- ~~dependent return type: `t = t'.[[returntype]]`~~
This commit is contained in:
Mofei Zhang
2023-03-23 15:08:17 -04:00
parent 9507493ee9
commit 76e6eff110
2 changed files with 27 additions and 19 deletions
+17 -13
View File
@@ -7,7 +7,7 @@ import {
FunctionType,
IdentifierId,
makeIdentifierId,
Type,
ObjectType,
ValueKind,
} from "./HIR";
import { BUILTIN_HOOKS, Hook } from "./Hooks";
@@ -75,19 +75,23 @@ export class Environment {
};
}
getPropertyType(receiver: Type, property: string): BuiltInType | null {
if (receiver.kind === "Object" || receiver.kind === "Function") {
const { shapeId } = receiver;
if (shapeId !== null) {
const shape = BUILTIN_SHAPES.get(shapeId);
invariant(
shape !== undefined,
`[HIR] Forget internal error: cannot resolve shape ${shapeId}`
);
return shape.properties.get(property) ?? null;
}
getPropertyType(
receiver: ObjectType | FunctionType,
property: string
): BuiltInType | null {
const { shapeId } = receiver;
if (shapeId !== null) {
// If an object or function has a shapeId, it must have been assigned
// by Forget (and be present in a builtin or user-defined registry)
const shape = BUILTIN_SHAPES.get(shapeId);
invariant(
shape !== undefined,
`[HIR] Forget internal error: cannot resolve shape ${shapeId}`
);
return shape.properties.get(property) ?? null;
} else {
return null;
}
return null;
}
getFunctionSignature(type: FunctionType): FunctionSignature | null {
@@ -231,13 +231,17 @@ class Unifier {
unify(tA: Type, tB: Type | PolyType): void {
if (tB.kind === "Property") {
const objectType = this.get(tB.object);
const propertyType = this.env.getPropertyType(
objectType,
tB.propertyName
);
if (propertyType !== null) {
this.unify(tA, propertyType);
if (objectType.kind === "Object" || objectType.kind === "Function") {
const propertyType = this.env.getPropertyType(
objectType,
tB.propertyName
);
if (propertyType !== null) {
this.unify(tA, propertyType);
}
}
// We do not error if tB is not a known object or function (even if it
// is a primitive), since JS implicit conversion to objects
return;
} else if (tB.kind === "FunctionCall") {
this.unifyFunctionCall(tA, tB);