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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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