Also use AST phases for ty

This commit is contained in:
nora 2023-07-31 16:41:03 +02:00
parent 5f191c72cc
commit 93f478546e
3 changed files with 84 additions and 69 deletions

View file

@ -3,32 +3,53 @@ import { LitIntType } from "./lexer";
export type Ast<P extends Phase> = { export type Ast<P extends Phase> = {
rootItems: Item<P>[]; rootItems: Item<P>[];
typeckResults?: TypeckResults;
itemsById: Map<ItemId, Item<P>>; itemsById: Map<ItemId, Item<P>>;
packageName: string; packageName: string;
}; } & P["typeckResults"];
export type Phase = { export type Phase = {
res: unknown; res: unknown;
defPath: unknown;
ty: unknown;
typeckResults: unknown;
}; };
type NoRes = object; type No = object;
type HasRes = { res: Resolution }; type HasRes = { res: Resolution };
type HasDefPath = { defPath: string[] };
type HasTy = { ty: Ty };
type HasTypeckResults = { typeckResults: TypeckResults };
export type Parsed = { export type Parsed = {
res: NoRes; res: No;
defPath: No;
ty: No;
typeckResults: No;
}; };
export type Built = { export type Built = {
res: NoRes; res: No;
defPath: No;
ty: No;
typeckResults: No;
}; };
export type Resolved = { export type Resolved = {
res: HasRes; res: HasRes;
defPath: HasDefPath;
ty: No;
typeckResults: No;
}; };
export type Typecked = { export type Typecked = {
res: HasRes; res: HasRes;
defPath: HasDefPath;
ty: HasTy;
typeckResults: HasTypeckResults;
}; };
export type AnyPhase = { export type AnyPhase = {
res: NoRes | HasRes; res: No | HasRes;
defPath: No | HasDefPath;
ty: No | HasTy;
typeckResults: No | HasTypeckResults;
}; };
export type Ident = { export type Ident = {
@ -64,8 +85,7 @@ export type ItemKind<P extends Phase> =
export type Item<P extends Phase> = ItemKind<P> & { export type Item<P extends Phase> = ItemKind<P> & {
span: Span; span: Span;
id: ItemId; id: ItemId;
defPath?: string[]; } & P["defPath"];
};
export type FunctionDef<P extends Phase> = { export type FunctionDef<P extends Phase> = {
name: string; name: string;
@ -246,8 +266,7 @@ export type ExprKind<P extends Phase> =
export type Expr<P extends Phase> = ExprKind<P> & { export type Expr<P extends Phase> = ExprKind<P> & {
span: Span; span: Span;
ty?: Ty; } & P["ty"];
};
export type StringLiteral = { export type StringLiteral = {
kind: "str"; kind: "str";
@ -335,7 +354,6 @@ export type TypeKind<P extends Phase> =
export type Type<P extends Phase> = TypeKind<P> & { export type Type<P extends Phase> = TypeKind<P> & {
span: Span; span: Span;
ty?: Ty;
}; };
// name resolution stuff // name resolution stuff
@ -526,7 +544,7 @@ export function foldAst<From extends Phase, To extends Phase>(
return { return {
rootItems: ast.rootItems.map((item) => folder.item(item)), rootItems: ast.rootItems.map((item) => folder.item(item)),
itemsById: folder.newItemsById, itemsById: folder.newItemsById,
typeckResults: ast.typeckResults, typeckResults: "typeckResults" in ast ? ast.typeckResults : undefined,
packageName: ast.packageName, packageName: ast.packageName,
}; };
} }

View file

@ -231,7 +231,7 @@ function lowerFunc(
const type = internFuncType(cx, wasmType); const type = internFuncType(cx, wasmType);
const wasmFunc: wasm.Func = { const wasmFunc: wasm.Func = {
_name: mangleDefPath(item.defPath!), _name: mangleDefPath(item.defPath),
type, type,
locals: [], locals: [],
body: [], body: [],
@ -269,7 +269,7 @@ function lowerExpr(
instrs: wasm.Instr[], instrs: wasm.Instr[],
expr: Expr<Typecked> expr: Expr<Typecked>
) { ) {
const ty = expr.ty!; const ty = expr.ty;
exprKind: switch (expr.kind) { exprKind: switch (expr.kind) {
case "empty": { case "empty": {
@ -278,7 +278,7 @@ function lowerExpr(
} }
case "let": { case "let": {
lowerExpr(fcx, instrs, expr.rhs); 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; const local = fcx.wasm.locals.length + fcx.wasmType.params.length;
@ -333,7 +333,7 @@ function lowerExpr(
const instr: wasm.Instr = { const instr: wasm.Instr = {
kind: "block", kind: "block",
instrs: lowerExprBlockBody(fcx, expr), instrs: lowerExprBlockBody(fcx, expr),
type: blockTypeForBody(fcx.cx, expr.ty!), type: blockTypeForBody(fcx.cx, expr.ty),
}; };
instrs.push(instr); instrs.push(instr);
@ -404,8 +404,8 @@ function lowerExpr(
lowerExpr(fcx, instrs, expr.lhs); lowerExpr(fcx, instrs, expr.lhs);
lowerExpr(fcx, instrs, expr.rhs); lowerExpr(fcx, instrs, expr.rhs);
const lhsTy = expr.lhs.ty!; const lhsTy = expr.lhs.ty;
const rhsTy = expr.rhs.ty!; const rhsTy = expr.rhs.ty;
if ( if (
(lhsTy.kind === "int" && rhsTy.kind === "int") || (lhsTy.kind === "int" && rhsTy.kind === "int") ||
(lhsTy.kind === "i32" && rhsTy.kind === "i32") (lhsTy.kind === "i32" && rhsTy.kind === "i32")
@ -592,11 +592,11 @@ function lowerExpr(
lowerExpr(fcx, instrs, expr.lhs); lowerExpr(fcx, instrs, expr.lhs);
switch (expr.lhs.ty!.kind) { switch (expr.lhs.ty.kind) {
case "tuple": { case "tuple": {
// Tuples have a by-value ABI, so we can simply index. // Tuples have a by-value ABI, so we can simply index.
const lhsSize = argRetAbi(expr.lhs.ty).length; const lhsSize = argRetAbi(expr.lhs.ty).length;
const resultAbi = argRetAbi(expr.ty!); const resultAbi = argRetAbi(expr.ty);
const resultSize = resultAbi.length; const resultSize = resultAbi.length;
const wasmIdx = wasmTypeIdxForTupleField( const wasmIdx = wasmTypeIdxForTupleField(
expr.lhs.ty, expr.lhs.ty,
@ -655,7 +655,7 @@ function lowerExpr(
kind: "if", kind: "if",
then: thenInstrs, then: thenInstrs,
else: elseInstrs, else: elseInstrs,
type: blockTypeForBody(fcx.cx, expr.ty!), type: blockTypeForBody(fcx.cx, expr.ty),
}); });
break; break;
@ -669,7 +669,7 @@ function lowerExpr(
lowerExpr(fcx, bodyInstrs, expr.body); lowerExpr(fcx, bodyInstrs, expr.body);
// For diverging bodies, we don't need to bother creating the back edge. // 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({ bodyInstrs.push({
kind: "br", kind: "br",
label: /*innermost control structure, the loop*/ 0, label: /*innermost control structure, the loop*/ 0,
@ -680,13 +680,13 @@ function lowerExpr(
outerBlockInstrs.push({ outerBlockInstrs.push({
kind: "loop", kind: "loop",
instrs: bodyInstrs, instrs: bodyInstrs,
type: blockTypeForBody(fcx.cx, expr.ty!), type: blockTypeForBody(fcx.cx, expr.ty),
}); });
instrs.push({ instrs.push({
kind: "block", kind: "block",
instrs: outerBlockInstrs, instrs: outerBlockInstrs,
type: blockTypeForBody(fcx.cx, expr.ty!), type: blockTypeForBody(fcx.cx, expr.ty),
}); });
fcx.loopDepths.delete(expr.loopId); fcx.loopDepths.delete(expr.loopId);
@ -695,8 +695,6 @@ function lowerExpr(
case "break": { case "break": {
const loopDepth = unwrap(fcx.loopDepths.get(expr.target!)); const loopDepth = unwrap(fcx.loopDepths.get(expr.target!));
console.log(loopDepth, fcx.currentBlockDepth);
instrs.push({ instrs.push({
kind: "br", kind: "br",
label: fcx.currentBlockDepth - loopDepth - 1, label: fcx.currentBlockDepth - loopDepth - 1,
@ -733,11 +731,11 @@ function lowerExprBlockBody(
for (const inner of headExprs) { for (const inner of headExprs) {
lowerExpr(fcx, innerInstrs, inner); 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. // The rest of the block is unreachable, so we don't bother codegening it.
break; break;
} }
const types = wasmTypeForBody(inner.ty!); const types = wasmTypeForBody(inner.ty);
types.forEach(() => innerInstrs.push({ kind: "drop" })); types.forEach(() => innerInstrs.push({ kind: "drop" }));
} }
@ -872,7 +870,7 @@ function addRt(cx: Context, ast: Ast<Typecked>) {
cx.relocations.push({ cx.relocations.push({
kind: "funccall", kind: "funccall",
instr: mainCall, instr: mainCall,
res: ast.typeckResults!.main, res: ast.typeckResults.main,
}); });
const start: wasm.Func = { const start: wasm.Func = {

View file

@ -341,7 +341,7 @@ export function typeck(ast: Ast<Resolved>): Ast<Typecked> {
if (item.kind === "function" && item.node.name === "main") { if (item.kind === "function" && item.node.name === "main") {
const func = item.node; const func = item.node;
if (func.returnType !== undefined) { if (func.returnType !== undefined) {
const ty = func.returnType.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`,
@ -586,7 +586,7 @@ export function checkBody(
: infcx.newVar(); : infcx.newVar();
const rhs = this.expr(expr.rhs); 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 // AST validation ensures that lets can only be in blocks, where
// the types will be popped. // the types will be popped.
@ -596,7 +596,6 @@ export function checkBody(
const type: Type<Typecked> | undefined = loweredBindingTy && { const type: Type<Typecked> | undefined = loweredBindingTy && {
...expr.type!, ...expr.type!,
ty: loweredBindingTy,
}; };
return { return {
@ -612,7 +611,7 @@ export function checkBody(
const lhs = this.expr(expr.lhs); const lhs = this.expr(expr.lhs);
const rhs = this.expr(expr.rhs); 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) { switch (lhs.kind) {
case "ident": case "ident":
@ -641,7 +640,7 @@ export function checkBody(
const exprs = expr.exprs.map((expr) => this.expr(expr)); 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; localTys.length = prevLocalTysLen;
@ -686,19 +685,19 @@ export function checkBody(
const lhs = this.expr(expr.lhs); const lhs = this.expr(expr.lhs);
const rhs = this.expr(expr.rhs); const rhs = this.expr(expr.rhs);
lhs.ty = infcx.resolveIfPossible(lhs.ty!); lhs.ty = infcx.resolveIfPossible(lhs.ty);
rhs.ty = infcx.resolveIfPossible(rhs.ty!); rhs.ty = infcx.resolveIfPossible(rhs.ty);
return checkBinary({ ...expr, lhs, rhs }); return checkBinary(expr, lhs, rhs);
} }
case "unary": { case "unary": {
const rhs = this.expr(expr.rhs); const rhs = this.expr(expr.rhs);
rhs.ty = infcx.resolveIfPossible(rhs.ty!); rhs.ty = infcx.resolveIfPossible(rhs.ty);
return checkUnary({ ...expr, rhs }); return checkUnary(expr, rhs);
} }
case "call": { case "call": {
const lhs = this.expr(expr.lhs); const lhs = this.expr(expr.lhs);
lhs.ty = infcx.resolveIfPossible(lhs.ty!); lhs.ty = infcx.resolveIfPossible(lhs.ty);
const lhsTy = lhs.ty; const lhsTy = lhs.ty;
if (lhsTy.kind !== "fn") { if (lhsTy.kind !== "fn") {
throw new CompilerError( throw new CompilerError(
@ -718,7 +717,7 @@ export function checkBody(
} }
const arg = checker.expr(args[i]); 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) { if (args.length > lhsTy.params.length) {
@ -732,7 +731,7 @@ export function checkBody(
} }
case "fieldAccess": { case "fieldAccess": {
const lhs = this.expr(expr.lhs); const lhs = this.expr(expr.lhs);
lhs.ty = infcx.resolveIfPossible(lhs.ty!); lhs.ty = infcx.resolveIfPossible(lhs.ty);
const { field } = expr; const { field } = expr;
let ty: Ty; let ty: Ty;
@ -807,14 +806,14 @@ export function checkBody(
const then = this.expr(expr.then); const then = this.expr(expr.then);
const elsePart = expr.else && this.expr(expr.else); 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; let ty: Ty;
if (elsePart) { if (elsePart) {
infcx.assign(then.ty!, elsePart.ty!, elsePart.span); infcx.assign(then.ty, elsePart.ty, elsePart.span);
ty = then.ty!; ty = then.ty!;
} else { } else {
infcx.assign(TY_UNIT, then.ty!, then.span); infcx.assign(TY_UNIT, then.ty, then.span);
ty = TY_UNIT; ty = TY_UNIT;
} }
@ -827,7 +826,7 @@ export function checkBody(
}); });
const body = this.expr(expr.body); 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 hadBreak = loopState.pop();
const ty = hadBreak ? TY_UNIT : TY_NEVER; const ty = hadBreak ? TY_UNIT : TY_NEVER;
@ -875,7 +874,7 @@ export function checkBody(
name.span name.span
); );
} }
infcx.assign(fieldTy[1], field.ty!, field.span); infcx.assign(fieldTy[1], field.ty, field.span);
assignedFields.add(name.name); assignedFields.add(name.name);
}); });
@ -899,7 +898,7 @@ export function checkBody(
const ty: Ty = { const ty: Ty = {
kind: "tuple", kind: "tuple",
elems: fields.map((field) => field.ty!), elems: fields.map((field) => field.ty),
}; };
return { ...expr, fields, ty }; return { ...expr, fields, ty };
@ -919,7 +918,7 @@ export function checkBody(
const checked = checker.expr(body); 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 resolveTy = (ty: Ty, span: Span) => {
const resTy = infcx.resolveIfPossible(ty); const resTy = infcx.resolveIfPossible(ty);
@ -933,7 +932,7 @@ export function checkBody(
const resolver: Folder<Typecked, Typecked> = { const resolver: Folder<Typecked, Typecked> = {
...mkDefaultFolder(), ...mkDefaultFolder(),
expr(expr) { expr(expr) {
const ty = resolveTy(expr.ty!, expr.span); const ty = resolveTy(expr.ty, expr.span);
if (expr.kind === "block") { if (expr.kind === "block") {
expr.locals!.forEach((local) => { expr.locals!.forEach((local) => {
@ -944,8 +943,7 @@ export function checkBody(
return { ...expr, ty }; return { ...expr, ty };
}, },
type(type) { type(type) {
const ty = resolveTy(type.ty!, type.span); return type;
return { ...type, ty };
}, },
ident(ident) { ident(ident) {
return ident; return ident;
@ -958,58 +956,61 @@ export function checkBody(
} }
function checkBinary( function checkBinary(
expr: Expr<Typecked> & ExprBinary<Typecked> expr: Expr<Resolved> & ExprBinary<Resolved>,
lhs: Expr<Typecked>,
rhs: Expr<Typecked>
): Expr<Typecked> { ): Expr<Typecked> {
const lhsTy = expr.lhs.ty!; const lhsTy = lhs.ty;
const rhsTy = expr.rhs.ty!; const rhsTy = rhs.ty;
if (COMPARISON_KINDS.includes(expr.binaryKind)) { if (COMPARISON_KINDS.includes(expr.binaryKind)) {
if (lhsTy.kind === "int" && rhsTy.kind === "int") { 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") { 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 (EQUALITY_KINDS.includes(expr.binaryKind)) {
if (lhsTy.kind === "bool" && rhsTy.kind === "bool") { 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") { 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") { 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 (LOGICAL_KINDS.includes(expr.binaryKind)) {
if (lhsTy.kind === "bool" && rhsTy.kind === "bool") { if (lhsTy.kind === "bool" && rhsTy.kind === "bool") {
return { ...expr, ty: TY_BOOL }; return { ...expr, lhs, rhs, ty: TY_BOOL };
} }
} }
throw new CompilerError( throw new CompilerError(
`invalid types for binary operation: ${printTy(expr.lhs.ty!)} ${ `invalid types for binary operation: ${printTy(lhs.ty)} ${
expr.binaryKind expr.binaryKind
} ${printTy(expr.rhs.ty!)}`, } ${printTy(rhs.ty)}`,
expr.span expr.span
); );
} }
function checkUnary( function checkUnary(
expr: Expr<Typecked> & ExprUnary<Typecked> expr: Expr<Resolved> & ExprUnary<Resolved>,
rhs: Expr<Typecked>
): Expr<Typecked> { ): Expr<Typecked> {
const rhsTy = expr.rhs.ty!; const rhsTy = rhs.ty;
if ( if (
expr.unaryKind === "!" && expr.unaryKind === "!" &&
(rhsTy.kind === "int" || rhsTy.kind === "bool") (rhsTy.kind === "int" || rhsTy.kind === "bool")
) { ) {
return { ...expr, ty: rhsTy }; return { ...expr, rhs, ty: rhsTy };
} }
if (expr.unaryKind === "-" && rhsTy.kind == "int") { if (expr.unaryKind === "-" && rhsTy.kind == "int") {
@ -1017,9 +1018,7 @@ function checkUnary(
} }
throw new CompilerError( throw new CompilerError(
`invalid types for unary operation: ${expr.unaryKind} ${printTy( `invalid types for unary operation: ${expr.unaryKind} ${printTy(rhs.ty)}`,
expr.rhs.ty!
)}`,
expr.span expr.span
); );
} }