mirror of
https://github.com/Noratrieb/riverdelta.git
synced 2026-01-14 16:35:03 +01:00
refactor items
This commit is contained in:
parent
cdbb26352e
commit
b021a9e218
7 changed files with 199 additions and 257 deletions
88
src/ast.ts
88
src/ast.ts
|
|
@ -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.body),
|
||||||
body: folder.expr(item.node.body),
|
returnType: item.returnType && folder.type(item.returnType),
|
||||||
returnType: item.node.returnType && folder.type(item.node.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.returnType && folder.type(item.returnType),
|
||||||
returnType: item.node.returnType && folder.type(item.node.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),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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: {
|
name: crate.packageName,
|
||||||
contents: crate.rootItems,
|
contents: erasedContents,
|
||||||
name: crate.packageName,
|
|
||||||
},
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 [
|
||||||
name: name.ident,
|
t,
|
||||||
contents,
|
{
|
||||||
};
|
kind: "mod",
|
||||||
|
name: name.ident,
|
||||||
return [t, { kind: "mod", node, span: name.span, id: ItemId.dummy() }];
|
contents,
|
||||||
|
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);
|
||||||
|
|
|
||||||
|
|
@ -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 ")});`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
142
src/typeck.ts
142
src/typeck.ts
|
|
@ -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,30 +332,26 @@ export function typeck(
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
node: {
|
name: item.name,
|
||||||
name: item.node.name,
|
type: {
|
||||||
type: {
|
kind: "struct",
|
||||||
kind: "struct",
|
fields: item.type.fields.map((field, i) => ({
|
||||||
fields: item.node.type.fields.map((field, i) => ({
|
name: field.name,
|
||||||
name: field.name,
|
type: {
|
||||||
type: {
|
...field.type,
|
||||||
...field.type,
|
ty: ty.fields[i][1],
|
||||||
ty: ty.fields[i][1],
|
},
|
||||||
},
|
})),
|
||||||
})),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case "alias": {
|
case "alias": {
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
node: {
|
name: item.name,
|
||||||
name: item.node.name,
|
type: {
|
||||||
type: {
|
kind: "alias",
|
||||||
kind: "alias",
|
type: item.type.type,
|
||||||
type: item.node.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: {
|
ty,
|
||||||
...item.node,
|
init: { ...init, ty },
|
||||||
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`,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue