make generics worse

This commit is contained in:
nora 2023-12-15 18:20:52 +01:00
parent 1a0828bd32
commit 5fefc46402
7 changed files with 107 additions and 73 deletions

View file

@ -538,7 +538,7 @@ export type TyStruct = {
kind: "struct";
itemId: ItemId;
params: string[];
args: Ty[];
genericArgs: Ty[];
_name: string;
fields_no_subst: [string, Ty][];
};
@ -563,6 +563,13 @@ export type TyParam = {
name: string;
};
export type TyAlias = {
kind: "alias";
actual: Ty;
genericArgs: Ty[];
params: string[];
};
export type TyError = {
kind: "error";
err: ErrorEmitted;
@ -580,6 +587,7 @@ export type Ty =
| TyRawPtr
| TyNever
| TyParam
| TyAlias
| TyError;
export function tyIsUnit(ty: Ty): ty is TyUnit {
@ -598,7 +606,7 @@ export type TypeckResults = {
};
export function structFieldsSubstituted(ty: TyStruct): [string, Ty][] {
const args = ty.args;
const args = ty.genericArgs;
return ty.fields_no_subst.map(([name, type]) => [
name,
substituteTy(args, type),
@ -607,12 +615,16 @@ export function structFieldsSubstituted(ty: TyStruct): [string, Ty][] {
// 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);
export function substituteTy(genericArgs: Ty[], ty: Ty): Ty {
const subst = (ty: Ty) => substituteTy(genericArgs, ty);
switch (ty.kind) {
case "param":
return params[ty.idx];
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":
@ -622,9 +634,10 @@ export function substituteTy(params: Ty[], ty: Ty): Ty {
params: ty.params.map(subst),
};
case "struct":
case "alias":
return {
...ty,
args: ty.args.map(subst),
genericArgs: ty.genericArgs.map(subst),
};
case "rawptr":
return { ...ty, inner: subst(ty.inner) };
@ -952,6 +965,6 @@ export function varUnreachable(): never {
unreachable("Type variables must not occur after type checking");
}
export function paramUnreachable(): never {
unreachable("Type parameters must not occur after monomophization");
export function paramUnreachable(ty: Ty): never {
unreachable(`type ${ty.kind} should never occur in codegen`);
}

View file

@ -5,7 +5,7 @@ it("should compute struct layout correctly", () => {
const ty: TyStruct = {
kind: "struct",
itemId: ItemId.dummy(),
args: [],
genericArgs: [],
params: [],
_name: "",
fields_no_subst: [
@ -52,7 +52,7 @@ it("should compute single field struct layout correctly", () => {
const ty: TyStruct = {
kind: "struct",
itemId: ItemId.dummy(),
args: [],
genericArgs: [],
params: [],
_name: "",
fields_no_subst: [["owo", TY_INT]],

View file

@ -20,7 +20,7 @@ import {
superFoldItem,
varUnreachable,
TyRawPtr,
paramUnreachable,
paramUnreachable as codegenUnreachableTy,
structFieldsSubstituted,
} from "./ast";
import { GlobalContext } from "./context";
@ -1256,11 +1256,10 @@ function argRetAbi(param: Ty): ArgRetAbi {
case "never":
return [];
case "var":
varUnreachable();
case "param":
paramUnreachable();
case "alias":
case "error":
unreachable("codegen should not see errors");
codegenUnreachableTy(param);
}
}
@ -1313,11 +1312,10 @@ function wasmTypeForBody(ty: Ty): wasm.ValType[] {
case "never":
return [];
case "var":
varUnreachable();
case "param":
paramUnreachable();
case "alias":
case "error":
unreachable("codegen should not see errors");
codegenUnreachableTy(ty);
}
}

View file

@ -13,6 +13,7 @@ import {
Type,
ItemType,
tyIsUnit,
substituteTy,
} from "./ast";
export function printAst(ast: Crate<AnyPhase>): string {
@ -255,40 +256,32 @@ function printIdent(ident: IdentWithRes<AnyPhase>): string {
export function printTy(ty: Ty): string {
switch (ty.kind) {
case "string": {
case "string":
return "String";
}
case "int": {
case "int":
return "Int";
}
case "i32": {
case "i32":
return "I32";
}
case "bool": {
case "bool":
return "Bool";
}
case "tuple": {
case "tuple":
return `(${ty.elems.map(printTy).join(", ")})`;
}
case "fn": {
const ret = tyIsUnit(ty.returnTy) ? "" : `: ${printTy(ty.returnTy)}`;
return `fn(${ty.params.map(printTy).join(", ")})${ret}`;
}
case "var": {
case "var":
return `?${ty.index}`;
}
case "struct": {
case "struct":
return ty._name;
}
case "rawptr": {
case "rawptr":
return `*${printTy(ty.inner)}`;
}
case "never": {
case "never":
return "!";
}
case "param": {
case "param":
return ty.name;
}
case "alias":
return printTy(substituteTy(ty.genericArgs, ty.actual));
case "error":
return "<ERROR>";
}

View file

@ -29,6 +29,7 @@ import {
StructLiteralField,
superFoldExpr,
ExprCall,
substituteTy,
} from "./ast";
import { GlobalContext } from "./context";
import {
@ -140,7 +141,7 @@ function lowerAstTy(cx: TypeckCtx, type: Type<Resolved>): Ty {
throw new Error("Item type cannot refer to local variable");
}
case "item": {
ty = typeOfItem(cx, res.id, type.span);
ty = typeOfItem(cx, res.id, generics, type.span);
break;
}
case "builtin": {
@ -157,9 +158,12 @@ function lowerAstTy(cx: TypeckCtx, type: Type<Resolved>): Ty {
}
}
if (ty.kind === "struct") {
if (ty.kind === "struct" || ty.kind === "alias") {
if (generics.length === ty.params.length) {
return { ...ty, args: generics };
if (ty.kind === "alias") {
return substituteTy(ty.genericArgs, ty.actual);
}
return { ...ty, genericArgs: generics };
} else {
return tyError(
cx,
@ -209,7 +213,12 @@ function lowerAstTy(cx: TypeckCtx, type: Type<Resolved>): Ty {
}
}
function typeOfItem(cx: TypeckCtx, itemId: ItemId, cause: Span): Ty {
function typeOfItem(
cx: TypeckCtx,
itemId: ItemId,
genericArgs: Ty[],
cause: Span,
): Ty {
if (itemId.crateId !== cx.ast.id) {
// Look up foreign items in the foreign crates, we don't need to lower those
// ourselves.
@ -220,7 +229,7 @@ function typeOfItem(cx: TypeckCtx, itemId: ItemId, cause: Span): Ty {
case "import":
case "type":
case "global":
return item.ty!;
return substituteTy(genericArgs, item.ty!);
case "mod": {
return tyError(
cx,
@ -276,7 +285,13 @@ function typeOfItem(cx: TypeckCtx, itemId: ItemId, cause: Span): Ty {
case "struct": {
ty = {
kind: "struct",
args: [],
genericArgs: item.generics.map(
({ name }, idx): Ty => ({
kind: "param",
name,
idx,
}),
),
params: item.generics.map((ident) => ident.name),
itemId: item.id,
_name: item.name,
@ -295,8 +310,20 @@ function typeOfItem(cx: TypeckCtx, itemId: ItemId, cause: Span): Ty {
break;
}
case "alias": {
// TODO: subst
ty = lowerAstTy(cx, item.type.type);
const actual = lowerAstTy(cx, item.type.type);
ty = {
kind: "alias",
actual,
genericArgs: item.generics.map(
({ name }, idx): Ty => ({
kind: "param",
name,
idx,
}),
),
params: item.generics.map((ident) => ident.name),
};
break;
}
}
@ -329,6 +356,8 @@ function typeOfItem(cx: TypeckCtx, itemId: ItemId, cause: Span): Ty {
}
}
ty = substituteTy(genericArgs, ty);
cx.itemTys.set(item.id, ty);
return ty;
}
@ -348,7 +377,8 @@ export function typeck(
itemInner(item: Item<Resolved>): Item<Typecked> {
switch (item.kind) {
case "function": {
const fnTy = typeOfItem(cx, item.id, item.span) as TyFn;
// Functions do not have generic arguments right now.
const fnTy = typeOfItem(cx, item.id, [], item.span) as TyFn;
const body = checkBody(cx, ast, item.body, fnTy);
return {
@ -360,7 +390,7 @@ export function typeck(
};
}
case "import": {
const fnTy = typeOfItem(cx, item.id, item.span) as TyFn;
const fnTy = typeOfItem(cx, item.id, [], item.span) as TyFn;
fnTy.params.forEach((param, i) => {
switch (param.kind) {
@ -404,7 +434,7 @@ export function typeck(
};
}
case "type": {
const ty = typeOfItem(cx, item.id, item.span);
const ty = typeOfItem(cx, item.id, [], item.span);
switch (item.type.kind) {
case "struct": {
@ -452,7 +482,7 @@ export function typeck(
return item;
}
case "global": {
const ty = typeOfItem(cx, item.id, item.span);
const ty = typeOfItem(cx, item.id, [], item.span);
const { init } = item;
let initChecked: Expr<Typecked>;
@ -720,7 +750,7 @@ function typeOfValue(fcx: FuncCtx, res: Resolution, span: Span): Ty {
return fcx.localTys[idx];
}
case "item": {
return typeOfItem(fcx.cx, res.id, span);
return typeOfItem(fcx.cx, res.id, [], span);
}
case "builtin":
return typeOfBuiltinValue(fcx, res.name, span);

View file

@ -1,21 +1,21 @@
type List[T] = struct {
ptr: Int,
len: Int,
cap: Int,
};
function new(): List[Int] = (
List { ptr: 0, len: 0, cap: 0 }
);
function push(list: List[Int], elem: Int) = (
growIfNeeded(list, 1);
);
//type List[T] = struct {
// ptr: Int,
// len: Int,
// cap: Int,
//};
//
//function new(): List[Int] = (
// List { ptr: 0, len: 0, cap: 0 }
//);
//
//function push(list: List[Int], elem: Int) = (
// growIfNeeded(list, 1);
//);
//
// PRIVATE:
function growIfNeeded(list: List[Int], elems: Int) = (
if (list.len + elems) < list.cap then (
let newMemory = std.rt.alloc.allocateItem(0_I32, 0_I32);
);
);
//
//function growIfNeeded(list: List[Int], elems: Int) = (
// if (list.len + elems) < list.cap then (
// let newMemory = std.rt.alloc.allocateItem(0_I32, 0_I32);
// );
//);

View file

@ -1,6 +1,6 @@
//@check-pass
type A[T] = struct { a: T };
type A[T] = T;
function main() = ;
function test(a: A[I32]) = ;
function aaaaaaaaaaa(a: A[I32]) = ;