Make type-checker catch all errors and update YAGL

This commit is contained in:
Sam Vervaeck 2022-09-06 15:13:07 +02:00
parent 469b0cc756
commit cf5978c86c
4 changed files with 131 additions and 71 deletions

28
package-lock.json generated
View file

@ -11,21 +11,21 @@
"dependencies": { "dependencies": {
"source-map-support": "^0.5.21", "source-map-support": "^0.5.21",
"tslib": "^2.4.0", "tslib": "^2.4.0",
"yagl": "^0.5.0", "yagl": "^0.5.1",
"yargs": "^17.5.1" "yargs": "^17.5.1"
}, },
"bin": { "bin": {
"bolt": "lib/bin/bolt.js" "bolt": "lib/bin/bolt.js"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^18.7.14", "@types/node": "^18.7.15",
"@types/yargs": "^17.0.12" "@types/yargs": "^17.0.12"
} }
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "18.7.14", "version": "18.7.15",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.14.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.15.tgz",
"integrity": "sha512-6bbDaETVi8oyIARulOE9qF1/Qdi/23z6emrUh0fNJRUmjznqrixD4MpGDdgOFk5Xb0m2H6Xu42JGdvAxaJR/wA==", "integrity": "sha512-XnjpaI8Bgc3eBag2Aw4t2Uj/49lLBSStHWfqKvIuXD7FIrZyMLWp8KuAFHAqxMZYTF9l08N1ctUn9YNybZJVmQ==",
"dev": true "dev": true
}, },
"node_modules/@types/yargs": { "node_modules/@types/yargs": {
@ -204,9 +204,9 @@
} }
}, },
"node_modules/yagl": { "node_modules/yagl": {
"version": "0.5.0", "version": "0.5.1",
"resolved": "https://registry.npmjs.org/yagl/-/yagl-0.5.0.tgz", "resolved": "https://registry.npmjs.org/yagl/-/yagl-0.5.1.tgz",
"integrity": "sha512-nM6dkvVEgPkDdtNUmGdgvTyAwNnp88g8RvduiNewECU9mKDmazMFqSxkfK8gsvW/Zhzno7hzRHVlR6atFZk44g==" "integrity": "sha512-DfJygWCefAq5eEOmwvVkiMFBUEQJs9aijGdhaYGSdj1TM2OqSbe/Vp37e/nMGXsgmWiryZapKMOtpYx3ECUrJQ=="
}, },
"node_modules/yargs": { "node_modules/yargs": {
"version": "17.5.1", "version": "17.5.1",
@ -236,9 +236,9 @@
}, },
"dependencies": { "dependencies": {
"@types/node": { "@types/node": {
"version": "18.7.14", "version": "18.7.15",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.14.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.15.tgz",
"integrity": "sha512-6bbDaETVi8oyIARulOE9qF1/Qdi/23z6emrUh0fNJRUmjznqrixD4MpGDdgOFk5Xb0m2H6Xu42JGdvAxaJR/wA==", "integrity": "sha512-XnjpaI8Bgc3eBag2Aw4t2Uj/49lLBSStHWfqKvIuXD7FIrZyMLWp8KuAFHAqxMZYTF9l08N1ctUn9YNybZJVmQ==",
"dev": true "dev": true
}, },
"@types/yargs": { "@types/yargs": {
@ -375,9 +375,9 @@
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
}, },
"yagl": { "yagl": {
"version": "0.5.0", "version": "0.5.1",
"resolved": "https://registry.npmjs.org/yagl/-/yagl-0.5.0.tgz", "resolved": "https://registry.npmjs.org/yagl/-/yagl-0.5.1.tgz",
"integrity": "sha512-nM6dkvVEgPkDdtNUmGdgvTyAwNnp88g8RvduiNewECU9mKDmazMFqSxkfK8gsvW/Zhzno7hzRHVlR6atFZk44g==" "integrity": "sha512-DfJygWCefAq5eEOmwvVkiMFBUEQJs9aijGdhaYGSdj1TM2OqSbe/Vp37e/nMGXsgmWiryZapKMOtpYx3ECUrJQ=="
}, },
"yargs": { "yargs": {
"version": "17.5.1", "version": "17.5.1",

View file

@ -24,11 +24,11 @@
"dependencies": { "dependencies": {
"source-map-support": "^0.5.21", "source-map-support": "^0.5.21",
"tslib": "^2.4.0", "tslib": "^2.4.0",
"yagl": "^0.5.0", "yagl": "^0.5.1",
"yargs": "^17.5.1" "yargs": "^17.5.1"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^18.7.14", "@types/node": "^18.7.15",
"@types/yargs": "^17.0.12" "@types/yargs": "^17.0.12"
} }
} }

View file

@ -2,15 +2,22 @@ import {
Expression, Expression,
LetDeclaration, LetDeclaration,
Pattern, Pattern,
ReferenceExpression,
SourceFile, SourceFile,
Syntax, Syntax,
SyntaxKind, SyntaxKind,
TypeExpression TypeExpression
} from "./cst"; } from "./cst";
import { ArityMismatchDiagnostic, BindingNotFoudDiagnostic, describeType, Diagnostics, UnificationFailedDiagnostic } from "./diagnostics"; import { ArityMismatchDiagnostic, BindingNotFoudDiagnostic, describeType, Diagnostics, UnificationFailedDiagnostic } from "./diagnostics";
import { assert } from "./util"; import { assert, isEmpty } from "./util";
import { DirectedHashGraph, Graph, strongconnect } from "yagl" import { LabeledDirectedHashGraph, LabeledGraph, strongconnect } from "yagl"
// FIXME Duplicate definitions are not checked
const MAX_TYPE_ERROR_COUNT = 5;
type NodeWithBindings = SourceFile | LetDeclaration;
type ReferenceGraph = LabeledGraph<NodeWithBindings, boolean>;
export enum TypeKind { export enum TypeKind {
Arrow, Arrow,
@ -386,7 +393,6 @@ export class Checker {
private boolType = new TCon(this.nextConTypeId++, [], 'Bool'); private boolType = new TCon(this.nextConTypeId++, [], 'Bool');
private contexts: InferContext[] = []; private contexts: InferContext[] = [];
private constraints: Constraint[] = [];
private solution = new TVSub(); private solution = new TVSub();
@ -416,10 +422,7 @@ export class Checker {
} }
private addConstraint(constraint: Constraint): void { private addConstraint(constraint: Constraint): void {
this.constraints.push(constraint); this.contexts[this.contexts.length-1].constraints.push(constraint);
if (this.contexts.length > 0) {
this.contexts[this.contexts.length-1].constraints.push(constraint);
}
} }
private pushContext(context: InferContext) { private pushContext(context: InferContext) {
@ -461,31 +464,6 @@ export class Checker {
context.env.add(name, scheme); context.env.add(name, scheme);
} }
private forwardDeclare(node: Syntax): void {
switch (node.kind) {
case SyntaxKind.SourceFile:
{
for (const element of node.elements) {
this.forwardDeclare(element);
}
break;
}
case SyntaxKind.ExpressionStatement:
case SyntaxKind.ReturnStatement:
{
// TODO This should be updated if block-scoped expressions are allowed.
break;
}
case SyntaxKind.LetDeclaration:
break;
}
}
public infer(node: Syntax): void { public infer(node: Syntax): void {
switch (node.kind) { switch (node.kind) {
@ -685,7 +663,7 @@ export class Checker {
} }
private addReferences(graph: Graph<LetDeclaration>, node: Syntax, source: LetDeclaration | null) { private addReferencesToGraph(graph: ReferenceGraph, node: Syntax, source: LetDeclaration | SourceFile) {
switch (node.kind) { switch (node.kind) {
@ -695,7 +673,7 @@ export class Checker {
case SyntaxKind.SourceFile: case SyntaxKind.SourceFile:
{ {
for (const element of node.elements) { for (const element of node.elements) {
this.addReferences(graph, element, source); this.addReferencesToGraph(graph, element, source);
} }
break; break;
} }
@ -704,38 +682,40 @@ export class Checker {
{ {
assert(node.name.modulePath.length === 0); assert(node.name.modulePath.length === 0);
const target = node.getScope().lookup(node.name.name.text); const target = node.getScope().lookup(node.name.name.text);
if (source !== null && target !== null && target.kind === SyntaxKind.LetDeclaration) { if (target === null || target.kind === SyntaxKind.Param) {
graph.addEdge(source, target); break;
} }
assert(target.kind === SyntaxKind.LetDeclaration || target.kind === SyntaxKind.SourceFile);
graph.addEdge(source, target, true);
break; break;
} }
case SyntaxKind.NamedTupleExpression: case SyntaxKind.NamedTupleExpression:
{ {
for (const arg of node.elements) { for (const arg of node.elements) {
this.addReferences(graph, arg, source); this.addReferencesToGraph(graph, arg, source);
} }
break; break;
} }
case SyntaxKind.NestedExpression: case SyntaxKind.NestedExpression:
{ {
this.addReferences(graph, node.expression, source); this.addReferencesToGraph(graph, node.expression, source);
break; break;
} }
case SyntaxKind.InfixExpression: case SyntaxKind.InfixExpression:
{ {
this.addReferences(graph, node.left, source); this.addReferencesToGraph(graph, node.left, source);
this.addReferences(graph, node.right, source); this.addReferencesToGraph(graph, node.right, source);
break; break;
} }
case SyntaxKind.CallExpression: case SyntaxKind.CallExpression:
{ {
this.addReferences(graph, node.func, source); this.addReferencesToGraph(graph, node.func, source);
for (const arg of node.args) { for (const arg of node.args) {
this.addReferences(graph, arg, source); this.addReferencesToGraph(graph, arg, source);
} }
break; break;
} }
@ -744,10 +724,10 @@ export class Checker {
{ {
for (const cs of node.cases) { for (const cs of node.cases) {
if (cs.test !== null) { if (cs.test !== null) {
this.addReferences(graph, cs.test, source); this.addReferencesToGraph(graph, cs.test, source);
} }
for (const element of cs.elements) { for (const element of cs.elements) {
this.addReferences(graph, element, source); this.addReferencesToGraph(graph, element, source);
} }
} }
break; break;
@ -755,13 +735,13 @@ export class Checker {
case SyntaxKind.ExpressionStatement: case SyntaxKind.ExpressionStatement:
{ {
this.addReferences(graph, node.expression, source); this.addReferencesToGraph(graph, node.expression, source);
break; break;
} }
case SyntaxKind.ReturnStatement: case SyntaxKind.ReturnStatement:
{ {
if (node.expression !== null) { if (node.expression !== null) {
this.addReferences(graph, node.expression, source); this.addReferencesToGraph(graph, node.expression, source);
} }
break; break;
} }
@ -772,13 +752,13 @@ export class Checker {
switch (node.body.kind) { switch (node.body.kind) {
case SyntaxKind.ExprBody: case SyntaxKind.ExprBody:
{ {
this.addReferences(graph, node.body.expression, node); this.addReferencesToGraph(graph, node.body.expression, node);
break; break;
} }
case SyntaxKind.BlockBody: case SyntaxKind.BlockBody:
{ {
for (const element of node.body.elements) { for (const element of node.body.elements) {
this.addReferences(graph, element, node); this.addReferencesToGraph(graph, element, node);
} }
break; break;
} }
@ -794,6 +774,44 @@ export class Checker {
} }
private completeReferenceGraph(graph: ReferenceGraph, node: Syntax): void {
switch (node.kind) {
case SyntaxKind.SourceFile:
{
for (const element of node.elements) {
this.completeReferenceGraph(graph, element);
}
break;
}
case SyntaxKind.LetDeclaration:
{
if (isEmpty(graph.getSourceVertices(node))) {
const source = node.parent!.getScope().node;
assert(source.kind === SyntaxKind.LetDeclaration || source.kind === SyntaxKind.SourceFile);
graph.addEdge(source, node, false);
}
if (node.body !== null && node.body.kind === SyntaxKind.BlockBody) {
for (const element of node.body.elements) {
this.completeReferenceGraph(graph, element);
}
}
break;
}
case SyntaxKind.ReturnStatement:
case SyntaxKind.ExpressionStatement:
break;
default:
throw new Error(`Unexpected ${node}`);
}
}
private initialize(node: Syntax, parentEnv: TypeEnv | null): void { private initialize(node: Syntax, parentEnv: TypeEnv | null): void {
switch (node.kind) { switch (node.kind) {
@ -851,8 +869,9 @@ export class Checker {
env.add('==', new Forall([ a ], [], new TArrow([ a, a ], this.boolType))); env.add('==', new Forall([ a ], [], new TArrow([ a, a ], this.boolType)));
env.add('not', new Forall([], [], new TArrow([ this.boolType ], this.boolType))); env.add('not', new Forall([], [], new TArrow([ this.boolType ], this.boolType)));
const graph = new DirectedHashGraph<LetDeclaration>(); const graph = new LabeledDirectedHashGraph<NodeWithBindings, boolean>();
this.addReferences(graph, node, null); this.addReferencesToGraph(graph, node, node);
this.completeReferenceGraph(graph, node);
this.initialize(node, env); this.initialize(node, env);
@ -860,11 +879,18 @@ export class Checker {
for (const nodes of sccs) { for (const nodes of sccs) {
if (nodes.some(n => n.kind === SyntaxKind.SourceFile)) {
assert(nodes.length === 1);
continue;
}
const typeVars = new TVSet(); const typeVars = new TVSet();
const constraints = new ConstraintSet(); const constraints = new ConstraintSet();
for (const node of nodes) { for (const node of nodes) {
assert(node.kind === SyntaxKind.LetDeclaration);
const env = node.typeEnv!; const env = node.typeEnv!;
const context: InferContext = { const context: InferContext = {
typeVars, typeVars,
@ -907,12 +933,20 @@ export class Checker {
for (const nodes of sccs) { for (const nodes of sccs) {
if (nodes.some(n => n.kind === SyntaxKind.SourceFile)) {
assert(nodes.length === 1);
continue;
}
for (const node of nodes) { for (const node of nodes) {
assert(node.kind === SyntaxKind.LetDeclaration);
node.active = true; node.active = true;
} }
for (const node of nodes) { for (const node of nodes) {
assert(node.kind === SyntaxKind.LetDeclaration);
const context = node.context!; const context = node.context!;
const returnType = context.returnType!; const returnType = context.returnType!;
this.contexts.push(context); this.contexts.push(context);
@ -933,7 +967,13 @@ export class Checker {
case SyntaxKind.BlockBody: case SyntaxKind.BlockBody:
{ {
for (const element of node.body.elements) { for (const element of node.body.elements) {
if (element.kind !== SyntaxKind.LetDeclaration) { if (element.kind === SyntaxKind.LetDeclaration
&& element.pattern.kind === SyntaxKind.BindPattern
&& graph.hasEdge(node, element, false)) {
const scheme = this.lookup(element.pattern.name.text);
assert(scheme !== null);
this.instantiate(scheme, null);
} else {
this.infer(element); this.infer(element);
} }
} }
@ -946,26 +986,35 @@ export class Checker {
} }
for (const node of nodes) { for (const node of nodes) {
assert(node.kind === SyntaxKind.LetDeclaration);
node.active = false; node.active = false;
} }
} }
for (const element of node.elements) { for (const element of node.elements) {
if (element.kind !== SyntaxKind.LetDeclaration) { if (element.kind === SyntaxKind.LetDeclaration
&& element.pattern.kind === SyntaxKind.BindPattern
&& graph.hasEdge(node, element, false)) {
const scheme = this.lookup(element.pattern.name.text);
assert(scheme !== null);
this.instantiate(scheme, null);
} else {
this.infer(element); this.infer(element);
} }
} }
this.popContext(context); this.popContext(context);
this.solve(new CMany(this.constraints), this.solution); this.solve(new CMany(constraints), this.solution);
} }
private solve(constraint: Constraint, solution: TVSub): void { private solve(constraint: Constraint, solution: TVSub): void {
const queue = [ constraint ]; const queue = [ constraint ];
let errorCount = 0;
while (queue.length > 0) { while (queue.length > 0) {
const constraint = queue.pop()!; const constraint = queue.pop()!;
@ -982,8 +1031,12 @@ export class Checker {
case ConstraintKind.Equal: case ConstraintKind.Equal:
{ {
//constraint.dump();
if (!this.unify(constraint.left, constraint.right, solution, constraint)) { if (!this.unify(constraint.left, constraint.right, solution, constraint)) {
// TODO break or continue? errorCount++;
if (errorCount === MAX_TYPE_ERROR_COUNT) {
return;
}
} }
break; break;
} }

View file

@ -9,6 +9,13 @@ export function countDigits(x: number, base: number = 10) {
return x === 0 ? 1 : Math.ceil(Math.log(x+1) / Math.log(base)) return x === 0 ? 1 : Math.ceil(Math.log(x+1) / Math.log(base))
} }
export function isEmpty<T>(iter: Iterable<T> | Iterator<T>): boolean {
if ((iter as any)[Symbol.iterator] !== undefined) {
iter = (iter as any)[Symbol.iterator]();
}
return !!(iter as Iterator<T>).next().done;
}
export type JSONValue = null | boolean | number | string | JSONArray | JSONObject export type JSONValue = null | boolean | number | string | JSONArray | JSONObject
export type JSONArray = Array<JSONValue>; export type JSONArray = Array<JSONValue>;
export type JSONObject = { [key: string]: JSONValue }; export type JSONObject = { [key: string]: JSONValue };