diff --git a/src/ast.ts b/src/ast.ts
index 500ffbe..85c5ce2 100644
--- a/src/ast.ts
+++ b/src/ast.ts
@@ -3,32 +3,53 @@ import { LitIntType } from "./lexer";
export type Ast
= {
rootItems: Item
[];
- typeckResults?: TypeckResults;
itemsById: Map>;
packageName: string;
-};
+} & P["typeckResults"];
export type Phase = {
res: unknown;
+ defPath: unknown;
+ ty: unknown;
+ typeckResults: unknown;
};
-type NoRes = object;
+type No = object;
+
type HasRes = { res: Resolution };
+type HasDefPath = { defPath: string[] };
+type HasTy = { ty: Ty };
+type HasTypeckResults = { typeckResults: TypeckResults };
export type Parsed = {
- res: NoRes;
+ res: No;
+ defPath: No;
+ ty: No;
+ typeckResults: No;
};
export type Built = {
- res: NoRes;
+ res: No;
+ defPath: No;
+ ty: No;
+ typeckResults: No;
};
export type Resolved = {
res: HasRes;
+ defPath: HasDefPath;
+ ty: No;
+ typeckResults: No;
};
export type Typecked = {
res: HasRes;
+ defPath: HasDefPath;
+ ty: HasTy;
+ typeckResults: HasTypeckResults;
};
export type AnyPhase = {
- res: NoRes | HasRes;
+ res: No | HasRes;
+ defPath: No | HasDefPath;
+ ty: No | HasTy;
+ typeckResults: No | HasTypeckResults;
};
export type Ident = {
@@ -64,8 +85,7 @@ export type ItemKind =
export type Item
= ItemKind
& {
span: Span;
id: ItemId;
- defPath?: string[];
-};
+} & P["defPath"];
export type FunctionDef
= {
name: string;
@@ -246,8 +266,7 @@ export type ExprKind
=
export type Expr
= ExprKind
& {
span: Span;
- ty?: Ty;
-};
+} & P["ty"];
export type StringLiteral = {
kind: "str";
@@ -335,7 +354,6 @@ export type TypeKind
=
export type Type
= TypeKind
& {
span: Span;
- ty?: Ty;
};
// name resolution stuff
@@ -526,7 +544,7 @@ export function foldAst(
return {
rootItems: ast.rootItems.map((item) => folder.item(item)),
itemsById: folder.newItemsById,
- typeckResults: ast.typeckResults,
+ typeckResults: "typeckResults" in ast ? ast.typeckResults : undefined,
packageName: ast.packageName,
};
}
diff --git a/src/lower.ts b/src/lower.ts
index f207c6e..1398ba8 100644
--- a/src/lower.ts
+++ b/src/lower.ts
@@ -231,7 +231,7 @@ function lowerFunc(
const type = internFuncType(cx, wasmType);
const wasmFunc: wasm.Func = {
- _name: mangleDefPath(item.defPath!),
+ _name: mangleDefPath(item.defPath),
type,
locals: [],
body: [],
@@ -269,7 +269,7 @@ function lowerExpr(
instrs: wasm.Instr[],
expr: Expr
) {
- const ty = expr.ty!;
+ const ty = expr.ty;
exprKind: switch (expr.kind) {
case "empty": {
@@ -278,7 +278,7 @@ function lowerExpr(
}
case "let": {
lowerExpr(fcx, instrs, expr.rhs);
- const types = wasmTypeForBody(expr.rhs.ty!);
+ const types = wasmTypeForBody(expr.rhs.ty);
const local = fcx.wasm.locals.length + fcx.wasmType.params.length;
@@ -333,7 +333,7 @@ function lowerExpr(
const instr: wasm.Instr = {
kind: "block",
instrs: lowerExprBlockBody(fcx, expr),
- type: blockTypeForBody(fcx.cx, expr.ty!),
+ type: blockTypeForBody(fcx.cx, expr.ty),
};
instrs.push(instr);
@@ -404,8 +404,8 @@ function lowerExpr(
lowerExpr(fcx, instrs, expr.lhs);
lowerExpr(fcx, instrs, expr.rhs);
- const lhsTy = expr.lhs.ty!;
- const rhsTy = expr.rhs.ty!;
+ const lhsTy = expr.lhs.ty;
+ const rhsTy = expr.rhs.ty;
if (
(lhsTy.kind === "int" && rhsTy.kind === "int") ||
(lhsTy.kind === "i32" && rhsTy.kind === "i32")
@@ -592,11 +592,11 @@ function lowerExpr(
lowerExpr(fcx, instrs, expr.lhs);
- switch (expr.lhs.ty!.kind) {
+ switch (expr.lhs.ty.kind) {
case "tuple": {
// Tuples have a by-value ABI, so we can simply index.
const lhsSize = argRetAbi(expr.lhs.ty).length;
- const resultAbi = argRetAbi(expr.ty!);
+ const resultAbi = argRetAbi(expr.ty);
const resultSize = resultAbi.length;
const wasmIdx = wasmTypeIdxForTupleField(
expr.lhs.ty,
@@ -655,7 +655,7 @@ function lowerExpr(
kind: "if",
then: thenInstrs,
else: elseInstrs,
- type: blockTypeForBody(fcx.cx, expr.ty!),
+ type: blockTypeForBody(fcx.cx, expr.ty),
});
break;
@@ -669,7 +669,7 @@ function lowerExpr(
lowerExpr(fcx, bodyInstrs, expr.body);
// For diverging bodies, we don't need to bother creating the back edge.
- if (expr.body.ty!.kind !== "never") {
+ if (expr.body.ty.kind !== "never") {
bodyInstrs.push({
kind: "br",
label: /*innermost control structure, the loop*/ 0,
@@ -680,13 +680,13 @@ function lowerExpr(
outerBlockInstrs.push({
kind: "loop",
instrs: bodyInstrs,
- type: blockTypeForBody(fcx.cx, expr.ty!),
+ type: blockTypeForBody(fcx.cx, expr.ty),
});
instrs.push({
kind: "block",
instrs: outerBlockInstrs,
- type: blockTypeForBody(fcx.cx, expr.ty!),
+ type: blockTypeForBody(fcx.cx, expr.ty),
});
fcx.loopDepths.delete(expr.loopId);
@@ -695,8 +695,6 @@ function lowerExpr(
case "break": {
const loopDepth = unwrap(fcx.loopDepths.get(expr.target!));
- console.log(loopDepth, fcx.currentBlockDepth);
-
instrs.push({
kind: "br",
label: fcx.currentBlockDepth - loopDepth - 1,
@@ -733,11 +731,11 @@ function lowerExprBlockBody(
for (const inner of headExprs) {
lowerExpr(fcx, innerInstrs, inner);
- if (inner.ty!.kind === "never") {
+ if (inner.ty.kind === "never") {
// The rest of the block is unreachable, so we don't bother codegening it.
break;
}
- const types = wasmTypeForBody(inner.ty!);
+ const types = wasmTypeForBody(inner.ty);
types.forEach(() => innerInstrs.push({ kind: "drop" }));
}
@@ -872,7 +870,7 @@ function addRt(cx: Context, ast: Ast) {
cx.relocations.push({
kind: "funccall",
instr: mainCall,
- res: ast.typeckResults!.main,
+ res: ast.typeckResults.main,
});
const start: wasm.Func = {
diff --git a/src/typeck.ts b/src/typeck.ts
index 7970f1f..703a941 100644
--- a/src/typeck.ts
+++ b/src/typeck.ts
@@ -341,7 +341,7 @@ export function typeck(ast: Ast): Ast {
if (item.kind === "function" && item.node.name === "main") {
const func = item.node;
if (func.returnType !== undefined) {
- const ty = func.returnType.ty!;
+ const ty = func.body.ty;
if (ty.kind !== "tuple" || ty.elems.length !== 0) {
throw new CompilerError(
`\`main\` has an invalid signature. main takes no arguments and returns nothing`,
@@ -586,7 +586,7 @@ export function checkBody(
: infcx.newVar();
const rhs = this.expr(expr.rhs);
- infcx.assign(bindingTy, rhs.ty!, expr.span);
+ infcx.assign(bindingTy, rhs.ty, expr.span);
// AST validation ensures that lets can only be in blocks, where
// the types will be popped.
@@ -596,7 +596,6 @@ export function checkBody(
const type: Type | undefined = loweredBindingTy && {
...expr.type!,
- ty: loweredBindingTy,
};
return {
@@ -612,7 +611,7 @@ export function checkBody(
const lhs = this.expr(expr.lhs);
const rhs = this.expr(expr.rhs);
- infcx.assign(lhs.ty!, rhs.ty!, expr.span);
+ infcx.assign(lhs.ty, rhs.ty, expr.span);
switch (lhs.kind) {
case "ident":
@@ -641,7 +640,7 @@ export function checkBody(
const exprs = expr.exprs.map((expr) => this.expr(expr));
- const ty = exprs.length > 0 ? exprs[exprs.length - 1].ty! : TY_UNIT;
+ const ty = exprs.length > 0 ? exprs[exprs.length - 1].ty : TY_UNIT;
localTys.length = prevLocalTysLen;
@@ -686,19 +685,19 @@ export function checkBody(
const lhs = this.expr(expr.lhs);
const rhs = this.expr(expr.rhs);
- lhs.ty = infcx.resolveIfPossible(lhs.ty!);
- rhs.ty = infcx.resolveIfPossible(rhs.ty!);
+ lhs.ty = infcx.resolveIfPossible(lhs.ty);
+ rhs.ty = infcx.resolveIfPossible(rhs.ty);
- return checkBinary({ ...expr, lhs, rhs });
+ return checkBinary(expr, lhs, rhs);
}
case "unary": {
const rhs = this.expr(expr.rhs);
- rhs.ty = infcx.resolveIfPossible(rhs.ty!);
- return checkUnary({ ...expr, rhs });
+ rhs.ty = infcx.resolveIfPossible(rhs.ty);
+ return checkUnary(expr, rhs);
}
case "call": {
const lhs = this.expr(expr.lhs);
- lhs.ty = infcx.resolveIfPossible(lhs.ty!);
+ lhs.ty = infcx.resolveIfPossible(lhs.ty);
const lhsTy = lhs.ty;
if (lhsTy.kind !== "fn") {
throw new CompilerError(
@@ -718,7 +717,7 @@ export function checkBody(
}
const arg = checker.expr(args[i]);
- infcx.assign(param, arg.ty!, args[i].span);
+ infcx.assign(param, arg.ty, args[i].span);
});
if (args.length > lhsTy.params.length) {
@@ -732,7 +731,7 @@ export function checkBody(
}
case "fieldAccess": {
const lhs = this.expr(expr.lhs);
- lhs.ty = infcx.resolveIfPossible(lhs.ty!);
+ lhs.ty = infcx.resolveIfPossible(lhs.ty);
const { field } = expr;
let ty: Ty;
@@ -807,14 +806,14 @@ export function checkBody(
const then = this.expr(expr.then);
const elsePart = expr.else && this.expr(expr.else);
- infcx.assign(TY_BOOL, cond.ty!, cond.span);
+ infcx.assign(TY_BOOL, cond.ty, cond.span);
let ty: Ty;
if (elsePart) {
- infcx.assign(then.ty!, elsePart.ty!, elsePart.span);
+ infcx.assign(then.ty, elsePart.ty, elsePart.span);
ty = then.ty!;
} else {
- infcx.assign(TY_UNIT, then.ty!, then.span);
+ infcx.assign(TY_UNIT, then.ty, then.span);
ty = TY_UNIT;
}
@@ -827,7 +826,7 @@ export function checkBody(
});
const body = this.expr(expr.body);
- infcx.assign(TY_UNIT, body.ty!, body.span);
+ infcx.assign(TY_UNIT, body.ty, body.span);
const hadBreak = loopState.pop();
const ty = hadBreak ? TY_UNIT : TY_NEVER;
@@ -875,7 +874,7 @@ export function checkBody(
name.span
);
}
- infcx.assign(fieldTy[1], field.ty!, field.span);
+ infcx.assign(fieldTy[1], field.ty, field.span);
assignedFields.add(name.name);
});
@@ -899,7 +898,7 @@ export function checkBody(
const ty: Ty = {
kind: "tuple",
- elems: fields.map((field) => field.ty!),
+ elems: fields.map((field) => field.ty),
};
return { ...expr, fields, ty };
@@ -919,7 +918,7 @@ export function checkBody(
const checked = checker.expr(body);
- infcx.assign(fnTy.returnTy, checked.ty!, body.span);
+ infcx.assign(fnTy.returnTy, checked.ty, body.span);
const resolveTy = (ty: Ty, span: Span) => {
const resTy = infcx.resolveIfPossible(ty);
@@ -933,7 +932,7 @@ export function checkBody(
const resolver: Folder = {
...mkDefaultFolder(),
expr(expr) {
- const ty = resolveTy(expr.ty!, expr.span);
+ const ty = resolveTy(expr.ty, expr.span);
if (expr.kind === "block") {
expr.locals!.forEach((local) => {
@@ -944,8 +943,7 @@ export function checkBody(
return { ...expr, ty };
},
type(type) {
- const ty = resolveTy(type.ty!, type.span);
- return { ...type, ty };
+ return type;
},
ident(ident) {
return ident;
@@ -958,58 +956,61 @@ export function checkBody(
}
function checkBinary(
- expr: Expr & ExprBinary
+ expr: Expr & ExprBinary,
+ lhs: Expr,
+ rhs: Expr
): Expr {
- const lhsTy = expr.lhs.ty!;
- const rhsTy = expr.rhs.ty!;
+ const lhsTy = lhs.ty;
+ const rhsTy = rhs.ty;
if (COMPARISON_KINDS.includes(expr.binaryKind)) {
if (lhsTy.kind === "int" && rhsTy.kind === "int") {
- return { ...expr, ty: TY_BOOL };
+ return { ...expr, lhs, rhs, ty: TY_BOOL };
}
if (lhsTy.kind === "string" && rhsTy.kind === "string") {
- return { ...expr, ty: TY_BOOL };
+ return { ...expr, lhs, rhs, ty: TY_BOOL };
}
if (EQUALITY_KINDS.includes(expr.binaryKind)) {
if (lhsTy.kind === "bool" && rhsTy.kind === "bool") {
- return { ...expr, ty: TY_BOOL };
+ return { ...expr, lhs, rhs, ty: TY_BOOL };
}
}
}
if (lhsTy.kind === "int" && rhsTy.kind === "int") {
- return { ...expr, ty: TY_INT };
+ return { ...expr, lhs, rhs, ty: TY_INT };
}
if (lhsTy.kind === "i32" && rhsTy.kind === "i32") {
- return { ...expr, ty: TY_I32 };
+ return { ...expr, lhs, rhs, ty: TY_I32 };
}
if (LOGICAL_KINDS.includes(expr.binaryKind)) {
if (lhsTy.kind === "bool" && rhsTy.kind === "bool") {
- return { ...expr, ty: TY_BOOL };
+ return { ...expr, lhs, rhs, ty: TY_BOOL };
}
}
throw new CompilerError(
- `invalid types for binary operation: ${printTy(expr.lhs.ty!)} ${
+ `invalid types for binary operation: ${printTy(lhs.ty)} ${
expr.binaryKind
- } ${printTy(expr.rhs.ty!)}`,
+ } ${printTy(rhs.ty)}`,
expr.span
);
}
function checkUnary(
- expr: Expr & ExprUnary
+ expr: Expr & ExprUnary,
+ rhs: Expr
): Expr {
- const rhsTy = expr.rhs.ty!;
+ const rhsTy = rhs.ty;
if (
expr.unaryKind === "!" &&
(rhsTy.kind === "int" || rhsTy.kind === "bool")
) {
- return { ...expr, ty: rhsTy };
+ return { ...expr, rhs, ty: rhsTy };
}
if (expr.unaryKind === "-" && rhsTy.kind == "int") {
@@ -1017,9 +1018,7 @@ function checkUnary(
}
throw new CompilerError(
- `invalid types for unary operation: ${expr.unaryKind} ${printTy(
- expr.rhs.ty!
- )}`,
+ `invalid types for unary operation: ${expr.unaryKind} ${printTy(rhs.ty)}`,
expr.span
);
}