Multiple fixes related to the type-checker
- Add more tests - Make struct-declarations type-check - Split environment into type bindings and variable bindings - Fix kind inference adding the wrong element to the env
This commit is contained in:
parent
2ec8649456
commit
2f359107c4
5 changed files with 199 additions and 102 deletions
167
src/checker.ts
167
src/checker.ts
|
@ -22,7 +22,7 @@ import {
|
||||||
UnificationFailedDiagnostic,
|
UnificationFailedDiagnostic,
|
||||||
KindMismatchDiagnostic
|
KindMismatchDiagnostic
|
||||||
} from "./diagnostics";
|
} from "./diagnostics";
|
||||||
import { assert, isEmpty } from "./util";
|
import { assert, isEmpty, MultiMap } from "./util";
|
||||||
import { LabeledDirectedHashGraph, LabeledGraph, strongconnect } from "yagl"
|
import { LabeledDirectedHashGraph, LabeledGraph, strongconnect } from "yagl"
|
||||||
|
|
||||||
const MAX_TYPE_ERROR_COUNT = 5;
|
const MAX_TYPE_ERROR_COUNT = 5;
|
||||||
|
@ -340,10 +340,10 @@ export class TApp extends TypeBase {
|
||||||
super(node);
|
super(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static build(types: Type[]): Type {
|
public static build(types: Type[], node: Syntax | null = null): Type {
|
||||||
let result = types[0];
|
let result = types[0];
|
||||||
for (let i = 1; i < types.length; i++) {
|
for (let i = 1; i < types.length; i++) {
|
||||||
result = new TApp(result, types[i]);
|
result = new TApp(result, types[i], node);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -716,22 +716,23 @@ type Scheme
|
||||||
|
|
||||||
export class TypeEnv {
|
export class TypeEnv {
|
||||||
|
|
||||||
private mapping = new Map<string, Scheme>();
|
private mapping = new MultiMap<string, [Symkind, Scheme]>();
|
||||||
|
|
||||||
public constructor(public parent: TypeEnv | null = null) {
|
public constructor(public parent: TypeEnv | null = null) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public add(name: string, scheme: Scheme): void {
|
public add(name: string, scheme: Scheme, kind: Symkind): void {
|
||||||
this.mapping.set(name, scheme);
|
this.mapping.add(name, [kind, scheme]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public lookup(name: string): Scheme | null {
|
public lookup(name: string, expectedKind: Symkind): Scheme | null {
|
||||||
let curr: TypeEnv | null = this;
|
let curr: TypeEnv | null = this;
|
||||||
do {
|
do {
|
||||||
const scheme = curr.mapping.get(name);
|
for (const [kind, scheme] of curr.mapping.get(name)) {
|
||||||
if (scheme !== undefined) {
|
if (kind & expectedKind) {
|
||||||
return scheme;
|
return scheme;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
curr = curr.parent;
|
curr = curr.parent;
|
||||||
} while(curr !== null);
|
} while(curr !== null);
|
||||||
|
@ -842,9 +843,9 @@ export class Checker {
|
||||||
this.contexts.pop();
|
this.contexts.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private lookup(name: string): Scheme | null {
|
private lookup(name: string, kind: Symkind): Scheme | null {
|
||||||
const context = this.contexts[this.contexts.length-1];
|
const context = this.contexts[this.contexts.length-1];
|
||||||
return context.env.lookup(name);
|
return context.env.lookup(name, kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getReturnType(): Type {
|
private getReturnType(): Type {
|
||||||
|
@ -871,9 +872,9 @@ export class Checker {
|
||||||
return scheme.type.substitute(sub);
|
return scheme.type.substitute(sub);
|
||||||
}
|
}
|
||||||
|
|
||||||
private addBinding(name: string, scheme: Scheme): void {
|
private addBinding(name: string, scheme: Scheme, kind: Symkind): void {
|
||||||
const context = this.contexts[this.contexts.length-1];
|
const context = this.contexts[this.contexts.length-1];
|
||||||
context.env.add(name, scheme);
|
context.env.add(name, scheme, kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
private inferKindFromTypeExpression(node: TypeExpression, env: KindEnv): Kind {
|
private inferKindFromTypeExpression(node: TypeExpression, env: KindEnv): Kind {
|
||||||
|
@ -959,6 +960,10 @@ export class Checker {
|
||||||
}
|
}
|
||||||
|
|
||||||
case SyntaxKind.StructDeclaration:
|
case SyntaxKind.StructDeclaration:
|
||||||
|
{
|
||||||
|
env.setNamed(node.name.text, this.createKindVar());
|
||||||
|
break;
|
||||||
|
}
|
||||||
case SyntaxKind.EnumDeclaration:
|
case SyntaxKind.EnumDeclaration:
|
||||||
{
|
{
|
||||||
env.setNamed(node.name.text, this.createKindVar());
|
env.setNamed(node.name.text, this.createKindVar());
|
||||||
|
@ -1136,7 +1141,7 @@ export class Checker {
|
||||||
let type;
|
let type;
|
||||||
if (node.pattern.kind === SyntaxKind.WrappedOperator) {
|
if (node.pattern.kind === SyntaxKind.WrappedOperator) {
|
||||||
type = this.createTypeVar();
|
type = this.createTypeVar();
|
||||||
this.addBinding(node.pattern.operator.text, new Forall([], [], type));
|
this.addBinding(node.pattern.operator.text, new Forall([], [], type), Symkind.Var);
|
||||||
} else {
|
} else {
|
||||||
type = this.inferBindings(node.pattern, [], []);
|
type = this.inferBindings(node.pattern, [], []);
|
||||||
}
|
}
|
||||||
|
@ -1185,27 +1190,6 @@ export class Checker {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildVariantType(decl: EnumDeclarationElement, type: Type): Type {
|
|
||||||
const enumDecl = decl.parent as EnumDeclaration;
|
|
||||||
const kindArgs = [];
|
|
||||||
for (const _ of enumDecl.varExps) {
|
|
||||||
kindArgs.push(this.createTypeVar());
|
|
||||||
}
|
|
||||||
const variantTypes = [];
|
|
||||||
if (enumDecl.members !== null) {
|
|
||||||
for (const member of enumDecl.members) {
|
|
||||||
let variantType;
|
|
||||||
if (member === decl) {
|
|
||||||
variantType = type;
|
|
||||||
} else {
|
|
||||||
variantType = this.createTypeVar();
|
|
||||||
}
|
|
||||||
variantTypes.push(variantType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TApp.build([ ...kindArgs, new TVariant(enumDecl, [], []) ]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public inferExpression(node: Expression): Type {
|
public inferExpression(node: Expression): Type {
|
||||||
|
|
||||||
switch (node.kind) {
|
switch (node.kind) {
|
||||||
|
@ -1221,7 +1205,7 @@ export class Checker {
|
||||||
if (target !== null && target.kind === SyntaxKind.LetDeclaration && target.active) {
|
if (target !== null && target.kind === SyntaxKind.LetDeclaration && target.active) {
|
||||||
return target.type!;
|
return target.type!;
|
||||||
}
|
}
|
||||||
const scheme = this.lookup(node.name.name.text);
|
const scheme = this.lookup(node.name.name.text, Symkind.Var);
|
||||||
if (scheme === null) {
|
if (scheme === null) {
|
||||||
this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.name.text, node.name.name));
|
this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.name.text, node.name.name));
|
||||||
return this.createTypeVar();
|
return this.createTypeVar();
|
||||||
|
@ -1285,7 +1269,7 @@ export class Checker {
|
||||||
case SyntaxKind.NamedTupleExpression:
|
case SyntaxKind.NamedTupleExpression:
|
||||||
{
|
{
|
||||||
// TODO Only lookup constructors and skip other bindings
|
// TODO Only lookup constructors and skip other bindings
|
||||||
const scheme = this.lookup(node.name.text);
|
const scheme = this.lookup(node.name.text, Symkind.Var);
|
||||||
if (scheme === null) {
|
if (scheme === null) {
|
||||||
this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.text, node.name));
|
this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.text, node.name));
|
||||||
return this.createTypeVar();
|
return this.createTypeVar();
|
||||||
|
@ -1310,20 +1294,19 @@ export class Checker {
|
||||||
case SyntaxKind.StructExpression:
|
case SyntaxKind.StructExpression:
|
||||||
{
|
{
|
||||||
const scope = node.getScope();
|
const scope = node.getScope();
|
||||||
const decl = scope.lookup(node.name.text, Symkind.Constructor);
|
const decl = scope.lookup(node.name.text, Symkind.Type);
|
||||||
if (decl === null) {
|
if (decl === null) {
|
||||||
this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.text, node.name));
|
this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.text, node.name));
|
||||||
return this.createTypeVar();
|
return this.createTypeVar();
|
||||||
}
|
}
|
||||||
assert(decl.kind === SyntaxKind.StructDeclaration || decl.kind === SyntaxKind.EnumDeclarationStructElement);
|
assert(decl.kind === SyntaxKind.StructDeclaration || decl.kind === SyntaxKind.EnumDeclarationStructElement);
|
||||||
const scheme = decl.scheme;
|
const scheme = decl.scheme;
|
||||||
const sub = this.createSubstitution(scheme);
|
const declType = this.instantiate(scheme, node);
|
||||||
const declType = this.instantiate(scheme, node, sub);
|
const kindArgs = [];
|
||||||
const argTypes = [];
|
const varExps = decl.kind === SyntaxKind.StructDeclaration
|
||||||
for (const typeVar of decl.tvs) {
|
? decl.varExps : (decl.parent! as EnumDeclaration).varExps;
|
||||||
const newTypeVar = sub.get(typeVar)!;
|
for (const _ of varExps) {
|
||||||
assert(newTypeVar.kind === TypeKind.Var);
|
kindArgs.push(this.createTypeVar());
|
||||||
argTypes.push(newTypeVar);
|
|
||||||
}
|
}
|
||||||
const fields = new Map();
|
const fields = new Map();
|
||||||
for (const member of node.members) {
|
for (const member of node.members) {
|
||||||
|
@ -1335,7 +1318,7 @@ export class Checker {
|
||||||
}
|
}
|
||||||
case SyntaxKind.PunnedStructExpressionField:
|
case SyntaxKind.PunnedStructExpressionField:
|
||||||
{
|
{
|
||||||
const scheme = this.lookup(member.name.text);
|
const scheme = this.lookup(member.name.text, Symkind.Var);
|
||||||
let fieldType;
|
let fieldType;
|
||||||
if (scheme === null) {
|
if (scheme === null) {
|
||||||
this.diagnostics.add(new BindingNotFoudDiagnostic(member.name.text, member.name));
|
this.diagnostics.add(new BindingNotFoudDiagnostic(member.name.text, member.name));
|
||||||
|
@ -1350,9 +1333,10 @@ export class Checker {
|
||||||
throw new Error(`Unexpected ${member}`);
|
throw new Error(`Unexpected ${member}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let type: Type = new TRecord(decl, argTypes, fields, node);
|
let type: Type = TApp.build([ ...kindArgs, new TRecord(decl, [], fields, node) ]);
|
||||||
if (decl.kind === SyntaxKind.EnumDeclarationStructElement) {
|
if (decl.kind === SyntaxKind.EnumDeclarationStructElement) {
|
||||||
type = this.buildVariantType(decl, type);
|
// TODO
|
||||||
|
// type = this.buildVariantType(decl, type);
|
||||||
}
|
}
|
||||||
this.addConstraint(
|
this.addConstraint(
|
||||||
new CEqual(
|
new CEqual(
|
||||||
|
@ -1366,7 +1350,7 @@ export class Checker {
|
||||||
|
|
||||||
case SyntaxKind.InfixExpression:
|
case SyntaxKind.InfixExpression:
|
||||||
{
|
{
|
||||||
const scheme = this.lookup(node.operator.text);
|
const scheme = this.lookup(node.operator.text, Symkind.Var);
|
||||||
if (scheme === null) {
|
if (scheme === null) {
|
||||||
this.diagnostics.add(new BindingNotFoudDiagnostic(node.operator.text, node.operator));
|
this.diagnostics.add(new BindingNotFoudDiagnostic(node.operator.text, node.operator));
|
||||||
return this.createTypeVar();
|
return this.createTypeVar();
|
||||||
|
@ -1398,7 +1382,7 @@ export class Checker {
|
||||||
|
|
||||||
case SyntaxKind.ReferenceTypeExpression:
|
case SyntaxKind.ReferenceTypeExpression:
|
||||||
{
|
{
|
||||||
const scheme = this.lookup(node.name.text);
|
const scheme = this.lookup(node.name.text, Symkind.Type);
|
||||||
if (scheme === null) {
|
if (scheme === null) {
|
||||||
this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.text, node.name));
|
this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.text, node.name));
|
||||||
return this.createTypeVar();
|
return this.createTypeVar();
|
||||||
|
@ -1414,13 +1398,13 @@ export class Checker {
|
||||||
|
|
||||||
case SyntaxKind.VarTypeExpression:
|
case SyntaxKind.VarTypeExpression:
|
||||||
{
|
{
|
||||||
const scheme = this.lookup(node.name.text);
|
const scheme = this.lookup(node.name.text, Symkind.Type);
|
||||||
if (scheme === null) {
|
if (scheme === null) {
|
||||||
if (!introduceTypeVars) {
|
if (!introduceTypeVars) {
|
||||||
this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.text, node.name));
|
this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.text, node.name));
|
||||||
}
|
}
|
||||||
const type = this.createTypeVar();
|
const type = this.createTypeVar();
|
||||||
this.addBinding(node.name.text, new Forall([], [], type));
|
this.addBinding(node.name.text, new Forall([], [], type), Symkind.Type);
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
assert(isEmpty(scheme.typeVars));
|
assert(isEmpty(scheme.typeVars));
|
||||||
|
@ -1460,13 +1444,13 @@ export class Checker {
|
||||||
case SyntaxKind.BindPattern:
|
case SyntaxKind.BindPattern:
|
||||||
{
|
{
|
||||||
const type = this.createTypeVar();
|
const type = this.createTypeVar();
|
||||||
this.addBinding(pattern.name.text, new Forall(typeVars, constraints, type));
|
this.addBinding(pattern.name.text, new Forall(typeVars, constraints, type), Symkind.Var);
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SyntaxKind.StructPattern:
|
case SyntaxKind.StructPattern:
|
||||||
{
|
{
|
||||||
const scheme = this.lookup(pattern.name.text);
|
const scheme = this.lookup(pattern.name.text, Symkind.Type);
|
||||||
let recordType;
|
let recordType;
|
||||||
if (scheme === null) {
|
if (scheme === null) {
|
||||||
this.diagnostics.add(new BindingNotFoudDiagnostic(pattern.name.text, pattern.name));
|
this.diagnostics.add(new BindingNotFoudDiagnostic(pattern.name.text, pattern.name));
|
||||||
|
@ -1492,7 +1476,7 @@ export class Checker {
|
||||||
case SyntaxKind.PunnedStructPatternField:
|
case SyntaxKind.PunnedStructPatternField:
|
||||||
{
|
{
|
||||||
const fieldType = this.createTypeVar();
|
const fieldType = this.createTypeVar();
|
||||||
this.addBinding(member.name.text, new Forall([], [], fieldType));
|
this.addBinding(member.name.text, new Forall([], [], fieldType), Symkind.Var);
|
||||||
this.addConstraint(
|
this.addConstraint(
|
||||||
new CEqual(
|
new CEqual(
|
||||||
new TLabeled(member.name.text, fieldType),
|
new TLabeled(member.name.text, fieldType),
|
||||||
|
@ -1756,7 +1740,7 @@ export class Checker {
|
||||||
const kindArgs = [];
|
const kindArgs = [];
|
||||||
for (const varExpr of node.varExps) {
|
for (const varExpr of node.varExps) {
|
||||||
const kindArg = this.createTypeVar();
|
const kindArg = this.createTypeVar();
|
||||||
env.add(varExpr.text, new Forall([], [], kindArg));
|
env.add(varExpr.text, new Forall([], [], kindArg), Symkind.Type);
|
||||||
kindArgs.push(kindArg);
|
kindArgs.push(kindArg);
|
||||||
}
|
}
|
||||||
let elementTypes: Type[] = [];
|
let elementTypes: Type[] = [];
|
||||||
|
@ -1769,7 +1753,7 @@ export class Checker {
|
||||||
{
|
{
|
||||||
const argTypes = member.elements.map(el => this.inferTypeExpression(el));
|
const argTypes = member.elements.map(el => this.inferTypeExpression(el));
|
||||||
elementType = new TArrow(argTypes, TApp.build([ ...kindArgs, type ]));
|
elementType = new TArrow(argTypes, TApp.build([ ...kindArgs, type ]));
|
||||||
parentEnv.add(member.name.text, new Forall(typeVars, constraints, elementType));
|
parentEnv.add(member.name.text, new Forall(typeVars, constraints, elementType), Symkind.Var);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// TODO
|
// TODO
|
||||||
|
@ -1780,7 +1764,7 @@ export class Checker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.popContext(context);
|
this.popContext(context);
|
||||||
parentEnv.add(node.name.text, new Forall(typeVars, constraints, type));
|
parentEnv.add(node.name.text, new Forall(typeVars, constraints, type), Symkind.Type);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1796,13 +1780,13 @@ export class Checker {
|
||||||
returnType: null,
|
returnType: null,
|
||||||
};
|
};
|
||||||
this.pushContext(context);
|
this.pushContext(context);
|
||||||
for (const varExpr of node.typeVars) {
|
for (const varExpr of node.varExps) {
|
||||||
env.add(varExpr.text, new Forall([], [], this.createTypeVar()));
|
env.add(varExpr.text, new Forall([], [], this.createTypeVar()), Symkind.Type);
|
||||||
}
|
}
|
||||||
const type = this.inferTypeExpression(node.typeExpression);
|
const type = this.inferTypeExpression(node.typeExpression);
|
||||||
this.popContext(context);
|
this.popContext(context);
|
||||||
const scheme = new Forall(typeVars, constraints, type);
|
const scheme = new Forall(typeVars, constraints, type);
|
||||||
parentEnv.add(node.name.text, scheme);
|
parentEnv.add(node.name.text, scheme, Symkind.Type);
|
||||||
node.scheme = scheme;
|
node.scheme = scheme;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1819,11 +1803,11 @@ export class Checker {
|
||||||
returnType: null,
|
returnType: null,
|
||||||
};
|
};
|
||||||
this.pushContext(context);
|
this.pushContext(context);
|
||||||
const argTypes = [];
|
const kindArgs = [];
|
||||||
for (const varExpr of node.typeVars) {
|
for (const varExpr of node.varExps) {
|
||||||
const type = this.createTypeVar();
|
const kindArg = this.createTypeVar();
|
||||||
env.add(varExpr.text, new Forall([], [], type));
|
env.add(varExpr.text, new Forall([], [], kindArg), Symkind.Type);
|
||||||
argTypes.push(type);
|
kindArgs.push(kindArg);
|
||||||
}
|
}
|
||||||
const fields = new Map<string, Type>();
|
const fields = new Map<string, Type>();
|
||||||
if (node.members !== null) {
|
if (node.members !== null) {
|
||||||
|
@ -1832,11 +1816,9 @@ export class Checker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.popContext(context);
|
this.popContext(context);
|
||||||
const type = new TRecord(node, argTypes, fields, node);
|
const type = new TRecord(node, [], fields, node);
|
||||||
const scheme = new Forall(typeVars, constraints, type);
|
parentEnv.add(node.name.text, new Forall(typeVars, constraints, type), Symkind.Type);
|
||||||
parentEnv.add(node.name.text, scheme);
|
node.scheme = new Forall(typeVars, constraints, TApp.build([ ...kindArgs, type ]));
|
||||||
node.tvs = argTypes;
|
|
||||||
node.scheme = scheme; //new Forall(typeVars, constraints, new TApp(type, argTypes));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1867,17 +1849,17 @@ export class Checker {
|
||||||
const b = this.createTypeVar();
|
const b = this.createTypeVar();
|
||||||
const f = this.createTypeVar();
|
const f = this.createTypeVar();
|
||||||
|
|
||||||
env.add('$', new Forall([ f, a ], [], new TArrow([ new TArrow([ a ], b), a ], b)));
|
env.add('$', new Forall([ f, a ], [], new TArrow([ new TArrow([ a ], b), a ], b)), Symkind.Var);
|
||||||
env.add('String', new Forall([], [], this.stringType));
|
env.add('String', new Forall([], [], this.stringType), Symkind.Type);
|
||||||
env.add('Int', new Forall([], [], this.intType));
|
env.add('Int', new Forall([], [], this.intType), Symkind.Type);
|
||||||
env.add('True', new Forall([], [], this.boolType));
|
env.add('True', new Forall([], [], this.boolType), Symkind.Var);
|
||||||
env.add('False', new Forall([], [], this.boolType));
|
env.add('False', new Forall([], [], this.boolType), Symkind.Var);
|
||||||
env.add('+', new Forall([], [], new TArrow([ this.intType, this.intType ], this.intType)));
|
env.add('+', new Forall([], [], new TArrow([ this.intType, this.intType ], this.intType)), Symkind.Var);
|
||||||
env.add('-', new Forall([], [], new TArrow([ this.intType, this.intType ], this.intType)));
|
env.add('-', new Forall([], [], new TArrow([ this.intType, this.intType ], this.intType)), Symkind.Var);
|
||||||
env.add('*', new Forall([], [], new TArrow([ this.intType, this.intType ], this.intType)));
|
env.add('*', new Forall([], [], new TArrow([ this.intType, this.intType ], this.intType)), Symkind.Var);
|
||||||
env.add('/', new Forall([], [], new TArrow([ this.intType, this.intType ], this.intType)));
|
env.add('/', new Forall([], [], new TArrow([ this.intType, this.intType ], this.intType)), Symkind.Var);
|
||||||
env.add('==', new Forall([ a ], [], new TArrow([ a, a ], this.boolType)));
|
env.add('==', new Forall([ a ], [], new TArrow([ a, a ], this.boolType)), Symkind.Var);
|
||||||
env.add('not', new Forall([], [], new TArrow([ this.boolType ], this.boolType)));
|
env.add('not', new Forall([], [], new TArrow([ this.boolType ], this.boolType)), Symkind.Var);
|
||||||
|
|
||||||
const graph = new LabeledDirectedHashGraph<NodeWithBindings, boolean>();
|
const graph = new LabeledDirectedHashGraph<NodeWithBindings, boolean>();
|
||||||
this.addReferencesToGraph(graph, node, node);
|
this.addReferencesToGraph(graph, node, node);
|
||||||
|
@ -1958,7 +1940,7 @@ export class Checker {
|
||||||
let ty2;
|
let ty2;
|
||||||
if (node.pattern.kind === SyntaxKind.WrappedOperator) {
|
if (node.pattern.kind === SyntaxKind.WrappedOperator) {
|
||||||
ty2 = this.createTypeVar();
|
ty2 = this.createTypeVar();
|
||||||
this.addBinding(node.pattern.operator.text, new Forall([], [], ty2));
|
this.addBinding(node.pattern.operator.text, new Forall([], [], ty2), Symkind.Var);
|
||||||
} else {
|
} else {
|
||||||
ty2 = this.inferBindings(node.pattern, typeVars, constraints);
|
ty2 = this.inferBindings(node.pattern, typeVars, constraints);
|
||||||
}
|
}
|
||||||
|
@ -1974,7 +1956,7 @@ export class Checker {
|
||||||
&& isFunctionDeclarationLike(element)
|
&& isFunctionDeclarationLike(element)
|
||||||
&& graph.hasEdge(node, element, false)) {
|
&& graph.hasEdge(node, element, false)) {
|
||||||
assert(element.pattern.kind === SyntaxKind.BindPattern);
|
assert(element.pattern.kind === SyntaxKind.BindPattern);
|
||||||
const scheme = this.lookup(element.pattern.name.text);
|
const scheme = this.lookup(element.pattern.name.text, Symkind.Var);
|
||||||
assert(scheme !== null);
|
assert(scheme !== null);
|
||||||
this.instantiate(scheme, null);
|
this.instantiate(scheme, null);
|
||||||
} else {
|
} else {
|
||||||
|
@ -2210,14 +2192,23 @@ export class Checker {
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (left.kind === TypeKind.Record && right.kind === TypeKind.Labeled) {
|
let leftElement: Type = left;
|
||||||
|
while (leftElement.kind === TypeKind.App) {
|
||||||
|
leftElement = leftElement.right;
|
||||||
|
}
|
||||||
|
let rightElement: Type = right;
|
||||||
|
while (rightElement.kind === TypeKind.App) {
|
||||||
|
rightElement = rightElement.right;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leftElement.kind === TypeKind.Record && right.kind === TypeKind.Labeled) {
|
||||||
let success = true;
|
let success = true;
|
||||||
if (right.fields === undefined) {
|
if (right.fields === undefined) {
|
||||||
right.fields = new Map([ [ right.name, right.type ] ]);
|
right.fields = new Map([ [ right.name, right.type ] ]);
|
||||||
}
|
}
|
||||||
for (const [fieldName, fieldType] of right.fields) {
|
for (const [fieldName, fieldType] of right.fields) {
|
||||||
if (left.fields.has(fieldName)) {
|
if (leftElement.fields.has(fieldName)) {
|
||||||
if (!unify(fieldType, left.fields.get(fieldName)!)) {
|
if (!unify(fieldType, leftElement.fields.get(fieldName)!)) {
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
24
src/cst.ts
24
src/cst.ts
|
@ -1,4 +1,4 @@
|
||||||
import { JSONObject, JSONValue } from "./util";
|
import { JSONObject, JSONValue, MultiMap } from "./util";
|
||||||
import type { InferContext, Type, TypeEnv } from "./checker"
|
import type { InferContext, Type, TypeEnv } from "./checker"
|
||||||
|
|
||||||
export type TextSpan = [number, number];
|
export type TextSpan = [number, number];
|
||||||
|
@ -210,13 +210,12 @@ function isNodeWithScope(node: Syntax): node is NodeWithScope {
|
||||||
export const enum Symkind {
|
export const enum Symkind {
|
||||||
Var = 1,
|
Var = 1,
|
||||||
Type = 2,
|
Type = 2,
|
||||||
Constructor = 4,
|
Any = Var | Type
|
||||||
Any = Var | Type | Constructor
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Scope {
|
export class Scope {
|
||||||
|
|
||||||
private mapping = new Map<string, [Symkind, Syntax]>();
|
private mapping = new MultiMap<string, [Symkind, Syntax]>();
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
public node: NodeWithScope,
|
public node: NodeWithScope,
|
||||||
|
@ -236,7 +235,7 @@ export class Scope {
|
||||||
}
|
}
|
||||||
|
|
||||||
private add(name: string, node: Syntax, kind: Symkind): void {
|
private add(name: string, node: Syntax, kind: Symkind): void {
|
||||||
this.mapping.set(name, [kind, node]);
|
this.mapping.add(name, [kind, node]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private scan(node: Syntax): void {
|
private scan(node: Syntax): void {
|
||||||
|
@ -262,13 +261,14 @@ export class Scope {
|
||||||
this.add(node.name.text, node, Symkind.Type);
|
this.add(node.name.text, node, Symkind.Type);
|
||||||
if (node.members !== null) {
|
if (node.members !== null) {
|
||||||
for (const member of node.members) {
|
for (const member of node.members) {
|
||||||
this.add(member.name.text, member, Symkind.Constructor);
|
this.add(member.name.text, member, Symkind.Var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case SyntaxKind.StructDeclaration:
|
case SyntaxKind.StructDeclaration:
|
||||||
{
|
{
|
||||||
this.add(node.name.text, node, Symkind.Constructor | Symkind.Type);
|
this.add(node.name.text, node, Symkind.Type);
|
||||||
|
this.add(node.name.text, node, Symkind.Var);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SyntaxKind.LetDeclaration:
|
case SyntaxKind.LetDeclaration:
|
||||||
|
@ -326,12 +326,10 @@ export class Scope {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public lookup(name: string, expectedKind = Symkind.Any): Syntax | null {
|
public lookup(name: string, expectedKind: Symkind = Symkind.Any): Syntax | null {
|
||||||
let curr: Scope | null = this;
|
let curr: Scope | null = this;
|
||||||
do {
|
do {
|
||||||
const match = curr.mapping.get(name);
|
for (const [kind, decl] of curr.mapping.get(name)) {
|
||||||
if (match !== undefined) {
|
|
||||||
const [kind, decl] = match;
|
|
||||||
if (kind & expectedKind) {
|
if (kind & expectedKind) {
|
||||||
return decl;
|
return decl;
|
||||||
}
|
}
|
||||||
|
@ -1818,7 +1816,7 @@ export class StructDeclaration extends SyntaxBase {
|
||||||
public pubKeyword: PubKeyword | null,
|
public pubKeyword: PubKeyword | null,
|
||||||
public structKeyword: StructKeyword,
|
public structKeyword: StructKeyword,
|
||||||
public name: IdentifierAlt,
|
public name: IdentifierAlt,
|
||||||
public typeVars: Identifier[],
|
public varExps: Identifier[],
|
||||||
public members: StructDeclarationField[] | null,
|
public members: StructDeclarationField[] | null,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
@ -1946,7 +1944,7 @@ export class TypeDeclaration extends SyntaxBase {
|
||||||
public pubKeyword: PubKeyword | null,
|
public pubKeyword: PubKeyword | null,
|
||||||
public typeKeyword: TypeKeyword,
|
public typeKeyword: TypeKeyword,
|
||||||
public name: IdentifierAlt,
|
public name: IdentifierAlt,
|
||||||
public typeVars: Identifier[],
|
public varExps: Identifier[],
|
||||||
public equals: Equals,
|
public equals: Equals,
|
||||||
public typeExpression: TypeExpression
|
public typeExpression: TypeExpression
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -306,7 +306,7 @@ export class FieldMissingDiagnostic {
|
||||||
public readonly level = Level.Error;
|
public readonly level = Level.Error;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
public recordType: TRecord,
|
public recordType: Type,
|
||||||
public fieldName: string,
|
public fieldName: string,
|
||||||
public node: Syntax | null,
|
public node: Syntax | null,
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -112,3 +112,43 @@ let fac n.
|
||||||
else.
|
else.
|
||||||
return n * fac (n-"foo")
|
return n * fac (n-"foo")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Enum-declarations are correctly typed
|
||||||
|
|
||||||
|
```
|
||||||
|
enum Maybe a.
|
||||||
|
Just a
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
let right_1 : Maybe Int = Just 1
|
||||||
|
let right_2 : Maybe String = Just "foo"
|
||||||
|
let wrong : Maybe Int = Just "foo"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Kind inference works
|
||||||
|
|
||||||
|
```
|
||||||
|
enum Maybe a.
|
||||||
|
Just a
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
let foo_1 : Maybe
|
||||||
|
let foo_2 : Maybe Int
|
||||||
|
let foo_3 : Maybe Int Int
|
||||||
|
```
|
||||||
|
|
||||||
|
## Can indirectly apply a polymorphic datatype to some type
|
||||||
|
|
||||||
|
```
|
||||||
|
enum Maybe a.
|
||||||
|
Just a
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
enum App a b.
|
||||||
|
MkApp (a b)
|
||||||
|
|
||||||
|
enum Foo.
|
||||||
|
MkFoo (App Maybe Int)
|
||||||
|
|
||||||
|
let f : Foo = MkFoo (MkApp (Just 1))
|
||||||
|
```
|
||||||
|
|
68
src/util.ts
68
src/util.ts
|
@ -118,3 +118,71 @@ export abstract class BufferedStream<T> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class MultiMap<K, V> {
|
||||||
|
|
||||||
|
private mapping = new Map<K, V[]>();
|
||||||
|
|
||||||
|
public get(key: K): V[] {
|
||||||
|
return this.mapping.get(key) ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public add(key: K, value: V): void {
|
||||||
|
let elements = this.mapping.get(key);
|
||||||
|
if (elements === undefined) {
|
||||||
|
elements = [];
|
||||||
|
this.mapping.set(key, elements);
|
||||||
|
}
|
||||||
|
elements.push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public has(key: K, value?: V): boolean {
|
||||||
|
if (value === undefined) {
|
||||||
|
return this.mapping.has(key);
|
||||||
|
}
|
||||||
|
const elements = this.mapping.get(key);
|
||||||
|
if (elements === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return elements.indexOf(value) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public keys(): Iterable<K> {
|
||||||
|
return this.mapping.keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
public *values(): Iterable<V> {
|
||||||
|
for (const elements of this.mapping.values()) {
|
||||||
|
yield* elements;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public *[Symbol.iterator](): Iterable<[K, V]> {
|
||||||
|
for (const [key, elements] of this.mapping) {
|
||||||
|
for (const value of elements) {
|
||||||
|
yield [key, value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public delete(key: K, value?: V): number {
|
||||||
|
const elements = this.mapping.get(key);
|
||||||
|
if (elements === undefined) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (value === undefined) {
|
||||||
|
this.mapping.delete(key);
|
||||||
|
return elements.length;
|
||||||
|
}
|
||||||
|
const i = elements.indexOf(value);
|
||||||
|
if (i !== -1) {
|
||||||
|
elements.splice(i, 1);
|
||||||
|
if (elements.length === 0) {
|
||||||
|
this.mapping.delete(key);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue