[WIP] Further attempt to get typeclasses working
This commit is contained in:
parent
719dbfcad4
commit
e01a970377
11 changed files with 424 additions and 102 deletions
|
@ -13,7 +13,7 @@ export class Analyser {
|
||||||
|
|
||||||
const addReference = (scope: Scope, name: string) => {
|
const addReference = (scope: Scope, name: string) => {
|
||||||
const target = scope.lookup(name);
|
const target = scope.lookup(name);
|
||||||
if (source === null || target === null || target.kind === SyntaxKind.Param) {
|
if (source === null || target === null || isParam(target.kind)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assert(source.kind === SyntaxKind.LetDeclaration);
|
assert(source.kind === SyntaxKind.LetDeclaration);
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
ExprOperator,
|
ExprOperator,
|
||||||
Identifier,
|
Identifier,
|
||||||
IdentifierAlt,
|
IdentifierAlt,
|
||||||
|
InstanceDeclaration,
|
||||||
LetDeclaration,
|
LetDeclaration,
|
||||||
Pattern,
|
Pattern,
|
||||||
ReferenceExpression,
|
ReferenceExpression,
|
||||||
|
@ -23,58 +24,31 @@ import {
|
||||||
KindMismatchDiagnostic,
|
KindMismatchDiagnostic,
|
||||||
ModuleNotFoundDiagnostic,
|
ModuleNotFoundDiagnostic,
|
||||||
TypeclassNotFoundDiagnostic,
|
TypeclassNotFoundDiagnostic,
|
||||||
TypeclassDeclaredTwiceDiagnostic,
|
|
||||||
FieldNotFoundDiagnostic,
|
FieldNotFoundDiagnostic,
|
||||||
TypeMismatchDiagnostic,
|
TypeMismatchDiagnostic,
|
||||||
} from "./diagnostics";
|
} from "./diagnostics";
|
||||||
import { assert, assertNever, isEmpty, MultiMap, toStringTag, InspectFn } from "./util";
|
import { assert, assertNever, isEmpty, MultiMap, toStringTag, InspectFn, implementationLimitation } from "./util";
|
||||||
import { Analyser } from "./analysis";
|
import { Analyser } from "./analysis";
|
||||||
import { InspectOptions } from "util";
|
import { InspectOptions } from "util";
|
||||||
import { TypeKind, TApp, TArrow, TCon, TField, TNil, TPresent, TRegularVar, TVSet, TVSub, Type, TypeBase, TAbsent, TRigidVar, TVar, buildTupleTypeWithLoc, buildTupleType, isTVar } from "./types";
|
import { TypeKind, TApp, TArrow, TCon, TField, TNil, TPresent, TRegularVar, TVSet, TVSub, Type, TypeBase, TAbsent, TRigidVar, TVar, buildTupleTypeWithLoc, buildTupleType, labelTag } from "./types";
|
||||||
import { CEmpty, CEqual, CMany, Constraint, ConstraintKind, ConstraintSet } from "./constraints";
|
import { CEmpty, CEqual, CMany, Constraint, ConstraintKind, ConstraintSet } from "./constraints";
|
||||||
|
|
||||||
// export class Qual {
|
// class IsIn {
|
||||||
|
|
||||||
// public constructor(
|
// public constructor(
|
||||||
// public preds: Pred[],
|
// public className: string,
|
||||||
// public type: Type,
|
|
||||||
// ) {
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public substitute(sub: TVSub): Qual {
|
|
||||||
// return new Qual(
|
|
||||||
// this.preds.map(pred => pred.substitute(sub)),
|
|
||||||
// this.type.substitute(sub),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public *getTypeVars() {
|
|
||||||
// for (const pred of this.preds) {
|
|
||||||
// yield* pred.type.getTypeVars();
|
|
||||||
// }
|
|
||||||
// yield* this.type.getTypeVars();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// class IsInPred {
|
|
||||||
|
|
||||||
// public constructor(
|
|
||||||
// public id: string,
|
|
||||||
// public type: Type,
|
// public type: Type,
|
||||||
// ) {
|
// ) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// public substitute(sub: TVSub): Pred {
|
// public substitute(sub: TVSub): Pred {
|
||||||
// return new IsInPred(this.id, this.type.substitute(sub));
|
// return new IsIn(this.className, this.type.substitute(sub));
|
||||||
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// type Pred = IsInPred;
|
// type Pred = IsIn;
|
||||||
|
|
||||||
export const enum KindType {
|
export const enum KindType {
|
||||||
Type,
|
Type,
|
||||||
|
@ -371,6 +345,11 @@ function hasTypeVar(typeVars: TVSet, type: Type): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ClassMeta {
|
||||||
|
decl: ClassDeclaration | null;
|
||||||
|
instances: Set<InstanceDeclaration>;
|
||||||
|
}
|
||||||
|
|
||||||
export class Checker {
|
export class Checker {
|
||||||
|
|
||||||
private nextTypeVarId = 0;
|
private nextTypeVarId = 0;
|
||||||
|
@ -382,7 +361,7 @@ export class Checker {
|
||||||
private boolType = this.createTCon('Bool');
|
private boolType = this.createTCon('Bool');
|
||||||
private unitType = buildTupleType([]);
|
private unitType = buildTupleType([]);
|
||||||
|
|
||||||
private classDecls = new Map<string, ClassDeclaration>();
|
private classDecls = new Map<string, ClassMeta>();
|
||||||
private globalKindEnv = new KindEnv();
|
private globalKindEnv = new KindEnv();
|
||||||
private globalTypeEnv = new TypeEnv();
|
private globalTypeEnv = new TypeEnv();
|
||||||
|
|
||||||
|
@ -779,8 +758,8 @@ export class Checker {
|
||||||
case ConstraintKind.Empty:
|
case ConstraintKind.Empty:
|
||||||
return constraint;
|
return constraint;
|
||||||
case ConstraintKind.Equal:
|
case ConstraintKind.Equal:
|
||||||
constraint.left = this.simplifyType(constraint.left)
|
// constraint.left = this.simplifyType(constraint.left)
|
||||||
constraint.right = this.simplifyType(constraint.right)
|
// constraint.right = this.simplifyType(constraint.right)
|
||||||
const newConstraint = constraint.substitute(sub);
|
const newConstraint = constraint.substitute(sub);
|
||||||
newConstraint.node = node;
|
newConstraint.node = node;
|
||||||
newConstraint.prevInstantiation = constraint;
|
newConstraint.prevInstantiation = constraint;
|
||||||
|
@ -877,13 +856,15 @@ export class Checker {
|
||||||
}
|
}
|
||||||
|
|
||||||
case SyntaxKind.NestedTypeExpression:
|
case SyntaxKind.NestedTypeExpression:
|
||||||
|
case SyntaxKind.InstanceTypeExpression:
|
||||||
{
|
{
|
||||||
kind = this.inferKindFromTypeExpression(node.typeExpr, env);
|
kind = this.inferKindFromTypeExpression(node.typeExpr, env);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unexpected ${node}`);
|
assertNever(node);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We store the kind on the node so there is a one-to-one correspondence
|
// We store the kind on the node so there is a one-to-one correspondence
|
||||||
|
@ -1580,6 +1561,7 @@ export class Checker {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case SyntaxKind.InstanceTypeExpression:
|
||||||
case SyntaxKind.NestedTypeExpression:
|
case SyntaxKind.NestedTypeExpression:
|
||||||
type = this.inferTypeExpression(node.typeExpr, introduceTypeVars);
|
type = this.inferTypeExpression(node.typeExpr, introduceTypeVars);
|
||||||
break;
|
break;
|
||||||
|
@ -1613,11 +1595,16 @@ export class Checker {
|
||||||
|
|
||||||
case SyntaxKind.TypeExpressionWithConstraints:
|
case SyntaxKind.TypeExpressionWithConstraints:
|
||||||
{
|
{
|
||||||
// TODO
|
for (const constraint of node.constraints) {
|
||||||
// for (const constraint of node.constraints) {
|
implementationLimitation(constraint.types.length === 1);
|
||||||
// implementationLimitation(constraint.types.length === 1);
|
this.addConstraint(
|
||||||
// this.addConstraint(new CClass(constraint.name.text, this.inferTypeExpression(constraint.types[0]), constraint.name));
|
new CClass(
|
||||||
// }
|
constraint.name.text,
|
||||||
|
this.inferTypeExpression(constraint.types[0]),
|
||||||
|
constraint.name
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
return this.inferTypeExpression(node.typeExpr, introduceTypeVars);
|
return this.inferTypeExpression(node.typeExpr, introduceTypeVars);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1646,7 +1633,7 @@ export class Checker {
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unrecognised ${node}`);
|
assertNever(node);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1778,6 +1765,18 @@ export class Checker {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getClassMeta(name: string): ClassMeta {
|
||||||
|
let meta = this.classDecls.get(name);
|
||||||
|
if (meta === undefined) {
|
||||||
|
meta = {
|
||||||
|
decl: null,
|
||||||
|
instances: new Set(),
|
||||||
|
};
|
||||||
|
this.classDecls.set(name, meta);
|
||||||
|
}
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
|
||||||
private initialize(node: Syntax): void {
|
private initialize(node: Syntax): void {
|
||||||
|
|
||||||
switch (node.kind) {
|
switch (node.kind) {
|
||||||
|
@ -1816,6 +1815,11 @@ export class Checker {
|
||||||
|
|
||||||
case SyntaxKind.ClassDeclaration:
|
case SyntaxKind.ClassDeclaration:
|
||||||
{
|
{
|
||||||
|
const meta = this.getClassMeta(node.name.text);
|
||||||
|
if (meta.decl !== undefined) {
|
||||||
|
// TODO class declaration already exists diagnostic
|
||||||
|
}
|
||||||
|
meta.decl = node;
|
||||||
const info = this.getInfo(node);
|
const info = this.getInfo(node);
|
||||||
const env = info.typeEnv = new TypeEnv();
|
const env = info.typeEnv = new TypeEnv();
|
||||||
for (const tv of node.types) {
|
for (const tv of node.types) {
|
||||||
|
@ -1830,14 +1834,19 @@ export class Checker {
|
||||||
|
|
||||||
case SyntaxKind.InstanceDeclaration:
|
case SyntaxKind.InstanceDeclaration:
|
||||||
{
|
{
|
||||||
if (!this.classDecls.has(node.name.text)) {
|
const meta = this.getClassMeta(node.name.text);
|
||||||
|
meta.instances.add(node);
|
||||||
|
|
||||||
|
if (meta.decl === null) {
|
||||||
this.diagnostics.add(new TypeclassNotFoundDiagnostic(node.name.text, node.name));
|
this.diagnostics.add(new TypeclassNotFoundDiagnostic(node.name.text, node.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
const info = this.getInfo(node);
|
const info = this.getInfo(node);
|
||||||
info.typeEnv = new TypeEnv();
|
info.typeEnv = new TypeEnv();
|
||||||
for (const element of node.elements) {
|
for (const element of node.elements) {
|
||||||
this.initialize(element);
|
this.initialize(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1974,23 +1983,30 @@ export class Checker {
|
||||||
typeArgs.push(typeArg);
|
typeArgs.push(typeArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const tagType = TApp.build(
|
||||||
|
// this.createTCon(node.name.text, node.name),
|
||||||
|
// typeArgs,
|
||||||
|
// node.name
|
||||||
|
// );
|
||||||
|
const tagType = this.createTCon(node.name.text, node.name);
|
||||||
const fields = new Map<string, Type>();
|
const fields = new Map<string, Type>();
|
||||||
const restType = new TNil(node);
|
const restType = new TNil(node);
|
||||||
|
|
||||||
|
fields.set(labelTag, tagType);
|
||||||
|
|
||||||
if (node.fields !== null) {
|
if (node.fields !== null) {
|
||||||
for (const field of node.fields) {
|
for (const field of node.fields) {
|
||||||
fields.set(field.name.text, this.inferTypeExpression(field.typeExpr));
|
fields.set(field.name.text, this.inferTypeExpression(field.typeExpr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const type = this.createTCon(node.name.text, node.name);
|
|
||||||
const recordType = TField.build(fields, restType);
|
const recordType = TField.build(fields, restType);
|
||||||
|
|
||||||
this.polyContextStack.pop();
|
this.polyContextStack.pop();
|
||||||
this.typeEnvStack.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), recordType), Symkind.Type);
|
||||||
parentEnv.add(node.name.text, new Forall(poly.typeVars, new CMany(poly.constraints), new TArrow(recordType, type)), Symkind.Var);
|
// parentEnv.add(node.name.text, new Forall(poly.typeVars, new CMany(poly.constraints), new TArrow(recordType, type)), Symkind.Var);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2041,7 +2057,16 @@ export class Checker {
|
||||||
|
|
||||||
const paramTypes = node.params.map(param => {
|
const paramTypes = node.params.map(param => {
|
||||||
const paramType = this.createTRegularVar();
|
const paramType = this.createTRegularVar();
|
||||||
this.inferBindings(param.pattern, paramType)
|
switch (param.kind) {
|
||||||
|
case SyntaxKind.PlainParam:
|
||||||
|
this.inferBindings(param.pattern, paramType)
|
||||||
|
break;
|
||||||
|
case SyntaxKind.InstanceParam:
|
||||||
|
this.addBinding(param.name.text, Forall.mono(paramType), Symkind.Var);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assertNever(param);
|
||||||
|
}
|
||||||
return paramType;
|
return paramType;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2094,17 +2119,35 @@ export class Checker {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// private findInstanceContext(sig: Type[], clazz: ClassDeclaration): Iterable<ClassDeclaration[]> {
|
||||||
|
|
||||||
|
// const contexts = [];
|
||||||
|
// const meta = this.getClassMeta(clazz.name.text);
|
||||||
|
|
||||||
|
// // TODO should be a seperate verification pass somewhere
|
||||||
|
// // if (meta.decl === null) {
|
||||||
|
// // this.diagnostics.add(new TypeclassNotFoundDiagnostic(clazz.name.text));
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// for (const instance of meta.instances) {
|
||||||
|
// let i = 0;
|
||||||
|
// for (const type of instance.types) {
|
||||||
|
// // TODO might need unification
|
||||||
|
// const left = sig[i++];
|
||||||
|
// const right = this.getTypeOfNode(type);
|
||||||
|
// if (assignableTo(left, right)) {
|
||||||
|
// contexts.push(context);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return contexts;
|
||||||
|
// }
|
||||||
|
|
||||||
private path: (string | number)[] = [];
|
private path: (string | number)[] = [];
|
||||||
private constraint: Constraint | null = null;
|
private constraint: Constraint | null = null;
|
||||||
private maxTypeErrorCount = 5;
|
private maxTypeErrorCount = 5;
|
||||||
|
|
||||||
private find(type: Type): Type {
|
|
||||||
while (type.kind === TypeKind.RegularVar && this.typeSolution.has(type)) {
|
|
||||||
type = this.typeSolution.get(type)!;
|
|
||||||
}
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
private unifyField(left: Type, right: Type, enableDiagnostics: boolean): boolean {
|
private unifyField(left: Type, right: Type, enableDiagnostics: boolean): boolean {
|
||||||
|
|
||||||
const swap = () => { [right, left] = [left, right]; }
|
const swap = () => { [right, left] = [left, right]; }
|
||||||
|
@ -2132,7 +2175,6 @@ export class Checker {
|
||||||
return this.unify(left.type, right.type, enableDiagnostics);
|
return this.unify(left.type, right.type, enableDiagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private unify(left: Type, right: Type, enableDiagnostics: boolean): boolean {
|
private unify(left: Type, right: Type, enableDiagnostics: boolean): boolean {
|
||||||
|
|
||||||
//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)} @ ${left.node && left.node.constructor && left.node.constructor.name} ~ ${describeType(right)} @ ${right.node && right.node.constructor && right.node.constructor.name}`);
|
||||||
|
@ -2167,30 +2209,33 @@ export class Checker {
|
||||||
// propagating the type classes that 'left' requires to 'right'.
|
// propagating the type classes that 'left' requires to 'right'.
|
||||||
// If 'right' is another type variable, we're lucky. We just copy
|
// If 'right' is another type variable, we're lucky. We just copy
|
||||||
// the missing type classes from 'left' to 'right'. Otherwise,
|
// the missing type classes from 'left' to 'right'. Otherwise,
|
||||||
const propagateClasses = (classes: Iterable<ClassDeclaration>, type: Type) => {
|
//const propagateClasses = (classes: Iterable<ClassDeclaration>, type: Type) => {
|
||||||
if (isTVar(type)) {
|
// if (isTVar(type)) {
|
||||||
for (const constraint of classes) {
|
// for (const constraint of classes) {
|
||||||
type.context.add(constraint);
|
// type.context.add(constraint);
|
||||||
}
|
// }
|
||||||
} else if (type.kind === TypeKind.Con) {
|
// } else if (isSignature(type)) {
|
||||||
for (const constraint of classes) {
|
// const sig = getSignature(type);
|
||||||
propagateClassTCon(constraint, type);
|
// for (const constraint of classes) {
|
||||||
}
|
// propagateClassTCon(constraint, sig);
|
||||||
} else {
|
// }
|
||||||
//assert(false);
|
// } else {
|
||||||
//this.diagnostics.add(new );
|
// assert(false);
|
||||||
}
|
// //this.diagnostics.add(new );
|
||||||
}
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
const propagateClassTCon = (clazz: ClassDeclaration, type: TCon) => {
|
//const propagateClassTCon = (clazz: ClassDeclaration, sig: Type[]) => {
|
||||||
const s = this.findInstanceContext(type, clazz);
|
// const s = this.findInstanceContext(sig, clazz);
|
||||||
let i = 0;
|
// let i = 1;
|
||||||
for (const classes of s) {
|
// for (const classes of s) {
|
||||||
propagateClasses(classes, type.types[i++]);
|
// propagateClasses(classes, sig[i++]);
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
propagateClasses(left.context, right);
|
//if (left.context.size > 0) {
|
||||||
|
// propagateClasses(left.context, right);
|
||||||
|
//}
|
||||||
|
|
||||||
// We are all clear; set the actual type of left to right.
|
// We are all clear; set the actual type of left to right.
|
||||||
left.set(right);
|
left.set(right);
|
||||||
|
@ -2305,6 +2350,35 @@ export class Checker {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// private inHnf(p: Pred): boolean {
|
||||||
|
// let curr = p.type;
|
||||||
|
// for (;;) {
|
||||||
|
// if (isTVar(curr)) {
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// if (curr.kind === TypeKind.Con) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// if (curr.kind === TypeKind.App) {
|
||||||
|
// curr = curr.left;
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// unreachable();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// private toHnf(p: Pred): Pred[] {
|
||||||
|
// if (this.inHnf(p)) {
|
||||||
|
// return [ p ];
|
||||||
|
// }
|
||||||
|
// const result = this.byInst(p);
|
||||||
|
// if (result === undefined) {
|
||||||
|
// // TODO add diagnostic
|
||||||
|
// throw new Error(`context reduction`);
|
||||||
|
// }
|
||||||
|
// result.map(this.toHnf.bind(this)).flatten();
|
||||||
|
// }
|
||||||
|
|
||||||
public solve(constraint: Constraint): void {
|
public solve(constraint: Constraint): void {
|
||||||
|
|
||||||
let queue = [ constraint ];
|
let queue = [ constraint ];
|
||||||
|
@ -2317,6 +2391,9 @@ export class Checker {
|
||||||
|
|
||||||
switch (constraint.kind) {
|
switch (constraint.kind) {
|
||||||
|
|
||||||
|
case ConstraintKind.Empty:
|
||||||
|
break;
|
||||||
|
|
||||||
case ConstraintKind.Many:
|
case ConstraintKind.Many:
|
||||||
{
|
{
|
||||||
for (const element of constraint.elements) {
|
for (const element of constraint.elements) {
|
||||||
|
@ -2337,13 +2414,20 @@ export class Checker {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case ConstraintKind.Class:
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assertNever(constraint);;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private lookupClass(name: string): ClassDeclaration | null {
|
private lookupClass(name: string): ClassMeta | null {
|
||||||
return this.classDecls.get(name) ?? null;
|
return this.classDecls.get(name) ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { first, InspectFn, last, toStringTag } from "./util";
|
||||||
|
|
||||||
export const enum ConstraintKind {
|
export const enum ConstraintKind {
|
||||||
Equal,
|
Equal,
|
||||||
|
// Class,
|
||||||
Many,
|
Many,
|
||||||
Empty,
|
Empty,
|
||||||
}
|
}
|
||||||
|
@ -99,19 +100,48 @@ export class CMany extends ConstraintBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public [toStringTag](currentDepth: number, { depth = 2, ...options }: InspectOptions, inspect: InspectFn): string {
|
public [toStringTag](_depth: number, opts: InspectOptions, inspect: InspectFn): string {
|
||||||
if (this.elements.length === 0) {
|
if (this.elements.length === 0) {
|
||||||
return '[]';
|
return '[]';
|
||||||
}
|
}
|
||||||
let out = '[\n';
|
let out = '[\n';
|
||||||
const newOptions = { ...options, depth: depth === null ? null : depth - 1 };
|
out += this.elements.map(constraint => ' ' + inspect(constraint, opts)).join('\n');
|
||||||
out += this.elements.map(constraint => ' ' + inspect(constraint, newOptions)).join('\n');
|
|
||||||
out += '\n]';
|
out += '\n]';
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// export class CClass extends ConstraintBase {
|
||||||
|
|
||||||
|
// public readonly kind = ConstraintKind.Class;
|
||||||
|
|
||||||
|
// public constructor(
|
||||||
|
// public className: string,
|
||||||
|
// public type: Type,
|
||||||
|
// public node: Syntax,
|
||||||
|
// ) {
|
||||||
|
// super();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public substitute(sub: TVSub): Constraint {
|
||||||
|
// return new CClass(
|
||||||
|
// this.className,
|
||||||
|
// this.type.substitute(sub),
|
||||||
|
// this.node,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public *freeTypeVars(): Iterable<TVar> {
|
||||||
|
// yield* this.type.getTypeVars();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public [toStringTag](_depth: number, opts: InspectOptions, inspect: InspectFn): string {
|
||||||
|
// return this.className + ' => ' + inspect(this.type, opts);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
export class CEmpty extends ConstraintBase {
|
export class CEmpty extends ConstraintBase {
|
||||||
|
|
||||||
public readonly kind = ConstraintKind.Empty;
|
public readonly kind = ConstraintKind.Empty;
|
||||||
|
@ -132,9 +162,10 @@ export class CEmpty extends ConstraintBase {
|
||||||
|
|
||||||
export type Constraint
|
export type Constraint
|
||||||
= CEqual
|
= CEqual
|
||||||
|
// | CClass
|
||||||
| CMany
|
| CMany
|
||||||
| CEmpty
|
| CEmpty
|
||||||
|
|
||||||
export class ConstraintSet extends Array<Constraint> {
|
export class ConstraintSet extends Array<Constraint> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { isNodeWithScope, Scope } from "./scope"
|
||||||
import type { Kind, Scheme } from "./checker"
|
import type { Kind, Scheme } from "./checker"
|
||||||
import type { Type } from "./types";
|
import type { Type } from "./types";
|
||||||
import { Emitter } from "./emitter";
|
import { Emitter } from "./emitter";
|
||||||
|
import { warn } from "console";
|
||||||
|
|
||||||
export type TextSpan = [number, number];
|
export type TextSpan = [number, number];
|
||||||
|
|
||||||
|
@ -81,7 +82,7 @@ export class TextFile {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum SyntaxKind {
|
export enum SyntaxKind {
|
||||||
|
|
||||||
// Tokens
|
// Tokens
|
||||||
Identifier,
|
Identifier,
|
||||||
|
@ -140,6 +141,7 @@ export const enum SyntaxKind {
|
||||||
NestedTypeExpression,
|
NestedTypeExpression,
|
||||||
TupleTypeExpression,
|
TupleTypeExpression,
|
||||||
ForallTypeExpression,
|
ForallTypeExpression,
|
||||||
|
InstanceTypeExpression,
|
||||||
TypeExpressionWithConstraints,
|
TypeExpressionWithConstraints,
|
||||||
|
|
||||||
// Patterns
|
// Patterns
|
||||||
|
@ -204,12 +206,15 @@ export const enum SyntaxKind {
|
||||||
EnumDeclarationStructElement,
|
EnumDeclarationStructElement,
|
||||||
EnumDeclarationTupleElement,
|
EnumDeclarationTupleElement,
|
||||||
|
|
||||||
|
// Parameters
|
||||||
|
PlainParam,
|
||||||
|
InstanceParam,
|
||||||
|
|
||||||
// Other nodes
|
// Other nodes
|
||||||
WrappedOperator,
|
WrappedOperator,
|
||||||
MatchArm,
|
MatchArm,
|
||||||
Initializer,
|
Initializer,
|
||||||
TypeAssert,
|
TypeAssert,
|
||||||
Param,
|
|
||||||
SourceFile,
|
SourceFile,
|
||||||
ClassConstraint,
|
ClassConstraint,
|
||||||
ClassConstraintClause,
|
ClassConstraintClause,
|
||||||
|
@ -1486,6 +1491,44 @@ export class NestedTypeExpression extends SyntaxBase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class InstanceTypeExpression extends SyntaxBase {
|
||||||
|
|
||||||
|
public readonly kind = SyntaxKind.InstanceTypeExpression;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public lbrace1: LBrace,
|
||||||
|
public lbrace2: LBrace,
|
||||||
|
public name: Identifier | null = null,
|
||||||
|
public colon: Colon | null = null,
|
||||||
|
public typeExpr: TypeExpression,
|
||||||
|
public rbrace1: RBrace,
|
||||||
|
public rbrace2: RBrace,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public clone(): InstanceTypeExpression {
|
||||||
|
return new InstanceTypeExpression(
|
||||||
|
this.lbrace1,
|
||||||
|
this.lbrace2,
|
||||||
|
this.name,
|
||||||
|
this.colon,
|
||||||
|
this.typeExpr,
|
||||||
|
this.rbrace1,
|
||||||
|
this.rbrace2,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getFirstToken(): Token {
|
||||||
|
return this.lbrace1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLastToken(): Token {
|
||||||
|
return this.rbrace2;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
export type TypeExpression
|
export type TypeExpression
|
||||||
= ReferenceTypeExpression
|
= ReferenceTypeExpression
|
||||||
| ArrowTypeExpression
|
| ArrowTypeExpression
|
||||||
|
@ -1495,6 +1538,7 @@ export type TypeExpression
|
||||||
| TupleTypeExpression
|
| TupleTypeExpression
|
||||||
| ForallTypeExpression
|
| ForallTypeExpression
|
||||||
| TypeExpressionWithConstraints
|
| TypeExpressionWithConstraints
|
||||||
|
| InstanceTypeExpression
|
||||||
|
|
||||||
export class NamedPattern extends SyntaxBase {
|
export class NamedPattern extends SyntaxBase {
|
||||||
|
|
||||||
|
@ -2494,9 +2538,43 @@ export type Statement
|
||||||
| IfStatement
|
| IfStatement
|
||||||
| AssignStatement
|
| AssignStatement
|
||||||
|
|
||||||
export class Param extends SyntaxBase {
|
export class InstanceParam extends SyntaxBase {
|
||||||
|
|
||||||
public readonly kind = SyntaxKind.Param;
|
public readonly kind = SyntaxKind.InstanceParam;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public lbrace1: LBrace,
|
||||||
|
public lbrace2: LBrace,
|
||||||
|
public name: Identifier,
|
||||||
|
public rbrace1: RBrace,
|
||||||
|
public rbrace2: RBrace,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public clone(): InstanceParam {
|
||||||
|
return new InstanceParam(
|
||||||
|
this.lbrace1,
|
||||||
|
this.lbrace2,
|
||||||
|
this.name,
|
||||||
|
this.rbrace1,
|
||||||
|
this.rbrace2,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getFirstToken(): Token {
|
||||||
|
return this.lbrace1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLastToken(): Token {
|
||||||
|
return this.rbrace2;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PlainParam extends SyntaxBase {
|
||||||
|
|
||||||
|
public readonly kind = SyntaxKind.PlainParam;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
public pattern: Pattern,
|
public pattern: Pattern,
|
||||||
|
@ -2504,8 +2582,8 @@ export class Param extends SyntaxBase {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public clone(): Param {
|
public clone(): PlainParam {
|
||||||
return new Param(
|
return new PlainParam(
|
||||||
this.pattern.clone(),
|
this.pattern.clone(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2520,6 +2598,15 @@ export class Param extends SyntaxBase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Param
|
||||||
|
= InstanceParam
|
||||||
|
| PlainParam
|
||||||
|
|
||||||
|
export function isParam(node: Syntax): node is Param {
|
||||||
|
return node.kind === SyntaxKind.PlainParam
|
||||||
|
|| node.kind === SyntaxKind.InstanceParam;
|
||||||
|
}
|
||||||
|
|
||||||
export class EnumDeclarationStructElement extends SyntaxBase {
|
export class EnumDeclarationStructElement extends SyntaxBase {
|
||||||
|
|
||||||
public readonly kind = SyntaxKind.EnumDeclarationStructElement;
|
public readonly kind = SyntaxKind.EnumDeclarationStructElement;
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
|
||||||
import { Kind, KindType } from "./checker";
|
import { Kind, KindType } from "./checker";
|
||||||
import { type Type, TypeKind } from "./types"
|
import { type Type, TypeKind, labelTag } from "./types"
|
||||||
import { ClassConstraint, ClassDeclaration, IdentifierAlt, InstanceDeclaration, Syntax, SyntaxKind, TextFile, TextPosition, TextRange, Token } from "./cst";
|
import { ClassConstraint, ClassDeclaration, IdentifierAlt, InstanceDeclaration, Syntax, SyntaxKind, TextFile, TextPosition, TextRange, Token } from "./cst";
|
||||||
import { assertNever, countDigits, IndentWriter } from "./util";
|
import { assert, assertNever, countDigits, IndentWriter } from "./util";
|
||||||
|
import { unwatchFile } from "fs";
|
||||||
|
import { warn } from "console";
|
||||||
|
|
||||||
const ANSI_RESET = "\u001b[0m"
|
const ANSI_RESET = "\u001b[0m"
|
||||||
const ANSI_BOLD = "\u001b[1m"
|
const ANSI_BOLD = "\u001b[1m"
|
||||||
|
@ -537,6 +539,13 @@ export function describeType(type: Type): string {
|
||||||
}
|
}
|
||||||
case TypeKind.Field:
|
case TypeKind.Field:
|
||||||
{
|
{
|
||||||
|
// let curr: Type = type;
|
||||||
|
// while (curr.kind === TypeKind.Field) {
|
||||||
|
// if (curr.name === labelTag) {
|
||||||
|
// return describeType(curr.type);
|
||||||
|
// }
|
||||||
|
// curr = curr.restType;
|
||||||
|
// }
|
||||||
let out = '{ ' + type.name + ': ' + describeType(type.type);
|
let out = '{ ' + type.name + ': ' + describeType(type.type);
|
||||||
type = type.restType;
|
type = type.restType;
|
||||||
while (type.kind === TypeKind.Field) {
|
while (type.kind === TypeKind.Field) {
|
||||||
|
|
|
@ -12,7 +12,7 @@ export class Emitter {
|
||||||
public emit(node: Syntax): void {
|
public emit(node: Syntax): void {
|
||||||
|
|
||||||
switch (node.kind) {
|
switch (node.kind) {
|
||||||
|
|
||||||
case SyntaxKind.ModuleDeclaration:
|
case SyntaxKind.ModuleDeclaration:
|
||||||
this.writer.write(`mod ${node.name.text}`);
|
this.writer.write(`mod ${node.name.text}`);
|
||||||
if (node.elements === null) {
|
if (node.elements === null) {
|
||||||
|
@ -91,7 +91,7 @@ export class Emitter {
|
||||||
this.writer.write(node.name.text);
|
this.writer.write(node.name.text);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SyntaxKind.Param:
|
case SyntaxKind.PlainParam:
|
||||||
this.emit(node.pattern);
|
this.emit(node.pattern);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
|
|
||||||
|
import { warn } from "console";
|
||||||
import {
|
import {
|
||||||
ReferenceTypeExpression,
|
ReferenceTypeExpression,
|
||||||
SourceFile,
|
SourceFile,
|
||||||
|
@ -73,8 +74,12 @@ import {
|
||||||
Annotations,
|
Annotations,
|
||||||
ExprOperator,
|
ExprOperator,
|
||||||
Integer,
|
Integer,
|
||||||
|
InstanceParam,
|
||||||
|
PlainParam,
|
||||||
|
InstanceTypeExpression,
|
||||||
} from "./cst"
|
} from "./cst"
|
||||||
import { Stream } from "./util";
|
import { Stream } from "./util";
|
||||||
|
import { wrap } from "module";
|
||||||
|
|
||||||
export class ParseError extends Error {
|
export class ParseError extends Error {
|
||||||
|
|
||||||
|
@ -196,7 +201,24 @@ export class Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
public parsePrimitiveTypeExpression(): TypeExpression {
|
public parsePrimitiveTypeExpression(): TypeExpression {
|
||||||
const t0 = this.peekToken();
|
const t0 = this.peekToken(1);
|
||||||
|
const t1 = this.peekToken(2);
|
||||||
|
if (t0.kind === SyntaxKind.LBrace && t1.kind === SyntaxKind.LBrace) {
|
||||||
|
this.getToken();
|
||||||
|
this.getToken();
|
||||||
|
let name = null;
|
||||||
|
let colon = null;
|
||||||
|
const t2 = this.peekToken(2);
|
||||||
|
if (t2.kind === SyntaxKind.Colon) {
|
||||||
|
name = this.expectToken(SyntaxKind.Identifier);
|
||||||
|
this.getToken();
|
||||||
|
colon = t2;
|
||||||
|
}
|
||||||
|
const typeExpr = this.parseTypeExpression();
|
||||||
|
const t3 = this.expectToken(SyntaxKind.RBrace);
|
||||||
|
const t4 = this.expectToken(SyntaxKind.RBrace);
|
||||||
|
return new InstanceTypeExpression(t0, t1, name, colon, typeExpr, t3, t4);
|
||||||
|
}
|
||||||
switch (t0.kind) {
|
switch (t0.kind) {
|
||||||
case SyntaxKind.Identifier:
|
case SyntaxKind.Identifier:
|
||||||
return this.parseVarTypeExpression();
|
return this.parseVarTypeExpression();
|
||||||
|
@ -893,8 +915,18 @@ export class Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
public parseParam(): Param {
|
public parseParam(): Param {
|
||||||
|
const t0 = this.peekToken(1);
|
||||||
|
const t1 = this.peekToken(2);
|
||||||
|
if (t0.kind === SyntaxKind.LBrace && t1.kind === SyntaxKind.LBrace) {
|
||||||
|
this.getToken();
|
||||||
|
this.getToken();
|
||||||
|
const name = this.expectToken(SyntaxKind.Identifier);
|
||||||
|
const t3 = this.expectToken(SyntaxKind.RBrace);
|
||||||
|
const t4 = this.expectToken(SyntaxKind.RBrace);
|
||||||
|
return new InstanceParam(t0, t1, name, t3, t4);
|
||||||
|
}
|
||||||
const pattern = this.parsePattern();
|
const pattern = this.parsePattern();
|
||||||
return new Param(pattern);
|
return new PlainParam(pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
private lookaheadIsAssignment(): boolean {
|
private lookaheadIsAssignment(): boolean {
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
FunctionExpression,
|
FunctionExpression,
|
||||||
Backslash,
|
Backslash,
|
||||||
canHaveInstanceDeclaration,
|
canHaveInstanceDeclaration,
|
||||||
vistEachChild
|
visitEachChild
|
||||||
} from "../cst";
|
} from "../cst";
|
||||||
import { Pass } from "../program";
|
import { Pass } from "../program";
|
||||||
import { assert } from "../util";
|
import { assert } from "../util";
|
||||||
|
@ -50,7 +50,7 @@ export class TypeclassDictPassing implements Pass<SourceFile, SourceFile> {
|
||||||
|
|
||||||
private visit(node: Syntax): Syntax {
|
private visit(node: Syntax): Syntax {
|
||||||
if (canHaveInstanceDeclaration(node)) {
|
if (canHaveInstanceDeclaration(node)) {
|
||||||
return vistEachChild(node, this.visit.bind(this));
|
return visitEachChild(node, this.visit.bind(this));
|
||||||
}
|
}
|
||||||
if (node.kind === SyntaxKind.InstanceDeclaration) {
|
if (node.kind === SyntaxKind.InstanceDeclaration) {
|
||||||
const decl = new LetDeclaration(
|
const decl = new LetDeclaration(
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
|
|
||||||
|
import { warn } from "console";
|
||||||
import {
|
import {
|
||||||
SyntaxKind,
|
SyntaxKind,
|
||||||
Token,
|
Token,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
import { warn } from "console";
|
||||||
import { LetDeclaration, Pattern, SourceFile, Syntax, SyntaxKind } from "./cst";
|
import { LetDeclaration, Pattern, SourceFile, Syntax, SyntaxKind } from "./cst";
|
||||||
import { MultiMap } from "./util";
|
import { MultiMap, assertNever } from "./util";
|
||||||
|
|
||||||
export type NodeWithScope
|
export type NodeWithScope
|
||||||
= SourceFile
|
= SourceFile
|
||||||
|
@ -96,13 +97,23 @@ export class Scope {
|
||||||
case SyntaxKind.StructDeclaration:
|
case SyntaxKind.StructDeclaration:
|
||||||
{
|
{
|
||||||
this.add(node.name.text, node, Symkind.Type);
|
this.add(node.name.text, node, Symkind.Type);
|
||||||
this.add(node.name.text, node, Symkind.Var);
|
// TODO remove this?
|
||||||
|
// this.add(node.name.text, node, Symkind.Var);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SyntaxKind.LetDeclaration:
|
case SyntaxKind.LetDeclaration:
|
||||||
{
|
{
|
||||||
for (const param of node.params) {
|
for (const param of node.params) {
|
||||||
this.scanPattern(param.pattern, param);
|
switch (param.kind) {
|
||||||
|
case SyntaxKind.PlainParam:
|
||||||
|
this.scanPattern(param.pattern, param);
|
||||||
|
break;
|
||||||
|
case SyntaxKind.InstanceParam:
|
||||||
|
this.add(node.name.text, param, Symkind.Var);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assertNever(param);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (node === this.node) {
|
if (node === this.node) {
|
||||||
if (node.body !== null && node.body.kind === SyntaxKind.BlockBody) {
|
if (node.body !== null && node.body.kind === SyntaxKind.BlockBody) {
|
||||||
|
@ -116,7 +127,7 @@ export class Scope {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unexpected ${node.constructor.name}`);
|
assertNever(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ export enum TypeKind {
|
||||||
Nil,
|
Nil,
|
||||||
Absent,
|
Absent,
|
||||||
Present,
|
Present,
|
||||||
|
Tag,
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class TypeBase {
|
export abstract class TypeBase {
|
||||||
|
@ -457,6 +458,40 @@ export class TApp extends TypeBase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const labelTag = '____tag';
|
||||||
|
|
||||||
|
// export class TTag extends TypeBase {
|
||||||
|
|
||||||
|
// public readonly kind = TypeKind.Tag;
|
||||||
|
|
||||||
|
// public constructor(
|
||||||
|
// public name: string,
|
||||||
|
// public node: Syntax | null = null,
|
||||||
|
// ) {
|
||||||
|
// super();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public shallowClone(): Type {
|
||||||
|
// return new TTag(
|
||||||
|
// this.name,
|
||||||
|
// this.node,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public *getTypeVars(): Iterable<TVar> {
|
||||||
|
// // noop
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public substitute(sub: TVSub): Type {
|
||||||
|
// return this;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public [toStringTag]() {
|
||||||
|
// return this.name;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
export type Type
|
export type Type
|
||||||
= TCon
|
= TCon
|
||||||
| TArrow
|
| TArrow
|
||||||
|
@ -467,11 +502,43 @@ export type Type
|
||||||
| TNil
|
| TNil
|
||||||
| TPresent
|
| TPresent
|
||||||
| TAbsent
|
| TAbsent
|
||||||
|
// | TTag
|
||||||
|
|
||||||
export type TVar
|
export type TVar
|
||||||
= TRegularVar
|
= TRegularVar
|
||||||
| TRigidVar
|
| TRigidVar
|
||||||
|
|
||||||
|
|
||||||
|
export function getSignature(type: Type): Type[] {
|
||||||
|
const out = [];
|
||||||
|
let stack = [ type ];
|
||||||
|
for (;;) {
|
||||||
|
const child = stack.pop()!;
|
||||||
|
if (child.kind === TypeKind.App) {
|
||||||
|
stack.push(child.left);
|
||||||
|
stack.push(child.right);
|
||||||
|
} else {
|
||||||
|
out.push(child);
|
||||||
|
}
|
||||||
|
if (stack.length === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isSignature(type: Type): boolean {
|
||||||
|
return type.kind === TypeKind.Con
|
||||||
|
|| type.kind === TypeKind.App;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assignableTo(left: Type, right: Type): boolean {
|
||||||
|
if (left.kind === TypeKind.Con && right.kind == TypeKind.Con) {
|
||||||
|
return left.id === right.id;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
export function typesEqual(a: Type, b: Type): boolean {
|
export function typesEqual(a: Type, b: Type): boolean {
|
||||||
if (a.kind !== b.kind) {
|
if (a.kind !== b.kind) {
|
||||||
return false;
|
return false;
|
||||||
|
|
Loading…
Reference in a new issue