diff --git a/src/ast.ts b/src/ast.ts index cb5de7a..3120960 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -411,10 +411,6 @@ export type TypeKind

= generics: Type

[]; value: IdentWithRes

; } - | { - kind: "list"; - elem: Type

; - } | { kind: "tuple"; elems: Type

[]; @@ -517,11 +513,6 @@ export type TyBool = { kind: "bool"; }; -export type TyList = { - kind: "list"; - elem: Ty; -}; - export type TyTuple = { kind: "tuple"; elems: Ty[]; @@ -549,7 +540,7 @@ export type TyStruct = { params: string[]; args: Ty[]; _name: string; - fields: [string, Ty][]; + fields_no_subst: [string, Ty][]; }; export type TyRawPtr = { @@ -582,7 +573,6 @@ export type Ty = | TyInt | TyI32 | TyBool - | TyList | TyTuple | TyFn | TyVar @@ -607,6 +597,49 @@ export type TypeckResults = { main: Resolution | undefined; }; +export function structFieldsSubstituted(ty: TyStruct): [string, Ty][] { + const args = ty.args; + 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(params: Ty[], ty: Ty): Ty { + const subst = (ty: Ty) => substituteTy(params, ty); + + switch (ty.kind) { + case "param": + return params[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": + return { + ...ty, + args: ty.args.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 export type FoldFn = (value: From) => To; @@ -894,13 +927,6 @@ export function superFoldType( span, }; } - case "list": { - return { - kind: "list", - elem: folder.type(type.elem), - span, - }; - } case "tuple": { return { kind: "tuple", diff --git a/src/codegen.test.ts b/src/codegen.test.ts index 91015e1..3859ef3 100644 --- a/src/codegen.test.ts +++ b/src/codegen.test.ts @@ -8,7 +8,7 @@ it("should compute struct layout correctly", () => { args: [], params: [], _name: "", - fields: [ + fields_no_subst: [ ["uwu", TY_I32], ["owo", TY_INT], ], @@ -55,7 +55,7 @@ it("should compute single field struct layout correctly", () => { args: [], params: [], _name: "", - fields: [["owo", TY_INT]], + fields_no_subst: [["owo", TY_INT]], }; const layout = layoutOfStruct(ty); diff --git a/src/codegen.ts b/src/codegen.ts index 7e2a063..b6d5879 100644 --- a/src/codegen.ts +++ b/src/codegen.ts @@ -21,6 +21,7 @@ import { varUnreachable, TyRawPtr, paramUnreachable, + structFieldsSubstituted, } from "./ast"; import { GlobalContext } from "./context"; import { unreachable } from "./error"; @@ -1247,8 +1248,6 @@ function argRetAbi(param: Ty): ArgRetAbi { return ["i32"]; case "bool": return ["i32"]; - case "list": - todo("list abi"); case "tuple": return param.elems.flatMap(argRetAbi); case "struct": @@ -1304,8 +1303,6 @@ function wasmTypeForBody(ty: Ty): wasm.ValType[] { return ["i32"]; case "bool": return ["i32"]; - case "list": - todo("list types"); case "tuple": return ty.elems.flatMap(wasmTypeForBody); case "fn": @@ -1344,7 +1341,8 @@ export function layoutOfStruct(ty_: TyStruct | TyRawPtr): StructLayout { if (ty.kind !== "struct") { unreachable("must be struct"); } - const fieldWasmTys = ty.fields.map(([, field]) => wasmTypeForBody(field)); + const fieldTys = structFieldsSubstituted(ty).map(([_, ty]) => ty); + const fieldWasmTys = fieldTys.map((field) => wasmTypeForBody(field)); // TODO: Use the max alignment instead. const align = fieldWasmTys.some((field) => @@ -1359,7 +1357,7 @@ export function layoutOfStruct(ty_: TyStruct | TyRawPtr): StructLayout { const fields: StructFieldLayout[] = fieldWasmTys.map((field, i) => { const value: StructFieldLayout = { types: [], - ty: ty.fields[i][1], + ty: fieldTys[i], }; const types = field.map((type) => { @@ -1457,8 +1455,6 @@ function needsRefcount(ty: Ty): StructLayout | "string" | undefined { return undefined; case "struct": return layoutOfStruct(ty); - case "list": - todo("no lists yet"); case "var": varUnreachable(); default: diff --git a/src/parser.ts b/src/parser.ts index 2ce727f..6824197 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -700,12 +700,6 @@ function parseType(t: State): [State, Type] { }, ]; } - case "[": { - let elem; - [t, elem] = parseType(t); - [t] = expectNext(t, "]"); - return [t, { kind: "list", elem, span }]; - } case "(": { // `()` is a the unit type, an empty tuple. // `(T)` is just `T` diff --git a/src/printer.ts b/src/printer.ts index d00544e..e6a4f0c 100644 --- a/src/printer.ts +++ b/src/printer.ts @@ -219,8 +219,6 @@ function printType(type: Type): string { switch (type.kind) { case "ident": return printIdent(type.value); - case "list": - return `[${printType(type.elem)}]`; case "tuple": return `(${type.elems.map(printType).join(", ")})`; case "rawptr": @@ -269,9 +267,6 @@ export function printTy(ty: Ty): string { case "bool": { return "Bool"; } - case "list": { - return `[${printTy(ty.elem)}]`; - } case "tuple": { return `(${ty.elems.map(printTy).join(", ")})`; } diff --git a/src/typeck.ts b/src/typeck.ts index 325cf35..9130bb4 100644 --- a/src/typeck.ts +++ b/src/typeck.ts @@ -183,12 +183,6 @@ function lowerAstTy(cx: TypeckCtx, type: Type): Ty { return ty; } - case "list": { - return { - kind: "list", - elem: lowerAstTy(cx, type.elem), - }; - } case "tuple": { return { kind: "tuple", @@ -286,7 +280,7 @@ function typeOfItem(cx: TypeckCtx, itemId: ItemId, cause: Span): Ty { params: item.generics.map((ident) => ident.name), itemId: item.id, _name: item.name, - fields: [ + fields_no_subst: [ /*dummy*/ ], }; @@ -297,7 +291,7 @@ function typeOfItem(cx: TypeckCtx, itemId: ItemId, cause: Span): Ty { ({ name, type }) => [name.name, lowerAstTy(cx, type)], ); - ty.fields = fields; + ty.fields_no_subst = fields; break; } case "alias": { @@ -667,13 +661,6 @@ export class InferContext { if (rhs.kind === "bool") return; break; } - case "list": { - if (rhs.kind === "list") { - this.assign(lhs.elem, rhs.elem, span); - return; - } - break; - } case "tuple": { if (rhs.kind === "tuple" && lhs.elems.length === rhs.elems.length) { lhs.elems.forEach((lhs, i) => this.assign(lhs, rhs.elems[i], span)); @@ -953,7 +940,7 @@ export function checkBody( case "rawptr": { let fields: [string, Ty][]; if (lhs.ty.kind === "struct") { - fields = lhs.ty.fields; + fields = lhs.ty.fields_no_subst; } else if (lhs.ty.kind === "rawptr") { let inner = fcx.infcx.resolveIfPossible(lhs.ty.inner); if (inner.kind !== "struct") { @@ -967,7 +954,7 @@ export function checkBody( ty = inner; break; } else { - fields = inner.fields; + fields = inner.fields_no_subst; } } else { fields = []; @@ -1100,7 +1087,7 @@ export function checkBody( const assignedFields = new Set(); fields.forEach(({ name, expr: field }, i) => { - const fieldIdx = structTy.fields.findIndex( + const fieldIdx = structTy.fields_no_subst.findIndex( (def) => def[0] === name.name, ); if (fieldIdx == -1) { @@ -1112,14 +1099,14 @@ export function checkBody( ), ); } - const fieldTy = structTy.fields[fieldIdx]; + const fieldTy = structTy.fields_no_subst[fieldIdx]; infcx.assign(fieldTy[1], field.ty, field.span); assignedFields.add(name.name); fields[i].fieldIdx = fieldIdx; }); const missing: string[] = []; - structTy.fields.forEach(([name]) => { + structTy.fields_no_subst.forEach(([name]) => { if (!assignedFields.has(name)) { missing.push(name); } diff --git a/test.nil b/test.nil index 7b7c8d8..60472c9 100644 --- a/test.nil +++ b/test.nil @@ -1,4 +1,6 @@ -function main() = ( - let list = std.list.new(); - std.list.push(list, 0); -); \ No newline at end of file +//@check-pass +type A[T] = struct { a: T }; + +function main() = ; + +function test(a: A[I32]) = ; diff --git a/ui-tests/type/generics/codegen.nil b/ui-tests/type/generics/generics_structs_in_args.nil similarity index 100% rename from ui-tests/type/generics/codegen.nil rename to ui-tests/type/generics/generics_structs_in_args.nil