From 985e2d0652f60303fc19f7f3364c36bb76004b99 Mon Sep 17 00:00:00 2001 From: Sam Vervaeck Date: Tue, 4 Jul 2023 20:38:40 +0200 Subject: [PATCH] Improve handing of struct/enum types and declarations Also removes TNominal from the list of types because it is redundant w.r.t. TCon. --- compiler/src/bin/bolt.ts | 1 + compiler/src/checker.ts | 121 +++++++++++++++++------------------- compiler/src/cst.ts | 4 +- compiler/src/diagnostics.ts | 12 +--- compiler/src/scanner.ts | 13 +++- compiler/src/types.ts | 69 +++++--------------- 6 files changed, 88 insertions(+), 132 deletions(-) diff --git a/compiler/src/bin/bolt.ts b/compiler/src/bin/bolt.ts index 8392f6146..5721c1090 100644 --- a/compiler/src/bin/bolt.ts +++ b/compiler/src/bin/bolt.ts @@ -201,6 +201,7 @@ program.command('verify', { hidden: true }) const uncaughtDiagnostics = new Set(diagnostics); + // TODO check comments that did not match any diagnostic for (const [line, comment] of file.comments) { if (comment[0].kind === SyntaxKind.At && comment[1].kind === SyntaxKind.Identifier && comment[1].text === 'expect_diagnostic' && comment[2].kind === SyntaxKind.StringLiteral) { for (const diagnostic of uncaughtDiagnostics) { diff --git a/compiler/src/checker.ts b/compiler/src/checker.ts index 116d801f1..359e52987 100644 --- a/compiler/src/checker.ts +++ b/compiler/src/checker.ts @@ -31,7 +31,7 @@ import { import { assert, assertNever, isEmpty, MultiMap, toStringTag, InspectFn, implementationLimitation } from "./util"; import { Analyser } from "./analysis"; import { InspectOptions } from "util"; -import { TypeKind, TApp, TArrow, TCon, TField, TNil, TNominal, TPresent, TTuple, TUniVar, TVSet, TVSub, Type, TypeBase, TAbsent, TRigidVar, TVar, TTupleIndex } from "./types"; +import { TypeKind, TApp, TArrow, TCon, TField, TNil, TPresent, TTuple, TUniVar, TVSet, TVSub, Type, TypeBase, TAbsent, TRigidVar, TVar, TTupleIndex } from "./types"; import { CClass, CEmpty, CEqual, CMany, Constraint, ConstraintKind, ConstraintSet } from "./constraints"; // export class Qual { @@ -384,9 +384,9 @@ export class Checker { private nextKindVarId = 0; private nextConTypeId = 0; - private stringType = this.createTCon([], 'String'); - private intType = this.createTCon([], 'Int'); - private boolType = this.createTCon([], 'Bool'); + private stringType = this.createTCon('String'); + private intType = this.createTCon('Int'); + private boolType = this.createTCon('Bool'); private contexts: InferContext[] = []; @@ -436,8 +436,8 @@ export class Checker { return this.boolType; } - private createTCon(types: Type[], name: string): TCon { - return new TCon(this.nextConTypeId++, types, name); + private createTCon(name: string, node: Syntax | null = null): TCon { + return new TCon(this.nextConTypeId++, name, node); } private createTypeVar(node: Syntax | null = null): TUniVar { @@ -1447,12 +1447,13 @@ export class Checker { case SyntaxKind.StructExpression: { - type = new TNil(node); + const fields = new Map(); + const restType = new TNil(node); for (const member of node.members) { switch (member.kind) { case SyntaxKind.StructExpressionField: { - type = new TField(member.name.text, new TPresent(this.inferExpression(member.expression)), type, node); + fields.set(member.name.text, this.inferExpression(member.expression)); break; } case SyntaxKind.PunnedStructExpressionField: @@ -1465,15 +1466,14 @@ export class Checker { } else { fieldType = this.instantiate(scheme, member); } - type = new TField(member.name.text, new TPresent(fieldType), type, node); + fields.set(member.name.text, fieldType); break; } default: throw new Error(`Unexpected ${member}`); } } - // FIXME build a type rather than sorting it - type = TField.sort(type); + type = TField.build(fields, restType); break; } @@ -1644,21 +1644,22 @@ export class Checker { this.inferBindings(pattern.pattern, type, typeVars, constraints); break; - // case SyntaxKind.NamedTuplePattern: - // { - // const scheme = this.lookup(pattern.name, Symkind.Type); - // if (scheme === null) { - // return this.createTypeVar(); - // } - // let tupleType = new TTuple(pattern.elements.map(p => - // this.inferBindings(p, this.createTypeVar(), typeVars, constraints)); - // // FIXME not tested - // this.addConstraint(new CEqual(tupleType, type, pattern)); - // return TApp.build( - // new TNominal(scheme.type.node as StructDeclaration | EnumDeclaration, pattern), - // tupleType - // ); - // } + case SyntaxKind.NamedTuplePattern: + { + const scheme = this.lookup(pattern.name, Symkind.Var); + if (scheme === null) { + return; + } + const ctorType = this.instantiate(scheme, pattern); + let elementTypes = []; + for (const element of pattern.elements) { + const tv = this.createTypeVar(); + this.inferBindings(element, tv, typeVars, constraints, generalize); + elementTypes.push(tv); + } + this.addConstraint(new CEqual(TArrow.build(elementTypes, type), ctorType, pattern)); + break; + } case SyntaxKind.LiteralPattern: { @@ -1693,13 +1694,14 @@ export class Checker { case SyntaxKind.StructPattern: { const variadicMember = getVariadicMember(pattern); - let structType: Type; + const fields = new Map(); + let restType: Type; if (variadicMember === null) { - structType = new TNil(pattern); + restType = new TNil(pattern); } else { - structType = this.createTypeVar(); + restType = this.createTypeVar(); if (variadicMember.pattern !== null) { - this.inferBindings(variadicMember.pattern, structType, typeVars, constraints); + this.inferBindings(variadicMember.pattern, restType, typeVars, constraints); } } for (const member of pattern.members) { @@ -1708,14 +1710,14 @@ export class Checker { { const fieldType = this.createTypeVar(); this.inferBindings(member.pattern, fieldType, typeVars, constraints); - structType = new TField(member.name.text, new TPresent(fieldType), structType, pattern); + fields.set(member.name.text, fieldType); break; } case SyntaxKind.PunnedStructPatternField: { const fieldType = this.createTypeVar(); this.addBinding(member.name.text, Forall.mono(fieldType), Symkind.Var); - structType = new TField(member.name.text, new TPresent(fieldType), structType, pattern); + fields.set(member.name.text, fieldType); break; } case SyntaxKind.VariadicStructPatternElement: @@ -1727,7 +1729,7 @@ export class Checker { this.addConstraint( new CEqual( type, - TField.sort(structType), + TField.build(fields, restType), pattern, ) ); @@ -1820,15 +1822,20 @@ export class Checker { constraints, returnType: null, } + this.pushContext(context); + const kindArgs = []; for (const name of node.varExps) { const kindArg = this.createTypeVar(); env.add(name.text, Forall.mono(kindArg), Symkind.Type); kindArgs.push(kindArg); } - const type = TApp.build(new TNominal(node, node), kindArgs); + + const type = this.createTCon(node.name.text, node); + const appliedType = TApp.build(type, kindArgs); parentEnv.add(node.name.text, new Forall(typeVars, new CMany(constraints), type), Symkind.Type); + let elementTypes: Type[] = []; if (node.members !== null) { for (const member of node.members) { @@ -1838,28 +1845,30 @@ export class Checker { { const argTypes = member.elements.map(el => this.inferTypeExpression(el, false)); elementType = new TTuple(argTypes, member); - ctorType = TArrow.build(argTypes, type, member); + ctorType = TArrow.build(argTypes, appliedType, member); break; } case SyntaxKind.EnumDeclarationStructElement: { - elementType = new TNil(member); + const restType = new TNil(member); + const fields = new Map(); for (const field of member.fields) { - elementType = new TField(field.name.text, new TPresent(this.inferTypeExpression(field.typeExpr, false)), elementType, member); + fields.set(field.name.text, this.inferTypeExpression(field.typeExpr, false)); } - elementType = TField.sort(elementType); - ctorType = new TArrow(elementType, type); + elementType = TField.build(fields, restType); + ctorType = new TArrow(elementType, appliedType, member); break; } default: throw new Error(`Unexpected ${member}`); } - // FIXME `typeVars` may contain too much irrelevant type variables parentEnv.add(member.name.text, new Forall(typeVars, new CMany(constraints), ctorType), Symkind.Var); elementTypes.push(elementType); } } + this.popContext(context); + break; } @@ -1906,15 +1915,18 @@ export class Checker { env.add(varExpr.text, Forall.mono(kindArg), Symkind.Type); kindArgs.push(kindArg); } - let type: Type = new TNil(node); + const fields = new Map(); + const restType = new TNil(node); if (node.fields !== null) { for (const field of node.fields) { - type = new TField(field.name.text, new TPresent(this.inferTypeExpression(field.typeExpr)), type, node); + fields.set(field.name.text, this.inferTypeExpression(field.typeExpr)); } } + const type = this.createTCon(node.name.text, node.name); + const recordType = TField.build(fields, restType); this.popContext(context); - parentEnv.add(node.name.text, new Forall(typeVars, new CMany(constraints), TField.sort(type)), Symkind.Type); - //parentEnv.add(node.name.text, new Forall(typeVars, constraints, new TArrow(type, TApp.build(type, kindArgs))), Symkind.Var); + parentEnv.add(node.name.text, new Forall(typeVars, new CMany(constraints), type), Symkind.Type); + parentEnv.add(node.name.text, new Forall(typeVars, new CMany(constraints), new TArrow(recordType, type)), Symkind.Var); break; } @@ -2186,18 +2198,8 @@ export class Checker { if (left.kind === TypeKind.Con && right.kind === TypeKind.Con) { if (left.id === right.id) { - assert(left.argTypes.length === right.argTypes.length); - const count = left.argTypes.length; - let success = true; - for (let i = 0; i < count; i++) { - if (!this.unify(left.argTypes[i], right.argTypes[i], enableDiagnostics)) { - success = false; - } - } - if (success) { - TypeBase.join(left, right); - } - return success; + TypeBase.join(left, right); + return true; } } @@ -2246,13 +2248,6 @@ export class Checker { return success } - if (left.kind === TypeKind.Nominal && right.kind === TypeKind.Nominal) { - if (left.decl === right.decl) { - return true; - } - // fall through to error reporting - } - if (left.kind === TypeKind.App && right.kind === TypeKind.App) { return this.unify(left.left, right.left, enableDiagnostics) && this.unify(left.right, right.right, enableDiagnostics); diff --git a/compiler/src/cst.ts b/compiler/src/cst.ts index 81cb3197b..34e8a73c8 100644 --- a/compiler/src/cst.ts +++ b/compiler/src/cst.ts @@ -470,11 +470,11 @@ abstract class TokenBase extends SyntaxBase { } public getFirstToken(): Token { - throw new Error(`Trying to get the first token of an object that is a token itself.`); + return this as Token; } public getLastToken(): Token { - throw new Error(`Trying to get the last token of an object that is a token itself.`); + return this as Token; } public getRange(): TextRange { diff --git a/compiler/src/diagnostics.ts b/compiler/src/diagnostics.ts index 37fc887a6..80ca074a2 100644 --- a/compiler/src/diagnostics.ts +++ b/compiler/src/diagnostics.ts @@ -548,11 +548,7 @@ export function describeType(type: Type): string { switch (type.kind) { case TypeKind.Con: { - let out = type.displayName; - for (const argType of type.argTypes) { - out += ' ' + describeType(argType); - } - return out; + return type.displayName; } case TypeKind.UniVar: return 'a' + type.id; @@ -575,10 +571,6 @@ export function describeType(type: Type): string { } case TypeKind.TupleIndex: return describeType(type.tupleType) + '.' + type.index; - case TypeKind.Nominal: - { - return type.decl.name.text; - } case TypeKind.Field: { let out = '{ ' + type.name + ': ' + describeType(type.type); @@ -594,7 +586,7 @@ export function describeType(type: Type): string { } case TypeKind.App: { - return describeType(type.right) + ' ' + describeType(type.left); + return describeType(type.left) + ' ' + describeType(type.right); } case TypeKind.Nil: return '{}'; diff --git a/compiler/src/scanner.ts b/compiler/src/scanner.ts index ffd7319ef..759024085 100644 --- a/compiler/src/scanner.ts +++ b/compiler/src/scanner.ts @@ -163,6 +163,13 @@ export class Scanner extends BufferedStream { if (c0 === '#') { const line = this.currPos.line; this.getChar(); + for (;;) { + const c1 = this.peekChar(); + if (!isWhiteSpace(c1) || c1 === '\n' || c1 === EOF) { + break; + } + this.getChar(); + } let text = ''; for (;;) { const c1 = this.getChar(); @@ -171,8 +178,10 @@ export class Scanner extends BufferedStream { } text += c1; } - const scanner = new Scanner(text, this.diagnostics, this.file, this.getCurrentPosition()); - this.file.comments.set(line, scanner.getAll()); + if (text[0] === '@') { + const scanner = new Scanner(text, this.diagnostics, this.file, this.getCurrentPosition()); + this.file.comments.set(line, scanner.getAll()); + } continue; } diff --git a/compiler/src/types.ts b/compiler/src/types.ts index a764a9765..467c48c22 100644 --- a/compiler/src/types.ts +++ b/compiler/src/types.ts @@ -283,7 +283,6 @@ export class TCon extends TypeBase { public constructor( public id: number, - public argTypes: Type[], public displayName: string, public node: Syntax | null = null, ) { @@ -291,35 +290,23 @@ export class TCon extends TypeBase { } public *getTypeVars(): Iterable { - for (const argType of this.argTypes) { - yield* argType.getTypeVars(); - } + } public shallowClone(): TCon { return new TCon( this.id, - this.argTypes, this.displayName, this.node, ); } - public substitute(sub: TVSub): Type { - let changed = false; - const newArgTypes = []; - for (const argType of this.argTypes) { - const newArgType = argType.substitute(sub); - if (newArgType !== argType) { - changed = true; - } - newArgTypes.push(newArgType); - } - return changed ? new TCon(this.id, newArgTypes, this.displayName, this.node) : this; + public substitute(_sub: TVSub): Type { + return this; } - public [toStringTag](_depth: number, options: InspectOptions, inspect: InspectFn) { - return this.displayName + ' ' + this.argTypes.map(t => inspect(t, options)).join(' '); + public [toStringTag](_depth: number, _options: InspectOptions, _inspect: InspectFn) { + return this.displayName; } } @@ -430,6 +417,14 @@ export class TField extends TypeBase { ); } + public static build(fields: Map, restType: Type): Type { + let out = restType; + for (const [name, type] of fields) { + out = new TField(name, new TPresent(type, type.node), out, type.node); + } + return out + } + public static sort(type: Type): Type { const fields = new Map(); while (type.kind === TypeKind.Field) { @@ -481,7 +476,7 @@ export class TApp extends TypeBase { public static build(resultType: Type, types: Type[], node: Syntax | null = null): Type { for (let i = 0; i < types.length; i++) { - resultType = new TApp(types[i], resultType, node); + resultType = new TApp(resultType, types[i], node); } return resultType; } @@ -518,38 +513,6 @@ export class TApp extends TypeBase { } -export class TNominal extends TypeBase { - - public readonly kind = TypeKind.Nominal; - - public constructor( - public decl: StructDeclaration | EnumDeclaration, - public node: Syntax | null = null, - ) { - super(); - } - - public *getTypeVars(): Iterable { - - } - - public shallowClone(): Type { - return new TNominal( - this.decl, - this.node, - ); - } - - public substitute(_sub: TVSub): Type { - return this; - } - - public [toStringTag]() { - return this.decl.name.text; - } - -} - export type Type = TCon | TArrow @@ -557,7 +520,6 @@ export type Type | TUniVar | TTuple | TApp - | TNominal | TField | TNil | TPresent @@ -586,9 +548,6 @@ export function typesEqual(a: Type, b: Type): boolean { case TypeKind.Nil: case TypeKind.Absent: return true; - case TypeKind.Nominal: - assert(b.kind === TypeKind.Nominal); - return a.decl === b.decl; case TypeKind.App: assert(b.kind === TypeKind.App); return typesEqual(a.left, b.left) && typesEqual(a.right, b.right);