Refactor and improve module references in kind inference
This commit is contained in:
parent
3e169e9ab9
commit
0d5c791a70
2 changed files with 116 additions and 27 deletions
130
src/checker.ts
130
src/checker.ts
|
@ -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 (;;) {
|
||||||
|
let currDown: SourceFile | ModuleDeclaration = currUp;
|
||||||
|
for (let i = 0; i < modulePath.length; i++) {
|
||||||
|
const moduleName = modulePath[i];
|
||||||
|
const nextDown = currDown.resolveModule(moduleName.text);
|
||||||
|
if (nextDown === null) {
|
||||||
|
if (currUp.kind === SyntaxKind.SourceFile) {
|
||||||
|
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 {
|
} else {
|
||||||
modulePath = [];
|
let curr: KindEnv | null = env;
|
||||||
name = node;
|
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,8 +939,15 @@ 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 {
|
||||||
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue