refactor items

This commit is contained in:
nora 2023-08-03 13:44:55 +02:00
parent cdbb26352e
commit b021a9e218
7 changed files with 199 additions and 257 deletions

View file

@ -98,38 +98,21 @@ export class ItemId {
} }
export type ItemKind<P extends Phase> = export type ItemKind<P extends Phase> =
| { | ItemFunction<P>
kind: "function"; | ItemType<P>
node: ItemFunction<P>; | ItemImport<P>
} | ItemMod<P>
| { | ItemExtern
kind: "type"; | ItemGlobal<P>;
node: ItemType<P>;
}
| {
kind: "import";
node: ItemImport<P>;
}
| {
kind: "mod";
node: ItemMod<P>;
}
| {
kind: "extern";
node: ItemExtern;
}
| {
kind: "global";
node: ItemGlobal<P>;
};
export type Item<P extends Phase> = ItemKind<P> & { export type Item<P extends Phase> = ItemKind<P> & {
span: Span; span: Span;
id: ItemId; id: ItemId;
name: string;
} & P["defPath"]; } & P["defPath"];
export type ItemFunction<P extends Phase> = { export type ItemFunction<P extends Phase> = {
name: string; kind: "function";
params: FunctionArg<P>[]; params: FunctionArg<P>[];
body: Expr<P>; body: Expr<P>;
returnType?: Type<P>; returnType?: Type<P>;
@ -143,7 +126,7 @@ export type FunctionArg<P extends Phase> = {
}; };
export type ItemType<P extends Phase> = { export type ItemType<P extends Phase> = {
name: string; kind: "type";
type: TypeDefKind<P>; type: TypeDefKind<P>;
ty?: TyStruct; ty?: TyStruct;
}; };
@ -164,23 +147,23 @@ export type FieldDef<P extends Phase> = {
}; };
export type ItemImport<P extends Phase> = { export type ItemImport<P extends Phase> = {
kind: "import";
module: StringLiteral; module: StringLiteral;
func: StringLiteral; func: StringLiteral;
name: string;
params: FunctionArg<P>[]; params: FunctionArg<P>[];
returnType?: Type<P>; returnType?: Type<P>;
ty?: TyFn; ty?: TyFn;
}; };
export type ItemMod<P extends Phase> = { export type ItemMod<P extends Phase> = {
name: string; kind: "mod";
contents: Item<P>[]; contents: Item<P>[];
}; };
export type ItemExtern = { name: string }; export type ItemExtern = { kind: "extern" };
export type ItemGlobal<P extends Phase> = { export type ItemGlobal<P extends Phase> = {
name: string; kind: "global";
type: Type<P>; type: Type<P>;
init: Expr<P>; init: Expr<P>;
ty?: Ty; ty?: Ty;
@ -616,7 +599,7 @@ export function superFoldItem<From extends Phase, To extends Phase>(
): Item<To> { ): Item<To> {
switch (item.kind) { switch (item.kind) {
case "function": { case "function": {
const args = item.node.params.map(({ name, type, span }) => ({ const args = item.params.map(({ name, type, span }) => ({
name, name,
type: folder.type(type), type: folder.type(type),
span, span,
@ -625,16 +608,14 @@ export function superFoldItem<From extends Phase, To extends Phase>(
return { return {
...item, ...item,
kind: "function", kind: "function",
node: { name: item.name,
name: item.node.name,
params: args, params: args,
body: folder.expr(item.node.body), body: folder.expr(item.body),
returnType: item.node.returnType && folder.type(item.node.returnType), returnType: item.returnType && folder.type(item.returnType),
},
}; };
} }
case "type": { case "type": {
const typeKind = item.node.type; const typeKind = item.type;
let type: TypeDefKind<To>; let type: TypeDefKind<To>;
switch (typeKind.kind) { switch (typeKind.kind) {
case "struct": { case "struct": {
@ -656,11 +637,12 @@ export function superFoldItem<From extends Phase, To extends Phase>(
return { return {
...item, ...item,
kind: "type", kind: "type",
node: { name: item.node.name, type }, name: item.name,
type,
}; };
} }
case "import": { case "import": {
const args = item.node.params.map(({ name, type, span }) => ({ const args = item.params.map(({ name, type, span }) => ({
name, name,
type: folder.type(type), type: folder.type(type),
span, span,
@ -668,23 +650,19 @@ export function superFoldItem<From extends Phase, To extends Phase>(
return { return {
...item, ...item,
kind: "import", kind: "import",
node: { module: item.module,
module: item.node.module, func: item.func,
func: item.node.func, name: item.name,
name: item.node.name,
params: args, params: args,
returnType: item.node.returnType && folder.type(item.node.returnType), returnType: item.returnType && folder.type(item.returnType),
},
}; };
} }
case "mod": { case "mod": {
return { return {
...item, ...item,
kind: "mod", kind: "mod",
node: { name: item.name,
name: item.node.name, contents: item.contents.map((item) => folder.item(item)),
contents: item.node.contents.map((item) => folder.item(item)),
},
}; };
} }
case "extern": { case "extern": {
@ -694,11 +672,9 @@ export function superFoldItem<From extends Phase, To extends Phase>(
return { return {
...item, ...item,
kind: "global", kind: "global",
node: { name: item.name,
name: item.node.name, type: folder.type(item.type),
type: folder.type(item.node.type), init: folder.expr(item.init),
init: folder.expr(item.node.init),
},
}; };
} }
} }

View file

@ -190,19 +190,19 @@ export function lower(gcx: GlobalContext): wasm.Module {
items.forEach((item) => { items.forEach((item) => {
switch (item.kind) { switch (item.kind) {
case "function": { case "function": {
lowerFunc(cx, item, item.node); lowerFunc(cx, item);
break; break;
} }
case "import": { case "import": {
lowerImport(cx, item, item.node); lowerImport(cx, item);
break; break;
} }
case "mod": { case "mod": {
lowerMod(item.node.contents); lowerMod(item.contents);
break; break;
} }
case "global": { case "global": {
lowerGlobal(cx, item, item.node); lowerGlobal(cx, item);
break; break;
} }
case "extern": case "extern":
@ -258,11 +258,7 @@ export function lower(gcx: GlobalContext): wasm.Module {
return mod; return mod;
} }
function lowerImport( function lowerImport(cx: Context, def: ItemImport<Typecked> & Item<Typecked>) {
cx: Context,
item: Item<Typecked>,
def: ItemImport<Typecked>,
) {
const existing = cx.mod.imports.findIndex( const existing = cx.mod.imports.findIndex(
(imp) => imp.module === def.module.value && imp.name === def.func.value, (imp) => imp.module === def.module.value && imp.name === def.func.value,
); );
@ -286,14 +282,10 @@ function lowerImport(
}); });
} }
cx.funcIndices.set({ kind: "item", id: item.id }, { kind: "import", idx }); cx.funcIndices.set({ kind: "item", id: def.id }, { kind: "import", idx });
} }
function lowerGlobal( function lowerGlobal(cx: Context, def: ItemGlobal<Typecked> & Item<Typecked>) {
cx: Context,
item: Item<Typecked>,
def: ItemGlobal<Typecked>,
) {
const globalIdx = cx.mod.globals.length; const globalIdx = cx.mod.globals.length;
let valtype: "i32" | "i64"; let valtype: "i32" | "i64";
@ -318,18 +310,17 @@ function lowerGlobal(
}; };
cx.mod.globals.push({ cx.mod.globals.push({
_name: mangleDefPath(item.defPath), _name: mangleDefPath(def.defPath),
type: { type: valtype, mut: "var" }, type: { type: valtype, mut: "var" },
init: [init], init: [init],
}); });
cx.globalIndices.set({ kind: "item", id: item.id }, globalIdx); cx.globalIndices.set({ kind: "item", id: def.id }, globalIdx);
} }
type FuncContext = { type FuncContext = {
cx: Context; cx: Context;
item: Item<Typecked>; func: Item<Typecked> & ItemFunction<Typecked>;
func: ItemFunction<Typecked>;
wasmType: wasm.FuncType; wasmType: wasm.FuncType;
wasm: wasm.Func; wasm: wasm.Func;
varLocations: VarLocation[]; varLocations: VarLocation[];
@ -355,17 +346,13 @@ type StructLayout = {
fields: StructFieldLayout[]; fields: StructFieldLayout[];
}; };
function lowerFunc( function lowerFunc(cx: Context, func: Item<Typecked> & ItemFunction<Typecked>) {
cx: Context,
item: Item<Typecked>,
func: ItemFunction<Typecked>,
) {
const abi = computeAbi(func.ty!); const abi = computeAbi(func.ty!);
const { type: wasmType, paramLocations } = wasmTypeForAbi(abi, func.ty!); const { type: wasmType, paramLocations } = wasmTypeForAbi(abi, func.ty!);
const type = internFuncType(cx, wasmType); const type = internFuncType(cx, wasmType);
const wasmFunc: wasm.Func = { const wasmFunc: wasm.Func = {
_name: mangleDefPath(item.defPath), _name: mangleDefPath(func.defPath),
type, type,
locals: [], locals: [],
body: [], body: [],
@ -373,7 +360,6 @@ function lowerFunc(
const fcx: FuncContext = { const fcx: FuncContext = {
cx, cx,
item,
func, func,
wasmType, wasmType,
wasm: wasmFunc, wasm: wasmFunc,
@ -398,7 +384,7 @@ function lowerFunc(
fcx.cx.mod.funcs.push(wasmFunc); fcx.cx.mod.funcs.push(wasmFunc);
fcx.cx.funcIndices.set( fcx.cx.funcIndices.set(
{ kind: "item", id: fcx.item.id }, { kind: "item", id: fcx.func.id },
{ kind: "func", idx }, { kind: "func", idx },
); );
} }

View file

@ -28,27 +28,36 @@ export class GlobalContext {
public findItem<P extends Phase>( public findItem<P extends Phase>(
id: ItemId, id: ItemId,
localCrate?: Crate<P>, localCrate?: Crate<P>,
): Item<P | Final> { ): Item<P> | Item<Final> {
const crate = unwrap( const allCrates: (Crate<P> | Crate<Final>)[] = [
[...(localCrate ? [localCrate] : []), ...this.finalizedCrates].find( ...(localCrate ? [localCrate] : []),
(crate) => crate.id === id.crateId, ...this.finalizedCrates,
), ];
const crate: Crate<P> | Crate<Final> = unwrap(
allCrates.find((crate) => crate.id === id.crateId),
); );
if (id.itemIdx === 0) { if (id.itemIdx === 0) {
const contents: Item<P>[] | Item<Final>[] = crate.rootItems;
// Typescript does not seem to be able to understand this here.
// The type of this is supposed to be (Item<P> | Item<Final>)["contents"] which is
// "too complex to represent".
const erasedContents: any = contents;
// Return a synthetic module representing the crate root. // Return a synthetic module representing the crate root.
return { const mod: Item<P> | Item<Final> = {
kind: "mod", kind: "mod",
node: {
contents: crate.rootItems,
name: crate.packageName, name: crate.packageName,
}, contents: erasedContents,
span: Span.startOfFile(crate.rootFile), span: Span.startOfFile(crate.rootFile),
id, id,
}; };
return mod;
} }
return unwrap(crate.itemsById.get(id)); const mod = unwrap(crate.itemsById.get(id));
return mod;
} }
} }

View file

@ -11,14 +11,10 @@ import {
FieldDef, FieldDef,
Folder, Folder,
FunctionArg, FunctionArg,
ItemFunction,
Ident, Ident,
ItemImport,
Item, Item,
LOGICAL_KINDS, LOGICAL_KINDS,
ItemMod,
Type, Type,
ItemType,
UNARY_KINDS, UNARY_KINDS,
UnaryKind, UnaryKind,
binaryExprPrecedenceClass, binaryExprPrecedenceClass,
@ -27,7 +23,6 @@ import {
superFoldItem, superFoldItem,
Built, Built,
Parsed, Parsed,
ItemExtern,
ItemId, ItemId,
ItemGlobal, ItemGlobal,
StructLiteralField, StructLiteralField,
@ -79,7 +74,7 @@ function parseItem(t: State): [State, Item<Parsed>] {
let tok; let tok;
[t, tok] = next(t); [t, tok] = next(t);
if (tok.kind === "function") { if (tok.kind === "function") {
let sig; let sig: FunctionSig;
[t, sig] = parseFunctionSig(t); [t, sig] = parseFunctionSig(t);
[t] = expectNext(t, "="); [t] = expectNext(t, "=");
@ -89,16 +84,12 @@ function parseItem(t: State): [State, Item<Parsed>] {
[t] = expectNext(t, ";"); [t] = expectNext(t, ";");
const def: ItemFunction<Parsed> = {
...sig,
body,
};
return [ return [
t, t,
{ {
kind: "function", kind: "function",
node: def, ...sig,
body,
span: tok.span, span: tok.span,
// Assigned later. // Assigned later.
id: ItemId.dummy(), id: ItemId.dummy(),
@ -145,14 +136,15 @@ function parseItem(t: State): [State, Item<Parsed>] {
[t] = expectNext(t, ";"); [t] = expectNext(t, ";");
const def: ItemType<Parsed> = {
name: name.ident,
type,
};
return [ return [
t, t,
{ kind: "type", node: def, span: name.span, id: ItemId.dummy() }, {
kind: "type",
name: name.ident,
type,
span: name.span,
id: ItemId.dummy(),
},
]; ];
} else if (tok.kind === "import") { } else if (tok.kind === "import") {
[t] = expectNext(t, "("); [t] = expectNext(t, "(");
@ -167,28 +159,28 @@ function parseItem(t: State): [State, Item<Parsed>] {
[t] = expectNext(t, ";"); [t] = expectNext(t, ";");
const def: ItemImport<Parsed> = {
module: { kind: "str", value: module.value, span: module.span },
func: { kind: "str", value: func.value, span: func.span },
...sig,
};
return [ return [
t, t,
{ kind: "import", node: def, span: tok.span, id: ItemId.dummy() }, {
kind: "import",
...sig,
module: { kind: "str", value: module.value, span: module.span },
func: { kind: "str", value: func.value, span: func.span },
span: tok.span,
id: ItemId.dummy(),
},
]; ];
} else if (tok.kind === "extern") { } else if (tok.kind === "extern") {
[t] = expectNext(t, "mod"); [t] = expectNext(t, "mod");
let name; let name;
[t, name] = expectNext<TokenIdent>(t, "identifier"); [t, name] = expectNext<TokenIdent>(t, "identifier");
const node: ItemExtern = {
name: name.ident,
};
[t] = expectNext(t, ";"); [t] = expectNext(t, ";");
return [t, { kind: "extern", node, span: name.span, id: ItemId.dummy() }]; return [
t,
{ kind: "extern", name: name.ident, span: name.span, id: ItemId.dummy() },
];
} else if (tok.kind === "mod") { } else if (tok.kind === "mod") {
let name; let name;
[t, name] = expectNext<TokenIdent>(t, "identifier"); [t, name] = expectNext<TokenIdent>(t, "identifier");
@ -221,12 +213,16 @@ function parseItem(t: State): [State, Item<Parsed>] {
[t] = expectNext(t, ";"); [t] = expectNext(t, ";");
const node: ItemMod<Parsed> = { return [
t,
{
kind: "mod",
name: name.ident, name: name.ident,
contents, contents,
}; span: name.span,
id: ItemId.dummy(),
return [t, { kind: "mod", node, span: name.span, id: ItemId.dummy() }]; },
];
} else if (tok.kind === "global") { } else if (tok.kind === "global") {
let name; let name;
[t, name] = expectNext<TokenIdent>(t, "identifier"); [t, name] = expectNext<TokenIdent>(t, "identifier");
@ -238,13 +234,15 @@ function parseItem(t: State): [State, Item<Parsed>] {
[t, init] = parseExpr(t); [t, init] = parseExpr(t);
[t] = expectNext(t, ";"); [t] = expectNext(t, ";");
const node: ItemGlobal<Parsed> = { const global: ItemGlobal<Parsed> & Item<Parsed> = {
kind: "global",
name: name.ident, name: name.ident,
type, type,
init, init,
span: name.span,
id: ItemId.dummy(),
}; };
return [t, global];
return [t, { kind: "global", node, span: name.span, id: ItemId.dummy() }];
} else { } else {
unexpectedToken(tok, "item"); unexpectedToken(tok, "item");
} }
@ -757,7 +755,7 @@ function validateAst(ast: Crate<Built>) {
itemInner(item: Item<Built>): Item<Built> { itemInner(item: Item<Built>): Item<Built> {
if (seenItemIds.has(item.id)) { if (seenItemIds.has(item.id)) {
throw new Error( throw new Error(
`duplicate item id: ${item.id.toString()} for ${item.node.name}`, `duplicate item id: ${item.id.toString()} for ${item.name}`,
); );
} }
seenItemIds.add(item.id); seenItemIds.add(item.id);

View file

@ -28,25 +28,25 @@ function printItem(item: Item<AnyPhase>): string {
switch (item.kind) { switch (item.kind) {
case "function": { case "function": {
return id + printFunction(item.node); return id + printFunction(item);
} }
case "type": { case "type": {
return id + printTypeDef(item.node); return id + printTypeDef(item);
} }
case "import": { case "import": {
return id + printImportDef(item.node); return id + printImportDef(item);
} }
case "mod": { case "mod": {
return id + printMod(item.node); return id + printMod(item);
} }
case "extern": { case "extern": {
return id + `extern mod ${item.node.name};`; return id + `extern mod ${item.name};`;
} }
case "global": { case "global": {
return ( return (
id + id +
`global ${item.node.name}: ${printType(item.node.type)} = ${printExpr( `global ${item.name}: ${printType(item.type)} = ${printExpr(
item.node.init, item.init,
0, 0,
)};` )};`
); );
@ -54,7 +54,7 @@ function printItem(item: Item<AnyPhase>): string {
} }
} }
function printFunction(func: ItemFunction<AnyPhase>): string { function printFunction(func: ItemFunction<AnyPhase> & Item<AnyPhase>): string {
const args = func.params const args = func.params
.map(({ name, type }) => `${name}: ${printType(type)}`) .map(({ name, type }) => `${name}: ${printType(type)}`)
.join(", "); .join(", ");
@ -62,7 +62,7 @@ function printFunction(func: ItemFunction<AnyPhase>): string {
return `function ${func.name}(${args})${ret} = ${printExpr(func.body, 0)};`; return `function ${func.name}(${args})${ret} = ${printExpr(func.body, 0)};`;
} }
function printTypeDef(type: ItemType<AnyPhase>): string { function printTypeDef(type: ItemType<AnyPhase> & Item<AnyPhase>): string {
switch (type.type.kind) { switch (type.type.kind) {
case "struct": { case "struct": {
const { fields } = type.type; const { fields } = type.type;
@ -92,7 +92,7 @@ function printImportDef(def: ItemImport<AnyPhase>): string {
)}(${args})${ret};`; )}(${args})${ret};`;
} }
function printMod(mod: ItemMod<AnyPhase>): string { function printMod(mod: ItemMod<AnyPhase> & Item<AnyPhase>): string {
return `mod ${mod.name} (\n${mod.contents.map(printItem).join("\n ")});`; return `mod ${mod.name} (\n${mod.contents.map(printItem).join("\n ")});`;
} }

View file

@ -35,7 +35,7 @@ function loadCrate(cx: Context, name: string, span: Span): Map<string, ItemId> {
const loadedCrate = cx.gcx.crateLoader(cx.gcx, name, span); const loadedCrate = cx.gcx.crateLoader(cx.gcx, name, span);
const contents = new Map( const contents = new Map(
loadedCrate.rootItems.map((item) => [item.node.name, item.id]), loadedCrate.rootItems.map((item) => [item.name, item.id]),
); );
return contents; return contents;
@ -43,11 +43,10 @@ function loadCrate(cx: Context, name: string, span: Span): Map<string, ItemId> {
function resolveModItem( function resolveModItem(
cx: Context, cx: Context,
mod: ItemMod<Built> | ItemExtern, mod: (ItemMod<Built> | ItemExtern) & Item<Built>,
item: Item<Built>,
name: string, name: string,
): ItemId | undefined { ): ItemId | undefined {
const cachedContents = cx.modContentsCache.get(item.id); const cachedContents = cx.modContentsCache.get(mod.id);
if (cachedContents) { if (cachedContents) {
return cachedContents.get(name); return cachedContents.get(name);
} }
@ -55,12 +54,12 @@ function resolveModItem(
let contents: Map<string, ItemId>; let contents: Map<string, ItemId>;
if ("contents" in mod) { if ("contents" in mod) {
contents = new Map(mod.contents.map((item) => [item.node.name, item.id])); contents = new Map(mod.contents.map((item) => [item.name, item.id]));
} else { } else {
contents = loadCrate(cx, item.node.name, item.span); contents = loadCrate(cx, mod.name, mod.span);
} }
cx.modContentsCache.set(item.id, contents); cx.modContentsCache.set(mod.id, contents);
return contents.get(name); return contents.get(name);
} }
@ -93,14 +92,14 @@ function resolveModule(
const items = new Map<string, ItemId>(); const items = new Map<string, ItemId>();
contents.forEach((item) => { contents.forEach((item) => {
const existing = items.get(item.node.name); const existing = items.get(item.name);
if (existing !== undefined) { if (existing !== undefined) {
throw new CompilerError( throw new CompilerError(
`item \`${item.node.name}\` has already been declared`, `item \`${item.name}\` has already been declared`,
item.span, item.span,
); );
} }
items.set(item.node.name, item.id); items.set(item.name, item.id);
}); });
const scopes: string[] = []; const scopes: string[] = [];
@ -157,43 +156,41 @@ function resolveModule(
const resolver: Folder<Built, Resolved> = { const resolver: Folder<Built, Resolved> = {
...mkDefaultFolder(), ...mkDefaultFolder(),
itemInner(item): Item<Resolved> { itemInner(item): Item<Resolved> {
const defPath = [...modName, item.node.name]; const defPath = [...modName, item.name];
switch (item.kind) { switch (item.kind) {
case "function": { case "function": {
const params = item.node.params.map(({ name, span, type }) => ({ const params = item.params.map(({ name, span, type }) => ({
name, name,
span, span,
type: this.type(type), type: this.type(type),
})); }));
const returnType = const returnType = item.returnType && this.type(item.returnType);
item.node.returnType && this.type(item.node.returnType);
item.node.params.forEach(({ name }) => scopes.push(name)); item.params.forEach(({ name }) => scopes.push(name));
const body = this.expr(item.node.body); const body = this.expr(item.body);
const revParams = item.node.params.slice(); const revParams = item.params.slice();
revParams.reverse(); revParams.reverse();
revParams.forEach(({ name }) => popScope(name)); revParams.forEach(({ name }) => popScope(name));
return { return {
kind: "function", kind: "function",
span: item.span, span: item.span,
node: { name: item.name,
name: item.node.name,
params, params,
returnType, returnType,
body, body,
},
id: item.id, id: item.id,
defPath, defPath,
}; };
} }
case "mod": { case "mod": {
const contents = resolveModule(cx, defPath, item.node.contents); const contents = resolveModule(cx, defPath, item.contents);
return { return {
...item, ...item,
kind: "mod", kind: "mod",
node: { ...item.node, contents }, contents,
defPath, defPath,
}; };
} }
@ -201,15 +198,10 @@ function resolveModule(
// Eagerly resolve the crate. // Eagerly resolve the crate.
// Note that because you can reference extern crates before the item, // Note that because you can reference extern crates before the item,
// we still need the loadCrate in the field access code above. // we still need the loadCrate in the field access code above.
loadCrate(cx, item.name, item.span);
loadCrate(cx, item.node.name, item.span);
const node: ItemExtern = {
...item.node,
};
return { return {
...item, ...item,
node,
defPath, defPath,
}; };
} }
@ -277,13 +269,12 @@ function resolveModule(
const pathResItem = resolveModItem( const pathResItem = resolveModItem(
cx, cx,
module.node,
module, module,
expr.field.value, expr.field.value,
); );
if (pathResItem === undefined) { if (pathResItem === undefined) {
throw new CompilerError( throw new CompilerError(
`module ${module.node.name} has no item ${expr.field.value}`, `module ${module.name} has no item ${expr.field.value}`,
expr.field.span, expr.field.span,
); );
} }

View file

@ -134,16 +134,16 @@ export function typeck(
case "import": case "import":
case "type": case "type":
case "global": case "global":
return item.node.ty!; return item.ty!;
case "mod": { case "mod": {
throw new CompilerError( throw new CompilerError(
`module ${item.node.name} cannot be used as a type or value`, `module ${item.name} cannot be used as a type or value`,
cause, cause,
); );
} }
case "extern": { case "extern": {
throw new CompilerError( throw new CompilerError(
`extern declaration ${item.node.name} cannot be used as a type or value`, `extern declaration ${item.name} cannot be used as a type or value`,
cause, cause,
); );
} }
@ -165,9 +165,9 @@ export function typeck(
switch (item.kind) { switch (item.kind) {
case "function": case "function":
case "import": { case "import": {
const args = item.node.params.map((arg) => lowerAstTy(arg.type)); const args = item.params.map((arg) => lowerAstTy(arg.type));
const returnTy: Ty = item.node.returnType const returnTy: Ty = item.returnType
? lowerAstTy(item.node.returnType) ? lowerAstTy(item.returnType)
: TY_UNIT; : TY_UNIT;
const ty: Ty = { kind: "fn", params: args, returnTy }; const ty: Ty = { kind: "fn", params: args, returnTy };
@ -175,11 +175,11 @@ export function typeck(
return ty; return ty;
} }
case "type": { case "type": {
switch (item.node.type.kind) { switch (item.type.kind) {
case "struct": { case "struct": {
const ty: Ty = { const ty: Ty = {
kind: "struct", kind: "struct",
name: item.node.name, name: item.name,
fields: [ fields: [
/*dummy*/ /*dummy*/
], ],
@ -187,7 +187,7 @@ export function typeck(
itemTys.set(item.id, ty); itemTys.set(item.id, ty);
const fields = item.node.type.fields.map<[string, Ty]>( const fields = item.type.fields.map<[string, Ty]>(
({ name, type }) => [name.name, lowerAstTy(type)], ({ name, type }) => [name.name, lowerAstTy(type)],
); );
@ -195,24 +195,24 @@ export function typeck(
return ty; return ty;
} }
case "alias": { case "alias": {
return lowerAstTy(item.node.type.type); return lowerAstTy(item.type.type);
} }
} }
} }
case "mod": { case "mod": {
throw new CompilerError( throw new CompilerError(
`module ${item.node.name} cannot be used as a type or value`, `module ${item.name} cannot be used as a type or value`,
cause, cause,
); );
} }
case "extern": { case "extern": {
throw new CompilerError( throw new CompilerError(
`extern declaration ${item.node.name} cannot be used as a type or value`, `extern declaration ${item.name} cannot be used as a type or value`,
cause, cause,
); );
} }
case "global": { case "global": {
const ty = lowerAstTy(item.node.type); const ty = lowerAstTy(item.type);
itemTys.set(item.id, ty); itemTys.set(item.id, ty);
return ty; return ty;
} }
@ -246,24 +246,22 @@ export function typeck(
switch (item.kind) { switch (item.kind) {
case "function": { case "function": {
const fnTy = typeOfItem(item.id, item.span) as TyFn; const fnTy = typeOfItem(item.id, item.span) as TyFn;
const body = checkBody(gcx, ast, item.node.body, fnTy, typeOfItem); const body = checkBody(gcx, ast, item.body, fnTy, typeOfItem);
const returnType = item.node.returnType && { const returnType = item.returnType && {
...item.node.returnType, ...item.returnType,
ty: fnTy.returnTy, ty: fnTy.returnTy,
}; };
return { return {
...item, ...item,
node: { name: item.name,
name: item.node.name, params: item.params.map((arg, i) => ({
params: item.node.params.map((arg, i) => ({
...arg, ...arg,
type: { ...arg.type, ty: fnTy.params[i] }, type: { ...arg.type, ty: fnTy.params[i] },
})), })),
body, body,
returnType, returnType,
ty: fnTy, ty: fnTy,
},
}; };
} }
case "import": { case "import": {
@ -277,7 +275,7 @@ export function typeck(
default: { default: {
throw new CompilerError( throw new CompilerError(
`import parameters must be I32 or Int`, `import parameters must be I32 or Int`,
item.node.params[i].span, item.params[i].span,
); );
} }
} }
@ -291,41 +289,39 @@ export function typeck(
default: { default: {
throw new CompilerError( throw new CompilerError(
`import return must be I32 or Int`, `import return must be I32 or Int`,
item.node.returnType!.span, item.returnType!.span,
); );
} }
} }
} }
const returnType = item.node.returnType && { const returnType = item.returnType && {
...item.node.returnType, ...item.returnType,
ty: fnTy.returnTy, ty: fnTy.returnTy,
}; };
return { return {
...item, ...item,
kind: "import", kind: "import",
node: { module: item.module,
module: item.node.module, func: item.func,
func: item.node.func, name: item.name,
name: item.node.name, params: item.params.map((arg, i) => ({
params: item.node.params.map((arg, i) => ({
...arg, ...arg,
type: { ...arg.type, ty: fnTy.params[i] }, type: { ...arg.type, ty: fnTy.params[i] },
})), })),
returnType, returnType,
ty: fnTy, ty: fnTy,
},
}; };
} }
case "type": { case "type": {
switch (item.node.type.kind) { switch (item.type.kind) {
case "struct": { case "struct": {
const fieldNames = new Set(); const fieldNames = new Set();
item.node.type.fields.forEach(({ name }) => { item.type.fields.forEach(({ name }) => {
if (fieldNames.has(name)) { if (fieldNames.has(name)) {
throw new CompilerError( throw new CompilerError(
`type ${item.node.name} has a duplicate field: ${name.name}`, `type ${item.name} has a duplicate field: ${name.name}`,
name.span, name.span,
); );
} }
@ -336,11 +332,10 @@ export function typeck(
return { return {
...item, ...item,
node: { name: item.name,
name: item.node.name,
type: { type: {
kind: "struct", kind: "struct",
fields: item.node.type.fields.map((field, i) => ({ fields: item.type.fields.map((field, i) => ({
name: field.name, name: field.name,
type: { type: {
...field.type, ...field.type,
@ -348,18 +343,15 @@ export function typeck(
}, },
})), })),
}, },
},
}; };
} }
case "alias": { case "alias": {
return { return {
...item, ...item,
node: { name: item.name,
name: item.node.name,
type: { type: {
kind: "alias", kind: "alias",
type: item.node.type.type, type: item.type.type,
},
}, },
}; };
} }
@ -368,22 +360,16 @@ export function typeck(
case "mod": { case "mod": {
return { return {
...item, ...item,
node: { contents: item.contents.map((item) => this.item(item)),
...item.node,
contents: item.node.contents.map((item) => this.item(item)),
},
}; };
} }
case "extern": { case "extern": {
// Nothing to check. // Nothing to check.
return { return item;
...item,
node: { ...item.node },
};
} }
case "global": { case "global": {
const ty = typeOfItem(item.id, item.span); const ty = typeOfItem(item.id, item.span);
const { init } = item.node; const { init } = item;
if (init.kind !== "literal" || init.value.kind !== "int") { if (init.kind !== "literal" || init.value.kind !== "int") {
throw new CompilerError( throw new CompilerError(
@ -398,11 +384,8 @@ export function typeck(
return { return {
...item, ...item,
node: {
...item.node,
ty, ty,
init: { ...init, ty }, init: { ...init, ty },
},
}; };
} }
} }
@ -421,10 +404,9 @@ export function typeck(
const typecked = foldAst(ast, checker); const typecked = foldAst(ast, checker);
const main = typecked.rootItems.find((item) => { const main = typecked.rootItems.find((item) => {
if (item.kind === "function" && item.node.name === "main") { if (item.kind === "function" && item.name === "main") {
const func = item.node; if (item.returnType !== undefined) {
if (func.returnType !== undefined) { const ty = item.body.ty;
const ty = func.body.ty;
if (ty.kind !== "tuple" || ty.elems.length !== 0) { if (ty.kind !== "tuple" || ty.elems.length !== 0) {
throw new CompilerError( throw new CompilerError(
`\`main\` has an invalid signature. main takes no arguments and returns nothing`, `\`main\` has an invalid signature. main takes no arguments and returns nothing`,