[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]]`~~
Mofei Zhang committed
Mar 23, 2023 at 15:08 UTC
76e6eff110688b9a8cdbe23956ca6cc24dd60cdb
2 files changed
+27
-19
compiler/forget/src/HIR/Environment.ts
+17
-13
@@ -7,7 +7,7 @@ import {
7
FunctionType,
8
IdentifierId,
9
makeIdentifierId,
10
- Type,
10
+ ObjectType,
11
ValueKind,
12
} from "./HIR";
13
import { BUILTIN_HOOKS, Hook } from "./Hooks";
@@ -75,19 +75,23 @@ export class Environment {
75
};
76
}
77
78
- getPropertyType(receiver: Type, property: string): BuiltInType | null {
79
- if (receiver.kind === "Object" || receiver.kind === "Function") {
80
- const { shapeId } = receiver;
81
- if (shapeId !== null) {
82
- const shape = BUILTIN_SHAPES.get(shapeId);
83
- invariant(
84
- shape !== undefined,
85
- `[HIR] Forget internal error: cannot resolve shape ${shapeId}`
86
- );
87
- return shape.properties.get(property) ?? null;
88
- }
78
+ getPropertyType(
79
+ receiver: ObjectType | FunctionType,
80
+ property: string
81
+ ): BuiltInType | null {
82
+ const { shapeId } = receiver;
83
+ if (shapeId !== null) {
84
+ // If an object or function has a shapeId, it must have been assigned
85
+ // by Forget (and be present in a builtin or user-defined registry)
86
+ const shape = BUILTIN_SHAPES.get(shapeId);
87
+ invariant(
88
+ shape !== undefined,
89
+ `[HIR] Forget internal error: cannot resolve shape ${shapeId}`
90
+ );
91
+ return shape.properties.get(property) ?? null;
92
+ } else {
93
+ return null;
94
}
90
- return null;
95
}
96
97
getFunctionSignature(type: FunctionType): FunctionSignature | null {
compiler/forget/src/TypeInference/InferTypes.ts
+10
-6
@@ -231,13 +231,17 @@ class Unifier {
231
unify(tA: Type, tB: Type | PolyType): void {
232
if (tB.kind === "Property") {
233
const objectType = this.get(tB.object);
234
- const propertyType = this.env.getPropertyType(
235
- objectType,
236
- tB.propertyName
237
- );
238
- if (propertyType !== null) {
239
- this.unify(tA, propertyType);
234
+ if (objectType.kind === "Object" || objectType.kind === "Function") {
235
+ const propertyType = this.env.getPropertyType(
236
+ objectType,
237
+ tB.propertyName
238
+ );
239
+ if (propertyType !== null) {
240
+ this.unify(tA, propertyType);
241
+ }
242
}
243
+ // We do not error if tB is not a known object or function (even if it
244
+ // is a primitive), since JS implicit conversion to objects
245
return;
246
} else if (tB.kind === "FunctionCall") {
247
this.unifyFunctionCall(tA, tB);