[typer] Track return type in FunctionType

Rather than having a special FunctionCall type that deduces the return type, 
change the FunctionType to include the return type. 

This return type is inferred as part of unification.
This commit is contained in:
Sathya Gunasekaran
2023-03-27 15:58:08 +01:00
parent a0fa5ede54
commit bb2325bdce
3 changed files with 38 additions and 36 deletions
+4
View File
@@ -28,6 +28,7 @@ function addFunction(
addShape(registry, shapeId, properties, fn);
return {
kind: "Function",
return: fn.returnType,
shapeId,
};
}
@@ -63,7 +64,10 @@ export type FunctionSignature = {
};
export type ObjectShape = {
// TODO(gsn): When can the key be null here?
properties: Map<string, BuiltInType | null>;
// TODO(gsn): Why do Objects have a `functionType`? Oh, this the constructor.
// Let's rename to constructor?
functionType: FunctionSignature | null;
};
+17 -3
View File
@@ -9,7 +9,13 @@ import { Hook } from "./Hooks";
export type BuiltInType = PrimitiveType | FunctionType | ObjectType;
export type Type = BuiltInType | HookType | PhiType | TypeVar | PolyType;
export type Type =
| BuiltInType
| HookType
| PhiType
| TypeVar
| PolyType
| PropType;
export type PrimitiveType = { kind: "Primitive" };
export type HookType = {
kind: "Hook";
@@ -30,6 +36,7 @@ export type HookType = {
export type FunctionType = {
kind: "Function";
shapeId: string | null;
return: Type;
};
export type ObjectType = {
@@ -48,7 +55,11 @@ export type PhiType = {
kind: "Phi";
operands: Array<Type>;
};
export type PropType = {
kind: "Property";
object: Type;
propertyName: string;
};
/**
* Simulated opaque type for TypeId to prevent using normal numbers as ids
* accidentally.
@@ -109,7 +120,10 @@ function objectTypeEquals(tA: Type, tB: Type): boolean {
}
function funcTypeEquals(tA: Type, tB: Type): boolean {
return typeKindCheck(tA, tB, "Function");
if (tA.kind !== "Function" || tB.kind !== "Function") {
return false;
}
return typeEquals(tA.return, tB.return);
}
function hookTypeEquals(tA: Type, tB: Type): boolean {
+17 -33
View File
@@ -63,25 +63,12 @@ function apply(func: HIRFunction, unifier: Unifier): void {
}
}
type FunctionCallType = {
kind: "FunctionCall";
returnType: TypeVar;
};
type PolyType =
| {
kind: "Property";
object: Type;
propertyName: string;
}
| FunctionCallType;
type TypeEquation = {
left: Type;
right: Type | PolyType;
right: Type;
};
function equation(left: Type, right: Type | PolyType): TypeEquation {
function equation(left: Type, right: Type): TypeEquation {
return {
left,
right,
@@ -165,7 +152,7 @@ function* generateInstructionTypes(
if (hook !== null) {
type = { kind: "Hook", definition: hook };
} else {
type = { kind: "Function", shapeId: null };
type = { kind: "Function", shapeId: null, return: left };
}
yield equation(value.callee.identifier.type, type);
break;
@@ -193,9 +180,11 @@ function* generateInstructionTypes(
case "MethodCall": {
const returnType = makeType();
yield equation(value.property.identifier.type, {
kind: "FunctionCall",
returnType,
kind: "Function",
return: returnType,
shapeId: null,
});
yield equation(left, returnType);
}
}
@@ -210,18 +199,7 @@ class Unifier {
this.env = env;
}
unifyFunctionCall(tA: Type, tB: FunctionCallType): void {
const propertyType = this.get(tA);
if (propertyType.kind === "Function") {
const fn = this.env.getFunctionSignature(propertyType);
const returnType = fn?.returnType ?? null;
if (returnType !== null) {
this.unify(tB.returnType, returnType);
}
}
}
unify(tA: Type, tB: Type | PolyType): void {
unify(tA: Type, tB: Type): void {
if (tB.kind === "Property") {
const objectType = this.get(tB.object);
if (objectType.kind === "Object" || objectType.kind === "Function") {
@@ -236,9 +214,6 @@ class Unifier {
// 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);
return;
}
if (typeEquals(tA, tB)) {
@@ -254,6 +229,11 @@ class Unifier {
this.bindVariableTo(tB, tA);
return;
}
if (tB.kind === "Function" && tA.kind === "Function") {
this.unify(tA.return, tB.return);
return;
}
}
bindVariableTo(v: TypeVar, type: Type): void {
@@ -298,6 +278,10 @@ class Unifier {
return type.operands.some((o) => this.occursCheck(v, o));
}
if (type.kind === "Function") {
return this.occursCheck(v, type.return);
}
return false;
}