diff --git a/src/ast.ts b/src/ast.ts index ea45c26..e98fd66 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -1,5 +1,6 @@ import { ErrorEmitted, LoadedFile, Span, unreachable } from "./error"; import { LitIntType } from "./lexer"; +import { Ty, TyFn, TypeckResults } from "./types"; import { ComplexMap } from "./utils"; import { Instr, ValType } from "./wasm/defs"; @@ -507,161 +508,6 @@ export type LocalInfo = { // types -export type TyString = { - kind: "string"; -}; - -export type TyInt = { - kind: "int"; -}; - -export type TyI32 = { - kind: "i32"; -}; - -export type TyBool = { - kind: "bool"; -}; - -export type TyTuple = { - kind: "tuple"; - elems: Ty[]; -}; - -export type TyUnit = { - kind: "tuple"; - elems: []; -}; - -export type TyFn = { - kind: "fn"; - params: Ty[]; - returnTy: Ty; -}; - -export type TyVar = { - kind: "var"; - index: number; -}; - -export type TyStruct = { - kind: "struct"; - itemId: ItemId; - params: string[]; - genericArgs: Ty[]; - _name: string; - fields_no_subst: [string, Ty][]; -}; - -export type TyRawPtr = { - kind: "rawptr"; - inner: Ty; -}; - -export type TyNever = { - kind: "never"; -}; - -export type TyParam = { - kind: "param"; - /** - * The index of the type parameter of the parent. - * If the parent is `type A[T, U] = U;` - * then `U` will have index 1. - */ - idx: number; - name: string; -}; - -export type TyAlias = { - kind: "alias"; - actual: Ty; - genericArgs: Ty[]; - params: string[]; -}; - -export type TyError = { - kind: "error"; - err: ErrorEmitted; -}; - -export type Ty = - | TyString - | TyInt - | TyI32 - | TyBool - | TyTuple - | TyFn - | TyVar - | TyStruct - | TyRawPtr - | TyNever - | TyParam - | TyAlias - | TyError; - -export function tyIsUnit(ty: Ty): ty is TyUnit { - return ty.kind === "tuple" && ty.elems.length === 0; -} - -export const TY_UNIT: Ty = { kind: "tuple", elems: [] }; -export const TY_STRING: Ty = { kind: "string" }; -export const TY_BOOL: Ty = { kind: "bool" }; -export const TY_INT: Ty = { kind: "int" }; -export const TY_I32: Ty = { kind: "i32" }; -export const TY_NEVER: Ty = { kind: "never" }; - -export type TypeckResults = { - main: Resolution | undefined; -}; - -export function structFieldsSubstituted(ty: TyStruct): [string, Ty][] { - const args = ty.genericArgs; - return ty.fields_no_subst.map(([name, type]) => [ - name, - substituteTy(args, type), - ]); -} - -// Substitute the parameter of a type. We are only able to handle one -// level of generic definitions, for example for fields the struct def or for exprs the function generics. -export function substituteTy(genericArgs: Ty[], ty: Ty): Ty { - const subst = (ty: Ty) => substituteTy(genericArgs, ty); - switch (ty.kind) { - case "param": - if (ty.idx >= genericArgs.length) { - throw new Error( - `substitution out of range, param index ${ty.idx} of param ${ty.name} out of range for length ${genericArgs.length}`, - ); - } - return genericArgs[ty.idx]; - case "tuple": - return { ...ty, elems: ty.elems.map(subst) }; - case "fn": - return { - ...ty, - returnTy: subst(ty.returnTy), - params: ty.params.map(subst), - }; - case "struct": - case "alias": - return { - ...ty, - genericArgs: ty.genericArgs.map(subst), - }; - case "rawptr": - return { ...ty, inner: subst(ty.inner) }; - // Primitives - case "var": - case "string": - case "int": - case "i32": - case "bool": - case "never": - case "error": - return ty; - } -} // folders diff --git a/src/codegen.test.ts b/src/codegen.test.ts index e71e1c4..97804d6 100644 --- a/src/codegen.test.ts +++ b/src/codegen.test.ts @@ -1,5 +1,6 @@ -import { ItemId, TY_I32, TY_INT, TyStruct } from "./ast"; +import { ItemId } from "./ast"; import { layoutOfStruct } from "./codegen"; +import { TY_I32, TY_INT, TyStruct } from "./types"; it("should compute struct layout correctly", () => { const ty: TyStruct = { diff --git a/src/codegen.ts b/src/codegen.ts index d16bcf6..705205d 100644 --- a/src/codegen.ts +++ b/src/codegen.ts @@ -10,22 +10,17 @@ import { ItemId, LoopId, Resolution, - Ty, - TyFn, - TyStruct, - TyTuple, Typecked, mkDefaultFolder, superFoldExpr, superFoldItem, varUnreachable, - TyRawPtr, paramUnreachable as codegenUnreachableTy, - structFieldsSubstituted, } from "./ast"; import { GlobalContext } from "./context"; import { unreachable } from "./error"; import { printTy } from "./printer"; +import { Ty, TyFn, TyRawPtr, TyStruct, TyTuple, structFieldsSubstituted } from "./types"; import { ComplexMap, encodeUtf8, unwrap } from "./utils"; import * as wasm from "./wasm/defs"; diff --git a/src/printer.ts b/src/printer.ts index 408dafc..ca7534f 100644 --- a/src/printer.ts +++ b/src/printer.ts @@ -9,12 +9,10 @@ import { ItemMod, Resolution, StringLiteral, - Ty, Type, ItemType, - tyIsUnit, - substituteTy, } from "./ast"; +import { Ty, substituteTy, tyIsUnit } from "./types"; export function printAst(ast: Pkg): string { return ast.rootItems.map(printItem).join("\n"); diff --git a/src/typeck/base.ts b/src/typeck/base.ts index eb42073..83d0a62 100644 --- a/src/typeck/base.ts +++ b/src/typeck/base.ts @@ -1,6 +1,7 @@ -import { ItemId, Pkg, Resolved, Ty } from "../ast"; +import { ItemId, Pkg, Resolved } from "../ast"; import { GlobalContext } from "../context"; import { CompilerError, ErrorEmitted } from "../error"; +import { Ty } from "../types"; import { ComplexMap } from "../utils"; export type TypeckCtx = { diff --git a/src/typeck/expr.ts b/src/typeck/expr.ts index a9eb8cc..d502ff0 100644 --- a/src/typeck/expr.ts +++ b/src/typeck/expr.ts @@ -13,14 +13,6 @@ import { Resolution, Resolved, StructLiteralField, - TY_BOOL, - TY_I32, - TY_INT, - TY_NEVER, - TY_STRING, - TY_UNIT, - Ty, - TyFn, Type, Typecked, mkDefaultFolder, @@ -28,6 +20,7 @@ import { } from "../ast"; import { CompilerError, ErrorEmitted, Span, unreachable } from "../error"; import { printTy } from "../printer"; +import { TY_BOOL, TY_I32, TY_INT, TY_NEVER, TY_STRING, TY_UNIT, Ty, TyFn } from "../types"; import { INSTRS, Instr, VALTYPES, ValType } from "../wasm/defs"; import { TypeckCtx, emitError, mkTyFn, tyError, tyErrorFrom } from "./base"; import { InferContext } from "./infer"; diff --git a/src/typeck/index.ts b/src/typeck/index.ts index 0b996f3..8c313e6 100644 --- a/src/typeck/index.ts +++ b/src/typeck/index.ts @@ -5,17 +5,13 @@ import { Item, ItemId, Resolved, - TY_I32, - TY_INT, - Ty, - TyFn, Typecked, foldAst, mkDefaultFolder, - tyIsUnit, } from "../ast"; import { GlobalContext } from "../context"; import { CompilerError, ErrorEmitted, Span } from "../error"; +import { TY_I32, TY_INT, Ty, TyFn, tyIsUnit } from "../types"; import { ComplexMap } from "../utils"; import { emitError } from "./base"; import { checkBody, exprError } from "./expr"; diff --git a/src/typeck/infer.test.ts b/src/typeck/infer.test.ts index 4453411..49d4a5d 100644 --- a/src/typeck/infer.test.ts +++ b/src/typeck/infer.test.ts @@ -1,5 +1,5 @@ -import { TY_INT, TY_STRING, TY_UNIT } from "../ast"; import { Emitter, ErrorHandler, Span } from "../error"; +import { TY_INT, TY_STRING, TY_UNIT } from "../types"; import { InferContext } from "./infer"; const SPAN: Span = Span.startOfFile({ content: "" }); diff --git a/src/typeck/infer.ts b/src/typeck/infer.ts index 9f75947..5a4fa8e 100644 --- a/src/typeck/infer.ts +++ b/src/typeck/infer.ts @@ -1,6 +1,6 @@ -import { Ty } from "../ast"; import { CompilerError, ErrorHandler, Span } from "../error"; import { printTy } from "../printer"; +import { Ty } from "../types"; type TyVarRes = | { diff --git a/src/typeck/item.ts b/src/typeck/item.ts index bb5cd15..ef8ea1c 100644 --- a/src/typeck/item.ts +++ b/src/typeck/item.ts @@ -1,18 +1,11 @@ import { ItemId, Resolved, - Ty, - TY_BOOL, - TY_I32, - TY_INT, - TY_NEVER, - TY_STRING, - TY_UNIT, Type, - substituteTy, } from "../ast"; import { CompilerError, Span } from "../error"; import { printTy } from "../printer"; +import { TY_BOOL, TY_I32, TY_INT, TY_NEVER, TY_STRING, TY_UNIT, Ty, substituteTy } from "../types"; import { TypeckCtx, tyError, tyErrorFrom } from "./base"; function builtinAsTy(cx: TypeckCtx, name: string, span: Span): Ty { diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..0b65819 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,158 @@ +import { ItemId, Resolution } from "./ast"; +import { ErrorEmitted } from "./error"; + +export type TyString = { + kind: "string"; +}; + +export type TyInt = { + kind: "int"; +}; + +export type TyI32 = { + kind: "i32"; +}; + +export type TyBool = { + kind: "bool"; +}; + +export type TyTuple = { + kind: "tuple"; + elems: Ty[]; +}; + +export type TyUnit = { + kind: "tuple"; + elems: []; +}; + +export type TyFn = { + kind: "fn"; + params: Ty[]; + returnTy: Ty; +}; + +export type TyVar = { + kind: "var"; + index: number; +}; + +export type TyStruct = { + kind: "struct"; + itemId: ItemId; + params: string[]; + genericArgs: Ty[]; + _name: string; + fields_no_subst: [string, Ty][]; +}; + +export type TyRawPtr = { + kind: "rawptr"; + inner: Ty; +}; + +export type TyNever = { + kind: "never"; +}; + +export type TyParam = { + kind: "param"; + /** + * The index of the type parameter of the parent. + * If the parent is `type A[T, U] = U;` + * then `U` will have index 1. + */ + idx: number; + name: string; +}; + +export type TyAlias = { + kind: "alias"; + actual: Ty; + genericArgs: Ty[]; + params: string[]; +}; + +export type TyError = { + kind: "error"; + err: ErrorEmitted; +}; + +export type Ty = + | TyString + | TyInt + | TyI32 + | TyBool + | TyTuple + | TyFn + | TyVar + | TyStruct + | TyRawPtr + | TyNever + | TyParam + | TyAlias + | TyError; + +export function tyIsUnit(ty: Ty): ty is TyUnit { + return ty.kind === "tuple" && ty.elems.length === 0; +} + +export const TY_UNIT: Ty = { kind: "tuple", elems: [] }; +export const TY_STRING: Ty = { kind: "string" }; +export const TY_BOOL: Ty = { kind: "bool" }; +export const TY_INT: Ty = { kind: "int" }; +export const TY_I32: Ty = { kind: "i32" }; +export const TY_NEVER: Ty = { kind: "never" }; + +export type TypeckResults = { + main: Resolution | undefined; +}; + +export function structFieldsSubstituted(ty: TyStruct): [string, Ty][] { + const args = ty.genericArgs; + return ty.fields_no_subst.map(([name, type]) => [ + name, + substituteTy(args, type), + ]); +} + +// Substitute the parameter of a type. We are only able to handle one +// level of generic definitions, for example for fields the struct def or for exprs the function generics. +export function substituteTy(genericArgs: Ty[], ty: Ty): Ty { + const subst = (ty: Ty) => substituteTy(genericArgs, ty); + switch (ty.kind) { + case "param": + if (ty.idx >= genericArgs.length) { + throw new Error( + `substitution out of range, param index ${ty.idx} of param ${ty.name} out of range for length ${genericArgs.length}`, + ); + } + return genericArgs[ty.idx]; + case "tuple": + return { ...ty, elems: ty.elems.map(subst) }; + case "fn": + return { + ...ty, + returnTy: subst(ty.returnTy), + params: ty.params.map(subst), + }; + case "struct": + case "alias": + return { + ...ty, + genericArgs: ty.genericArgs.map(subst), + }; + case "rawptr": + return { ...ty, inner: subst(ty.inner) }; + // Primitives + case "var": + case "string": + case "int": + case "i32": + case "bool": + case "never": + case "error": + return ty; + } +} diff --git a/ui-tests/type/generics/generics_structs_in_args.stderr b/ui-tests/type/generics/generics_structs_in_args.stderr index f4499a9..a668fbc 100644 --- a/ui-tests/type/generics/generics_structs_in_args.stderr +++ b/ui-tests/type/generics/generics_structs_in_args.stderr @@ -14,4 +14,4 @@ Error: substitution out of range, param index 0 of param T out of range for leng at Array.map () at foldAst (/home/nils/projects/riverdelta/target/ast.js:164:34) -Node.js v18.18.2 +Node.js v20.10.0 diff --git a/ui-tests/type/generics/structs.stderr b/ui-tests/type/generics/structs.stderr index f4499a9..a668fbc 100644 --- a/ui-tests/type/generics/structs.stderr +++ b/ui-tests/type/generics/structs.stderr @@ -14,4 +14,4 @@ Error: substitution out of range, param index 0 of param T out of range for leng at Array.map () at foldAst (/home/nils/projects/riverdelta/target/ast.js:164:34) -Node.js v18.18.2 +Node.js v20.10.0 diff --git a/ui-tests/type/generics/wrong_amount.stderr b/ui-tests/type/generics/wrong_amount.stderr index f4499a9..a668fbc 100644 --- a/ui-tests/type/generics/wrong_amount.stderr +++ b/ui-tests/type/generics/wrong_amount.stderr @@ -14,4 +14,4 @@ Error: substitution out of range, param index 0 of param T out of range for leng at Array.map () at foldAst (/home/nils/projects/riverdelta/target/ast.js:164:34) -Node.js v18.18.2 +Node.js v20.10.0