2023-03-16 21:50:15 +01:00
|
|
|
|
2022-08-31 13:29:56 +02:00
|
|
|
import {
|
2023-02-03 17:51:27 +01:00
|
|
|
ClassDeclaration,
|
2022-08-31 13:29:56 +02:00
|
|
|
Expression,
|
2022-09-18 14:33:22 +02:00
|
|
|
ExprOperator,
|
|
|
|
Identifier,
|
|
|
|
IdentifierAlt,
|
2022-09-05 19:38:55 +02:00
|
|
|
LetDeclaration,
|
2022-08-31 13:29:56 +02:00
|
|
|
Pattern,
|
2022-09-18 14:33:22 +02:00
|
|
|
ReferenceExpression,
|
|
|
|
ReferenceTypeExpression,
|
2022-09-01 20:06:43 +02:00
|
|
|
SourceFile,
|
2023-03-11 14:24:02 +01:00
|
|
|
StructPattern,
|
2022-08-31 13:29:56 +02:00
|
|
|
Syntax,
|
|
|
|
SyntaxKind,
|
2022-09-18 14:33:22 +02:00
|
|
|
TypeExpression,
|
2022-08-31 13:29:56 +02:00
|
|
|
} from "./cst";
|
2023-02-03 17:51:27 +01:00
|
|
|
import { Symkind } from "./scope"
|
2022-09-11 11:20:21 +02:00
|
|
|
import {
|
|
|
|
describeType,
|
2023-02-03 17:51:27 +01:00
|
|
|
BindingNotFoundDiagnostic,
|
2022-09-11 11:20:21 +02:00
|
|
|
Diagnostics,
|
2022-09-15 13:49:45 +02:00
|
|
|
KindMismatchDiagnostic,
|
2022-09-18 14:33:22 +02:00
|
|
|
ModuleNotFoundDiagnostic,
|
2023-02-03 17:51:27 +01:00
|
|
|
TypeclassNotFoundDiagnostic,
|
2023-03-16 21:50:15 +01:00
|
|
|
TypeclassDeclaredTwiceDiagnostic,
|
2023-06-22 16:19:51 +02:00
|
|
|
FieldNotFoundDiagnostic,
|
|
|
|
TypeMismatchDiagnostic,
|
2022-09-11 11:20:21 +02:00
|
|
|
} from "./diagnostics";
|
2023-08-02 10:37:13 +02:00
|
|
|
import { assert, assertNever, isEmpty, MultiMap, toStringTag, InspectFn } from "./util";
|
2022-09-15 16:04:49 +02:00
|
|
|
import { Analyser } from "./analysis";
|
2023-04-12 21:16:48 +02:00
|
|
|
import { InspectOptions } from "util";
|
2023-08-30 12:07:53 +02:00
|
|
|
import { TypeKind, TApp, TArrow, TCon, TField, TNil, TPresent, TRegularVar, TVSet, TVSub, Type, TypeBase, TAbsent, TRigidVar, TVar, buildTupleTypeWithLoc, buildTupleType, isTVar } from "./types";
|
2023-08-02 10:37:13 +02:00
|
|
|
import { CEmpty, CEqual, CMany, Constraint, ConstraintKind, ConstraintSet } from "./constraints";
|
2022-09-06 15:13:07 +02:00
|
|
|
|
2023-06-21 16:56:04 +02:00
|
|
|
// export class Qual {
|
2022-09-06 15:13:07 +02:00
|
|
|
|
2023-06-21 16:56:04 +02:00
|
|
|
// public constructor(
|
|
|
|
// public preds: Pred[],
|
|
|
|
// public type: Type,
|
|
|
|
// ) {
|
2022-08-31 13:29:56 +02:00
|
|
|
|
2023-06-21 16:56:04 +02:00
|
|
|
// }
|
2023-04-14 19:57:59 +02:00
|
|
|
|
2023-06-21 16:56:04 +02:00
|
|
|
// public substitute(sub: TVSub): Qual {
|
|
|
|
// return new Qual(
|
|
|
|
// this.preds.map(pred => pred.substitute(sub)),
|
|
|
|
// this.type.substitute(sub),
|
|
|
|
// );
|
|
|
|
// }
|
2022-08-31 13:29:56 +02:00
|
|
|
|
2023-06-21 16:56:04 +02:00
|
|
|
// public *getTypeVars() {
|
|
|
|
// for (const pred of this.preds) {
|
|
|
|
// yield* pred.type.getTypeVars();
|
|
|
|
// }
|
|
|
|
// yield* this.type.getTypeVars();
|
|
|
|
// }
|
2022-08-31 13:29:56 +02:00
|
|
|
|
2023-06-21 16:56:04 +02:00
|
|
|
// }
|
2023-03-11 14:24:02 +01:00
|
|
|
|
2023-06-21 16:56:04 +02:00
|
|
|
// class IsInPred {
|
2022-08-31 13:29:56 +02:00
|
|
|
|
2023-06-21 16:56:04 +02:00
|
|
|
// public constructor(
|
|
|
|
// public id: string,
|
|
|
|
// public type: Type,
|
|
|
|
// ) {
|
2022-08-31 13:29:56 +02:00
|
|
|
|
2023-06-21 16:56:04 +02:00
|
|
|
// }
|
2022-09-09 20:02:35 +02:00
|
|
|
|
2023-06-21 16:56:04 +02:00
|
|
|
// public substitute(sub: TVSub): Pred {
|
|
|
|
// return new IsInPred(this.id, this.type.substitute(sub));
|
2022-08-31 13:29:56 +02:00
|
|
|
|
2023-06-21 16:56:04 +02:00
|
|
|
// }
|
2022-09-09 20:02:35 +02:00
|
|
|
|
2023-06-21 16:56:04 +02:00
|
|
|
// }
|
2023-03-16 21:50:15 +01:00
|
|
|
|
2023-06-21 16:56:04 +02:00
|
|
|
// type Pred = IsInPred;
|
2023-03-16 21:50:15 +01:00
|
|
|
|
2022-09-14 16:46:30 +02:00
|
|
|
export const enum KindType {
|
2023-08-02 10:37:13 +02:00
|
|
|
Type,
|
2022-09-14 16:46:30 +02:00
|
|
|
Arrow,
|
|
|
|
Var,
|
|
|
|
}
|
|
|
|
|
|
|
|
class KVSub {
|
|
|
|
|
|
|
|
private mapping = new Map<number, Kind>();
|
|
|
|
|
|
|
|
public set(kv: KVar, kind: Kind): void {
|
|
|
|
this.mapping.set(kv.id, kind);
|
|
|
|
}
|
|
|
|
|
|
|
|
public get(kv: KVar): Kind | undefined {
|
|
|
|
return this.mapping.get(kv.id);
|
|
|
|
}
|
|
|
|
|
|
|
|
public has(kv: KVar): boolean {
|
|
|
|
return this.mapping.has(kv.id);
|
|
|
|
}
|
|
|
|
|
|
|
|
public values(): Iterable<Kind> {
|
|
|
|
return this.mapping.values();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
abstract class KindBase {
|
2022-09-15 22:39:20 +02:00
|
|
|
|
2022-09-14 16:46:30 +02:00
|
|
|
public abstract readonly type: KindType;
|
|
|
|
|
|
|
|
public abstract substitute(sub: KVSub): Kind;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
class KVar extends KindBase {
|
|
|
|
|
|
|
|
public readonly type = KindType.Var;
|
|
|
|
|
|
|
|
public constructor(
|
|
|
|
public id: number,
|
|
|
|
) {
|
|
|
|
super();
|
|
|
|
}
|
|
|
|
|
|
|
|
public substitute(sub: KVSub): Kind {
|
|
|
|
const other = sub.get(this);
|
|
|
|
return other === undefined
|
|
|
|
? this : other.substitute(sub);
|
|
|
|
}
|
|
|
|
|
2022-09-15 22:39:20 +02:00
|
|
|
public hasFailed(): boolean {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-09-14 16:46:30 +02:00
|
|
|
}
|
|
|
|
|
2023-03-11 14:24:02 +01:00
|
|
|
class KType extends KindBase {
|
2022-09-14 16:46:30 +02:00
|
|
|
|
2023-08-02 10:37:13 +02:00
|
|
|
public readonly type = KindType.Type;
|
2023-03-11 14:24:02 +01:00
|
|
|
|
|
|
|
public substitute(_sub: KVSub): Kind {
|
|
|
|
return this;
|
2022-09-15 22:39:20 +02:00
|
|
|
}
|
|
|
|
|
2022-09-14 16:46:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
class KArrow extends KindBase {
|
|
|
|
|
|
|
|
public readonly type = KindType.Arrow;
|
|
|
|
|
|
|
|
public constructor(
|
|
|
|
public left: Kind,
|
|
|
|
public right: Kind,
|
|
|
|
) {
|
|
|
|
super();
|
|
|
|
}
|
|
|
|
|
|
|
|
public substitute(sub: KVSub): Kind {
|
|
|
|
return new KArrow(
|
|
|
|
this.left.substitute(sub),
|
|
|
|
this.right.substitute(sub),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-06-22 15:30:14 +02:00
|
|
|
// TODO actually use these
|
2023-03-11 14:24:02 +01:00
|
|
|
const kindOfTypes = new KType();
|
2023-06-26 18:51:48 +02:00
|
|
|
//const kindOfTypes = new KCon('*');
|
|
|
|
//const kindOfRows = new KCon('r');
|
|
|
|
//const kindOfConstraints = new KCon();
|
2023-03-11 14:24:02 +01:00
|
|
|
|
2022-09-14 16:46:30 +02:00
|
|
|
export type Kind
|
2023-03-11 14:24:02 +01:00
|
|
|
= KType
|
2022-09-14 16:46:30 +02:00
|
|
|
| KArrow
|
|
|
|
| KVar
|
2022-08-31 13:29:56 +02:00
|
|
|
|
|
|
|
abstract class SchemeBase {
|
|
|
|
}
|
|
|
|
|
|
|
|
class Forall extends SchemeBase {
|
|
|
|
|
|
|
|
public constructor(
|
2023-04-12 21:16:48 +02:00
|
|
|
public typeVars: TVSet,
|
|
|
|
public constraint: Constraint,
|
2022-08-31 13:29:56 +02:00
|
|
|
public type: Type,
|
|
|
|
) {
|
|
|
|
super();
|
2023-04-12 21:16:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public *freeTypeVars(): Iterable<TVar> {
|
|
|
|
for (const tv of this.constraint.freeTypeVars()) {
|
|
|
|
if (!this.typeVars.has(tv)) {
|
|
|
|
yield tv;
|
|
|
|
}
|
2023-03-16 21:50:15 +01:00
|
|
|
}
|
2023-04-12 21:16:48 +02:00
|
|
|
for (const tv of this.type.getTypeVars()) {
|
|
|
|
if (!this.typeVars.has(tv)) {
|
|
|
|
yield tv;
|
2023-03-19 15:04:47 +01:00
|
|
|
}
|
2023-03-16 21:50:15 +01:00
|
|
|
}
|
2022-08-31 13:29:56 +02:00
|
|
|
}
|
|
|
|
|
2023-04-13 14:34:52 +02:00
|
|
|
protected [toStringTag](_depth: number, options: InspectOptions, inspect: InspectFn): string {
|
2023-03-19 15:02:19 +01:00
|
|
|
let out = 'forall';
|
|
|
|
if (this.typeVars.size > 0) {
|
2023-04-13 14:34:52 +02:00
|
|
|
out += ' ' + [...this.typeVars].map(tv => inspect(tv, options)).join(' ');
|
2023-03-19 15:02:19 +01:00
|
|
|
}
|
2023-04-13 14:34:52 +02:00
|
|
|
out += '. ' + inspect(this.type, options);
|
2023-03-19 15:02:19 +01:00
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2023-04-12 21:16:48 +02:00
|
|
|
public static mono(type: Type): Forall {
|
|
|
|
return new Forall(new TVSet, new CEmpty, type);
|
|
|
|
}
|
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
public static fromArrays(typeVars: TRegularVar[], constraints: Constraint[], type: Type): Forall {
|
2023-04-12 21:16:48 +02:00
|
|
|
return new Forall(new TVSet(typeVars), new CMany(constraints), type);
|
2022-08-31 13:29:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-09-15 16:56:02 +02:00
|
|
|
export type Scheme
|
2022-08-31 13:29:56 +02:00
|
|
|
= Forall
|
|
|
|
|
2022-09-18 14:33:22 +02:00
|
|
|
type NodeWithReference
|
|
|
|
= Identifier
|
|
|
|
| IdentifierAlt
|
|
|
|
| ExprOperator
|
|
|
|
| ReferenceExpression
|
|
|
|
| ReferenceTypeExpression
|
|
|
|
|
2023-03-16 21:50:15 +01:00
|
|
|
function validateScheme(scheme: Scheme): void {
|
2023-08-30 12:07:53 +02:00
|
|
|
const isMonoVar = scheme.type.kind === TypeKind.RegularVar && scheme.typeVars.size === 0;
|
2023-03-16 21:50:15 +01:00
|
|
|
if (!isMonoVar) {
|
|
|
|
const tvs = new TVSet(scheme.type.getTypeVars())
|
|
|
|
for (const tv of tvs) {
|
|
|
|
if (!scheme.typeVars.has(tv)) {
|
|
|
|
throw new Error(`Type variable ${describeType(tv)} is free because does not appear in the scheme's type variable list`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (const tv of scheme.typeVars) {
|
|
|
|
if (!tvs.has(tv)) {
|
|
|
|
throw new Error(`Polymorphic type variable ${describeType(tv)} does not occur anywhere in scheme's type ${describeType(scheme.type)}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-03 17:51:27 +01:00
|
|
|
class TypeEnv {
|
2022-09-05 19:33:08 +02:00
|
|
|
|
2022-09-15 11:49:53 +02:00
|
|
|
private mapping = new MultiMap<string, [Symkind, Scheme]>();
|
2022-09-05 19:33:08 +02:00
|
|
|
|
|
|
|
public constructor(public parent: TypeEnv | null = null) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-09-15 11:49:53 +02:00
|
|
|
public add(name: string, scheme: Scheme, kind: Symkind): void {
|
|
|
|
this.mapping.add(name, [kind, scheme]);
|
2022-09-05 19:33:08 +02:00
|
|
|
}
|
|
|
|
|
2022-09-18 14:33:22 +02:00
|
|
|
public get(name: string, expectedKind: Symkind): Scheme | null {
|
|
|
|
for (const [kind, scheme] of this.mapping.get(name)) {
|
|
|
|
if (kind & expectedKind) {
|
|
|
|
return scheme;
|
2022-09-05 19:33:08 +02:00
|
|
|
}
|
2022-09-18 14:33:22 +02:00
|
|
|
}
|
2022-09-05 19:33:08 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2023-04-12 21:16:48 +02:00
|
|
|
public hasTypeVar(seek: TVar): boolean {
|
|
|
|
for (const [_name, [_kind, scheme]] of this.mapping) {
|
|
|
|
for (const tv of scheme.freeTypeVars()) {
|
|
|
|
if (tv.id === seek.id) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-08-31 13:29:56 +02:00
|
|
|
}
|
|
|
|
|
2022-09-14 16:46:30 +02:00
|
|
|
class KindEnv {
|
|
|
|
|
2022-09-15 13:56:58 +02:00
|
|
|
private mapping = new Map<string, Kind>();
|
2022-09-14 16:46:30 +02:00
|
|
|
|
|
|
|
public constructor(public parent: KindEnv | null = null) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-09-18 19:33:34 +02:00
|
|
|
public get(name: string): Kind | null {
|
|
|
|
return this.mapping.get(name) ?? null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public set(name: string, kind: Kind): void {
|
2022-09-15 13:56:58 +02:00
|
|
|
assert(!this.mapping.has(name));
|
|
|
|
this.mapping.set(name, kind);
|
2022-09-14 16:46:30 +02:00
|
|
|
}
|
|
|
|
|
2022-09-15 13:56:58 +02:00
|
|
|
public lookup(name: string): Kind | null {
|
2022-09-14 16:46:30 +02:00
|
|
|
let curr: KindEnv | null = this;
|
|
|
|
do {
|
2022-09-15 13:56:58 +02:00
|
|
|
const kind = curr.mapping.get(name);
|
2022-09-14 16:46:30 +02:00
|
|
|
if (kind !== undefined) {
|
|
|
|
return kind;
|
|
|
|
}
|
|
|
|
curr = curr.parent;
|
|
|
|
} while (curr !== null);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-02-03 17:51:27 +01:00
|
|
|
export type { KindEnv, TypeEnv };
|
|
|
|
|
2022-09-18 19:33:34 +02:00
|
|
|
function splitReferences(node: NodeWithReference): [IdentifierAlt[], Identifier | IdentifierAlt | ExprOperator] {
|
|
|
|
let modulePath: IdentifierAlt[];
|
|
|
|
let name: Identifier | IdentifierAlt | ExprOperator;
|
|
|
|
if (node.kind === SyntaxKind.ReferenceExpression || node.kind === SyntaxKind.ReferenceTypeExpression) {
|
|
|
|
modulePath = node.modulePath.map(([name, _dot]) => name);
|
|
|
|
name = node.name;
|
|
|
|
} else {
|
|
|
|
modulePath = [];
|
|
|
|
name = node;
|
|
|
|
}
|
|
|
|
return [modulePath, name]
|
|
|
|
}
|
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
class PolyContext {
|
|
|
|
|
|
|
|
public constructor(
|
|
|
|
public typeVars = new TVSet(),
|
|
|
|
public constraints: ConstraintSet = [],
|
|
|
|
) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface TCInfo {
|
|
|
|
inferredType?: Type;
|
|
|
|
inferredKind?: Kind;
|
|
|
|
poly?: PolyContext;
|
|
|
|
kindEnv?: KindEnv;
|
|
|
|
typeEnv?: TypeEnv;
|
|
|
|
returnType?: Type | null;
|
2022-08-31 13:29:56 +02:00
|
|
|
}
|
|
|
|
|
2023-06-22 15:30:14 +02:00
|
|
|
function isSignatureDeclarationLike(node: LetDeclaration): boolean {
|
|
|
|
return false; // May be foreignKeyword !== null later
|
|
|
|
}
|
|
|
|
|
|
|
|
function isVariableDeclarationLike(node: LetDeclaration): boolean {
|
|
|
|
return node.pattern.kind !== SyntaxKind.NamedPattern || !node.body;
|
|
|
|
}
|
|
|
|
|
2022-09-09 22:37:14 +02:00
|
|
|
function isFunctionDeclarationLike(node: LetDeclaration): boolean {
|
2023-08-13 15:42:48 +02:00
|
|
|
return node.parent!.kind === SyntaxKind.ClassDeclaration
|
|
|
|
|| (!isSignatureDeclarationLike(node) && !isVariableDeclarationLike(node));
|
2023-06-22 15:30:14 +02:00
|
|
|
// return (node.pattern.kind === SyntaxKind.NamedPattern || node.pattern.kind === SyntaxKind.NestedPattern && node.pattern.pattern.kind === SyntaxKind.NamedPattern)
|
|
|
|
// && (node.params.length > 0 || (node.body !== null && node.body.kind === SyntaxKind.BlockBody));
|
2022-09-09 22:37:14 +02:00
|
|
|
}
|
|
|
|
|
2023-06-22 16:19:51 +02:00
|
|
|
function hasTypeVar(typeVars: TVSet, type: Type): boolean {
|
|
|
|
for (const tv of type.getTypeVars()) {
|
|
|
|
if (typeVars.has(tv)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-08-31 13:29:56 +02:00
|
|
|
export class Checker {
|
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
private nextTypeVarId = 0;
|
2022-09-14 16:46:30 +02:00
|
|
|
private nextKindVarId = 0;
|
2022-08-31 13:29:56 +02:00
|
|
|
private nextConTypeId = 0;
|
|
|
|
|
2023-07-04 20:38:40 +02:00
|
|
|
private stringType = this.createTCon('String');
|
|
|
|
private intType = this.createTCon('Int');
|
|
|
|
private boolType = this.createTCon('Bool');
|
2023-08-02 10:37:13 +02:00
|
|
|
private unitType = buildTupleType([]);
|
2022-08-31 13:29:56 +02:00
|
|
|
|
2023-03-16 21:50:15 +01:00
|
|
|
private classDecls = new Map<string, ClassDeclaration>();
|
|
|
|
private globalKindEnv = new KindEnv();
|
|
|
|
private globalTypeEnv = new TypeEnv();
|
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
private typeSolution = new TVSub();
|
2022-09-14 16:46:30 +02:00
|
|
|
private kindSolution = new KVSub();
|
2022-08-31 13:29:56 +02:00
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
private typeEnvStack: TypeEnv[] = [];
|
|
|
|
private polyContextStack: PolyContext[] = [];
|
|
|
|
private returnTypeStack: (Type | null)[] = [];
|
|
|
|
|
2022-08-31 13:29:56 +02:00
|
|
|
public constructor(
|
2022-09-15 16:04:49 +02:00
|
|
|
private analyser: Analyser,
|
2022-08-31 13:29:56 +02:00
|
|
|
private diagnostics: Diagnostics
|
|
|
|
) {
|
|
|
|
|
2023-06-26 18:51:48 +02:00
|
|
|
this.globalKindEnv.set('Int', kindOfTypes);
|
|
|
|
this.globalKindEnv.set('String', kindOfTypes);
|
|
|
|
this.globalKindEnv.set('Bool', kindOfTypes);
|
2023-03-16 21:50:15 +01:00
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
const a = new TRegularVar(this.nextTypeVarId++);
|
|
|
|
const b = new TRegularVar(this.nextTypeVarId++);
|
2023-03-16 21:50:15 +01:00
|
|
|
|
2023-04-12 21:16:48 +02:00
|
|
|
this.globalTypeEnv.add('$', Forall.fromArrays([ a, b ], [], new TArrow(new TArrow(new TArrow(a, b), a), b)), Symkind.Var);
|
|
|
|
this.globalTypeEnv.add('String', Forall.fromArrays([], [], this.stringType), Symkind.Type);
|
|
|
|
this.globalTypeEnv.add('Int', Forall.fromArrays([], [], this.intType), Symkind.Type);
|
|
|
|
this.globalTypeEnv.add('Bool', Forall.fromArrays([], [], this.boolType), Symkind.Type);
|
|
|
|
this.globalTypeEnv.add('True', Forall.fromArrays([], [], this.boolType), Symkind.Var);
|
|
|
|
this.globalTypeEnv.add('False', Forall.fromArrays([], [], this.boolType), Symkind.Var);
|
|
|
|
this.globalTypeEnv.add('+', Forall.fromArrays([], [], TArrow.build([ this.intType, this.intType ], this.intType)), Symkind.Var);
|
|
|
|
this.globalTypeEnv.add('-', Forall.fromArrays([], [], TArrow.build([ this.intType, this.intType ], this.intType)), Symkind.Var);
|
|
|
|
this.globalTypeEnv.add('*', Forall.fromArrays([], [], TArrow.build([ this.intType, this.intType ], this.intType)), Symkind.Var);
|
|
|
|
this.globalTypeEnv.add('/', Forall.fromArrays([], [], TArrow.build([ this.intType, this.intType ], this.intType)), Symkind.Var);
|
|
|
|
this.globalTypeEnv.add('==', Forall.fromArrays([ a ], [], TArrow.build([ a, a ], this.boolType)), Symkind.Var);
|
|
|
|
this.globalTypeEnv.add('not', Forall.fromArrays([], [], new TArrow(this.boolType, this.boolType)), Symkind.Var);
|
2023-03-16 21:50:15 +01:00
|
|
|
|
2022-08-31 13:29:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public getIntType(): Type {
|
|
|
|
return this.intType;
|
|
|
|
}
|
|
|
|
|
|
|
|
public getStringType(): Type {
|
|
|
|
return this.stringType;
|
|
|
|
}
|
|
|
|
|
|
|
|
public getBoolType(): Type {
|
|
|
|
return this.boolType;
|
|
|
|
}
|
|
|
|
|
2023-07-04 20:38:40 +02:00
|
|
|
private createTCon(name: string, node: Syntax | null = null): TCon {
|
|
|
|
return new TCon(this.nextConTypeId++, name, node);
|
2023-03-11 14:24:02 +01:00
|
|
|
}
|
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
private getInfo(node: Syntax): TCInfo {
|
|
|
|
return node as unknown as TCInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
private getPolyContext(): PolyContext {
|
|
|
|
return this.polyContextStack[this.polyContextStack.length-1];
|
|
|
|
}
|
|
|
|
|
|
|
|
private pushInfo(info: TCInfo): void {
|
|
|
|
if (info.poly !== undefined) {
|
|
|
|
this.polyContextStack.push(info.poly);
|
|
|
|
}
|
|
|
|
if (info.returnType !== undefined) {
|
|
|
|
this.returnTypeStack.push(info.returnType);
|
|
|
|
}
|
|
|
|
if (info.typeEnv !== undefined) {
|
|
|
|
this.typeEnvStack.push(info.typeEnv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private popInfo(info: TCInfo): void {
|
|
|
|
if (info.poly !== undefined) {
|
|
|
|
this.polyContextStack.pop();
|
|
|
|
}
|
|
|
|
if (info.returnType !== undefined) {
|
|
|
|
this.returnTypeStack.pop();
|
|
|
|
}
|
|
|
|
if (info.typeEnv !== undefined) {
|
|
|
|
this.typeEnvStack.pop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public getReturnType(): Type {
|
|
|
|
const ty = this.returnTypeStack[this.returnTypeStack.length-1];
|
|
|
|
assert(ty !== null);
|
|
|
|
return ty;
|
|
|
|
}
|
|
|
|
|
|
|
|
private getTypeEnv(): TypeEnv {
|
|
|
|
return this.typeEnvStack[this.typeEnvStack.length-1];
|
|
|
|
}
|
|
|
|
|
|
|
|
private createTRegularVar(node: Syntax | null = null): TRegularVar {
|
|
|
|
const typeVar = new TRegularVar(this.nextTypeVarId++, node);
|
|
|
|
this.getPolyContext().typeVars.add(typeVar);
|
2022-08-31 13:29:56 +02:00
|
|
|
return typeVar;
|
|
|
|
}
|
|
|
|
|
2023-06-26 18:51:48 +02:00
|
|
|
private createRigidVar(displayName: string, node: Syntax | null = null): TRigidVar {
|
2023-08-12 13:46:19 +02:00
|
|
|
const tv = new TRigidVar(this.nextTypeVarId++, displayName, node);
|
|
|
|
this.getPolyContext().typeVars.add(tv);
|
2023-06-26 18:51:48 +02:00
|
|
|
return tv;
|
|
|
|
}
|
|
|
|
|
2022-08-31 13:29:56 +02:00
|
|
|
private addConstraint(constraint: Constraint): void {
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2023-06-22 16:19:51 +02:00
|
|
|
switch (constraint.kind) {
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2023-06-22 16:19:51 +02:00
|
|
|
case ConstraintKind.Empty:
|
|
|
|
break;
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2023-06-22 16:19:51 +02:00
|
|
|
case ConstraintKind.Many:
|
|
|
|
for (const element of constraint.elements) {
|
|
|
|
this.addConstraint(element);
|
|
|
|
}
|
|
|
|
break;
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2023-06-22 16:19:51 +02:00
|
|
|
case ConstraintKind.Equal:
|
|
|
|
{
|
|
|
|
const global = 0;
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2023-06-22 16:19:51 +02:00
|
|
|
let maxLevelLeft = global;
|
2023-08-12 13:46:19 +02:00
|
|
|
for (let i = this.polyContextStack.length; i-- > 0;) {
|
|
|
|
const ctx = this.polyContextStack[i];
|
2023-06-22 16:19:51 +02:00
|
|
|
if (hasTypeVar(ctx.typeVars, constraint.left)) {
|
|
|
|
maxLevelLeft = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2023-06-22 16:19:51 +02:00
|
|
|
let maxLevelRight = global;
|
2023-08-12 13:46:19 +02:00
|
|
|
for (let i = this.polyContextStack.length; i-- > 0;) {
|
|
|
|
const ctx = this.polyContextStack[i];
|
2023-06-26 18:51:48 +02:00
|
|
|
if (hasTypeVar(ctx.typeVars, constraint.right)) {
|
2023-06-22 16:19:51 +02:00
|
|
|
maxLevelRight = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2023-06-22 16:19:51 +02:00
|
|
|
const upperLevel = Math.max(maxLevelLeft, maxLevelRight);
|
|
|
|
let lowerLevel = upperLevel;
|
2023-08-12 13:46:19 +02:00
|
|
|
for (let i = 0; i < this.polyContextStack.length; i++) {
|
|
|
|
const ctx = this.polyContextStack[i];
|
2023-06-22 16:19:51 +02:00
|
|
|
if (hasTypeVar(ctx.typeVars, constraint.left) || hasTypeVar(ctx.typeVars, constraint.right)) {
|
|
|
|
lowerLevel = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2023-06-22 16:19:51 +02:00
|
|
|
if (upperLevel == lowerLevel || maxLevelLeft == global || maxLevelRight == global) {
|
|
|
|
this.solve(constraint);
|
|
|
|
} else {
|
2023-08-12 13:46:19 +02:00
|
|
|
this.polyContextStack[upperLevel].constraints.push(constraint);
|
2023-06-22 16:19:51 +02:00
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2023-06-22 16:19:51 +02:00
|
|
|
break;
|
|
|
|
}
|
2022-08-31 13:29:56 +02:00
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
}
|
2022-08-31 13:29:56 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-04-12 21:16:48 +02:00
|
|
|
private generalize(type: Type, constraints: Constraint[], env: TypeEnv): Scheme {
|
|
|
|
const tvs = new TVSet();
|
|
|
|
for (const tv of type.getTypeVars()) {
|
|
|
|
if (!env.hasTypeVar(tv)) {
|
|
|
|
tvs.add(tv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (const constraint of constraints) {
|
|
|
|
for (const tv of constraint.freeTypeVars()) {
|
|
|
|
if (!env.hasTypeVar(tv)) {
|
|
|
|
tvs.add(tv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return new Forall(tvs, new CMany(constraints), type);
|
|
|
|
}
|
|
|
|
|
2023-02-03 17:51:27 +01:00
|
|
|
private lookupKind(env: KindEnv, node: NodeWithReference, emitDiagnostic = true): Kind | null {
|
2022-09-18 19:33:34 +02:00
|
|
|
const [modulePath, name] = splitReferences(node);
|
|
|
|
if (modulePath.length > 0) {
|
|
|
|
let maxIndex = 0;
|
|
|
|
let currUp = node.getEnclosingModule();
|
|
|
|
outer: for (;;) {
|
2023-03-16 21:50:15 +01:00
|
|
|
let currDown = currUp;
|
2022-09-18 19:33:34 +02:00
|
|
|
for (let i = 0; i < modulePath.length; i++) {
|
|
|
|
const moduleName = modulePath[i];
|
|
|
|
const nextDown = currDown.resolveModule(moduleName.text);
|
|
|
|
if (nextDown === null) {
|
|
|
|
if (currUp.kind === SyntaxKind.SourceFile) {
|
2023-02-03 17:51:27 +01:00
|
|
|
if (emitDiagnostic) {
|
|
|
|
this.diagnostics.add(
|
|
|
|
new ModuleNotFoundDiagnostic(
|
|
|
|
modulePath.slice(maxIndex).map(id => id.text),
|
|
|
|
modulePath[maxIndex],
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2022-09-18 19:33:34 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
currUp = currUp.getEnclosingModule();
|
|
|
|
continue outer;
|
|
|
|
}
|
|
|
|
maxIndex = Math.max(maxIndex, i+1);
|
|
|
|
currDown = nextDown;
|
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
const currDownInfo = this.getInfo(currDown);
|
|
|
|
const found = currDownInfo.kindEnv!.get(name.text);
|
2022-09-18 19:33:34 +02:00
|
|
|
if (found !== null) {
|
|
|
|
return found;
|
|
|
|
}
|
2023-02-03 17:51:27 +01:00
|
|
|
if (emitDiagnostic) {
|
|
|
|
this.diagnostics.add(
|
|
|
|
new BindingNotFoundDiagnostic(
|
|
|
|
modulePath.map(id => id.text),
|
|
|
|
name.text,
|
|
|
|
name,
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2022-09-18 19:33:34 +02:00
|
|
|
return null;
|
|
|
|
}
|
2022-09-18 14:33:22 +02:00
|
|
|
} else {
|
2022-09-18 19:33:34 +02:00
|
|
|
let curr: KindEnv | null = env;
|
|
|
|
do {
|
|
|
|
const found = curr.get(name.text);
|
|
|
|
if (found !== null) {
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
curr = curr.parent;
|
|
|
|
} while(curr !== null);
|
2023-02-03 17:51:27 +01:00
|
|
|
if (emitDiagnostic) {
|
|
|
|
this.diagnostics.add(
|
|
|
|
new BindingNotFoundDiagnostic(
|
|
|
|
[],
|
|
|
|
name.text,
|
|
|
|
name,
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2022-09-18 19:33:34 +02:00
|
|
|
return null;
|
2022-09-18 14:33:22 +02:00
|
|
|
}
|
2022-09-18 19:33:34 +02:00
|
|
|
}
|
|
|
|
|
2023-06-26 18:51:48 +02:00
|
|
|
private lookup(node: NodeWithReference, expectedKind: Symkind, enableDiagnostics = true): Scheme | null {
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-18 19:33:34 +02:00
|
|
|
const [modulePath, name] = splitReferences(node);
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-18 14:33:22 +02:00
|
|
|
if (modulePath.length > 0) {
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-18 14:33:22 +02:00
|
|
|
let maxIndex = 0;
|
|
|
|
let currUp = node.getEnclosingModule();
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-18 14:33:22 +02:00
|
|
|
outer: for (;;) {
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2023-03-16 21:50:15 +01:00
|
|
|
let currDown = currUp;
|
2022-09-18 14:33:22 +02:00
|
|
|
for (let i = 0; i < modulePath.length; i++) {
|
|
|
|
const moduleName = modulePath[i];
|
|
|
|
const nextDown = currDown.resolveModule(moduleName.text);
|
|
|
|
if (nextDown === null) {
|
|
|
|
if (currUp.kind === SyntaxKind.SourceFile) {
|
2023-06-26 18:51:48 +02:00
|
|
|
if (enableDiagnostics) {
|
|
|
|
this.diagnostics.add(
|
|
|
|
new ModuleNotFoundDiagnostic(
|
|
|
|
modulePath.slice(maxIndex).map(id => id.text),
|
|
|
|
modulePath[maxIndex],
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2022-09-18 14:33:22 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
currUp = currUp.getEnclosingModule();
|
|
|
|
continue outer;
|
|
|
|
}
|
|
|
|
maxIndex = Math.max(maxIndex, i+1);
|
|
|
|
currDown = nextDown;
|
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
|
|
|
const currDownInfo = this.getInfo(currDown);
|
|
|
|
|
|
|
|
const found = currDownInfo.typeEnv!.get(name.text, expectedKind);
|
2022-09-18 14:33:22 +02:00
|
|
|
if (found !== null) {
|
|
|
|
return found;
|
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2023-06-26 18:51:48 +02:00
|
|
|
if (enableDiagnostics) {
|
|
|
|
this.diagnostics.add(
|
|
|
|
new BindingNotFoundDiagnostic(
|
|
|
|
modulePath.map(id => id.text),
|
|
|
|
name.text,
|
|
|
|
name,
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-18 14:33:22 +02:00
|
|
|
return null;
|
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-18 14:33:22 +02:00
|
|
|
} else {
|
2023-08-12 13:46:19 +02:00
|
|
|
|
|
|
|
for (let i = this.typeEnvStack.length-1; i >= 0; i--) {
|
|
|
|
const curr = this.typeEnvStack[i];
|
2022-09-18 14:33:22 +02:00
|
|
|
const found = curr.get(name.text, expectedKind);
|
|
|
|
if (found !== null) {
|
|
|
|
return found;
|
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
}
|
|
|
|
|
2023-06-26 18:51:48 +02:00
|
|
|
if (enableDiagnostics) {
|
|
|
|
this.diagnostics.add(
|
|
|
|
new BindingNotFoundDiagnostic(
|
|
|
|
[],
|
|
|
|
name.text,
|
|
|
|
name,
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-18 19:33:34 +02:00
|
|
|
return null;
|
2022-08-31 13:29:56 +02:00
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
}
|
2022-08-31 13:29:56 +02:00
|
|
|
|
2023-04-12 21:16:48 +02:00
|
|
|
}
|
|
|
|
|
2022-09-11 11:20:21 +02:00
|
|
|
private createSubstitution(scheme: Scheme): TVSub {
|
2022-08-31 13:29:56 +02:00
|
|
|
const sub = new TVSub();
|
2023-03-19 15:02:19 +01:00
|
|
|
const tvs = [...scheme.typeVars]
|
2023-03-16 21:50:15 +01:00
|
|
|
for (const tv of tvs) {
|
2023-08-12 13:46:19 +02:00
|
|
|
sub.set(tv, this.createTRegularVar());
|
2022-08-31 13:29:56 +02:00
|
|
|
}
|
2022-09-11 11:20:21 +02:00
|
|
|
return sub;
|
|
|
|
}
|
|
|
|
|
2023-06-28 22:09:17 +02:00
|
|
|
private simplifyType(type: Type): Type {
|
|
|
|
type = type.find();
|
|
|
|
switch (type.kind) {
|
2023-08-30 12:07:53 +02:00
|
|
|
case TypeKind.RegularVar:
|
2023-06-28 22:09:17 +02:00
|
|
|
case TypeKind.RigidVar:
|
|
|
|
case TypeKind.Nil:
|
|
|
|
case TypeKind.Absent:
|
|
|
|
case TypeKind.Con:
|
|
|
|
return type;
|
|
|
|
case TypeKind.App:
|
|
|
|
{
|
|
|
|
const left = type.left.find();
|
|
|
|
const right = type.right.find();
|
|
|
|
if (left === type.left && right === type.right) {
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
return new TApp(left, right, type.node);
|
|
|
|
}
|
|
|
|
case TypeKind.Arrow:
|
|
|
|
{
|
|
|
|
const paramType = type.paramType.find();
|
|
|
|
const returnType = type.returnType.find();
|
|
|
|
if (paramType === type.paramType && returnType === type.returnType) {
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
return new TArrow(paramType, returnType, type.node);
|
|
|
|
}
|
|
|
|
case TypeKind.Field:
|
|
|
|
{
|
|
|
|
const newType = type.type.find();
|
|
|
|
const newRestType = type.restType.find();
|
|
|
|
if (newType === type.type && newRestType === type.restType) {
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
return new TField(type.name, newType, newRestType, type.node);
|
|
|
|
}
|
|
|
|
case TypeKind.Present:
|
|
|
|
{
|
|
|
|
const newType = type.type.find();
|
|
|
|
if (newType === type.type) {
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
return new TPresent(newType, type.node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-11 11:20:21 +02:00
|
|
|
private instantiate(scheme: Scheme, node: Syntax | null, sub = this.createSubstitution(scheme)): Type {
|
2023-04-12 21:16:48 +02:00
|
|
|
const transform = (constraint: Constraint): Constraint => {
|
|
|
|
switch (constraint.kind) {
|
|
|
|
case ConstraintKind.Many:
|
|
|
|
const newConstraints: Constraint[] = [];
|
|
|
|
for (const element of constraint.elements) {
|
|
|
|
newConstraints.push(transform(element));
|
|
|
|
}
|
|
|
|
return new CMany(newConstraints);
|
|
|
|
case ConstraintKind.Empty:
|
|
|
|
return constraint;
|
|
|
|
case ConstraintKind.Equal:
|
2023-06-28 22:09:17 +02:00
|
|
|
constraint.left = this.simplifyType(constraint.left)
|
|
|
|
constraint.right = this.simplifyType(constraint.right)
|
2023-04-12 21:16:48 +02:00
|
|
|
const newConstraint = constraint.substitute(sub);
|
2023-04-13 17:22:45 +02:00
|
|
|
newConstraint.node = node;
|
|
|
|
newConstraint.prevInstantiation = constraint;
|
2023-04-12 21:16:48 +02:00
|
|
|
return newConstraint;
|
2023-06-21 16:56:04 +02:00
|
|
|
default:
|
|
|
|
assertNever(constraint);
|
2023-04-12 21:16:48 +02:00
|
|
|
}
|
2022-08-31 13:29:56 +02:00
|
|
|
}
|
2023-04-12 21:16:48 +02:00
|
|
|
this.addConstraint(transform(scheme.constraint));
|
2023-06-28 22:09:17 +02:00
|
|
|
return this.simplifyType(scheme.type).substitute(sub);
|
2022-08-31 13:29:56 +02:00
|
|
|
}
|
|
|
|
|
2022-09-15 11:49:53 +02:00
|
|
|
private addBinding(name: string, scheme: Scheme, kind: Symkind): void {
|
2023-08-12 13:46:19 +02:00
|
|
|
this.getTypeEnv().add(name, scheme, kind);
|
2022-08-31 13:29:56 +02:00
|
|
|
}
|
|
|
|
|
2023-03-11 14:24:02 +01:00
|
|
|
private unifyKindMany(first: Kind, rest: Kind[], node: TypeExpression): boolean {
|
|
|
|
return rest.every(kind => this.unifyKind(kind, first, node));
|
|
|
|
}
|
|
|
|
|
2022-09-14 16:46:30 +02:00
|
|
|
private inferKindFromTypeExpression(node: TypeExpression, env: KindEnv): Kind {
|
2023-03-11 14:24:02 +01:00
|
|
|
|
|
|
|
// Store the resluting kind in this variable whenever we didn't encounter
|
|
|
|
// any errors and wish to proceed with type inference on this node.
|
|
|
|
let kind: Kind | undefined;
|
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
// Fetch the type checking information for this node because we're going to use it anyways.
|
|
|
|
const info = this.getInfo(node);
|
|
|
|
|
2022-09-14 16:46:30 +02:00
|
|
|
switch (node.kind) {
|
2023-03-11 14:24:02 +01:00
|
|
|
|
2022-09-16 12:43:06 +02:00
|
|
|
case SyntaxKind.TupleTypeExpression:
|
|
|
|
{
|
2023-03-11 14:24:02 +01:00
|
|
|
if (this.unifyKindMany(kindOfTypes, node.elements.map(el => this.inferKindFromTypeExpression(el, env)), node)) {
|
|
|
|
kind = kindOfTypes;
|
2022-09-16 12:43:06 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2023-03-11 14:24:02 +01:00
|
|
|
|
2022-09-15 16:04:02 +02:00
|
|
|
case SyntaxKind.ArrowTypeExpression:
|
|
|
|
{
|
2023-03-11 14:24:02 +01:00
|
|
|
if (node.paramTypeExprs.every(param => this.unifyKind(kindOfTypes, this.inferKindFromTypeExpression(param, env), node))
|
|
|
|
&& this.unifyKind(kindOfTypes, this.inferKindFromTypeExpression(node.returnTypeExpr, env), node)) {
|
|
|
|
kind = kindOfTypes;
|
2022-09-15 16:04:02 +02:00
|
|
|
}
|
2022-09-15 22:39:20 +02:00
|
|
|
break;
|
2022-09-15 16:04:02 +02:00
|
|
|
}
|
2023-03-11 14:24:02 +01:00
|
|
|
|
2022-09-14 16:46:30 +02:00
|
|
|
case SyntaxKind.ReferenceTypeExpression:
|
|
|
|
{
|
2022-09-18 19:33:34 +02:00
|
|
|
const matchedKind = this.lookupKind(env, node);
|
2023-03-11 14:24:02 +01:00
|
|
|
if (matchedKind !== null) {
|
2022-09-18 14:33:22 +02:00
|
|
|
kind = matchedKind;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2023-03-11 14:24:02 +01:00
|
|
|
|
2023-06-21 16:56:04 +02:00
|
|
|
case SyntaxKind.ForallTypeExpression:
|
|
|
|
{
|
2023-06-26 18:51:48 +02:00
|
|
|
// TODO we currently automatically introduce type variables but maybe we should use the ForallTypeExpression?
|
2023-06-21 16:56:04 +02:00
|
|
|
kind = this.inferKindFromTypeExpression(node.typeExpr, env);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SyntaxKind.TypeExpressionWithConstraints:
|
|
|
|
{
|
|
|
|
// TODO check if we need to kind node.constraints
|
|
|
|
kind = this.inferKindFromTypeExpression(node.typeExpr, env);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-09-18 14:33:22 +02:00
|
|
|
case SyntaxKind.VarTypeExpression:
|
|
|
|
{
|
2023-02-03 17:51:27 +01:00
|
|
|
const matchedKind = this.lookupKind(env, node.name, false);
|
2023-03-11 14:24:02 +01:00
|
|
|
// If no kind is associated to the type variable with the given name,
|
|
|
|
// we can assign a fresh kind variable to the type variable. Next time,
|
|
|
|
// the type variable will remember whatever unified with it in-between.
|
2022-09-18 14:33:22 +02:00
|
|
|
if (matchedKind === null) {
|
|
|
|
kind = this.createKindVar();
|
2023-02-03 17:51:27 +01:00
|
|
|
env.set(node.name.text, kind);
|
2022-09-15 22:39:20 +02:00
|
|
|
} else {
|
|
|
|
kind = matchedKind;
|
2022-09-14 16:46:30 +02:00
|
|
|
}
|
2022-09-15 22:39:20 +02:00
|
|
|
break;
|
2022-09-14 16:46:30 +02:00
|
|
|
}
|
2023-03-11 14:24:02 +01:00
|
|
|
|
2022-09-14 16:46:30 +02:00
|
|
|
case SyntaxKind.AppTypeExpression:
|
|
|
|
{
|
2022-09-15 22:39:20 +02:00
|
|
|
kind = this.inferKindFromTypeExpression(node.operator, env);
|
2022-09-14 22:34:53 +02:00
|
|
|
for (const arg of node.args) {
|
2022-09-15 22:39:20 +02:00
|
|
|
kind = this.applyKind(kind, this.inferKindFromTypeExpression(arg, env), node);
|
2022-09-14 16:46:30 +02:00
|
|
|
}
|
2022-09-15 22:39:20 +02:00
|
|
|
break;
|
2022-09-14 16:46:30 +02:00
|
|
|
}
|
2023-03-11 14:24:02 +01:00
|
|
|
|
2022-09-14 16:46:30 +02:00
|
|
|
case SyntaxKind.NestedTypeExpression:
|
|
|
|
{
|
2022-09-15 22:39:20 +02:00
|
|
|
kind = this.inferKindFromTypeExpression(node.typeExpr, env);
|
|
|
|
break;
|
2022-09-14 16:46:30 +02:00
|
|
|
}
|
2023-03-11 14:24:02 +01:00
|
|
|
|
2022-09-14 16:46:30 +02:00
|
|
|
default:
|
|
|
|
throw new Error(`Unexpected ${node}`);
|
|
|
|
}
|
2023-03-11 14:24:02 +01:00
|
|
|
|
|
|
|
// We store the kind on the node so there is a one-to-one correspondence
|
|
|
|
// and this way the kind can be refrieved very efficiently.
|
|
|
|
// Note that at this point `kind` may be undefined. This signals further
|
|
|
|
// inference logic that this node should be skipped because it already contains errors.
|
2023-08-12 13:46:19 +02:00
|
|
|
info.inferredKind = kind;
|
2023-03-11 14:24:02 +01:00
|
|
|
|
|
|
|
// Set a filler default for the node in a way that allows other unification
|
|
|
|
// errors to be caught.
|
|
|
|
if (kind === undefined) {
|
|
|
|
kind = this.createKindVar();
|
|
|
|
}
|
|
|
|
|
2022-09-15 22:39:20 +02:00
|
|
|
return kind;
|
2022-09-14 16:46:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private createKindVar(): KVar {
|
|
|
|
return new KVar(this.nextKindVarId++);
|
|
|
|
}
|
|
|
|
|
|
|
|
private applyKind(operator: Kind, arg: Kind, node: Syntax): Kind {
|
|
|
|
switch (operator.type) {
|
|
|
|
case KindType.Var:
|
|
|
|
{
|
|
|
|
const a1 = this.createKindVar();
|
|
|
|
const a2 = this.createKindVar();
|
|
|
|
const arrow = new KArrow(a1, a2);
|
|
|
|
this.unifyKind(arrow, operator, node);
|
2022-09-15 20:46:07 +02:00
|
|
|
this.unifyKind(a1, arg, node);
|
|
|
|
return a2;
|
2022-09-14 16:46:30 +02:00
|
|
|
}
|
|
|
|
case KindType.Arrow:
|
|
|
|
{
|
|
|
|
// Unify the argument to the operator's argument kind and return
|
|
|
|
// whatever the operator returns.
|
|
|
|
this.unifyKind(operator.left, arg, node);
|
|
|
|
return operator.right;
|
|
|
|
}
|
2023-03-11 14:24:02 +01:00
|
|
|
default:
|
2022-09-14 16:46:30 +02:00
|
|
|
{
|
|
|
|
this.diagnostics.add(
|
|
|
|
new KindMismatchDiagnostic(
|
|
|
|
operator,
|
|
|
|
new KArrow(
|
|
|
|
this.createKindVar(),
|
|
|
|
this.createKindVar()
|
|
|
|
),
|
|
|
|
node
|
|
|
|
)
|
|
|
|
);
|
|
|
|
// Create a filler kind variable that still will be able to catch other errors.
|
2023-03-11 14:24:02 +01:00
|
|
|
return this.createKindVar();
|
2022-09-14 16:46:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private forwardDeclareKind(node: Syntax, env: KindEnv): void {
|
|
|
|
switch (node.kind) {
|
2022-09-18 14:33:22 +02:00
|
|
|
case SyntaxKind.ModuleDeclaration:
|
|
|
|
{
|
2023-08-12 13:46:19 +02:00
|
|
|
const info = this.getInfo(node);
|
|
|
|
const innerEnv = info.kindEnv = new KindEnv(env);
|
2022-09-18 14:33:22 +02:00
|
|
|
for (const element of node.elements) {
|
|
|
|
this.forwardDeclareKind(element, innerEnv);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2022-09-14 16:46:30 +02:00
|
|
|
case SyntaxKind.SourceFile:
|
|
|
|
{
|
|
|
|
for (const element of node.elements) {
|
|
|
|
this.forwardDeclareKind(element, env);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2022-09-15 22:39:20 +02:00
|
|
|
case SyntaxKind.TypeDeclaration:
|
|
|
|
{
|
|
|
|
const innerEnv = new KindEnv(env);
|
2023-06-26 18:51:48 +02:00
|
|
|
let kind: Kind = kindOfTypes;
|
2022-09-15 22:39:20 +02:00
|
|
|
for (let i = node.varExps.length-1; i >= 0; i--) {
|
|
|
|
const varExpr = node.varExps[i];
|
|
|
|
const paramKind = this.createKindVar();
|
2022-09-18 19:33:34 +02:00
|
|
|
innerEnv.set(varExpr.text, paramKind);
|
2022-09-15 22:39:20 +02:00
|
|
|
kind = new KArrow(paramKind, kind);
|
|
|
|
}
|
2022-09-18 19:33:34 +02:00
|
|
|
env.set(node.name.text, this.inferKindFromTypeExpression(node.typeExpression, innerEnv));
|
2022-09-15 22:39:20 +02:00
|
|
|
break;
|
|
|
|
}
|
2022-09-14 16:46:30 +02:00
|
|
|
case SyntaxKind.StructDeclaration:
|
2022-09-15 11:49:53 +02:00
|
|
|
{
|
2022-09-18 19:33:34 +02:00
|
|
|
env.set(node.name.text, this.createKindVar());
|
2022-09-15 11:49:53 +02:00
|
|
|
break;
|
|
|
|
}
|
2022-09-14 16:46:30 +02:00
|
|
|
case SyntaxKind.EnumDeclaration:
|
|
|
|
{
|
2022-09-18 19:33:34 +02:00
|
|
|
env.set(node.name.text, this.createKindVar());
|
2022-09-14 16:46:30 +02:00
|
|
|
if (node.members !== null) {
|
|
|
|
for (const member of node.members) {
|
2022-09-18 19:33:34 +02:00
|
|
|
env.set(member.name.text, this.createKindVar());
|
2022-09-14 16:46:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-03-11 14:24:02 +01:00
|
|
|
|
2022-09-14 16:46:30 +02:00
|
|
|
private inferKind(node: Syntax, env: KindEnv): void {
|
2023-03-11 14:24:02 +01:00
|
|
|
|
2022-09-14 16:46:30 +02:00
|
|
|
switch (node.kind) {
|
2023-03-11 14:24:02 +01:00
|
|
|
|
2022-09-18 14:33:22 +02:00
|
|
|
case SyntaxKind.ModuleDeclaration:
|
|
|
|
{
|
2023-08-12 13:46:19 +02:00
|
|
|
const info = this.getInfo(node);
|
|
|
|
const innerEnv = info.kindEnv!;
|
2022-09-18 14:33:22 +02:00
|
|
|
for (const element of node.elements) {
|
|
|
|
this.inferKind(element, innerEnv);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2023-03-11 14:24:02 +01:00
|
|
|
|
2023-02-03 17:51:27 +01:00
|
|
|
case SyntaxKind.ClassDeclaration:
|
|
|
|
case SyntaxKind.InstanceDeclaration:
|
|
|
|
{
|
2023-03-16 21:50:15 +01:00
|
|
|
if (node.constraintClause !== null) {
|
|
|
|
for (const constraint of node.constraintClause.constraints) {
|
2023-02-03 17:51:27 +01:00
|
|
|
for (const typeExpr of constraint.types) {
|
2023-06-26 18:51:48 +02:00
|
|
|
this.unifyKind(this.inferKindFromTypeExpression(typeExpr, env), kindOfTypes, typeExpr);
|
2023-02-03 17:51:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-03-16 21:50:15 +01:00
|
|
|
for (const typeExpr of node.types) {
|
2023-06-26 18:51:48 +02:00
|
|
|
this.unifyKind(this.inferKindFromTypeExpression(typeExpr, env), kindOfTypes, typeExpr);
|
2023-02-03 17:51:27 +01:00
|
|
|
}
|
|
|
|
for (const element of node.elements) {
|
|
|
|
this.inferKind(element, env);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2023-03-11 14:24:02 +01:00
|
|
|
|
2022-09-14 16:46:30 +02:00
|
|
|
case SyntaxKind.SourceFile:
|
|
|
|
{
|
|
|
|
for (const element of node.elements) {
|
|
|
|
this.inferKind(element, env);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2023-03-11 14:24:02 +01:00
|
|
|
|
2022-09-14 16:46:30 +02:00
|
|
|
case SyntaxKind.StructDeclaration:
|
|
|
|
{
|
2022-09-15 20:33:34 +02:00
|
|
|
const declKind = env.lookup(node.name.text)!;
|
|
|
|
const innerEnv = new KindEnv(env);
|
2023-06-26 18:51:48 +02:00
|
|
|
let kind: Kind = kindOfTypes;
|
2022-09-15 20:33:34 +02:00
|
|
|
for (let i = node.varExps.length-1; i >= 0; i--) {
|
|
|
|
const varExpr = node.varExps[i];
|
|
|
|
const paramKind = this.createKindVar();
|
2022-09-18 19:33:34 +02:00
|
|
|
innerEnv.set(varExpr.text, paramKind);
|
2022-09-15 20:33:34 +02:00
|
|
|
kind = new KArrow(paramKind, kind);
|
|
|
|
}
|
|
|
|
this.unifyKind(declKind, kind, node);
|
|
|
|
if (node.fields !== null) {
|
|
|
|
for (const field of node.fields) {
|
2023-06-26 18:51:48 +02:00
|
|
|
this.unifyKind(this.inferKindFromTypeExpression(field.typeExpr, innerEnv), kindOfTypes, field.typeExpr);
|
2022-09-15 20:33:34 +02:00
|
|
|
}
|
|
|
|
}
|
2022-09-14 16:46:30 +02:00
|
|
|
break;
|
|
|
|
}
|
2023-03-11 14:24:02 +01:00
|
|
|
|
2022-09-14 16:46:30 +02:00
|
|
|
case SyntaxKind.EnumDeclaration:
|
|
|
|
{
|
2022-09-15 13:56:58 +02:00
|
|
|
const declKind = env.lookup(node.name.text)!;
|
2022-09-14 16:46:30 +02:00
|
|
|
const innerEnv = new KindEnv(env);
|
2023-06-26 18:51:48 +02:00
|
|
|
let kind: Kind = kindOfTypes;
|
2022-09-14 16:46:30 +02:00
|
|
|
// FIXME should I go from right to left or left to right?
|
|
|
|
for (let i = node.varExps.length-1; i >= 0; i--) {
|
|
|
|
const varExpr = node.varExps[i];
|
|
|
|
const paramKind = this.createKindVar();
|
2022-09-18 19:33:34 +02:00
|
|
|
innerEnv.set(varExpr.text, paramKind);
|
2022-09-14 16:46:30 +02:00
|
|
|
kind = new KArrow(paramKind, kind);
|
|
|
|
}
|
|
|
|
this.unifyKind(declKind, kind, node);
|
|
|
|
if (node.members !== null) {
|
|
|
|
for (const member of node.members) {
|
|
|
|
switch (member.kind) {
|
|
|
|
case SyntaxKind.EnumDeclarationTupleElement:
|
|
|
|
{
|
|
|
|
for (const element of member.elements) {
|
2023-06-26 18:51:48 +02:00
|
|
|
this.unifyKind(this.inferKindFromTypeExpression(element, innerEnv), kindOfTypes, element);
|
2022-09-14 16:46:30 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2022-09-15 22:39:20 +02:00
|
|
|
case SyntaxKind.EnumDeclarationStructElement:
|
|
|
|
{
|
|
|
|
for (const field of member.fields) {
|
2023-06-26 18:51:48 +02:00
|
|
|
this.unifyKind(this.inferKindFromTypeExpression(field.typeExpr, innerEnv), kindOfTypes, field.typeExpr);
|
2022-09-15 22:39:20 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
throw new Error(`Unexpected ${member}`);
|
2022-09-14 16:46:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2023-03-11 14:24:02 +01:00
|
|
|
|
2022-09-14 22:34:53 +02:00
|
|
|
case SyntaxKind.LetDeclaration:
|
|
|
|
{
|
|
|
|
if (node.typeAssert !== null) {
|
2023-06-26 18:51:48 +02:00
|
|
|
this.unifyKind(this.inferKindFromTypeExpression(node.typeAssert.typeExpression, env), kindOfTypes, node.typeAssert.typeExpression);
|
2022-09-14 22:34:53 +02:00
|
|
|
}
|
|
|
|
if (node.body !== null && node.body.kind === SyntaxKind.BlockBody) {
|
2022-09-15 13:56:58 +02:00
|
|
|
const innerEnv = new KindEnv(env);
|
2022-09-14 22:34:53 +02:00
|
|
|
for (const element of node.body.elements) {
|
2022-09-15 13:56:58 +02:00
|
|
|
this.inferKind(element, innerEnv);
|
2022-09-14 22:34:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2023-03-11 14:24:02 +01:00
|
|
|
|
2022-09-14 16:46:30 +02:00
|
|
|
}
|
2023-03-11 14:24:02 +01:00
|
|
|
|
2022-09-14 16:46:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private unifyKind(a: Kind, b: Kind, node: Syntax): boolean {
|
|
|
|
|
|
|
|
const find = (kind: Kind): Kind => {
|
|
|
|
let curr = kind;
|
|
|
|
while (curr.type === KindType.Var && this.kindSolution.has(curr)) {
|
|
|
|
curr = this.kindSolution.get(curr)!;
|
|
|
|
}
|
|
|
|
// if (kind.type === KindType.Var && ) {
|
|
|
|
// this.kindSolution.set(kind.id, curr);
|
|
|
|
// }
|
|
|
|
return curr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const solve = (kind: Kind) => kind.substitute(this.kindSolution);
|
|
|
|
|
|
|
|
a = find(a);
|
|
|
|
b = find(b);
|
|
|
|
|
|
|
|
if (a.type === KindType.Var) {
|
|
|
|
this.kindSolution.set(a, b);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (b.type === KindType.Var) {
|
|
|
|
return this.unifyKind(b, a, node);
|
|
|
|
}
|
|
|
|
|
2023-08-02 10:37:13 +02:00
|
|
|
if (a.type === KindType.Type && b.type === KindType.Type) {
|
2022-09-14 16:46:30 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a.type === KindType.Arrow && b.type === KindType.Arrow) {
|
|
|
|
return this.unifyKind(a.left, b.left, node)
|
2022-09-15 20:46:07 +02:00
|
|
|
&& this.unifyKind(a.right, b.right, node);
|
2022-09-14 16:46:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
this.diagnostics.add(new KindMismatchDiagnostic(solve(a), solve(b), node));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
private infer(node: Syntax): void {
|
2022-08-31 13:29:56 +02:00
|
|
|
|
|
|
|
switch (node.kind) {
|
|
|
|
|
|
|
|
case SyntaxKind.SourceFile:
|
2022-09-17 13:20:49 +02:00
|
|
|
case SyntaxKind.ModuleDeclaration:
|
2022-08-31 13:29:56 +02:00
|
|
|
{
|
|
|
|
for (const element of node.elements) {
|
|
|
|
this.infer(element);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-02-03 17:51:27 +01:00
|
|
|
case SyntaxKind.ClassDeclaration:
|
|
|
|
{
|
|
|
|
for (const element of node.elements) {
|
|
|
|
this.infer(element);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SyntaxKind.InstanceDeclaration:
|
|
|
|
{
|
|
|
|
for (const element of node.elements) {
|
|
|
|
this.infer(element);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-08-31 13:29:56 +02:00
|
|
|
case SyntaxKind.ExpressionStatement:
|
|
|
|
{
|
|
|
|
this.inferExpression(node.expression);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-09-01 20:06:43 +02:00
|
|
|
case SyntaxKind.IfStatement:
|
|
|
|
{
|
|
|
|
for (const cs of node.cases) {
|
|
|
|
if (cs.test !== null) {
|
|
|
|
this.addConstraint(
|
|
|
|
new CEqual(
|
|
|
|
this.inferExpression(cs.test),
|
|
|
|
this.getBoolType(),
|
|
|
|
cs.test
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
for (const element of cs.elements) {
|
|
|
|
this.infer(element);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-08-31 13:29:56 +02:00
|
|
|
case SyntaxKind.ReturnStatement:
|
|
|
|
{
|
|
|
|
let type;
|
|
|
|
if (node.expression === null) {
|
2023-08-02 10:37:13 +02:00
|
|
|
type = this.unitType;
|
2022-08-31 13:29:56 +02:00
|
|
|
} else {
|
|
|
|
type = this.inferExpression(node.expression);
|
|
|
|
}
|
|
|
|
this.addConstraint(
|
|
|
|
new CEqual(
|
|
|
|
this.getReturnType(),
|
|
|
|
type,
|
|
|
|
node
|
|
|
|
)
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SyntaxKind.LetDeclaration:
|
2022-09-07 12:45:38 +02:00
|
|
|
{
|
2023-08-12 13:46:19 +02:00
|
|
|
const info = this.getInfo(node);
|
|
|
|
|
2022-09-09 22:37:14 +02:00
|
|
|
if (isFunctionDeclarationLike(node)) {
|
2023-06-22 15:30:14 +02:00
|
|
|
|
|
|
|
node.activeCycle = true;
|
|
|
|
node.visited = true;
|
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
this.pushInfo(info);
|
|
|
|
|
|
|
|
const returnType = info.returnType!;
|
2023-06-22 15:30:14 +02:00
|
|
|
|
|
|
|
if (node.body !== null) {
|
|
|
|
switch (node.body.kind) {
|
|
|
|
case SyntaxKind.ExprBody:
|
|
|
|
{
|
|
|
|
this.addConstraint(
|
|
|
|
new CEqual(
|
|
|
|
this.inferExpression(node.body.expression),
|
|
|
|
returnType,
|
|
|
|
node.body.expression
|
|
|
|
)
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SyntaxKind.BlockBody:
|
|
|
|
{
|
|
|
|
for (const element of node.body.elements) {
|
|
|
|
this.infer(element);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2022-09-07 12:45:38 +02:00
|
|
|
}
|
2023-06-22 15:30:14 +02:00
|
|
|
}
|
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
this.popInfo(info);
|
|
|
|
|
2023-06-22 15:30:14 +02:00
|
|
|
node.activeCycle = false;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
// const constraints = new ConstraintSet;
|
|
|
|
// this.polyContextStack.push(new PolyContext(parentPoly.typeVars, constraints));
|
|
|
|
|
2023-06-22 15:30:14 +02:00
|
|
|
let type;
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2023-06-22 15:30:14 +02:00
|
|
|
if (node.typeAssert !== null) {
|
|
|
|
type = this.inferTypeExpression(node.typeAssert.typeExpression);
|
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2023-06-22 15:30:14 +02:00
|
|
|
if (node.body !== null) {
|
|
|
|
let bodyType;
|
|
|
|
switch (node.body.kind) {
|
|
|
|
case SyntaxKind.ExprBody:
|
|
|
|
{
|
|
|
|
bodyType = this.inferExpression(node.body.expression);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SyntaxKind.BlockBody:
|
|
|
|
{
|
|
|
|
// TODO
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (type === undefined) {
|
|
|
|
type = bodyType;
|
|
|
|
} else {
|
2023-08-12 13:46:19 +02:00
|
|
|
this.addConstraint(
|
2023-06-22 15:30:14 +02:00
|
|
|
new CEqual(
|
|
|
|
type,
|
|
|
|
bodyType,
|
|
|
|
node.body
|
|
|
|
)
|
|
|
|
);
|
2022-09-07 12:45:38 +02:00
|
|
|
}
|
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2023-04-12 21:16:48 +02:00
|
|
|
if (type === undefined) {
|
2023-08-12 13:46:19 +02:00
|
|
|
type = this.createTRegularVar();
|
2023-04-12 21:16:48 +02:00
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
|
|
|
// this.polyContextStack.pop();
|
|
|
|
|
|
|
|
this.inferBindings(node.pattern, type, undefined, undefined, true);
|
2022-09-07 12:45:38 +02:00
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-07 12:45:38 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-09-11 11:20:21 +02:00
|
|
|
case SyntaxKind.TypeDeclaration:
|
|
|
|
case SyntaxKind.EnumDeclaration:
|
2022-09-07 12:45:38 +02:00
|
|
|
case SyntaxKind.StructDeclaration:
|
2022-08-31 13:29:56 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2022-09-07 12:45:38 +02:00
|
|
|
throw new Error(`Unexpected ${node.constructor.name}`);
|
2022-08-31 13:29:56 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public inferExpression(node: Expression): Type {
|
|
|
|
|
2023-06-30 19:30:42 +02:00
|
|
|
for (const annotation of node.annotations) {
|
|
|
|
if (annotation.kind === SyntaxKind.TypeAnnotation) {
|
|
|
|
this.inferTypeExpression(annotation.typeExpr, false, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
// We're going to use this eventually so might as well fetch it now
|
|
|
|
const info = this.getInfo(node);
|
|
|
|
|
2023-06-30 19:30:42 +02:00
|
|
|
let type: Type;
|
|
|
|
|
2022-08-31 13:29:56 +02:00
|
|
|
switch (node.kind) {
|
|
|
|
|
2022-09-01 20:06:43 +02:00
|
|
|
case SyntaxKind.NestedExpression:
|
2023-06-30 19:30:42 +02:00
|
|
|
type = this.inferExpression(node.expression);
|
|
|
|
break;
|
2022-09-01 20:06:43 +02:00
|
|
|
|
2022-09-16 11:31:34 +02:00
|
|
|
case SyntaxKind.MatchExpression:
|
|
|
|
{
|
|
|
|
let exprType;
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-16 11:31:34 +02:00
|
|
|
if (node.expression !== null) {
|
|
|
|
exprType = this.inferExpression(node.expression);
|
|
|
|
} else {
|
2023-08-12 13:46:19 +02:00
|
|
|
exprType = this.createTRegularVar();
|
2022-09-16 11:31:34 +02:00
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
|
|
|
type = this.createTRegularVar();
|
|
|
|
|
2022-09-16 11:31:34 +02:00
|
|
|
for (const arm of node.arms) {
|
2023-08-12 13:46:19 +02:00
|
|
|
|
|
|
|
const newEnv = new TypeEnv();
|
|
|
|
this.typeEnvStack.push(newEnv);
|
|
|
|
|
|
|
|
const armPatternType = this.createTRegularVar();
|
|
|
|
|
2023-04-12 21:16:48 +02:00
|
|
|
this.inferBindings(arm.pattern, armPatternType);
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-16 11:31:34 +02:00
|
|
|
this.addConstraint(
|
|
|
|
new CEqual(
|
2023-04-12 21:16:48 +02:00
|
|
|
armPatternType,
|
2022-09-16 11:31:34 +02:00
|
|
|
exprType,
|
|
|
|
arm.pattern,
|
|
|
|
)
|
|
|
|
);
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-16 11:31:34 +02:00
|
|
|
this.addConstraint(
|
|
|
|
new CEqual(
|
2023-06-30 19:30:42 +02:00
|
|
|
type,
|
2022-09-16 11:31:34 +02:00
|
|
|
this.inferExpression(arm.expression),
|
|
|
|
arm.expression
|
|
|
|
)
|
|
|
|
);
|
2023-08-12 13:46:19 +02:00
|
|
|
|
|
|
|
this.typeEnvStack.pop();
|
2022-09-16 11:31:34 +02:00
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-16 11:57:45 +02:00
|
|
|
if (node.expression === null) {
|
2023-06-30 19:30:42 +02:00
|
|
|
type = new TArrow(exprType, type);
|
2022-09-16 11:31:34 +02:00
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2023-06-30 19:30:42 +02:00
|
|
|
break;
|
2022-09-16 11:31:34 +02:00
|
|
|
}
|
|
|
|
|
2022-09-16 12:43:06 +02:00
|
|
|
case SyntaxKind.TupleExpression:
|
2023-08-02 10:37:13 +02:00
|
|
|
type = buildTupleTypeWithLoc(node.elements.map(el => [el, this.inferExpression(el)]), node);
|
2023-06-30 19:30:42 +02:00
|
|
|
break;
|
2022-09-16 12:43:06 +02:00
|
|
|
|
2022-08-31 13:29:56 +02:00
|
|
|
case SyntaxKind.ReferenceExpression:
|
|
|
|
{
|
2022-09-05 17:25:55 +02:00
|
|
|
const scope = node.getScope();
|
2022-09-15 20:33:34 +02:00
|
|
|
const target = scope.lookup(node.name.text);
|
2023-06-22 15:30:14 +02:00
|
|
|
if (target !== null && target.kind === SyntaxKind.LetDeclaration) {
|
|
|
|
if (target.activeCycle) {
|
2023-08-12 13:46:19 +02:00
|
|
|
return this.getInfo(target).inferredType!;
|
2023-06-22 15:30:14 +02:00
|
|
|
}
|
|
|
|
if (!target.visited) {
|
|
|
|
this.infer(target);
|
|
|
|
}
|
2022-09-01 20:06:43 +02:00
|
|
|
}
|
2022-09-18 14:33:22 +02:00
|
|
|
const scheme = this.lookup(node, Symkind.Var);
|
2022-08-31 13:29:56 +02:00
|
|
|
if (scheme === null) {
|
2023-06-30 19:30:42 +02:00
|
|
|
//this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.text, node.name));
|
2023-08-12 13:46:19 +02:00
|
|
|
type = this.createTRegularVar();
|
2023-06-30 19:30:42 +02:00
|
|
|
break;
|
2022-08-31 13:29:56 +02:00
|
|
|
}
|
2023-06-30 19:30:42 +02:00
|
|
|
type = this.instantiate(scheme, node);
|
2022-09-09 20:02:35 +02:00
|
|
|
type.node = node;
|
2023-06-30 19:30:42 +02:00
|
|
|
break;
|
2022-08-31 13:29:56 +02:00
|
|
|
}
|
|
|
|
|
2022-09-09 00:00:28 +02:00
|
|
|
case SyntaxKind.MemberExpression:
|
|
|
|
{
|
2023-06-30 19:30:42 +02:00
|
|
|
type = this.inferExpression(node.expression);
|
2022-09-09 00:00:28 +02:00
|
|
|
for (const [_dot, name] of node.path) {
|
2023-08-02 10:37:13 +02:00
|
|
|
let label;
|
2023-06-30 19:30:42 +02:00
|
|
|
switch (name.kind) {
|
|
|
|
case SyntaxKind.Identifier:
|
2023-08-02 10:37:13 +02:00
|
|
|
label = name.text;
|
2023-06-30 19:30:42 +02:00
|
|
|
break;
|
|
|
|
case SyntaxKind.Integer:
|
2023-08-02 10:37:13 +02:00
|
|
|
label = Number(name.value);
|
2023-06-30 19:30:42 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assertNever(name);
|
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
const newFieldType = this.createTRegularVar(name);
|
|
|
|
const newRestType = this.createTRegularVar();
|
2023-08-02 10:37:13 +02:00
|
|
|
this.addConstraint(
|
|
|
|
new CEqual(
|
|
|
|
type,
|
|
|
|
new TField(label, new TPresent(newFieldType), newRestType, name),
|
|
|
|
node,
|
|
|
|
)
|
|
|
|
);
|
|
|
|
type = newFieldType;
|
2022-09-09 00:00:28 +02:00
|
|
|
}
|
2023-06-30 19:30:42 +02:00
|
|
|
break;
|
2022-09-09 00:00:28 +02:00
|
|
|
}
|
|
|
|
|
2022-08-31 13:29:56 +02:00
|
|
|
case SyntaxKind.CallExpression:
|
|
|
|
{
|
|
|
|
const opType = this.inferExpression(node.func);
|
2023-08-12 13:46:19 +02:00
|
|
|
type = this.createTRegularVar(node);
|
2022-08-31 13:29:56 +02:00
|
|
|
const paramTypes = [];
|
|
|
|
for (const arg of node.args) {
|
|
|
|
paramTypes.push(this.inferExpression(arg));
|
|
|
|
}
|
|
|
|
this.addConstraint(
|
|
|
|
new CEqual(
|
|
|
|
opType,
|
2023-06-30 19:30:42 +02:00
|
|
|
TArrow.build(paramTypes, type),
|
2022-08-31 13:29:56 +02:00
|
|
|
node
|
|
|
|
)
|
|
|
|
);
|
2023-06-30 19:30:42 +02:00
|
|
|
break;
|
2022-08-31 13:29:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
case SyntaxKind.ConstantExpression:
|
|
|
|
{
|
|
|
|
switch (node.token.kind) {
|
|
|
|
case SyntaxKind.StringLiteral:
|
2023-06-30 19:30:42 +02:00
|
|
|
type = this.getStringType();
|
2022-08-31 13:29:56 +02:00
|
|
|
break;
|
|
|
|
case SyntaxKind.Integer:
|
2023-06-30 19:30:42 +02:00
|
|
|
type = this.getIntType();
|
2022-08-31 13:29:56 +02:00
|
|
|
break;
|
|
|
|
}
|
2023-06-30 19:30:42 +02:00
|
|
|
type = type.shallowClone();
|
|
|
|
type.node = node;
|
|
|
|
break;
|
2022-08-31 13:29:56 +02:00
|
|
|
}
|
|
|
|
|
2022-09-07 12:45:38 +02:00
|
|
|
case SyntaxKind.StructExpression:
|
|
|
|
{
|
2023-07-04 20:38:40 +02:00
|
|
|
const fields = new Map<string, Type>();
|
|
|
|
const restType = new TNil(node);
|
2022-09-07 12:45:38 +02:00
|
|
|
for (const member of node.members) {
|
|
|
|
switch (member.kind) {
|
|
|
|
case SyntaxKind.StructExpressionField:
|
|
|
|
{
|
2023-07-04 20:38:40 +02:00
|
|
|
fields.set(member.name.text, this.inferExpression(member.expression));
|
2022-09-07 12:45:38 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SyntaxKind.PunnedStructExpressionField:
|
|
|
|
{
|
2022-09-18 14:33:22 +02:00
|
|
|
const scheme = this.lookup(member.name, Symkind.Var);
|
2022-09-07 12:45:38 +02:00
|
|
|
let fieldType;
|
|
|
|
if (scheme === null) {
|
2022-09-18 14:33:22 +02:00
|
|
|
// this.diagnostics.add(new BindingNotFoudDiagnostic(member.name.text, member.name));
|
2023-08-12 13:46:19 +02:00
|
|
|
fieldType = this.createTRegularVar();
|
2022-09-07 12:45:38 +02:00
|
|
|
} else {
|
|
|
|
fieldType = this.instantiate(scheme, member);
|
|
|
|
}
|
2023-07-04 20:38:40 +02:00
|
|
|
fields.set(member.name.text, fieldType);
|
2022-09-07 12:45:38 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
throw new Error(`Unexpected ${member}`);
|
|
|
|
}
|
|
|
|
}
|
2023-07-04 20:38:40 +02:00
|
|
|
type = TField.build(fields, restType);
|
2023-06-30 19:30:42 +02:00
|
|
|
break;
|
2022-09-07 12:45:38 +02:00
|
|
|
}
|
|
|
|
|
2022-08-31 13:29:56 +02:00
|
|
|
case SyntaxKind.InfixExpression:
|
|
|
|
{
|
2022-09-18 14:33:22 +02:00
|
|
|
const scheme = this.lookup(node.operator, Symkind.Var);
|
2022-08-31 13:29:56 +02:00
|
|
|
if (scheme === null) {
|
2022-09-18 14:33:22 +02:00
|
|
|
// this.diagnostics.add(new BindingNotFoudDiagnostic(node.operator.text, node.operator));
|
2023-08-12 13:46:19 +02:00
|
|
|
return this.createTRegularVar();
|
2022-08-31 13:29:56 +02:00
|
|
|
}
|
2022-09-06 11:56:17 +02:00
|
|
|
const opType = this.instantiate(scheme, node.operator);
|
2022-08-31 13:29:56 +02:00
|
|
|
const leftType = this.inferExpression(node.left);
|
|
|
|
const rightType = this.inferExpression(node.right);
|
2023-08-12 13:46:19 +02:00
|
|
|
type = this.createTRegularVar();
|
2022-08-31 13:29:56 +02:00
|
|
|
this.addConstraint(
|
|
|
|
new CEqual(
|
2023-06-30 19:30:42 +02:00
|
|
|
new TArrow(leftType, new TArrow(rightType, type)),
|
2022-08-31 13:29:56 +02:00
|
|
|
opType,
|
|
|
|
node,
|
|
|
|
),
|
|
|
|
);
|
2023-06-30 19:30:42 +02:00
|
|
|
break;
|
2022-08-31 13:29:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
2022-09-01 20:06:43 +02:00
|
|
|
throw new Error(`Unexpected ${node.constructor.name}`);
|
2022-08-31 13:29:56 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
info.inferredType = type;
|
2023-06-30 19:30:42 +02:00
|
|
|
|
|
|
|
return type;
|
|
|
|
|
2022-08-31 13:29:56 +02:00
|
|
|
}
|
|
|
|
|
2023-06-30 19:30:42 +02:00
|
|
|
public inferTypeExpression(node: TypeExpression, introduceTypeVars = false, checkKind = true): Type {
|
2022-08-31 13:29:56 +02:00
|
|
|
|
2022-09-15 22:39:20 +02:00
|
|
|
let type;
|
2022-08-31 13:29:56 +02:00
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
const info = this.getInfo(node);
|
2022-08-31 13:29:56 +02:00
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
if (checkKind && info.inferredKind === undefined) {
|
|
|
|
|
|
|
|
type = this.createTRegularVar();
|
2022-09-14 22:34:53 +02:00
|
|
|
|
2022-09-15 22:39:20 +02:00
|
|
|
} else {
|
|
|
|
|
|
|
|
switch (node.kind) {
|
|
|
|
|
|
|
|
case SyntaxKind.ReferenceTypeExpression:
|
|
|
|
{
|
2022-09-18 14:33:22 +02:00
|
|
|
const scheme = this.lookup(node, Symkind.Type);
|
2022-09-15 22:39:20 +02:00
|
|
|
if (scheme === null) {
|
2022-09-18 14:33:22 +02:00
|
|
|
// this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.text, node.name));
|
2023-08-12 13:46:19 +02:00
|
|
|
type = this.createTRegularVar();
|
2023-06-30 19:30:42 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
type = this.instantiate(scheme, node.name);
|
|
|
|
// It is not guaranteed that `type` is copied during instantiation,
|
|
|
|
// so the following check ensures that we really are holding a copy
|
|
|
|
// that we can mutate.
|
|
|
|
if (type === scheme.type) {
|
|
|
|
type = type.shallowClone();
|
2022-09-15 23:05:34 +02:00
|
|
|
}
|
2023-06-30 19:30:42 +02:00
|
|
|
// Mutate the type
|
|
|
|
type.node = node;
|
2022-09-15 22:39:20 +02:00
|
|
|
break;
|
2022-09-10 16:52:14 +02:00
|
|
|
}
|
|
|
|
|
2022-09-16 12:43:06 +02:00
|
|
|
case SyntaxKind.TupleTypeExpression:
|
|
|
|
{
|
2023-08-02 10:37:13 +02:00
|
|
|
type = buildTupleTypeWithLoc(node.elements.map(el => [el, this.inferTypeExpression(el, introduceTypeVars)]), node);
|
2022-09-16 12:43:06 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-09-15 22:39:20 +02:00
|
|
|
case SyntaxKind.NestedTypeExpression:
|
2022-09-19 14:06:12 +02:00
|
|
|
type = this.inferTypeExpression(node.typeExpr, introduceTypeVars);
|
|
|
|
break;
|
2022-09-11 11:20:21 +02:00
|
|
|
|
2022-09-15 22:39:20 +02:00
|
|
|
case SyntaxKind.VarTypeExpression:
|
|
|
|
{
|
2023-06-26 18:51:48 +02:00
|
|
|
const scheme = this.lookup(node.name, Symkind.Type, !introduceTypeVars);
|
2022-09-15 22:39:20 +02:00
|
|
|
if (scheme === null) {
|
|
|
|
if (!introduceTypeVars) {
|
2023-03-16 21:50:15 +01:00
|
|
|
this.diagnostics.add(new BindingNotFoundDiagnostic([], node.name.text, node.name));
|
2022-09-15 22:39:20 +02:00
|
|
|
}
|
2023-06-26 18:51:48 +02:00
|
|
|
type = this.createRigidVar(node.name.text, node);
|
2023-06-21 16:56:04 +02:00
|
|
|
// TODO if !introduceTypeVars: re-emit a 'var not found' whenever the same var is encountered
|
2023-04-12 21:16:48 +02:00
|
|
|
this.addBinding(node.name.text, Forall.mono(type), Symkind.Type);
|
2022-09-15 22:39:20 +02:00
|
|
|
} else {
|
|
|
|
assert(isEmpty(scheme.typeVars));
|
2023-04-12 21:16:48 +02:00
|
|
|
assert(scheme.constraint.kind === ConstraintKind.Empty);
|
2022-09-15 22:39:20 +02:00
|
|
|
type = scheme.type;
|
|
|
|
}
|
|
|
|
break;
|
2022-09-09 22:37:14 +02:00
|
|
|
}
|
|
|
|
|
2022-09-15 22:39:20 +02:00
|
|
|
case SyntaxKind.AppTypeExpression:
|
|
|
|
{
|
|
|
|
type = TApp.build(
|
|
|
|
this.inferTypeExpression(node.operator, introduceTypeVars),
|
|
|
|
node.args.map(arg => this.inferTypeExpression(arg, introduceTypeVars)),
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-06-21 16:56:04 +02:00
|
|
|
case SyntaxKind.TypeExpressionWithConstraints:
|
|
|
|
{
|
2023-08-02 10:37:13 +02:00
|
|
|
// TODO
|
|
|
|
// for (const constraint of node.constraints) {
|
|
|
|
// implementationLimitation(constraint.types.length === 1);
|
|
|
|
// this.addConstraint(new CClass(constraint.name.text, this.inferTypeExpression(constraint.types[0]), constraint.name));
|
|
|
|
// }
|
2023-06-21 16:56:04 +02:00
|
|
|
return this.inferTypeExpression(node.typeExpr, introduceTypeVars);
|
|
|
|
}
|
|
|
|
|
|
|
|
case SyntaxKind.ForallTypeExpression:
|
|
|
|
{
|
2023-08-12 13:46:19 +02:00
|
|
|
const env = this.getTypeEnv();
|
|
|
|
const poly = this.getPolyContext();
|
2023-06-21 16:56:04 +02:00
|
|
|
// FIXME this is an ugly hack that doesn't even work. Either disallow Forall in this method or create a new TForall
|
|
|
|
for (const varExpr of node.varTypeExps) {
|
2023-08-12 13:46:19 +02:00
|
|
|
const tv = this.createTRegularVar();
|
|
|
|
env.add(varExpr.name.text, Forall.mono(tv), Symkind.Type);
|
|
|
|
poly.typeVars.add(tv);
|
2023-06-21 16:56:04 +02:00
|
|
|
}
|
|
|
|
return this.inferTypeExpression(node.typeExpr, introduceTypeVars);
|
|
|
|
}
|
|
|
|
|
2022-09-15 22:39:20 +02:00
|
|
|
case SyntaxKind.ArrowTypeExpression:
|
|
|
|
{
|
|
|
|
const paramTypes = [];
|
|
|
|
for (const paramTypeExpr of node.paramTypeExprs) {
|
|
|
|
paramTypes.push(this.inferTypeExpression(paramTypeExpr, introduceTypeVars));
|
|
|
|
}
|
|
|
|
const returnType = this.inferTypeExpression(node.returnTypeExpr, introduceTypeVars);
|
2022-09-16 12:00:00 +02:00
|
|
|
type = TArrow.build(paramTypes, returnType, node);
|
2022-09-15 22:39:20 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw new Error(`Unrecognised ${node}`);
|
|
|
|
|
|
|
|
}
|
2022-08-31 13:29:56 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
info.inferredType = type;
|
2022-09-15 22:39:20 +02:00
|
|
|
|
|
|
|
return type;
|
|
|
|
|
2022-08-31 13:29:56 +02:00
|
|
|
}
|
|
|
|
|
2023-04-12 21:16:48 +02:00
|
|
|
public inferBindings(pattern: Pattern, type: Type, typeVars = new TVSet, constraints: Constraint[] = [], generalize = false): void {
|
2022-08-31 13:29:56 +02:00
|
|
|
|
|
|
|
switch (pattern.kind) {
|
|
|
|
|
2023-03-11 14:24:02 +01:00
|
|
|
case SyntaxKind.NamedPattern:
|
2022-08-31 13:29:56 +02:00
|
|
|
{
|
2023-04-12 21:16:48 +02:00
|
|
|
let scheme;
|
|
|
|
const env = this.getTypeEnv();
|
|
|
|
if (generalize) {
|
|
|
|
scheme = this.generalize(type, constraints, env);
|
|
|
|
} else {
|
|
|
|
scheme = new Forall(typeVars, new CMany(constraints), type);
|
|
|
|
}
|
|
|
|
this.addBinding(pattern.name.text, scheme, Symkind.Var);
|
|
|
|
break;
|
2022-08-31 13:29:56 +02:00
|
|
|
}
|
|
|
|
|
2023-03-11 14:24:02 +01:00
|
|
|
case SyntaxKind.NestedPattern:
|
2023-08-12 13:46:19 +02:00
|
|
|
this.inferBindings(pattern.pattern, type, typeVars, constraints, generalize);
|
2023-04-12 21:16:48 +02:00
|
|
|
break;
|
2023-03-11 14:24:02 +01:00
|
|
|
|
2023-07-04 20:38:40 +02:00
|
|
|
case SyntaxKind.NamedTuplePattern:
|
|
|
|
{
|
|
|
|
const scheme = this.lookup(pattern.name, Symkind.Var);
|
|
|
|
if (scheme === null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const ctorType = this.instantiate(scheme, pattern);
|
|
|
|
let elementTypes = [];
|
|
|
|
for (const element of pattern.elements) {
|
2023-08-12 13:46:19 +02:00
|
|
|
const tv = this.createTRegularVar();
|
2023-07-04 20:38:40 +02:00
|
|
|
this.inferBindings(element, tv, typeVars, constraints, generalize);
|
|
|
|
elementTypes.push(tv);
|
|
|
|
}
|
|
|
|
this.addConstraint(new CEqual(TArrow.build(elementTypes, type), ctorType, pattern));
|
|
|
|
break;
|
|
|
|
}
|
2023-02-03 17:51:27 +01:00
|
|
|
|
2022-09-16 11:31:34 +02:00
|
|
|
case SyntaxKind.LiteralPattern:
|
|
|
|
{
|
2023-04-12 21:16:48 +02:00
|
|
|
let literalType;
|
2022-09-16 11:31:34 +02:00
|
|
|
switch (pattern.token.kind) {
|
|
|
|
case SyntaxKind.Integer:
|
2023-04-12 21:16:48 +02:00
|
|
|
literalType = this.getIntType();
|
2022-09-16 11:31:34 +02:00
|
|
|
break;
|
|
|
|
case SyntaxKind.StringLiteral:
|
2023-04-12 21:16:48 +02:00
|
|
|
literalType = this.getStringType();
|
2022-09-16 11:31:34 +02:00
|
|
|
break;
|
|
|
|
}
|
2023-04-12 21:16:48 +02:00
|
|
|
literalType = literalType.shallowClone();
|
|
|
|
literalType.node = pattern;
|
2022-09-16 11:31:34 +02:00
|
|
|
this.addConstraint(
|
|
|
|
new CEqual(
|
2023-04-12 21:16:48 +02:00
|
|
|
literalType,
|
2022-09-16 11:31:34 +02:00
|
|
|
type,
|
2023-04-12 21:16:48 +02:00
|
|
|
pattern,
|
2022-09-16 11:31:34 +02:00
|
|
|
)
|
|
|
|
);
|
2023-04-12 21:16:48 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SyntaxKind.DisjunctivePattern:
|
|
|
|
{
|
2023-08-12 13:46:19 +02:00
|
|
|
this.inferBindings(pattern.left, type, typeVars, constraints, generalize),
|
|
|
|
this.inferBindings(pattern.right, type, typeVars, constraints, generalize);
|
2023-04-12 21:16:48 +02:00
|
|
|
break;
|
2022-09-16 11:31:34 +02:00
|
|
|
}
|
|
|
|
|
2022-09-07 12:45:38 +02:00
|
|
|
case SyntaxKind.StructPattern:
|
|
|
|
{
|
2023-03-11 14:24:02 +01:00
|
|
|
const variadicMember = getVariadicMember(pattern);
|
2023-07-04 20:38:40 +02:00
|
|
|
const fields = new Map<string, Type>();
|
|
|
|
let restType: Type;
|
2023-03-11 14:24:02 +01:00
|
|
|
if (variadicMember === null) {
|
2023-07-04 20:38:40 +02:00
|
|
|
restType = new TNil(pattern);
|
2022-09-07 12:45:38 +02:00
|
|
|
} else {
|
2023-08-12 13:46:19 +02:00
|
|
|
restType = this.createTRegularVar();
|
2023-04-12 21:16:48 +02:00
|
|
|
if (variadicMember.pattern !== null) {
|
2023-08-12 13:46:19 +02:00
|
|
|
this.inferBindings(variadicMember.pattern, restType, typeVars, constraints, generalize);
|
2023-04-12 21:16:48 +02:00
|
|
|
}
|
2022-09-07 12:45:38 +02:00
|
|
|
}
|
|
|
|
for (const member of pattern.members) {
|
|
|
|
switch (member.kind) {
|
|
|
|
case SyntaxKind.StructPatternField:
|
|
|
|
{
|
2023-08-12 13:46:19 +02:00
|
|
|
const fieldType = this.createTRegularVar();
|
|
|
|
this.inferBindings(member.pattern, fieldType, typeVars, constraints, generalize);
|
2023-07-04 20:38:40 +02:00
|
|
|
fields.set(member.name.text, fieldType);
|
2022-09-07 12:45:38 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SyntaxKind.PunnedStructPatternField:
|
|
|
|
{
|
2023-08-12 13:46:19 +02:00
|
|
|
const fieldType = this.createTRegularVar();
|
2023-04-12 21:16:48 +02:00
|
|
|
this.addBinding(member.name.text, Forall.mono(fieldType), Symkind.Var);
|
2023-07-04 20:38:40 +02:00
|
|
|
fields.set(member.name.text, fieldType);
|
2022-09-07 12:45:38 +02:00
|
|
|
break;
|
|
|
|
}
|
2023-03-11 14:24:02 +01:00
|
|
|
case SyntaxKind.VariadicStructPatternElement:
|
|
|
|
break;
|
2022-09-07 12:45:38 +02:00
|
|
|
default:
|
2023-03-11 14:24:02 +01:00
|
|
|
assertNever(member);
|
2022-09-07 12:45:38 +02:00
|
|
|
}
|
|
|
|
}
|
2023-04-12 21:16:48 +02:00
|
|
|
this.addConstraint(
|
|
|
|
new CEqual(
|
|
|
|
type,
|
2023-07-04 20:38:40 +02:00
|
|
|
TField.build(fields, restType),
|
2023-04-12 21:16:48 +02:00
|
|
|
pattern,
|
|
|
|
)
|
|
|
|
);
|
|
|
|
break;
|
2022-09-07 12:45:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw new Error(`Unexpected ${pattern.constructor.name}`);
|
|
|
|
|
2022-08-31 13:29:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
private initialize(node: Syntax): void {
|
2022-09-05 19:33:08 +02:00
|
|
|
|
|
|
|
switch (node.kind) {
|
|
|
|
|
|
|
|
case SyntaxKind.SourceFile:
|
2023-08-12 13:46:19 +02:00
|
|
|
{
|
|
|
|
const info = this.getInfo(node);
|
|
|
|
const poly = info.poly = new PolyContext();
|
|
|
|
const returnType = info.returnType = null;
|
|
|
|
const env = info.typeEnv = new TypeEnv();
|
|
|
|
|
|
|
|
this.polyContextStack.push(poly);
|
|
|
|
this.typeEnvStack.push(env);
|
|
|
|
this.returnTypeStack.push(returnType);
|
|
|
|
|
|
|
|
for (const element of node.elements) {
|
|
|
|
this.initialize(element);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.polyContextStack.pop();
|
|
|
|
this.typeEnvStack.pop();
|
|
|
|
this.returnTypeStack.pop();
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-09-17 13:20:49 +02:00
|
|
|
case SyntaxKind.ModuleDeclaration:
|
2022-09-05 19:33:08 +02:00
|
|
|
{
|
2023-08-12 13:46:19 +02:00
|
|
|
const info = this.getInfo(node);
|
|
|
|
info.typeEnv = new TypeEnv();
|
2022-09-05 19:33:08 +02:00
|
|
|
for (const element of node.elements) {
|
2023-08-12 13:46:19 +02:00
|
|
|
this.initialize(element);
|
2022-09-05 19:33:08 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-02-03 17:51:27 +01:00
|
|
|
case SyntaxKind.ClassDeclaration:
|
|
|
|
{
|
2023-08-12 13:46:19 +02:00
|
|
|
const info = this.getInfo(node);
|
|
|
|
const env = info.typeEnv = new TypeEnv();
|
2023-03-16 21:50:15 +01:00
|
|
|
for (const tv of node.types) {
|
|
|
|
assert(tv.kind === SyntaxKind.VarTypeExpression);
|
2023-08-12 13:46:19 +02:00
|
|
|
env.add(tv.name.text, Forall.mono(this.createTRegularVar(tv)), Symkind.Type);
|
2023-03-16 21:50:15 +01:00
|
|
|
}
|
|
|
|
for (const element of node.elements) {
|
2023-08-12 13:46:19 +02:00
|
|
|
this.initialize(element);
|
2023-03-16 21:50:15 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SyntaxKind.InstanceDeclaration:
|
|
|
|
{
|
2023-04-12 14:26:39 +02:00
|
|
|
if (!this.classDecls.has(node.name.text)) {
|
2023-06-21 16:56:04 +02:00
|
|
|
this.diagnostics.add(new TypeclassNotFoundDiagnostic(node.name.text, node.name));
|
2023-04-12 14:26:39 +02:00
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
const info = this.getInfo(node);
|
|
|
|
info.typeEnv = new TypeEnv();
|
2023-02-03 17:51:27 +01:00
|
|
|
for (const element of node.elements) {
|
2023-08-12 13:46:19 +02:00
|
|
|
this.initialize(element);
|
2023-02-03 17:51:27 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-09-05 19:33:08 +02:00
|
|
|
case SyntaxKind.LetDeclaration:
|
|
|
|
{
|
2023-08-12 13:46:19 +02:00
|
|
|
const info = this.getInfo(node);
|
|
|
|
info.typeEnv = new TypeEnv();
|
|
|
|
// The rest of the info properties are set in Checker.check()
|
2022-09-05 19:33:08 +02:00
|
|
|
if (node.body !== null && node.body.kind === SyntaxKind.BlockBody) {
|
|
|
|
for (const element of node.body.elements) {
|
2023-08-12 13:46:19 +02:00
|
|
|
this.initialize(element);
|
2022-09-05 19:33:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-09-06 15:17:27 +02:00
|
|
|
case SyntaxKind.IfStatement:
|
2022-09-05 19:33:08 +02:00
|
|
|
case SyntaxKind.ExpressionStatement:
|
|
|
|
case SyntaxKind.ReturnStatement:
|
2022-09-07 12:45:38 +02:00
|
|
|
break;
|
|
|
|
|
2022-09-10 16:52:14 +02:00
|
|
|
case SyntaxKind.EnumDeclaration:
|
|
|
|
{
|
2023-08-12 13:46:19 +02:00
|
|
|
const info = this.getInfo(node);
|
|
|
|
const env = info.typeEnv = new TypeEnv();;
|
|
|
|
const poly = info.poly = new PolyContext();
|
|
|
|
const parentEnv = this.getTypeEnv();
|
2023-07-04 20:38:40 +02:00
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
this.typeEnvStack.push(env);
|
|
|
|
this.polyContextStack.push(poly);
|
2023-07-04 20:38:40 +02:00
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
const typeArgs = [];
|
2023-03-16 21:50:15 +01:00
|
|
|
for (const name of node.varExps) {
|
2023-08-12 13:46:19 +02:00
|
|
|
const typeArg = this.createTRegularVar();
|
|
|
|
env.add(name.text, Forall.mono(typeArg), Symkind.Type);
|
|
|
|
typeArgs.push(typeArg);
|
2022-09-14 22:34:53 +02:00
|
|
|
}
|
2023-07-04 20:38:40 +02:00
|
|
|
|
|
|
|
const type = this.createTCon(node.name.text, node);
|
2023-08-12 13:46:19 +02:00
|
|
|
const appliedType = TApp.build(type, typeArgs);
|
|
|
|
parentEnv.add(node.name.text, new Forall(poly.typeVars, new CMany(poly.constraints), type), Symkind.Type);
|
2023-07-04 20:38:40 +02:00
|
|
|
|
2022-09-14 22:34:53 +02:00
|
|
|
let elementTypes: Type[] = [];
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-14 22:34:53 +02:00
|
|
|
if (node.members !== null) {
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-14 22:34:53 +02:00
|
|
|
for (const member of node.members) {
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2023-03-16 21:50:15 +01:00
|
|
|
let ctorType, elementType;
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-14 22:34:53 +02:00
|
|
|
switch (member.kind) {
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-14 22:34:53 +02:00
|
|
|
case SyntaxKind.EnumDeclarationTupleElement:
|
|
|
|
{
|
2023-08-02 10:37:13 +02:00
|
|
|
const args: Array<[Syntax, Type]> = member.elements.map(el => [el, this.inferTypeExpression(el, false)]);
|
|
|
|
elementType = buildTupleTypeWithLoc(args, member);
|
|
|
|
ctorType = TArrow.build(args.map(a => a[1]), appliedType, member);
|
2022-09-15 20:33:34 +02:00
|
|
|
break;
|
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-15 20:33:34 +02:00
|
|
|
case SyntaxKind.EnumDeclarationStructElement:
|
|
|
|
{
|
2023-07-04 20:38:40 +02:00
|
|
|
const restType = new TNil(member);
|
|
|
|
const fields = new Map<string, Type>();
|
2022-09-15 20:33:34 +02:00
|
|
|
for (const field of member.fields) {
|
2023-07-04 20:38:40 +02:00
|
|
|
fields.set(field.name.text, this.inferTypeExpression(field.typeExpr, false));
|
2022-09-15 20:33:34 +02:00
|
|
|
}
|
2023-07-04 20:38:40 +02:00
|
|
|
elementType = TField.build(fields, restType);
|
|
|
|
ctorType = new TArrow(elementType, appliedType, member);
|
2022-09-14 22:34:53 +02:00
|
|
|
break;
|
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-14 22:34:53 +02:00
|
|
|
default:
|
|
|
|
throw new Error(`Unexpected ${member}`);
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-14 22:34:53 +02:00
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
|
|
|
parentEnv.add(member.name.text, new Forall(poly.typeVars, new CMany(poly.constraints), ctorType), Symkind.Var);
|
2023-03-16 21:50:15 +01:00
|
|
|
elementTypes.push(elementType);
|
2022-09-14 22:34:53 +02:00
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-14 22:34:53 +02:00
|
|
|
}
|
2023-07-04 20:38:40 +02:00
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
this.polyContextStack.pop();
|
|
|
|
this.typeEnvStack.pop();
|
2023-07-04 20:38:40 +02:00
|
|
|
|
2022-09-10 16:52:14 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-09-11 11:20:21 +02:00
|
|
|
case SyntaxKind.TypeDeclaration:
|
|
|
|
{
|
2023-08-12 13:46:19 +02:00
|
|
|
const info = this.getInfo(node);
|
|
|
|
const parentEnv = this.getTypeEnv();
|
|
|
|
const env = info.typeEnv = new TypeEnv();;
|
|
|
|
const poly = info.poly = new PolyContext();
|
|
|
|
|
|
|
|
this.polyContextStack.push(poly);
|
|
|
|
this.typeEnvStack.push(env);
|
|
|
|
|
|
|
|
const typeArgs = [];
|
|
|
|
|
2022-09-15 11:49:53 +02:00
|
|
|
for (const varExpr of node.varExps) {
|
2023-08-12 13:46:19 +02:00
|
|
|
const typeVar = this.createTRegularVar();
|
|
|
|
typeArgs.push(typeVar);
|
2023-04-12 21:16:48 +02:00
|
|
|
env.add(varExpr.text, Forall.mono(typeVar), Symkind.Type);
|
2022-09-11 11:20:21 +02:00
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-11 11:20:21 +02:00
|
|
|
const type = this.inferTypeExpression(node.typeExpression);
|
2023-08-12 13:46:19 +02:00
|
|
|
|
|
|
|
this.polyContextStack.pop();
|
|
|
|
this.typeEnvStack.pop();
|
|
|
|
|
|
|
|
const scheme = new Forall(poly.typeVars, new CMany(poly.constraints), TApp.build(type, typeArgs));
|
|
|
|
|
2022-09-15 11:49:53 +02:00
|
|
|
parentEnv.add(node.name.text, scheme, Symkind.Type);
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-11 11:20:21 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-09-05 19:33:08 +02:00
|
|
|
case SyntaxKind.StructDeclaration:
|
2022-09-07 12:45:38 +02:00
|
|
|
{
|
2023-08-12 13:46:19 +02:00
|
|
|
const info = this.getInfo(node);
|
|
|
|
const parentEnv = this.getTypeEnv();
|
|
|
|
const env = info.typeEnv = new TypeEnv();
|
|
|
|
const poly = info.poly = new PolyContext();
|
|
|
|
|
|
|
|
this.polyContextStack.push(poly);
|
|
|
|
this.typeEnvStack.push(env);
|
|
|
|
|
|
|
|
const typeArgs = [];
|
2022-09-15 11:49:53 +02:00
|
|
|
for (const varExpr of node.varExps) {
|
2023-08-12 13:46:19 +02:00
|
|
|
const typeArg = this.createTRegularVar();
|
|
|
|
env.add(varExpr.text, Forall.mono(typeArg), Symkind.Type);
|
|
|
|
typeArgs.push(typeArg);
|
2022-09-10 16:52:14 +02:00
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2023-07-04 20:38:40 +02:00
|
|
|
const fields = new Map<string, Type>();
|
|
|
|
const restType = new TNil(node);
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-15 20:33:34 +02:00
|
|
|
if (node.fields !== null) {
|
2022-09-15 22:39:20 +02:00
|
|
|
for (const field of node.fields) {
|
2023-07-04 20:38:40 +02:00
|
|
|
fields.set(field.name.text, this.inferTypeExpression(field.typeExpr));
|
2022-09-07 12:45:38 +02:00
|
|
|
}
|
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2023-07-04 20:38:40 +02:00
|
|
|
const type = this.createTCon(node.name.text, node.name);
|
|
|
|
const recordType = TField.build(fields, restType);
|
2023-08-12 13:46:19 +02:00
|
|
|
|
|
|
|
this.polyContextStack.pop();
|
|
|
|
this.typeEnvStack.pop();
|
|
|
|
|
|
|
|
parentEnv.add(node.name.text, new Forall(poly.typeVars, new CMany(poly.constraints), type), Symkind.Type);
|
|
|
|
parentEnv.add(node.name.text, new Forall(poly.typeVars, new CMany(poly.constraints), new TArrow(recordType, type)), Symkind.Var);
|
|
|
|
|
2022-09-05 19:33:08 +02:00
|
|
|
break;
|
2022-09-07 12:45:38 +02:00
|
|
|
}
|
2022-09-05 19:33:08 +02:00
|
|
|
|
|
|
|
default:
|
2022-09-15 20:33:34 +02:00
|
|
|
throw new Error(`Unexpected ${node.constructor.name}`);
|
2022-09-05 19:33:08 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-06-22 15:30:14 +02:00
|
|
|
public check(sourceFile: SourceFile): void {
|
2022-09-01 20:06:43 +02:00
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
// Kind inference
|
|
|
|
const kindEnv = new KindEnv(this.globalKindEnv);
|
|
|
|
this.forwardDeclareKind(sourceFile, kindEnv);
|
|
|
|
this.inferKind(sourceFile, kindEnv);
|
2022-09-14 16:46:30 +02:00
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
// Type inference
|
2023-06-22 16:19:51 +02:00
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
this.typeEnvStack.push(this.globalTypeEnv);
|
2023-06-22 16:19:51 +02:00
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
this.initialize(sourceFile);
|
|
|
|
|
|
|
|
const sourceFileInfo = this.getInfo(sourceFile);
|
|
|
|
this.pushInfo(sourceFileInfo);
|
2022-09-07 12:45:38 +02:00
|
|
|
|
2022-09-15 16:04:49 +02:00
|
|
|
const sccs = [...this.analyser.getSortedDeclarations()];
|
2022-09-05 19:33:08 +02:00
|
|
|
|
|
|
|
for (const nodes of sccs) {
|
2022-09-05 17:25:55 +02:00
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
const poly = new PolyContext();
|
|
|
|
|
|
|
|
this.polyContextStack.push(poly);
|
2022-09-05 17:25:55 +02:00
|
|
|
|
2022-09-01 20:06:43 +02:00
|
|
|
for (const node of nodes) {
|
2022-09-05 17:25:55 +02:00
|
|
|
|
2022-09-09 22:37:14 +02:00
|
|
|
if (!isFunctionDeclarationLike(node)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
const info = this.getInfo(node);
|
|
|
|
info.poly = poly;
|
|
|
|
const returnType = info.returnType = this.createTRegularVar();
|
2022-09-05 17:25:55 +02:00
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
this.typeEnvStack.push(info.typeEnv!);
|
|
|
|
this.returnTypeStack.push(info.returnType!);
|
2022-09-05 17:25:55 +02:00
|
|
|
|
2023-04-12 21:16:48 +02:00
|
|
|
const paramTypes = node.params.map(param => {
|
2023-08-12 13:46:19 +02:00
|
|
|
const paramType = this.createTRegularVar();
|
2023-04-12 21:16:48 +02:00
|
|
|
this.inferBindings(param.pattern, paramType)
|
|
|
|
return paramType;
|
|
|
|
});
|
2022-09-05 17:25:55 +02:00
|
|
|
|
2022-09-16 12:00:00 +02:00
|
|
|
let type = TArrow.build(paramTypes, returnType, node);
|
2023-04-12 21:16:48 +02:00
|
|
|
|
2022-09-05 17:25:55 +02:00
|
|
|
if (node.typeAssert !== null) {
|
|
|
|
this.addConstraint(
|
|
|
|
new CEqual(
|
2023-02-03 17:51:27 +01:00
|
|
|
this.inferTypeExpression(node.typeAssert.typeExpression, true),
|
2022-09-05 17:25:55 +02:00
|
|
|
type,
|
2022-09-06 13:40:20 +02:00
|
|
|
node
|
2022-09-05 17:25:55 +02:00
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
|
|
|
info.inferredType = type;
|
2022-09-05 17:25:55 +02:00
|
|
|
|
2023-02-03 17:51:27 +01:00
|
|
|
// if (node.parent!.kind === SyntaxKind.InstanceDeclaration) {
|
|
|
|
// const inst = node.parent!;
|
|
|
|
// const cls = inst.getScope().lookup(node.parent!.constraint.name.text, Symkind.Typeclass) as ClassDeclaration;
|
2023-03-11 14:24:02 +01:00
|
|
|
// const other = cls.lookup(node)! as LetDeclaration;
|
|
|
|
// assert(other.pattern.kind === SyntaxKind.BindPattern);
|
2023-02-03 17:51:27 +01:00
|
|
|
// console.log(describeType(type));
|
2023-03-11 14:24:02 +01:00
|
|
|
// const otherScheme = this.lookup(other.pattern.name, Symkind.Var)!;
|
|
|
|
// addAll(otherScheme.typeVars, typeVars);
|
|
|
|
// constraints.push(...otherScheme.constraints);
|
2023-02-03 17:51:27 +01:00
|
|
|
// this.addConstraint(new CEqual(type, other.inferredType!, node));
|
|
|
|
// }
|
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
this.returnTypeStack.pop();
|
|
|
|
this.typeEnvStack.pop();
|
2022-09-05 17:25:55 +02:00
|
|
|
|
2023-03-11 14:24:02 +01:00
|
|
|
if (node.parent!.kind !== SyntaxKind.InstanceDeclaration) {
|
2023-08-12 13:46:19 +02:00
|
|
|
this.inferBindings(node.pattern, type, poly.typeVars, poly.constraints);
|
2022-09-09 22:37:14 +02:00
|
|
|
}
|
2023-08-12 13:46:19 +02:00
|
|
|
|
2022-09-01 20:06:43 +02:00
|
|
|
}
|
2022-09-05 17:25:55 +02:00
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
this.polyContextStack.pop();
|
|
|
|
|
2022-09-05 19:33:08 +02:00
|
|
|
}
|
|
|
|
|
2023-06-22 15:30:14 +02:00
|
|
|
this.infer(sourceFile);
|
2022-09-01 20:06:43 +02:00
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
// Pop off whatever we pushed in during initialization
|
|
|
|
this.popInfo(sourceFileInfo);
|
|
|
|
this.typeEnvStack.pop();
|
2023-06-22 16:19:51 +02:00
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
this.solve(new CMany(sourceFileInfo.poly!.constraints));
|
2023-06-22 16:19:51 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-08-02 10:37:13 +02:00
|
|
|
private path: (string | number)[] = [];
|
2023-06-22 16:19:51 +02:00
|
|
|
private constraint: Constraint | null = null;
|
|
|
|
private maxTypeErrorCount = 5;
|
|
|
|
|
|
|
|
private find(type: Type): Type {
|
2023-08-30 12:07:53 +02:00
|
|
|
while (type.kind === TypeKind.RegularVar && this.typeSolution.has(type)) {
|
2023-08-12 13:46:19 +02:00
|
|
|
type = this.typeSolution.get(type)!;
|
2023-06-22 16:19:51 +02:00
|
|
|
}
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
|
|
|
private unifyField(left: Type, right: Type, enableDiagnostics: boolean): boolean {
|
|
|
|
|
|
|
|
const swap = () => { [right, left] = [left, right]; }
|
|
|
|
|
|
|
|
if (left.kind === TypeKind.Absent && right.kind === TypeKind.Absent) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (right.kind === TypeKind.Absent) {
|
|
|
|
swap();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (left.kind === TypeKind.Absent) {
|
|
|
|
assert(right.kind === TypeKind.Present);
|
|
|
|
const fieldName = this.path[this.path.length-1];
|
|
|
|
if (enableDiagnostics) {
|
|
|
|
this.diagnostics.add(
|
|
|
|
new FieldNotFoundDiagnostic(fieldName, left.node, right.type.node, this.constraint!.firstNode)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(left.kind === TypeKind.Present && right.kind === TypeKind.Present);
|
|
|
|
return this.unify(left.type, right.type, enableDiagnostics);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private unify(left: Type, right: Type, enableDiagnostics: boolean): boolean {
|
|
|
|
|
2023-08-12 13:46:19 +02:00
|
|
|
//console.log(`unify ${describeType(left)} @ ${left.node && left.node.constructor && left.node.constructor.name} ~ ${describeType(right)} @ ${right.node && right.node.constructor && right.node.constructor.name}`);
|
|
|
|
//console.log(`unify ${describeType(left)} ~ ${describeType(right)}`);
|
2023-06-26 18:51:48 +02:00
|
|
|
|
2023-06-28 22:09:17 +02:00
|
|
|
left = this.simplifyType(left);
|
|
|
|
right = this.simplifyType(right);
|
2023-06-22 16:19:51 +02:00
|
|
|
|
|
|
|
const swap = () => { [right, left] = [left, right]; }
|
|
|
|
|
2023-06-26 18:51:48 +02:00
|
|
|
if (left.kind === TypeKind.RigidVar && right.kind === TypeKind.RigidVar) {
|
|
|
|
if (left.id === right.id) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-30 12:07:53 +02:00
|
|
|
if (left.kind !== TypeKind.RegularVar && right.kind === TypeKind.RegularVar) {
|
2023-06-22 16:19:51 +02:00
|
|
|
swap();
|
|
|
|
}
|
|
|
|
|
2023-08-30 12:07:53 +02:00
|
|
|
if (left.kind === TypeKind.RegularVar) {
|
2023-06-22 16:19:51 +02:00
|
|
|
|
|
|
|
// Perform an occurs check, verifying whether left occurs
|
|
|
|
// somewhere inside the structure of right. If so, unification
|
|
|
|
// makes no sense.
|
|
|
|
if (right.hasTypeVar(left)) {
|
|
|
|
// TODO print a diagnostic
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We are ready to join the types, so the first thing we do is
|
|
|
|
// propagating the type classes that 'left' requires to 'right'.
|
|
|
|
// If 'right' is another type variable, we're lucky. We just copy
|
|
|
|
// the missing type classes from 'left' to 'right'. Otherwise,
|
2023-08-30 12:07:53 +02:00
|
|
|
const propagateClasses = (classes: Iterable<ClassDeclaration>, type: Type) => {
|
|
|
|
if (isTVar(type)) {
|
|
|
|
for (const constraint of classes) {
|
|
|
|
type.context.add(constraint);
|
|
|
|
}
|
|
|
|
} else if (type.kind === TypeKind.Con) {
|
|
|
|
for (const constraint of classes) {
|
|
|
|
propagateClassTCon(constraint, type);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//assert(false);
|
|
|
|
//this.diagnostics.add(new );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const propagateClassTCon = (clazz: ClassDeclaration, type: TCon) => {
|
|
|
|
const s = this.findInstanceContext(type, clazz);
|
|
|
|
let i = 0;
|
|
|
|
for (const classes of s) {
|
|
|
|
propagateClasses(classes, type.types[i++]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
propagateClasses(left.context, right);
|
2023-06-22 16:19:51 +02:00
|
|
|
|
|
|
|
// We are all clear; set the actual type of left to right.
|
2023-06-28 22:09:17 +02:00
|
|
|
left.set(right);
|
2023-06-22 16:19:51 +02:00
|
|
|
|
|
|
|
// This is a very specific adjustment that is critical to the
|
|
|
|
// well-functioning of the infer/unify algorithm. When addConstraint() is
|
|
|
|
// called, it may decide to solve the constraint immediately during
|
|
|
|
// inference. If this happens, a type variable might get assigned a concrete
|
|
|
|
// type such as Int. We therefore never want the variable to be polymorphic
|
|
|
|
// and be instantiated with a fresh variable, as that would allow Bool to
|
|
|
|
// collide with Int.
|
|
|
|
//
|
|
|
|
// Should it get assigned another unification variable, that's OK too
|
|
|
|
// because then that variable is what matters and it will become the new
|
|
|
|
// (possibly polymorphic) variable.
|
2023-08-12 13:46:19 +02:00
|
|
|
if (this.polyContextStack.length > 0) {
|
|
|
|
this.polyContextStack[this.polyContextStack.length-1].typeVars.delete(left);
|
2023-06-22 16:19:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// These types will be join, and we'd like to track that
|
|
|
|
// into a special chain.
|
|
|
|
TypeBase.join(left, right);
|
|
|
|
|
|
|
|
// if (left.node !== null) {
|
|
|
|
// right.node = left.node;
|
|
|
|
// }
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (left.kind === TypeKind.Arrow && right.kind === TypeKind.Arrow) {
|
|
|
|
let success = true;
|
|
|
|
if (!this.unify(left.paramType, right.paramType, enableDiagnostics)) {
|
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
if (!this.unify(left.returnType, right.returnType, enableDiagnostics)) {
|
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
if (success) {
|
|
|
|
TypeBase.join(left, right);
|
|
|
|
}
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (left.kind === TypeKind.Con && right.kind === TypeKind.Con) {
|
|
|
|
if (left.id === right.id) {
|
2023-07-04 20:38:40 +02:00
|
|
|
TypeBase.join(left, right);
|
|
|
|
return true;
|
2023-06-22 16:19:51 +02:00
|
|
|
}
|
|
|
|
}
|
2022-09-01 20:06:43 +02:00
|
|
|
|
2023-06-22 16:19:51 +02:00
|
|
|
if (left.kind === TypeKind.Nil && right.kind === TypeKind.Nil) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (left.kind === TypeKind.Field && right.kind === TypeKind.Field) {
|
|
|
|
if (left.name === right.name) {
|
|
|
|
let success = true;
|
|
|
|
this.path.push(left.name);
|
|
|
|
if (!this.unifyField(left.type, right.type, enableDiagnostics)) {
|
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
this.path.pop();
|
|
|
|
if (!this.unify(left.restType, right.restType, enableDiagnostics)) {
|
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
let success = true;
|
2023-08-12 13:46:19 +02:00
|
|
|
const newRestType = new TRegularVar(this.nextTypeVarId++);
|
2023-06-22 16:19:51 +02:00
|
|
|
if (!this.unify(left.restType, new TField(right.name, right.type, newRestType), enableDiagnostics)) {
|
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
if (!this.unify(right.restType, new TField(left.name, left.type, newRestType), enableDiagnostics)) {
|
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
return success;
|
|
|
|
}
|
2023-06-21 16:56:04 +02:00
|
|
|
|
2023-06-22 16:19:51 +02:00
|
|
|
if (left.kind === TypeKind.Nil && right.kind === TypeKind.Field) {
|
|
|
|
swap();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (left.kind === TypeKind.Field && right.kind === TypeKind.Nil) {
|
|
|
|
let success = true;
|
|
|
|
this.path.push(left.name);
|
|
|
|
if (!this.unifyField(left.type, new TAbsent(right.node), enableDiagnostics)) {
|
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
this.path.pop();
|
|
|
|
if (!this.unify(left.restType, right, enableDiagnostics)) {
|
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
return success
|
|
|
|
}
|
2023-06-21 16:56:04 +02:00
|
|
|
|
2023-06-22 16:19:51 +02:00
|
|
|
if (left.kind === TypeKind.App && right.kind === TypeKind.App) {
|
|
|
|
return this.unify(left.left, right.left, enableDiagnostics)
|
|
|
|
&& this.unify(left.right, right.right, enableDiagnostics);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (enableDiagnostics) {
|
|
|
|
this.diagnostics.add(
|
|
|
|
new TypeMismatchDiagnostic(
|
2023-06-28 22:09:17 +02:00
|
|
|
this.simplifyType(left),
|
|
|
|
this.simplifyType(right),
|
2023-06-22 16:19:51 +02:00
|
|
|
[...this.constraint!.getNodes()],
|
|
|
|
this.path,
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public solve(constraint: Constraint): void {
|
|
|
|
|
|
|
|
let queue = [ constraint ];
|
|
|
|
|
|
|
|
let errorCount = 0;
|
|
|
|
|
|
|
|
while (queue.length > 0) {
|
|
|
|
|
|
|
|
const constraint = queue.shift()!;
|
|
|
|
|
|
|
|
switch (constraint.kind) {
|
|
|
|
|
|
|
|
case ConstraintKind.Many:
|
|
|
|
{
|
|
|
|
for (const element of constraint.elements) {
|
|
|
|
queue.push(element);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ConstraintKind.Equal:
|
|
|
|
{
|
|
|
|
this.constraint = constraint;
|
|
|
|
if (!this.unify(constraint.left, constraint.right, true)) {
|
|
|
|
errorCount++;
|
|
|
|
if (errorCount === this.maxTypeErrorCount) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2023-03-11 14:24:02 +01:00
|
|
|
|
|
|
|
}
|
2023-03-19 15:02:19 +01:00
|
|
|
|
2023-03-16 21:50:15 +01:00
|
|
|
private lookupClass(name: string): ClassDeclaration | null {
|
|
|
|
return this.classDecls.get(name) ?? null;
|
|
|
|
}
|
2023-03-11 14:24:02 +01:00
|
|
|
|
2023-06-30 19:30:42 +02:00
|
|
|
public getTypeOfNode(node: Syntax): Type {
|
2023-08-12 13:46:19 +02:00
|
|
|
const info = this.getInfo(node);
|
|
|
|
assert(info.inferredType !== undefined);
|
|
|
|
return this.simplifyType(info.inferredType);
|
2023-06-30 19:30:42 +02:00
|
|
|
}
|
|
|
|
|
2023-06-21 16:56:04 +02:00
|
|
|
// private *findInstanceContext(type: TCon, clazz: ClassDeclaration): Iterable<ClassDeclaration[]> {
|
|
|
|
// for (const instance of clazz.getInstances()) {
|
|
|
|
// assert(instance.types.length === 1);
|
|
|
|
// const instTy0 = instance.types[0];
|
|
|
|
// if ((instTy0.kind === SyntaxKind.AppTypeExpression
|
|
|
|
// && instTy0.operator.kind === SyntaxKind.ReferenceTypeExpression
|
|
|
|
// && instTy0.operator.name.text === type.displayName)
|
|
|
|
// || (instTy0.kind === SyntaxKind.ReferenceTypeExpression
|
|
|
|
// && instTy0.name.text === type.displayName)) {
|
|
|
|
// if (instance.constraintClause === null) {
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
// for (const argType of type.argTypes) {
|
|
|
|
// const classes = [];
|
|
|
|
// for (const constraint of instance.constraintClause.constraints) {
|
|
|
|
// assert(constraint.types.length === 1);
|
|
|
|
// const classDecl = this.lookupClass(constraint.name.text);
|
|
|
|
// if (classDecl === null) {
|
|
|
|
// this.diagnostics.add(new TypeclassNotFoundDiagnostic(constraint.name));
|
|
|
|
// } else {
|
|
|
|
// classes.push(classDecl);
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// yield classes;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
2022-08-31 13:29:56 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-04-12 21:16:48 +02:00
|
|
|
function getVariadicMember(node: StructPattern) {1713
|
2023-03-11 14:24:02 +01:00
|
|
|
for (const member of node.members) {
|
|
|
|
if (member.kind === SyntaxKind.VariadicStructPatternElement) {
|
|
|
|
return member;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|