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