Refactor and improve module references in kind inference

This commit is contained in:
Sam Vervaeck 2022-09-18 19:33:34 +02:00
parent 3e169e9ab9
commit 0d5c791a70
2 changed files with 116 additions and 27 deletions

View file

@ -1,5 +1,4 @@
import { import {
CustomOperator,
EnumDeclaration, EnumDeclaration,
Expression, Expression,
ExprOperator, ExprOperator,
@ -728,7 +727,11 @@ class KindEnv {
} }
public setNamed(name: string, kind: Kind): void { public get(name: string): Kind | null {
return this.mapping.get(name) ?? null;
}
public set(name: string, kind: Kind): void {
assert(!this.mapping.has(name)); assert(!this.mapping.has(name));
this.mapping.set(name, kind); this.mapping.set(name, kind);
} }
@ -747,6 +750,19 @@ class KindEnv {
} }
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]
}
export interface InferContext { export interface InferContext {
typeVars: TVSet; typeVars: TVSet;
env: TypeEnv; env: TypeEnv;
@ -816,16 +832,67 @@ export class Checker {
this.contexts.pop(); this.contexts.pop();
} }
private lookup(node: NodeWithReference, expectedKind: Symkind): Scheme | null { private lookupKind(env: KindEnv, node: NodeWithReference): Kind | null {
let modulePath: IdentifierAlt[]; const [modulePath, name] = splitReferences(node);
let name: Identifier | IdentifierAlt | ExprOperator; if (modulePath.length > 0) {
if (node.kind === SyntaxKind.ReferenceExpression || node.kind === SyntaxKind.ReferenceTypeExpression) { let maxIndex = 0;
modulePath = node.modulePath.map(([name, _dot]) => name); let currUp = node.getEnclosingModule();
name = node.name; outer: for (;;) {
} else { let currDown: SourceFile | ModuleDeclaration = currUp;
modulePath = []; for (let i = 0; i < modulePath.length; i++) {
name = node; const moduleName = modulePath[i];
const nextDown = currDown.resolveModule(moduleName.text);
if (nextDown === null) {
if (currUp.kind === SyntaxKind.SourceFile) {
this.diagnostics.add(
new ModuleNotFoundDiagnostic(
modulePath.slice(maxIndex).map(id => id.text),
modulePath[maxIndex],
)
);
return null;
} }
currUp = currUp.getEnclosingModule();
continue outer;
}
maxIndex = Math.max(maxIndex, i+1);
currDown = nextDown;
}
const found = currDown.kindEnv!.get(name.text);
if (found !== null) {
return found;
}
this.diagnostics.add(
new BindingNotFoudDiagnostic(
modulePath.map(id => id.text),
name.text,
name,
)
);
return null;
}
} else {
let curr: KindEnv | null = env;
do {
const found = curr.get(name.text);
if (found !== null) {
return found;
}
curr = curr.parent;
} while(curr !== null);
this.diagnostics.add(
new BindingNotFoudDiagnostic(
[],
name.text,
name,
)
);
return null;
}
}
private lookup(node: NodeWithReference, expectedKind: Symkind): Scheme | null {
const [modulePath, name] = splitReferences(node);
if (modulePath.length > 0) { if (modulePath.length > 0) {
let maxIndex = 0; let maxIndex = 0;
let currUp = node.getEnclosingModule(); let currUp = node.getEnclosingModule();
@ -872,9 +939,16 @@ export class Checker {
} }
curr = curr.parent; curr = curr.parent;
} while(curr !== null); } while(curr !== null);
} this.diagnostics.add(
new BindingNotFoudDiagnostic(
[],
name.text,
name,
)
);
return null; return null;
} }
}
private getReturnType(): Type { private getReturnType(): Type {
const context = this.getContext(); const context = this.getContext();
@ -926,9 +1000,9 @@ export class Checker {
} }
case SyntaxKind.ReferenceTypeExpression: case SyntaxKind.ReferenceTypeExpression:
{ {
const matchedKind = env.lookup(node.name.text); const matchedKind = this.lookupKind(env, node);
if (matchedKind === null) { if (matchedKind === null) {
this.diagnostics.add(new BindingNotFoudDiagnostic([], node.name.text, node.name)); // this.diagnostics.add(new BindingNotFoudDiagnostic([], node.name.text, node.name));
// Create a filler kind variable that still will be able to catch other errors. // Create a filler kind variable that still will be able to catch other errors.
kind = this.createKindVar(); kind = this.createKindVar();
kind.flags |= KindFlags.UnificationFailed; kind.flags |= KindFlags.UnificationFailed;
@ -939,9 +1013,9 @@ export class Checker {
} }
case SyntaxKind.VarTypeExpression: case SyntaxKind.VarTypeExpression:
{ {
const matchedKind = env.lookup(node.name.text); const matchedKind = this.lookupKind(env, node.name);
if (matchedKind === null) { if (matchedKind === null) {
this.diagnostics.add(new BindingNotFoudDiagnostic([], node.name.text, node.name)); // this.diagnostics.add(new BindingNotFoudDiagnostic([], node.name.text, node.name));
// Create a filler kind variable that still will be able to catch other errors. // Create a filler kind variable that still will be able to catch other errors.
kind = this.createKindVar(); kind = this.createKindVar();
kind.flags |= KindFlags.UnificationFailed; kind.flags |= KindFlags.UnificationFailed;
@ -1005,7 +1079,9 @@ export class Checker {
) )
); );
// Create a filler kind variable that still will be able to catch other errors. // Create a filler kind variable that still will be able to catch other errors.
return this.createKindVar(); const kind = this.createKindVar();
kind.flags |= KindFlags.UnificationFailed;
return kind;
} }
} }
} }
@ -1034,23 +1110,23 @@ export class Checker {
for (let i = node.varExps.length-1; i >= 0; i--) { for (let i = node.varExps.length-1; i >= 0; i--) {
const varExpr = node.varExps[i]; const varExpr = node.varExps[i];
const paramKind = this.createKindVar(); const paramKind = this.createKindVar();
innerEnv.setNamed(varExpr.text, paramKind); innerEnv.set(varExpr.text, paramKind);
kind = new KArrow(paramKind, kind); kind = new KArrow(paramKind, kind);
} }
env.setNamed(node.name.text, this.inferKindFromTypeExpression(node.typeExpression, innerEnv)); env.set(node.name.text, this.inferKindFromTypeExpression(node.typeExpression, innerEnv));
break; break;
} }
case SyntaxKind.StructDeclaration: case SyntaxKind.StructDeclaration:
{ {
env.setNamed(node.name.text, this.createKindVar()); env.set(node.name.text, this.createKindVar());
break; break;
} }
case SyntaxKind.EnumDeclaration: case SyntaxKind.EnumDeclaration:
{ {
env.setNamed(node.name.text, this.createKindVar()); env.set(node.name.text, this.createKindVar());
if (node.members !== null) { if (node.members !== null) {
for (const member of node.members) { for (const member of node.members) {
env.setNamed(member.name.text, this.createKindVar()); env.set(member.name.text, this.createKindVar());
} }
} }
break; break;
@ -1083,7 +1159,7 @@ export class Checker {
for (let i = node.varExps.length-1; i >= 0; i--) { for (let i = node.varExps.length-1; i >= 0; i--) {
const varExpr = node.varExps[i]; const varExpr = node.varExps[i];
const paramKind = this.createKindVar(); const paramKind = this.createKindVar();
innerEnv.setNamed(varExpr.text, paramKind); innerEnv.set(varExpr.text, paramKind);
kind = new KArrow(paramKind, kind); kind = new KArrow(paramKind, kind);
} }
this.unifyKind(declKind, kind, node); this.unifyKind(declKind, kind, node);
@ -1103,7 +1179,7 @@ export class Checker {
for (let i = node.varExps.length-1; i >= 0; i--) { for (let i = node.varExps.length-1; i >= 0; i--) {
const varExpr = node.varExps[i]; const varExpr = node.varExps[i];
const paramKind = this.createKindVar(); const paramKind = this.createKindVar();
innerEnv.setNamed(varExpr.text, paramKind); innerEnv.set(varExpr.text, paramKind);
kind = new KArrow(paramKind, kind); kind = new KArrow(paramKind, kind);
} }
this.unifyKind(declKind, kind, node); this.unifyKind(declKind, kind, node);
@ -1483,7 +1559,7 @@ export class Checker {
let type; let type;
if (node.inferredKind!.substitute(this.kindSolution).hasFailed()) { if (node.inferredKind!.hasFailed()) {
type = this.createTypeVar(); type = this.createTypeVar();
@ -1821,9 +1897,9 @@ export class Checker {
public check(node: SourceFile): void { public check(node: SourceFile): void {
const kenv = new KindEnv(); const kenv = new KindEnv();
kenv.setNamed('Int', new KStar()); kenv.set('Int', new KStar());
kenv.setNamed('String', new KStar()); kenv.set('String', new KStar());
kenv.setNamed('Bool', new KStar()); kenv.set('Bool', new KStar());
const skenv = new KindEnv(kenv); const skenv = new KindEnv(kenv);
this.forwardDeclareKind(node, skenv); this.forwardDeclareKind(node, skenv);
this.inferKind(node, skenv); this.inferKind(node, skenv);

View file

@ -442,19 +442,28 @@ function printNode(node: Syntax, options?: PrintExcerptOptions): string {
} }
function printExcerpt(file: TextFile, span: TextRange, { indentation = ' ', extraLineCount = 2 } = {}): string { function printExcerpt(file: TextFile, span: TextRange, { indentation = ' ', extraLineCount = 2 } = {}): string {
let out = ''; let out = '';
const content = file.text; const content = file.text;
const startLine = Math.max(0, span.start.line-1-extraLineCount) const startLine = Math.max(0, span.start.line-1-extraLineCount)
const lines = content.split('\n') const lines = content.split('\n')
const endLine = Math.min(lines.length, (span.end !== undefined ? span.end.line : startLine) + extraLineCount) const endLine = Math.min(lines.length, (span.end !== undefined ? span.end.line : startLine) + extraLineCount)
const gutterWidth = Math.max(2, countDigits(endLine+1)) const gutterWidth = Math.max(2, countDigits(endLine+1))
for (let i = startLine; i < endLine; i++) { for (let i = startLine; i < endLine; i++) {
const line = lines[i]; const line = lines[i];
let j = firstIndexOfNonEmpty(line); let j = firstIndexOfNonEmpty(line);
out += indentation + ' ' + ANSI_FG_BLACK + ANSI_BG_WHITE + ' '.repeat(gutterWidth-countDigits(i+1))+(i+1).toString() + ANSI_RESET + ' ' + line + '\n' out += indentation + ' ' + ANSI_FG_BLACK + ANSI_BG_WHITE + ' '.repeat(gutterWidth-countDigits(i+1))+(i+1).toString() + ANSI_RESET + ' ' + line + '\n'
const gutter = indentation + ' ' + ANSI_FG_BLACK + ANSI_BG_WHITE + ' '.repeat(gutterWidth) + ANSI_RESET + ' ' const gutter = indentation + ' ' + ANSI_FG_BLACK + ANSI_BG_WHITE + ' '.repeat(gutterWidth) + ANSI_RESET + ' '
let mark: number; let mark: number;
let skip: number; let skip: number;
if (i === span.start.line-1 && i === span.end.line-1) { if (i === span.start.line-1 && i === span.end.line-1) {
skip = span.start.column-1; skip = span.start.column-1;
mark = span.end.column-span.start.column; mark = span.end.column-span.start.column;
@ -470,11 +479,15 @@ function printExcerpt(file: TextFile, span: TextRange, { indentation = ' ', ext
} else { } else {
continue; continue;
} }
if (j <= skip) { if (j <= skip) {
j = 0; j = 0;
} }
out += gutter + ' '.repeat(j+skip) + ANSI_FG_RED + '~'.repeat(mark-j) + ANSI_RESET + '\n' out += gutter + ' '.repeat(j+skip) + ANSI_FG_RED + '~'.repeat(mark-j) + ANSI_RESET + '\n'
} }
return out; return out;
} }