Adjust the way let-declarations are visited in the type-checker

Let-declarations are now roughly visited in the order they are
referenced, resulting in constraints being propagated in the same way.
This commit is contained in:
Sam Vervaeck 2023-06-22 15:30:14 +02:00
parent 6df807440a
commit 2f9b6db5af
Signed by: samvv
SSH key fingerprint: SHA256:dIg0ywU1OP+ZYifrYxy8c5esO72cIKB+4/9wkZj1VaY
3 changed files with 107 additions and 142 deletions

View file

@ -3,24 +3,21 @@ import { assert } from "./util";
import { Syntax, LetDeclaration, SourceFile, SyntaxKind } from "./cst";
import type { Scope } from "./scope"
type NodeWithBlock
= LetDeclaration
| SourceFile
export class Analyser {
private referenceGraph = new DirectedHashGraph<NodeWithBlock>();
private referenceGraph = new DirectedHashGraph<LetDeclaration>();
public addSourceFile(node: SourceFile): void {
const visit = (node: Syntax, source: NodeWithBlock) => {
const visit = (node: Syntax, source: Syntax | null) => {
const addReference = (scope: Scope, name: string) => {
const target = scope.lookup(name);
if (target === null || target.kind === SyntaxKind.Param) {
if (source === null || target === null || target.kind === SyntaxKind.Param) {
return;
}
assert(target.kind === SyntaxKind.LetDeclaration || target.kind === SyntaxKind.SourceFile);
assert(source.kind === SyntaxKind.LetDeclaration);
assert(target.kind === SyntaxKind.LetDeclaration);
this.referenceGraph.addEdge(source, target);
}
@ -173,7 +170,7 @@ export class Analyser {
}
visit(node, node);
visit(node, null);
}
@ -196,7 +193,7 @@ export class Analyser {
* a let-declaration is not recusive, it will simply show up as a collection
* with only one element.
*/
public getSortedDeclarations(): Iterable<NodeWithBlock[]> {
public getSortedDeclarations(): Iterable<LetDeclaration[]> {
return strongconnect(this.referenceGraph);
}

View file

@ -179,6 +179,7 @@ class KArrow extends KindBase {
}
// TODO actually use these
const kindOfTypes = new KType();
const kindOfRows = new KRow();
@ -348,9 +349,18 @@ export interface InferContext {
returnType: Type | null;
}
function isSignatureDeclarationLike(node: LetDeclaration): boolean {
return false; // May be foreignKeyword !== null later
}
function isVariableDeclarationLike(node: LetDeclaration): boolean {
return node.pattern.kind !== SyntaxKind.NamedPattern || !node.body;
}
function isFunctionDeclarationLike(node: LetDeclaration): boolean {
return (node.pattern.kind === SyntaxKind.NamedPattern || node.pattern.kind === SyntaxKind.NestedPattern && node.pattern.pattern.kind === SyntaxKind.NamedPattern)
&& (node.params.length > 0 || (node.body !== null && node.body.kind === SyntaxKind.BlockBody));
return !isSignatureDeclarationLike(node) && !isVariableDeclarationLike(node);
// return (node.pattern.kind === SyntaxKind.NamedPattern || node.pattern.kind === SyntaxKind.NestedPattern && node.pattern.pattern.kind === SyntaxKind.NamedPattern)
// && (node.params.length > 0 || (node.body !== null && node.body.kind === SyntaxKind.BlockBody));
}
export class Checker {
@ -1047,8 +1057,42 @@ export class Checker {
case SyntaxKind.LetDeclaration:
{
if (isFunctionDeclarationLike(node)) {
node.activeCycle = true;
node.visited = true;
const context = node.context!;
const returnType = context.returnType!;
this.contexts.push(context);
if (node.body !== null) {
switch (node.body.kind) {
case SyntaxKind.ExprBody:
{
this.addConstraint(
new CEqual(
this.inferExpression(node.body.expression),
returnType,
node.body.expression
)
);
break;
}
case SyntaxKind.BlockBody:
{
for (const element of node.body.elements) {
this.infer(element);
}
break;
}
}
}
this.contexts.pop();
node.activeCycle = false;
} else {
const ctx = this.getContext();
const constraints = new ConstraintSet;
const innerCtx: InferContext = {
@ -1091,6 +1135,7 @@ export class Checker {
}
this.popContext(innerCtx);
this.inferBindings(node.pattern, type, undefined, constraints, true);
}
break;
}
@ -1163,9 +1208,14 @@ export class Checker {
{
const scope = node.getScope();
const target = scope.lookup(node.name.text);
if (target !== null && target.kind === SyntaxKind.LetDeclaration && target.activeCycle) {
if (target !== null && target.kind === SyntaxKind.LetDeclaration) {
if (target.activeCycle) {
return target.inferredType!;
}
if (!target.visited) {
this.infer(target);
}
}
const scheme = this.lookup(node, Symkind.Var);
if (scheme === null) {
// this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.text, node.name));
@ -1701,11 +1751,11 @@ export class Checker {
}
public check(node: SourceFile): void {
public check(sourceFile: SourceFile): void {
const kenv = new KindEnv(this.globalKindEnv);
this.forwardDeclareKind(node, kenv);
this.inferKind(node, kenv);
this.forwardDeclareKind(sourceFile, kenv);
this.inferKind(sourceFile, kenv);
const typeVars = new TVSet();
const constraints = new ConstraintSet();
@ -1714,12 +1764,12 @@ export class Checker {
this.pushContext(context);
this.initialize(node, env);
this.initialize(sourceFile, env);
this.pushContext({
typeVars,
constraints,
env: node.typeEnv!,
env: sourceFile.typeEnv!,
returnType: null
});
@ -1727,18 +1777,11 @@ export class Checker {
for (const nodes of sccs) {
if (nodes.some(n => n.kind === SyntaxKind.SourceFile)) {
assert(nodes.length === 1);
continue;
}
const typeVars = new TVSet();
const constraints = new ConstraintSet();
for (const node of nodes) {
assert(node.kind === SyntaxKind.LetDeclaration);
if (!isFunctionDeclarationLike(node)) {
continue;
}
@ -1806,84 +1849,7 @@ export class Checker {
}
const visitElements = (elements: Syntax[]) => {
for (const element of elements) {
if (element.kind === SyntaxKind.LetDeclaration
&& isFunctionDeclarationLike(element)) {
if (!this.analyser.isReferencedInParentScope(element)) {
const scheme = this.lookup(element.name, Symkind.Var);
assert(scheme !== null);
this.instantiate(scheme, null);
}
} else {
const shouldChangeTypeEnv = shouldChangeTypeEnvDuringVisit(element);
if (shouldChangeTypeEnv) {
this.pushContext({ ...this.getContext(), env: element.typeEnv! });
}
this.infer(element);
if(shouldChangeTypeEnv) {
this.contexts.pop();
}
}
}
}
for (const nodes of sccs) {
if (nodes[0].kind === SyntaxKind.SourceFile) {
assert(nodes.length === 1);
continue;
}
for (const node of nodes) {
assert(node.kind === SyntaxKind.LetDeclaration);
node.activeCycle = true;
}
for (const node of nodes) {
assert(node.kind === SyntaxKind.LetDeclaration);
if (!isFunctionDeclarationLike(node)) {
continue;
}
const context = node.context!;
const returnType = context.returnType!;
this.contexts.push(context);
if (node.body !== null) {
switch (node.body.kind) {
case SyntaxKind.ExprBody:
{
this.addConstraint(
new CEqual(
this.inferExpression(node.body.expression),
returnType,
node.body.expression
)
);
break;
}
case SyntaxKind.BlockBody:
{
visitElements(node.body.elements);
break;
}
}
}
this.contexts.pop();
}
for (const node of nodes) {
assert(node.kind === SyntaxKind.LetDeclaration);
node.activeCycle = false;
}
}
visitElements(node.elements);
this.infer(sourceFile);
this.contexts.pop();
this.popContext(context);

View file

@ -2874,6 +2874,8 @@ export class LetDeclaration extends SyntaxBase {
@nonenumerable
public activeCycle?: boolean;
@nonenumerable
public visited?: boolean;
@nonenumerable
public context?: InferContext;