From 2f9b6db5afe241ec7d3eae9e9b7399252b9dddbe Mon Sep 17 00:00:00 2001 From: Sam Vervaeck Date: Thu, 22 Jun 2023 15:30:14 +0200 Subject: [PATCH] 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. --- compiler/src/analysis.ts | 17 ++- compiler/src/checker.ts | 230 +++++++++++++++++---------------------- compiler/src/cst.ts | 2 + 3 files changed, 107 insertions(+), 142 deletions(-) diff --git a/compiler/src/analysis.ts b/compiler/src/analysis.ts index 644cb0c2d..7179ef3e5 100644 --- a/compiler/src/analysis.ts +++ b/compiler/src/analysis.ts @@ -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(); + private referenceGraph = new DirectedHashGraph(); 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 { + public getSortedDeclarations(): Iterable { return strongconnect(this.referenceGraph); } diff --git a/compiler/src/checker.ts b/compiler/src/checker.ts index cd4e7c292..0f4a9f576 100644 --- a/compiler/src/checker.ts +++ b/compiler/src/checker.ts @@ -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,50 +1057,85 @@ export class Checker { case SyntaxKind.LetDeclaration: { if (isFunctionDeclarationLike(node)) { - break; - } - const ctx = this.getContext(); - const constraints = new ConstraintSet; - const innerCtx: InferContext = { - ...ctx, - constraints, - }; - this.pushContext(innerCtx); - let type; - if (node.typeAssert !== null) { - type = this.inferTypeExpression(node.typeAssert.typeExpression); - } - if (node.body !== null) { - let bodyType; - switch (node.body.kind) { - case SyntaxKind.ExprBody: - { - bodyType = this.inferExpression(node.body.expression); - break; + + 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; + } } - case SyntaxKind.BlockBody: - { - // TODO - assert(false); + } + + this.contexts.pop(); + node.activeCycle = false; + + } else { + + const ctx = this.getContext(); + const constraints = new ConstraintSet; + const innerCtx: InferContext = { + ...ctx, + constraints, + }; + this.pushContext(innerCtx); + let type; + if (node.typeAssert !== null) { + type = this.inferTypeExpression(node.typeAssert.typeExpression); + } + if (node.body !== null) { + let bodyType; + switch (node.body.kind) { + case SyntaxKind.ExprBody: + { + bodyType = this.inferExpression(node.body.expression); + break; + } + case SyntaxKind.BlockBody: + { + // TODO + assert(false); + } + } + if (type === undefined) { + type = bodyType; + } else { + constraints.push( + new CEqual( + type, + bodyType, + node.body + ) + ); } } if (type === undefined) { - type = bodyType; - } else { - constraints.push( - new CEqual( - type, - bodyType, - node.body - ) - ); + type = this.createTypeVar(); } + this.popContext(innerCtx); + this.inferBindings(node.pattern, type, undefined, constraints, true); } - if (type === undefined) { - type = this.createTypeVar(); - } - this.popContext(innerCtx); - this.inferBindings(node.pattern, type, undefined, constraints, true); break; } @@ -1163,8 +1208,13 @@ export class Checker { { const scope = node.getScope(); const target = scope.lookup(node.name.text); - if (target !== null && target.kind === SyntaxKind.LetDeclaration && target.activeCycle) { - return target.inferredType!; + 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) { @@ -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); diff --git a/compiler/src/cst.ts b/compiler/src/cst.ts index 7d9dd659c..a5db2b93c 100644 --- a/compiler/src/cst.ts +++ b/compiler/src/cst.ts @@ -2874,6 +2874,8 @@ export class LetDeclaration extends SyntaxBase { @nonenumerable public activeCycle?: boolean; + @nonenumerable + public visited?: boolean; @nonenumerable public context?: InferContext;