Minor improvements, add some experimental type class logic and refactor diagnostics

This commit is contained in:
Sam Vervaeck 2023-03-16 21:50:15 +01:00
parent df5f857905
commit c559d13df9
8 changed files with 658 additions and 366 deletions

View file

@ -1,3 +1,7 @@
// TODO support rigid vs free variables
// https://www.reddit.com/r/haskell/comments/d4v83/comment/c0xmc3r/
import {
ClassDeclaration,
EnumDeclaration,
@ -23,14 +27,14 @@ import {
describeType,
BindingNotFoundDiagnostic,
Diagnostics,
FieldMissingDiagnostic,
UnificationFailedDiagnostic,
FieldNotFoundDiagnostic,
TypeMismatchDiagnostic,
KindMismatchDiagnostic,
ModuleNotFoundDiagnostic,
TypeclassNotFoundDiagnostic,
FieldTypeMismatchDiagnostic,
TypeclassDeclaredTwiceDiagnostic,
} from "./diagnostics";
import { assert, assertNever, first, isEmpty, last, MultiMap } from "./util";
import { assert, isDebug, assertNever, first, isEmpty, last, MultiMap } from "./util";
import { Analyser } from "./analysis";
const MAX_TYPE_ERROR_COUNT = 5;
@ -440,6 +444,49 @@ export type Type
| TPresent
| TAbsent
export class Qual {
public constructor(
public preds: Pred[],
public type: Type,
) {
}
public substitute(sub: TVSub): Qual {
return new Qual(
this.preds.map(pred => pred.substitute(sub)),
this.type.substitute(sub),
);
}
public *getTypeVars() {
for (const pred of this.preds) {
yield* pred.type.getTypeVars();
}
yield* this.type.getTypeVars();
}
}
class IsInPred {
public constructor(
public id: string,
public type: Type,
) {
}
public substitute(sub: TVSub): Pred {
return new IsInPred(this.id, this.type.substitute(sub));
}
}
type Pred = IsInPred;
export const enum KindType {
Star,
Arrow,
@ -552,6 +599,14 @@ class TVSet {
private mapping = new Map<number, TVar>();
public constructor(iterable?: Iterable<TVar>) {
if (iterable !== undefined) {
for (const tv of iterable) {
this.add(tv);
}
}
}
public add(tv: TVar): void {
this.mapping.set(tv.id, tv);
}
@ -573,6 +628,10 @@ class TVSet {
this.mapping.delete(tv.id);
}
public get size(): number {
return this.mapping.size;
}
public [Symbol.iterator](): Iterator<TVar> {
return this.mapping.values();
}
@ -690,10 +749,6 @@ class CMany extends ConstraintBase {
}
interface InstanceRef {
node: InstanceDeclaration | null;
}
type Constraint
= CEqual
| CMany
@ -706,12 +761,19 @@ abstract class SchemeBase {
class Forall extends SchemeBase {
public typeVars: TVSet;
public constructor(
public typeVars: Iterable<TVar>,
typeVars: Iterable<TVar>,
public constraints: Iterable<Constraint>,
public type: Type,
) {
super();
if (typeVars instanceof TVSet) {
this.typeVars = typeVars;
} else {
this.typeVars = new TVSet(typeVars);
}
}
}
@ -726,6 +788,23 @@ type NodeWithReference
| ReferenceExpression
| ReferenceTypeExpression
function validateScheme(scheme: Scheme): void {
const isMonoVar = scheme.type.kind === TypeKind.Var && scheme.typeVars.size === 0;
if (!isMonoVar) {
const tvs = new TVSet(scheme.type.getTypeVars())
for (const tv of tvs) {
if (!scheme.typeVars.has(tv)) {
throw new Error(`Type variable ${describeType(tv)} is free because does not appear in the scheme's type variable list`);
}
}
for (const tv of scheme.typeVars) {
if (!tvs.has(tv)) {
throw new Error(`Polymorphic type variable ${describeType(tv)} does not occur anywhere in scheme's type ${describeType(scheme.type)}`);
}
}
}
}
class TypeEnv {
private mapping = new MultiMap<string, [Symkind, Scheme]>();
@ -735,6 +814,9 @@ class TypeEnv {
}
public add(name: string, scheme: Scheme, kind: Symkind): void {
if (isDebug) {
validateScheme(scheme);
}
this.mapping.add(name, [kind, scheme]);
}
@ -819,6 +901,10 @@ export class Checker {
private contexts: InferContext[] = [];
private classDecls = new Map<string, ClassDeclaration>();
private globalKindEnv = new KindEnv();
private globalTypeEnv = new TypeEnv();
private solution = new TVSub();
private kindSolution = new KVSub();
@ -827,6 +913,26 @@ export class Checker {
private diagnostics: Diagnostics
) {
this.globalKindEnv.set('Int', new KType());
this.globalKindEnv.set('String', new KType());
this.globalKindEnv.set('Bool', new KType());
const a = new TVar(this.nextTypeVarId++);
const b = new TVar(this.nextTypeVarId++);
this.globalTypeEnv.add('$', new Forall([ a, b ], [], new TArrow(new TArrow(new TArrow(a, b), a), b)), Symkind.Var);
this.globalTypeEnv.add('String', new Forall([], [], this.stringType), Symkind.Type);
this.globalTypeEnv.add('Int', new Forall([], [], this.intType), Symkind.Type);
this.globalTypeEnv.add('Bool', new Forall([], [], this.boolType), Symkind.Type);
this.globalTypeEnv.add('True', new Forall([], [], this.boolType), Symkind.Var);
this.globalTypeEnv.add('False', new Forall([], [], this.boolType), Symkind.Var);
this.globalTypeEnv.add('+', new Forall([], [], TArrow.build([ this.intType, this.intType ], this.intType)), Symkind.Var);
this.globalTypeEnv.add('-', new Forall([], [], TArrow.build([ this.intType, this.intType ], this.intType)), Symkind.Var);
this.globalTypeEnv.add('*', new Forall([], [], TArrow.build([ this.intType, this.intType ], this.intType)), Symkind.Var);
this.globalTypeEnv.add('/', new Forall([], [], TArrow.build([ this.intType, this.intType ], this.intType)), Symkind.Var);
this.globalTypeEnv.add('==', new Forall([ a ], [], TArrow.build([ a, a ], this.boolType)), Symkind.Var);
this.globalTypeEnv.add('not', new Forall([], [], new TArrow(this.boolType, this.boolType)), Symkind.Var);
}
public getIntType(): Type {
@ -874,7 +980,7 @@ export class Checker {
let maxIndex = 0;
let currUp = node.getEnclosingModule();
outer: for (;;) {
let currDown: SourceFile | ModuleDeclaration = currUp;
let currDown = currUp;
for (let i = 0; i < modulePath.length; i++) {
const moduleName = modulePath[i];
const nextDown = currDown.resolveModule(moduleName.text);
@ -939,7 +1045,7 @@ export class Checker {
let maxIndex = 0;
let currUp = node.getEnclosingModule();
outer: for (;;) {
let currDown: SourceFile | ModuleDeclaration = currUp;
let currDown = currUp;
for (let i = 0; i < modulePath.length; i++) {
const moduleName = modulePath[i];
const nextDown = currDown.resolveModule(moduleName.text);
@ -1000,7 +1106,8 @@ export class Checker {
private createSubstitution(scheme: Scheme): TVSub {
const sub = new TVSub();
for (const tv of scheme.typeVars) {
const tvs = [...scheme.typeVars]
for (const tv of tvs) {
sub.set(tv, this.createTypeVar());
}
return sub;
@ -1211,14 +1318,14 @@ export class Checker {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InstanceDeclaration:
{
if (node.constraints !== null) {
for (const constraint of node.constraints.constraints) {
if (node.constraintClause !== null) {
for (const constraint of node.constraintClause.constraints) {
for (const typeExpr of constraint.types) {
this.unifyKind(this.inferKindFromTypeExpression(typeExpr, env), new KType(), typeExpr);
}
}
}
for (const typeExpr of node.constraint.types) {
for (const typeExpr of node.types) {
this.unifyKind(this.inferKindFromTypeExpression(typeExpr, env), new KType(), typeExpr);
}
for (const element of node.elements) {
@ -1374,9 +1481,9 @@ export class Checker {
case SyntaxKind.InstanceDeclaration:
{
const cls = node.getScope().lookup(node.constraint.name.text, Symkind.Typeclass) as ClassDeclaration | null;
const cls = node.getScope().lookup(node.name.text, Symkind.Typeclass) as ClassDeclaration | null;
if (cls === null) {
this.diagnostics.add(new TypeclassNotFoundDiagnostic(node.constraint.name.text, node.constraint.name));
this.diagnostics.add(new TypeclassNotFoundDiagnostic(node.name));
}
for (const element of node.elements) {
this.infer(element);
@ -1695,7 +1802,7 @@ export class Checker {
case SyntaxKind.TupleTypeExpression:
{
type = new TTuple(node.elements.map(el => this.inferTypeExpression(el)), node);
type = new TTuple(node.elements.map(el => this.inferTypeExpression(el, introduceTypeVars)), node);
break;
}
@ -1708,7 +1815,7 @@ export class Checker {
const scheme = this.lookup(node.name, Symkind.Type);
if (scheme === null) {
if (!introduceTypeVars) {
// this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.text, node.name));
this.diagnostics.add(new BindingNotFoundDiagnostic([], node.name.text, node.name));
}
type = this.createTypeVar();
this.addBinding(node.name.text, new Forall([], [], type), Symkind.Type);
@ -1873,11 +1980,37 @@ export class Checker {
break;
}
case SyntaxKind.InstanceDeclaration:
case SyntaxKind.ClassDeclaration:
{
const other = this.classDecls.get(node.name.text);
if (other !== undefined) {
this.diagnostics.add(new TypeclassDeclaredTwiceDiagnostic(node.name, other));
} else {
if (node.constraintClause !== null) {
for (const constraint of node.constraintClause.constraints) {
if (!this.classDecls.has(constraint.name.text)) {
this.diagnostics.add(new TypeclassNotFoundDiagnostic(constraint.name));
}
}
}
this.classDecls.set(node.name.text, node);
}
const env = node.typeEnv = new TypeEnv(parentEnv);
for (const tv of node.types) {
assert(tv.kind === SyntaxKind.VarTypeExpression);
env.add(tv.name.text, new Forall([], [], this.createTypeVar(tv)), Symkind.Type);
}
for (const element of node.elements) {
this.initialize(element, parentEnv);
this.initialize(element, env);
}
break;
}
case SyntaxKind.InstanceDeclaration:
{
const env = node.typeEnv = new TypeEnv(parentEnv);
for (const element of node.elements) {
this.initialize(element, env);
}
break;
}
@ -1911,41 +2044,44 @@ export class Checker {
}
this.pushContext(context);
const kindArgs = [];
for (const varExpr of node.varExps) {
for (const name of node.varExps) {
const kindArg = this.createTypeVar();
env.add(varExpr.text, new Forall([], [], kindArg), Symkind.Type);
env.add(name.text, new Forall([], [], kindArg), Symkind.Type);
kindArgs.push(kindArg);
}
const type = TApp.build(new TNominal(node, node), kindArgs);
parentEnv.add(node.name.text, new Forall(typeVars, constraints, type), Symkind.Type);
let elementTypes: Type[] = [];
const type = new TNominal(node, node);
if (node.members !== null) {
for (const member of node.members) {
let ctorType;
let ctorType, elementType;
switch (member.kind) {
case SyntaxKind.EnumDeclarationTupleElement:
{
const argTypes = member.elements.map(el => this.inferTypeExpression(el));
ctorType = TArrow.build(argTypes, TApp.build(type, kindArgs), member);
const argTypes = member.elements.map(el => this.inferTypeExpression(el, false));
elementType = new TTuple(argTypes, member);
ctorType = TArrow.build(argTypes, type, member);
break;
}
case SyntaxKind.EnumDeclarationStructElement:
{
ctorType = new TNil(member);
elementType = new TNil(member);
for (const field of member.fields) {
ctorType = new TField(field.name.text, new TPresent(this.inferTypeExpression(field.typeExpr)), ctorType, member);
elementType = new TField(field.name.text, new TPresent(this.inferTypeExpression(field.typeExpr, false)), elementType, member);
}
ctorType = new TArrow(TField.sort(ctorType), TApp.build(type, kindArgs));
elementType = TField.sort(elementType);
ctorType = new TArrow(elementType, type);
break;
}
default:
throw new Error(`Unexpected ${member}`);
}
// FIXME `typeVars` may contain too much irrelevant type variables
parentEnv.add(member.name.text, new Forall(typeVars, constraints, ctorType), Symkind.Var);
elementTypes.push(ctorType);
elementTypes.push(elementType);
}
}
this.popContext(context);
parentEnv.add(node.name.text, new Forall(typeVars, constraints, type), Symkind.Type);
break;
}
@ -2013,37 +2149,17 @@ export class Checker {
public check(node: SourceFile): void {
const kenv = new KindEnv();
kenv.set('Int', new KType());
kenv.set('String', new KType());
kenv.set('Bool', new KType());
const skenv = new KindEnv(kenv);
this.forwardDeclareKind(node, skenv);
this.inferKind(node, skenv);
const kenv = new KindEnv(this.globalKindEnv);
this.forwardDeclareKind(node, kenv);
this.inferKind(node, kenv);
const typeVars = new TVSet();
const constraints = new ConstraintSet();
const env = new TypeEnv();
const env = new TypeEnv(this.globalTypeEnv);
const context: InferContext = { typeVars, constraints, env, returnType: null };
this.pushContext(context);
const a = this.createTypeVar();
const b = this.createTypeVar();
env.add('$', new Forall([ a, b ], [], new TArrow(new TArrow(new TArrow(a, b), a), b)), Symkind.Var);
env.add('String', new Forall([], [], this.stringType), Symkind.Type);
env.add('Int', new Forall([], [], this.intType), Symkind.Type);
env.add('Bool', new Forall([], [], this.boolType), Symkind.Type);
env.add('True', new Forall([], [], this.boolType), Symkind.Var);
env.add('False', new Forall([], [], this.boolType), Symkind.Var);
env.add('+', new Forall([], [], TArrow.build([ this.intType, this.intType ], this.intType)), Symkind.Var);
env.add('-', new Forall([], [], TArrow.build([ this.intType, this.intType ], this.intType)), Symkind.Var);
env.add('*', new Forall([], [], TArrow.build([ this.intType, this.intType ], this.intType)), Symkind.Var);
env.add('/', new Forall([], [], TArrow.build([ this.intType, this.intType ], this.intType)), Symkind.Var);
env.add('==', new Forall([ a ], [], TArrow.build([ a, a ], this.boolType)), Symkind.Var);
env.add('not', new Forall([], [], new TArrow(this.boolType, this.boolType)), Symkind.Var);
this.initialize(node, env);
this.pushContext({
@ -2141,7 +2257,6 @@ export class Checker {
}
const visitElements = (elements: Syntax[]) => {
for (const element of elements) {
if (element.kind === SyntaxKind.LetDeclaration
&& isFunctionDeclarationLike(element)) {
@ -2152,14 +2267,21 @@ export class Checker {
this.instantiate(scheme, null);
}
} else {
const elementHasTypeEnv = hasTypeEnv(element);
if (elementHasTypeEnv) {
this.pushContext({ ...this.getContext(), env: element.typeEnv! });
}
this.infer(element);
if(elementHasTypeEnv) {
this.contexts.pop();
}
}
}
}
for (const nodes of sccs) {
if (nodes.some(n => n.kind === SyntaxKind.SourceFile)) {
if (nodes[0].kind === SyntaxKind.SourceFile) {
assert(nodes.length === 1);
continue;
}
@ -2221,24 +2343,30 @@ export class Checker {
}
private lookupClass(name: string): ClassDeclaration | null {
return this.classDecls.get(name) ?? null;
}
private *findInstanceContext(type: TCon, clazz: ClassDeclaration): Iterable<ClassDeclaration[]> {
for (const instance of clazz.getInstances()) {
assert(instance.constraint.types.length === 1);
const instTy0 = instance.constraint.types[0];
if (instTy0.kind === SyntaxKind.AppTypeExpression
assert(instance.types.length === 1);
const instTy0 = instance.types[0];
if ((instTy0.kind === SyntaxKind.AppTypeExpression
&& instTy0.operator.kind === SyntaxKind.ReferenceTypeExpression
&& instTy0.operator.name.text === type.displayName) {
if (instance.constraints === null) {
&& instTy0.operator.name.text === type.displayName)
|| (instTy0.kind === SyntaxKind.ReferenceTypeExpression
&& instTy0.name.text === type.displayName)) {
if (instance.constraintClause === null) {
return;
}
for (const argType of type.argTypes) {
const classes = [];
for (const constraint of instance.constraints.constraints) {
for (const constraint of instance.constraintClause.constraints) {
assert(constraint.types.length === 1);
const classDecl = this.lookupClass(constraint.name);
const classDecl = this.lookupClass(constraint.name.text);
if (classDecl === null) {
this.diagnostics.add(new TypeclassNotFoundDiagnostic(constraint.name.text, constraint.name));
this.diagnostics.add(new TypeclassNotFoundDiagnostic(constraint.name));
} else {
classes.push(classDecl);
}
@ -2296,7 +2424,7 @@ export class Checker {
assert(right.kind === TypeKind.Present);
const fieldName = path[path.length-1];
this.diagnostics.add(
new FieldMissingDiagnostic(fieldName, left.node, right.type.node, constraint.firstNode)
new FieldNotFoundDiagnostic(fieldName, left.node, right.type.node, constraint.firstNode)
);
return false;
}
@ -2305,6 +2433,13 @@ export class Checker {
return unify(left.type, right.type);
}
const unifyPred = (left: Pred, right: Pred) => {
if (left.id === right.id) {
return unify(left.type, right.type);
}
throw new Error(`Classes do not match and no diagnostic defined`);
}
const unify = (left: Type, right: Type): boolean => {
left = find(left);
@ -2342,6 +2477,7 @@ export class Checker {
propagateClassTCon(constraint, type);
}
} else {
//assert(false);
//this.diagnostics.add(new );
}
}
@ -2475,7 +2611,7 @@ export class Checker {
}
this.diagnostics.add(
new UnificationFailedDiagnostic(
new TypeMismatchDiagnostic(
left.substitute(solution),
right.substitute(solution),
[...constraint.getNodes()],
@ -2512,3 +2648,18 @@ function getVariadicMember(node: StructPattern) {
return null;
}
type HasTypeEnv
= ClassDeclaration
| InstanceDeclaration
| LetDeclaration
| ModuleDeclaration
| SourceFile
function hasTypeEnv(node: Syntax): node is HasTypeEnv {
return node.kind === SyntaxKind.ClassDeclaration
|| node.kind === SyntaxKind.InstanceDeclaration
|| node.kind === SyntaxKind.LetDeclaration
|| node.kind === SyntaxKind.ModuleDeclaration
|| node.kind === SyntaxKind.SourceFile
}

View file

@ -2,7 +2,7 @@
import type stream from "stream";
import path from "path"
import { assert, IndentWriter, JSONObject, JSONValue } from "./util";
import { assert, implementationLimitation, IndentWriter, JSONObject, JSONValue } from "./util";
import { isNodeWithScope, Scope } from "./scope"
import { InferContext, Kind, KindEnv, Scheme, Type, TypeEnv } from "./checker"
import { Emitter } from "./emitter";
@ -2828,22 +2828,33 @@ export class ClassDeclaration extends SyntaxBase {
public readonly kind = SyntaxKind.ClassDeclaration;
public typeEnv?: TypeEnv;
public constructor(
public pubKeyword: PubKeyword | null,
public classKeyword: ClassKeyword,
public constraints: ClassConstraintClause | null,
public constraint: ClassConstraint,
public constraintClause: ClassConstraintClause | null,
public name: IdentifierAlt,
public types: VarTypeExpression[],
public elements: ClassDeclarationElement[],
) {
super();
}
public *getSupers(): Iterable<IdentifierAlt> {
if (this.constraintClause !== null) {
for (const constraint of this.constraintClause.constraints) {
yield constraint.name;
}
}
}
public lookup(element: InstanceDeclarationElement): ClassDeclarationElement | null {
switch (element.kind) {
case SyntaxKind.LetDeclaration:
assert(element.pattern.kind === SyntaxKind.NamedPattern);
implementationLimitation(element.pattern.kind === SyntaxKind.NamedPattern);
for (const other of this.elements) {
if (other.kind === SyntaxKind.LetDeclaration
&& other.pattern.kind === SyntaxKind.NamedPattern
@ -2875,7 +2886,7 @@ export class ClassDeclaration extends SyntaxBase {
curr = curr.parent!;
}
for (const element of getElements(curr)) {
if (element.kind === SyntaxKind.InstanceDeclaration && element.constraint.name === this.constraint.name) {
if (element.kind === SyntaxKind.InstanceDeclaration && element.name.text === this.name.text) {
yield element;
}
}
@ -2886,8 +2897,9 @@ export class ClassDeclaration extends SyntaxBase {
return new ClassDeclaration(
this.pubKeyword !== null ? this.pubKeyword.clone() : null,
this.classKeyword.clone(),
this.constraints !== null ? this.constraints.clone() : null,
this.constraint.clone(),
this.constraintClause !== null ? this.constraintClause.clone() : null,
this.name.clone(),
this.types.map(t => t.clone()),
this.elements.map(element => element.clone()),
);
}
@ -2903,7 +2915,10 @@ export class ClassDeclaration extends SyntaxBase {
if (this.elements.length > 0) {
return this.elements[this.elements.length-1].getLastToken();
}
return this.constraint.getLastToken();
if (this.types.length > 0) {
return this.types[this.types.length-1].getLastToken();
}
return this.name;
}
}
@ -2916,11 +2931,14 @@ export class InstanceDeclaration extends SyntaxBase {
public readonly kind = SyntaxKind.InstanceDeclaration;
public typeEnv?: TypeEnv;
public constructor(
public pubKeyword: PubKeyword | null,
public classKeyword: InstanceKeyword,
public constraints: ClassConstraintClause | null,
public constraint: ClassConstraint,
public constraintClause: ClassConstraintClause | null,
public name: IdentifierAlt,
public types: TypeExpression[],
public elements: InstanceDeclarationElement[],
) {
super();
@ -2930,8 +2948,9 @@ export class InstanceDeclaration extends SyntaxBase {
return new InstanceDeclaration(
this.pubKeyword !== null ? this.pubKeyword.clone() : null,
this.classKeyword.clone(),
this.constraints !== null ? this.constraints.clone() : null,
this.constraint.clone(),
this.constraintClause !== null ? this.constraintClause.clone() : null,
this.name.clone(),
this.types.map(t => t.clone()),
this.elements.map(element => element.clone()),
);
}
@ -2947,7 +2966,10 @@ export class InstanceDeclaration extends SyntaxBase {
if (this.elements.length > 0) {
return this.elements[this.elements.length-1].getLastToken();
}
return this.constraint.getLastToken();
if (this.types.length > 0) {
return this.types[this.types.length-1].getLastToken();
}
return this.name;
}
}
@ -3095,7 +3117,7 @@ export function vistEachChild<T extends Syntax>(node: T, proc: (node: Syntax) =>
if (!changed) {
return node;
}
return new node.constructor(...newArgs);
return new (node as any).constructor(...newArgs);
}
export function canHaveInstanceDeclaration(node: Syntax): boolean {

View file

@ -1,7 +1,7 @@
import { TypeKind, type Type, type TArrow, TRecord, Kind, KindType } from "./checker";
import { Syntax, SyntaxKind, TextFile, TextPosition, TextRange, Token } from "./cst";
import { countDigits, IndentWriter } from "./util";
import { TypeKind, type Type, Kind, KindType } from "./checker";
import { ClassConstraint, ClassDeclaration, IdentifierAlt, InstanceDeclaration, Syntax, SyntaxKind, TextFile, TextPosition, TextRange, Token } from "./cst";
import { assertNever, countDigits, IndentWriter } from "./util";
const ANSI_RESET = "\u001b[0m"
const ANSI_BOLD = "\u001b[1m"
@ -35,9 +35,28 @@ const enum Level {
Fatal,
}
export class UnexpectedCharDiagnostic {
const enum DiagnosticKind {
UnexpectedChar,
UnexpectedToken,
KindMismatch,
TypeMismatch,
TypeclassNotFound,
TypeclassDecaredTwice,
BindingNotFound,
ModuleNotFound,
FieldNotFound,
}
public readonly level = Level.Error;
interface DiagnosticBase {
level: Level;
readonly kind: DiagnosticKind;
}
export class UnexpectedCharDiagnostic implements DiagnosticBase {
public readonly kind = DiagnosticKind.UnexpectedChar;
public level = Level.Error;
public constructor(
public file: TextFile,
@ -47,12 +66,322 @@ export class UnexpectedCharDiagnostic {
}
public format(out: IndentWriter): void {
const endPos = this.position.clone();
endPos.advance(this.actual);
out.write(ANSI_FG_RED + ANSI_BOLD + 'error: ' + ANSI_RESET);
out.write(`unexpected character sequence '${this.actual}'.\n\n`);
out.write(printExcerpt(this.file, new TextRange(this.position, endPos)) + '\n');
}
export class UnexpectedTokenDiagnostic implements DiagnosticBase {
public readonly kind = DiagnosticKind.UnexpectedToken;
public level = Level.Error;
public constructor(
public file: TextFile,
public actual: Token,
public expected: SyntaxKind[],
) {
}
}
export class TypeclassDeclaredTwiceDiagnostic implements DiagnosticBase {
public readonly kind = DiagnosticKind.TypeclassDecaredTwice;
public level = Level.Error;
public constructor(
public name: IdentifierAlt,
public origDecl: ClassDeclaration,
) {
}
}
export class TypeclassNotFoundDiagnostic implements DiagnosticBase {
public readonly kind = DiagnosticKind.TypeclassNotFound;
public level = Level.Error;
public constructor(
public name: IdentifierAlt,
public origin: InstanceDeclaration | ClassConstraint | null = null,
) {
}
}
export class BindingNotFoundDiagnostic implements DiagnosticBase {
public readonly kind = DiagnosticKind.BindingNotFound;
public level = Level.Error;
public constructor(
public modulePath: string[],
public name: string,
public node: Syntax,
) {
}
}
export class TypeMismatchDiagnostic implements DiagnosticBase {
public readonly kind = DiagnosticKind.TypeMismatch;
public level = Level.Error;
public constructor(
public left: Type,
public right: Type,
public trace: Syntax[],
public fieldPath: string[],
) {
}
}
export class FieldNotFoundDiagnostic implements DiagnosticBase {
public readonly kind = DiagnosticKind.FieldNotFound;
public level = Level.Error;
public constructor(
public fieldName: string,
public missing: Syntax | null,
public present: Syntax | null,
public cause: Syntax | null = null,
) {
}
}
export class KindMismatchDiagnostic implements DiagnosticBase {
public readonly kind = DiagnosticKind.KindMismatch;
public level = Level.Error;
public constructor(
public left: Kind,
public right: Kind,
public origin: Syntax | null,
) {
}
}
export class ModuleNotFoundDiagnostic implements DiagnosticBase {
public readonly kind = DiagnosticKind.ModuleNotFound;
public level = Level.Error;
public constructor(
public modulePath: string[],
public node: Syntax,
) {
}
}
export type Diagnostic
= UnexpectedCharDiagnostic
| TypeclassNotFoundDiagnostic
| TypeclassDeclaredTwiceDiagnostic
| BindingNotFoundDiagnostic
| TypeMismatchDiagnostic
| UnexpectedTokenDiagnostic
| FieldNotFoundDiagnostic
| KindMismatchDiagnostic
| ModuleNotFoundDiagnostic
export interface Diagnostics {
readonly hasError: boolean;
readonly hasFatal: boolean;
add(diagnostic: Diagnostic): void;
}
export class DiagnosticStore implements Diagnostics {
private storage: Diagnostic[] = [];
public hasError = false;
public hasFatal = false;
public add(diagnostic: Diagnostic): void {
this.storage.push(diagnostic);
if (diagnostic.level >= Level.Error) {
this.hasError = true;
}
if (diagnostic.level >= Level.Fatal) {
this.hasFatal = true;
}
}
public getDiagnostics(): Iterable<Diagnostic> {
return this.storage;
}
}
export class ConsoleDiagnostics implements Diagnostics {
private writer = new IndentWriter(process.stderr);
public hasError = false;
public hasFatal = false;
public add(diagnostic: Diagnostic): void {
if (diagnostic.level >= Level.Error) {
this.hasError = true;
}
if (diagnostic.level >= Level.Fatal) {
this.hasFatal = true;
}
switch (diagnostic.level) {
case Level.Fatal:
this.writer.write(ANSI_FG_RED + ANSI_BOLD + 'fatal: ' + ANSI_RESET);
break;
case Level.Error:
this.writer.write(ANSI_FG_RED + ANSI_BOLD + 'error: ' + ANSI_RESET);
break;
case Level.Warning:
this.writer.write(ANSI_FG_RED + ANSI_BOLD + 'warning: ' + ANSI_RESET);
break;
case Level.Info:
this.writer.write(ANSI_FG_YELLOW + ANSI_BOLD + 'info: ' + ANSI_RESET);
break;
case Level.Verbose:
this.writer.write(ANSI_FG_CYAN + ANSI_BOLD + 'verbose: ' + ANSI_RESET);
break;
}
switch (diagnostic.kind) {
case DiagnosticKind.UnexpectedChar:
const endPos = diagnostic.position.clone();
endPos.advance(diagnostic.actual);
this.writer.write(`unexpected character sequence '${diagnostic.actual}'.\n\n`);
this.writer.write(printExcerpt(diagnostic.file, new TextRange(diagnostic.position, endPos)) + '\n');
break;
case DiagnosticKind.UnexpectedToken:
this.writer.write(`expected ${describeExpected(diagnostic.expected)} but got ${describeActual(diagnostic.actual)}\n\n`);
this.writer.write(printExcerpt(diagnostic.file, diagnostic.actual.getRange()) + '\n');
break;
case DiagnosticKind.TypeclassDecaredTwice:
this.writer.write(`type class '${diagnostic.name.text}' was already declared somewhere else.\n\n`);
this.writer.write(ANSI_FG_YELLOW + ANSI_BOLD + 'info: ' + ANSI_RESET);
this.writer.write(`type class '${diagnostic.name.text}' is already declared here\n\n`);
this.writer.write(printNode(diagnostic.origDecl) + '\n');
break;
case DiagnosticKind.TypeclassNotFound:
this.writer.write(`the type class ${ANSI_FG_MAGENTA + diagnostic.name.text + ANSI_RESET} was not found.\n\n`);
this.writer.write(printNode(diagnostic.name) + '\n');
if (diagnostic.origin !== null) {
this.writer.indent();
this.writer.write(ANSI_FG_YELLOW + ANSI_BOLD + 'info: ' + ANSI_RESET);
this.writer.write(`${ANSI_FG_MAGENTA + diagnostic.name.text + ANSI_RESET} is required by ${ANSI_FG_MAGENTA + diagnostic.origin.name.text + ANSI_RESET}\n\n`);
this.writer.write(printNode(diagnostic.origin.name) + '\n');
this.writer.dedent();
}
break;
case DiagnosticKind.BindingNotFound:
this.writer.write(`binding '${diagnostic.name}' was not found`);
if (diagnostic.modulePath.length > 0) {
this.writer.write(` in module ${ANSI_FG_BLUE + diagnostic.modulePath.join('.') + ANSI_RESET}`);
}
this.writer.write(`.\n\n`);
this.writer.write(printNode(diagnostic.node) + '\n');
break;
case DiagnosticKind.TypeMismatch:
const leftNode = getFirstNodeInTypeChain(diagnostic.left);
const rightNode = getFirstNodeInTypeChain(diagnostic.right);
const node = diagnostic.trace[0];
this.writer.write(`unification of ` + ANSI_FG_GREEN + describeType(diagnostic.left) + ANSI_RESET);
this.writer.write(' and ' + ANSI_FG_GREEN + describeType(diagnostic.right) + ANSI_RESET + ' failed');
if (diagnostic.fieldPath.length > 0) {
this.writer.write(` in field '${diagnostic.fieldPath.join('.')}'`);
}
this.writer.write('.\n\n');
this.writer.write(printNode(node) + '\n');
for (let i = 1; i < diagnostic.trace.length; i++) {
const node = diagnostic.trace[i];
this.writer.write(' ... in an instantiation of the following expression\n\n');
this.writer.write(printNode(node, { indentation: i === 0 ? ' ' : ' ' }) + '\n');
}
if (leftNode !== null) {
this.writer.indent();
this.writer.write(ANSI_FG_YELLOW + ANSI_BOLD + `info: ` + ANSI_RESET);
this.writer.write(`type ` + ANSI_FG_GREEN + describeType(diagnostic.left) + ANSI_RESET + ` was inferred from diagnostic expression:\n\n`);
this.writer.write(printNode(leftNode) + '\n');
this.writer.dedent();
}
if (rightNode !== null) {
this.writer.indent();
this.writer.write(ANSI_FG_YELLOW + ANSI_BOLD + `info: ` + ANSI_RESET);
this.writer.write(`type ` + ANSI_FG_GREEN + describeType(diagnostic.right) + ANSI_RESET + ` was inferred from diagnostic expression:\n\n`);
this.writer.write(printNode(rightNode) + '\n');
this.writer.dedent();
}
break;
case DiagnosticKind.KindMismatch:
this.writer.write(`kind ${describeKind(diagnostic.left)} does not match with ${describeKind(diagnostic.right)}\n\n`);
if (diagnostic.origin !== null) {
this.writer.write(printNode(diagnostic.origin) + '\n');
}
break;
case DiagnosticKind.ModuleNotFound:
this.writer.write(`a module named ${ANSI_FG_BLUE + diagnostic.modulePath.join('.') + ANSI_RESET} was not found.\n\n`);
this.writer.write(printNode(diagnostic.node) + '\n');
break;
case DiagnosticKind.FieldNotFound:
this.writer.write(`field '${diagnostic.fieldName}' is required in one type but missing in another\n\n`);
this.writer.indent();
if (diagnostic.missing !== null) {
this.writer.write(ANSI_FG_YELLOW + ANSI_BOLD + 'info: ' + ANSI_RESET);
this.writer.write(`field '${diagnostic.fieldName}' is missing in diagnostic construct\n\n`);
this.writer.write(printNode(diagnostic.missing) + '\n');
}
if (diagnostic.present !== null) {
this.writer.write(ANSI_FG_YELLOW + ANSI_BOLD + 'info: ' + ANSI_RESET);
this.writer.write(`field '${diagnostic.fieldName}' is required in diagnostic construct\n\n`);
this.writer.write(printNode(diagnostic.present) + '\n');
}
if (diagnostic.cause !== null) {
this.writer.write(ANSI_FG_YELLOW + ANSI_BOLD + 'info: ' + ANSI_RESET);
this.writer.write(`because of a constraint on diagnostic node:\n\n`);
this.writer.write(printNode(diagnostic.cause) + '\n');
}
this.writer.dedent();
break;
default:
assertNever(diagnostic);
}
}
}
@ -60,6 +389,9 @@ export class UnexpectedCharDiagnostic {
const DESCRIPTIONS: Partial<Record<SyntaxKind, string>> = {
[SyntaxKind.StringLiteral]: 'a string literal',
[SyntaxKind.Identifier]: "an identifier",
[SyntaxKind.RArrow]: "'->'",
[SyntaxKind.RArrowAlt]: '"=>"',
[SyntaxKind.VBar]: "'|'",
[SyntaxKind.Comma]: "','",
[SyntaxKind.Colon]: "':'",
[SyntaxKind.Integer]: "an integer",
@ -86,8 +418,6 @@ const DESCRIPTIONS: Partial<Record<SyntaxKind, string>> = {
[SyntaxKind.BlockEnd]: 'the end of an indented block',
[SyntaxKind.LineFoldEnd]: 'the end of the current line-fold',
[SyntaxKind.EndOfFile]: 'end-of-file',
[SyntaxKind.RArrowAlt]: '"=>"',
[SyntaxKind.VBar]: "'|'",
}
function describeSyntaxKind(kind: SyntaxKind): string {
@ -126,70 +456,6 @@ function describeActual(token: Token): string {
}
}
export class UnexpectedTokenDiagnostic {
public readonly level = Level.Error;
public constructor(
public file: TextFile,
public actual: Token,
public expected: SyntaxKind[],
) {
}
public format(out: IndentWriter): void {
out.write(ANSI_FG_RED + ANSI_BOLD + 'fatal: ' + ANSI_RESET);
out.write(`expected ${describeExpected(this.expected)} but got ${describeActual(this.actual)}\n\n`);
out.write(printExcerpt(this.file, this.actual.getRange()) + '\n');
}
}
export class TypeclassNotFoundDiagnostic {
public readonly level = Level.Error;
public constructor(
public name: string,
public node: Syntax,
) {
}
public format(out: IndentWriter): void {
out.write(ANSI_FG_RED + ANSI_BOLD + 'error: ' + ANSI_RESET);
out.write(`could not implement type class because class '${this.name}' was not found.\n\n`);
out.write(printNode(this.node) + '\n');
}
}
export class BindingNotFoundDiagnostic {
public readonly level = Level.Error;
public constructor(
public modulePath: string[],
public name: string,
public node: Syntax,
) {
}
public format(out: IndentWriter): void {
out.write(ANSI_FG_RED + ANSI_BOLD + 'error: ' + ANSI_RESET);
out.write(`binding '${this.name}' was not found`);
if (this.modulePath.length > 0) {
out.write(` in module ${ANSI_FG_BLUE + this.modulePath.join('.') + ANSI_RESET}`);
}
out.write(`.\n\n`);
out.write(printNode(this.node) + '\n');
}
}
export function describeType(type: Type): string {
switch (type.kind) {
case TypeKind.Con:
@ -267,196 +533,14 @@ function getFirstNodeInTypeChain(type: Type): Syntax | null {
return curr.node;
}
export class UnificationFailedDiagnostic {
public readonly level = Level.Error;
public constructor(
public left: Type,
public right: Type,
public nodes: Syntax[],
public path: string[],
) {
}
public format(out: IndentWriter): void {
const leftNode = getFirstNodeInTypeChain(this.left);
const rightNode = getFirstNodeInTypeChain(this.right);
const node = this.nodes[0];
out.write(ANSI_FG_RED + ANSI_BOLD + `error: ` + ANSI_RESET);
out.write(`unification of ` + ANSI_FG_GREEN + describeType(this.left) + ANSI_RESET);
out.write(' and ' + ANSI_FG_GREEN + describeType(this.right) + ANSI_RESET + ' failed');
if (this.path.length > 0) {
out.write(` in field '${this.path.join('.')}'`);
}
out.write('.\n\n');
out.write(printNode(node) + '\n');
for (let i = 1; i < this.nodes.length; i++) {
const node = this.nodes[i];
out.write(' ... in an instantiation of the following expression\n\n');
out.write(printNode(node, { indentation: i === 0 ? ' ' : ' ' }) + '\n');
}
if (leftNode !== null) {
out.indent();
out.write(ANSI_FG_YELLOW + ANSI_BOLD + `info: ` + ANSI_RESET);
out.write(`type ` + ANSI_FG_GREEN + describeType(this.left) + ANSI_RESET + ` was inferred from this expression:\n\n`);
out.write(printNode(leftNode) + '\n');
out.dedent();
}
if (rightNode !== null) {
out.indent();
out.write(ANSI_FG_YELLOW + ANSI_BOLD + `info: ` + ANSI_RESET);
out.write(`type ` + ANSI_FG_GREEN + describeType(this.right) + ANSI_RESET + ` was inferred from this expression:\n\n`);
out.write(printNode(rightNode) + '\n');
out.dedent();
}
}
}
export class FieldMissingDiagnostic {
public readonly level = Level.Error;
public constructor(
public fieldName: string,
public missing: Syntax | null,
public present: Syntax | null,
public cause: Syntax | null = null,
) {
}
public format(out: IndentWriter): void {
out.write(ANSI_FG_RED + ANSI_BOLD + 'error: ' + ANSI_RESET);
out.write(`field '${this.fieldName}' is required in one type but missing in another\n\n`);
out.indent();
if (this.missing !== null) {
out.write(ANSI_FG_YELLOW + ANSI_BOLD + 'info: ' + ANSI_RESET);
out.write(`field '${this.fieldName}' is missing in this construct\n\n`);
out.write(printNode(this.missing) + '\n');
}
if (this.present !== null) {
out.write(ANSI_FG_YELLOW + ANSI_BOLD + 'info: ' + ANSI_RESET);
out.write(`field '${this.fieldName}' is required in this construct\n\n`);
out.write(printNode(this.present) + '\n');
}
if (this.cause !== null) {
out.write(ANSI_FG_YELLOW + ANSI_BOLD + 'info: ' + ANSI_RESET);
out.write(`because of a constraint on this node:\n\n`);
out.write(printNode(this.cause) + '\n');
}
out.dedent();
}
}
export class KindMismatchDiagnostic {
public readonly level = Level.Error;
public constructor(
public left: Kind,
public right: Kind,
public node: Syntax | null,
) {
}
public format(out: IndentWriter): void {
out.write(ANSI_FG_RED + ANSI_BOLD + 'error: ' + ANSI_RESET);
out.write(`kind ${describeKind(this.left)} does not match with ${describeKind(this.right)}\n\n`);
if (this.node !== null) {
out.write(printNode(this.node) + '\n');
}
}
}
export class ModuleNotFoundDiagnostic {
public readonly level = Level.Error;
public constructor(
public modulePath: string[],
public node: Syntax,
) {
}
public format(out: IndentWriter): void {
out.write(ANSI_FG_RED + ANSI_BOLD + 'error: ' + ANSI_RESET);
out.write(`a module named ${ANSI_FG_BLUE + this.modulePath.join('.') + ANSI_RESET} was not found.\n\n`);
out.write(printNode(this.node) + '\n');
}
}
export type Diagnostic
= UnexpectedCharDiagnostic
| TypeclassNotFoundDiagnostic
| BindingNotFoundDiagnostic
| UnificationFailedDiagnostic
| UnexpectedTokenDiagnostic
| FieldMissingDiagnostic
| KindMismatchDiagnostic
| ModuleNotFoundDiagnostic
export interface Diagnostics {
readonly hasError: boolean;
readonly hasFatal: boolean;
add(diagnostic: Diagnostic): void;
}
export class DiagnosticStore {
private storage: Diagnostic[] = [];
public hasError = false;
public hasFatal = false;
public add(diagnostic: Diagnostic): void {
this.storage.push(diagnostic);
if (diagnostic.level >= Level.Error) {
this.hasError = true;
}
if (diagnostic.level >= Level.Fatal) {
this.hasFatal = true;
}
}
public getDiagnostics(): Iterable<Diagnostic> {
return this.storage;
}
}
export class ConsoleDiagnostics {
private writer = new IndentWriter(process.stderr);
public hasError = false;
public hasFatal = false;
public add(diagnostic: Diagnostic): void {
diagnostic.format(this.writer);
if (diagnostic.level >= Level.Error) {
this.hasError = true;
}
if (diagnostic.level >= Level.Fatal) {
this.hasFatal = true;
}
}
}
interface PrintExcerptOptions {
indentation?: string;
extraLineCount?: number;
}
function printNode(node: Syntax, options?: PrintExcerptOptions): string {
interface PrintNodeOptions extends PrintExcerptOptions { }
function printNode(node: Syntax, options?: PrintNodeOptions): string {
const file = node.getSourceFile().getFile();
return printExcerpt(file, node.getRange(), options);
}

View file

@ -167,14 +167,18 @@ export class Emitter {
this.writer.write('pub ');
}
this.writer.write(`class `);
if (node.constraints) {
for (const constraint of node.constraints.constraints) {
if (node.constraintClause) {
for (const constraint of node.constraintClause.constraints) {
this.emit(constraint);
this.writer.write(`, `);
}
this.writer.write(' => ');
}
this.emit(node.constraint);
this.emit(node.name);
for (const type of node.types) {
this.writer.write(' ');
this.emit(type);
}
if (node.elements !== null) {
this.writer.write('.\n');
this.writer.indent();

View file

@ -183,14 +183,18 @@ export class Parser {
return new ReferenceTypeExpression(modulePath, name);
}
public parseVarTypeExpression(): VarTypeExpression {
const name = this.expectToken(SyntaxKind.Identifier);
return new VarTypeExpression(name);
}
public parsePrimitiveTypeExpression(): TypeExpression {
const t0 = this.peekToken();
switch (t0.kind) {
case SyntaxKind.Identifier:
{
this.getToken();
return new VarTypeExpression(t0);
}
return this.parseVarTypeExpression();
case SyntaxKind.IdentifierAlt:
return this.parseReferenceTypeExpression();
case SyntaxKind.LParen:
{
this.getToken();
@ -220,14 +224,12 @@ export class Parser {
}
return new TupleTypeExpression(t0, elements, rparen);
}
case SyntaxKind.IdentifierAlt:
return this.parseReferenceTypeExpression();
default:
this.raiseParseError(t0, [ SyntaxKind.IdentifierAlt ]);
}
}
private tryParseAppTypeExpression(): TypeExpression {
private parseAppTypeExpressionOrBelow(): TypeExpression {
const operator = this.parsePrimitiveTypeExpression();
const args = [];
for (;;) {
@ -251,7 +253,7 @@ export class Parser {
}
public parseTypeExpression(): TypeExpression {
let returnType = this.tryParseAppTypeExpression();
let returnType = this.parseAppTypeExpressionOrBelow();
const paramTypes = [];
for (;;) {
const t1 = this.peekToken();
@ -260,7 +262,7 @@ export class Parser {
}
this.getToken();
paramTypes.push(returnType);
returnType = this.tryParseAppTypeExpression();
returnType = this.parseAppTypeExpressionOrBelow();
}
if (paramTypes.length === 0) {
return returnType;
@ -1048,13 +1050,22 @@ export class Parser {
}
clause = new ClassConstraintClause(constraints, rarrowAlt);
}
const constraint = this.parseClassConstraint();
const name = this.expectToken(SyntaxKind.IdentifierAlt);
const types = [];
for (;;) {
const t3 = this.peekToken();
if (t3.kind === SyntaxKind.BlockStart || t3.kind === SyntaxKind.LineFoldEnd) {
break;
}
const type = this.parseTypeExpression();
types.push(type);
}
this.expectToken(SyntaxKind.BlockStart);
const elements = [];
loop: for (;;) {
const t3 = this.peekToken();
const t4 = this.peekToken();
let element;
switch (t3.kind) {
switch (t4.kind) {
case SyntaxKind.BlockEnd:
this.getToken();
break loop;
@ -1065,12 +1076,12 @@ export class Parser {
element = this.parseTypeDeclaration();
break;
default:
this.raiseParseError(t3, [ SyntaxKind.LetKeyword, SyntaxKind.TypeKeyword, SyntaxKind.BlockEnd ]);
this.raiseParseError(t4, [ SyntaxKind.LetKeyword, SyntaxKind.TypeKeyword, SyntaxKind.BlockEnd ]);
}
elements.push(element);
}
this.expectToken(SyntaxKind.LineFoldEnd);
return new InstanceDeclaration(pubKeyword, t0, clause, constraint, elements);
return new InstanceDeclaration(pubKeyword, t0, clause, name, types, elements);
}
public parseClassDeclaration(): ClassDeclaration {
@ -1097,7 +1108,17 @@ export class Parser {
}
clause = new ClassConstraintClause(constraints, rarrowAlt);
}
const constraint = this.parseClassConstraint();
const name = this.expectToken(SyntaxKind.IdentifierAlt);
const types = [];
for (;;) {
const t1 = this.peekToken();
if (t1.kind === SyntaxKind.Identifier) {
const type = this.parseVarTypeExpression();
types.push(type);
} else {
break;
}
}
this.expectToken(SyntaxKind.BlockStart);
const elements = [];
loop: for (;;) {
@ -1119,7 +1140,7 @@ export class Parser {
elements.push(element);
}
this.expectToken(SyntaxKind.LineFoldEnd);
return new ClassDeclaration(pubKeyword, t0 as ClassKeyword, clause, constraint, elements);
return new ClassDeclaration(pubKeyword, t0 as ClassKeyword, clause, name, types, elements);
}
public parseSourceFileElement(): SourceFileElement {

View file

@ -45,7 +45,7 @@ function lcfirst(text: string): string {
export class TypeclassDictPassing implements Pass<SourceFile, SourceFile> {
private mangleInstance(node: InstanceDeclaration): string {
return lcfirst(node.constraint.name.text) + '_' + node.constraint.types.map(encode).join('');
return lcfirst(node.name.text) + '_' + node.types.map(encode).join('');
}
private visit(node: Syntax): Syntax {

View file

@ -57,7 +57,7 @@ export class Scope {
switch (node.kind) {
case SyntaxKind.ClassDeclaration:
{
this.add(node.constraint.name.text, node, Symkind.Typeclass);
this.add(node.name.text, node, Symkind.Typeclass);
}
case SyntaxKind.InstanceDeclaration:
case SyntaxKind.SourceFile:

View file

@ -2,6 +2,8 @@
import path from "path"
import stream from "stream"
export const isDebug = true;
export function first<T>(iter: Iterator<T>): T | undefined {
return iter.next().value;
}
@ -60,9 +62,17 @@ export class IndentWriter {
}
const GITHUB_ISSUE_URL = 'https://github.com/boltlang/bolt/issues/'
export function assert(test: boolean): asserts test {
if (!test) {
throw new Error(`Assertion failed. See the stack trace for more information.`);
throw new Error(`Assertion failed. See the stack trace for more information. You are invited to search this issue on GitHub or to create a new one at ${GITHUB_ISSUE_URL} .`);
}
}
export function implementationLimitation(test: boolean): asserts test {
if (!test) {
throw new Error(`We encountered a limitation to the implementation of this compiler. You are invited to search this issue on GitHub or to create a new one at ${GITHUB_ISSUE_URL} .`);
}
}