compiler: if-else + copyMove + more

This commit is contained in:
SimonFJ20 2025-02-10 17:53:33 +01:00
parent c5e42844af
commit 21c737d06a
6 changed files with 151 additions and 129 deletions

View File

@ -45,7 +45,9 @@ export class PackCompiler {
const resols = new Resolver(this.ctx, entryFileAst).resolve(); const resols = new Resolver(this.ctx, entryFileAst).resolve();
const checker = new Checker(this.ctx, entryFileAst, resols); const checker = new Checker(this.ctx, entryFileAst, resols);
console.log( console.log(
"=== HIR ===\n" + new HirStringifyer(checker).file(entryFileAst), "=== HIR ===\n" +
new HirStringifyer(this.ctx, checker)
.file(entryFileAst),
); );
const astLowerer = new AstLowerer( const astLowerer = new AstLowerer(
this.ctx, this.ctx,

View File

@ -178,7 +178,10 @@ export class FnLowerer {
place: { local: patLocal, proj: [] }, place: { local: patLocal, proj: [] },
rval: { rval: {
tag: "use", tag: "use",
operand: { tag: "move", place: { local, proj } }, operand: this.copyOrMoveLocal(
local,
this.locals.get(local)!.ty,
),
}, },
}); });
return; return;
@ -290,10 +293,40 @@ export class FnLowerer {
} }
private lowerIfExpr(expr: ast.Expr, kind: ast.IfExpr): RVal { private lowerIfExpr(expr: ast.Expr, kind: ast.IfExpr): RVal {
const cond = this.lowerExprToOperand(kind.cond); const ty = this.ch.exprTy(expr);
const discr = this.lowerExprToOperand(kind.cond);
const condBlock = this.currentBlock!; const condBlock = this.currentBlock!;
if (kind.falsy) { if (kind.falsy) {
return todo(); const local = this.local(ty);
const truthBlock = this.pushBlock();
const truthy = this.lowerExpr(kind.truthy);
this.addStmt({
tag: "assign",
place: { local, proj: [] },
rval: truthy,
});
const falsyBlock = this.pushBlock();
const falsy = this.lowerExpr(kind.falsy);
this.addStmt({
tag: "assign",
place: { local, proj: [] },
rval: falsy,
});
const exit = this.pushBlock();
this.setTer({ tag: "goto", target: exit.id }, truthBlock);
this.setTer({ tag: "goto", target: exit.id }, falsyBlock);
this.setTer({
tag: "switch",
discr,
targets: [{ value: 1, target: truthBlock.id }],
otherwise: falsyBlock.id,
}, condBlock);
return { tag: "use", operand: this.copyOrMoveLocal(local, ty) };
} else { } else {
if (this.ch.exprTy(expr).kind.tag !== "null") { if (this.ch.exprTy(expr).kind.tag !== "null") {
throw new Error(); throw new Error();
@ -304,7 +337,7 @@ export class FnLowerer {
this.setTer({ tag: "goto", target: exit.id }, truthBlock); this.setTer({ tag: "goto", target: exit.id }, truthBlock);
this.setTer({ this.setTer({
tag: "switch", tag: "switch",
discr: cond, discr,
targets: [{ value: 1, target: truthBlock.id }], targets: [{ value: 1, target: truthBlock.id }],
otherwise: exit.id, otherwise: exit.id,
}, condBlock); }, condBlock);
@ -361,7 +394,7 @@ export class FnLowerer {
place: { local, proj: [] }, place: { local, proj: [] },
rval, rval,
}); });
return { tag: "move", place: { local, proj: [] } }; return this.copyOrMoveLocal(local, ty);
} }
} }
exhausted(k); exhausted(k);
@ -388,24 +421,7 @@ export class FnLowerer {
if (patRes.kind.tag === "fn_param") { if (patRes.kind.tag === "fn_param") {
this.paramLocals.set(local, patRes.kind.paramIdx); this.paramLocals.set(local, patRes.kind.paramIdx);
} }
const isCopyable = (() => { return this.copyOrMoveLocal(local, ty);
switch (ty.kind.tag) {
case "error":
case "unknown":
return false;
case "null":
case "int":
case "bool":
return true;
case "fn":
return false;
}
exhausted(ty.kind);
})();
return {
tag: isCopyable ? "copy" : "move",
place: { local, proj: [] },
};
} }
case "fn": { case "fn": {
const { item, kind } = re.kind; const { item, kind } = re.kind;
@ -415,6 +431,27 @@ export class FnLowerer {
exhausted(re.kind); exhausted(re.kind);
} }
private copyOrMoveLocal(local: LocalId, ty: Ty): Operand {
const isCopyable = (() => {
switch (ty.kind.tag) {
case "error":
case "unknown":
return false;
case "null":
case "int":
case "bool":
return true;
case "fn":
return false;
}
exhausted(ty.kind);
})();
return {
tag: isCopyable ? "copy" : "move",
place: { local, proj: [] },
};
}
private local(ty: Ty, ident?: ast.Ident): LocalId { private local(ty: Ty, ident?: ast.Ident): LocalId {
const id = this.localIds.nextThenStep(); const id = this.localIds.nextThenStep();
this.locals.set(id, { id, ty, ident }); this.locals.set(id, { id, ty, ident });

View File

@ -134,8 +134,8 @@ export class Parser {
this.step(); this.step();
} }
private parseExpr(): Expr { private parseExpr(rs: ExprRestricts = 0): Expr {
return this.parseBinary(); return this.parseBinary(rs);
} }
private parseBlock(): ParseRes<Block, undefined> { private parseBlock(): ParseRes<Block, undefined> {
@ -698,12 +698,12 @@ export class Parser {
private parseIf(): Expr { private parseIf(): Expr {
const pos = this.span(); const pos = this.span();
this.step(); this.step();
const cond = this.parseExpr(); const cond = this.parseExpr(ExprRestricts.NoStructs);
if (!this.test("{")) { if (!this.test("{")) {
this.report("expected block"); this.report("expected block");
return this.expr({ tag: "error" }, pos); return this.expr({ tag: "error" }, pos);
} }
const truthy = this.parseExpr(); const truthy = this.parseBlockExpr();
if (!this.test("else")) { if (!this.test("else")) {
return this.expr({ tag: "if", cond, truthy }, pos); return this.expr({ tag: "if", cond, truthy }, pos);
} }
@ -721,16 +721,16 @@ export class Parser {
return this.expr({ tag: "if", cond, truthy, falsy }, pos); return this.expr({ tag: "if", cond, truthy, falsy }, pos);
} }
private parseBinary(): Expr { private parseBinary(rs: ExprRestricts): Expr {
return this.parseOr(); return this.parseOr(rs);
} }
private parseOr(): Expr { private parseOr(rs: ExprRestricts): Expr {
const pos = this.span(); const pos = this.span();
let left = this.parseAnd(); let left = this.parseAnd(rs);
while (true) { while (true) {
if (this.test("or")) { if (this.test("or")) {
left = this.parBinTail(left, pos, this.parseAnd, "or"); left = this.parBinTail(left, pos, rs, this.parseAnd, "or");
} else { } else {
break; break;
} }
@ -738,12 +738,12 @@ export class Parser {
return left; return left;
} }
private parseAnd(): Expr { private parseAnd(rs: ExprRestricts): Expr {
const pos = this.span(); const pos = this.span();
let left = this.parseEquality(); let left = this.parseEq(rs);
while (true) { while (true) {
if (this.test("and")) { if (this.test("and")) {
left = this.parBinTail(left, pos, this.parseEquality, "and"); left = this.parBinTail(left, pos, rs, this.parseEq, "and");
} else { } else {
break; break;
} }
@ -751,46 +751,36 @@ export class Parser {
return left; return left;
} }
private parseEquality(): Expr { private parseEq(rs: ExprRestricts): Expr {
const pos = this.span(); const pos = this.span();
const left = this.parseComparison(); const left = this.parseComparison(rs);
if (this.test("==")) { if (this.test("==") || this.test("!=")) {
return this.parBinTail(left, pos, this.parseComparison, "=="); const op = this.current().type as BinaryType;
} return this.parBinTail(left, pos, rs, this.parseComparison, op);
if (this.test("!=")) {
return this.parBinTail(left, pos, this.parseComparison, "!=");
} }
return left; return left;
} }
private parseComparison(): Expr { private parseComparison(rs: ExprRestricts): Expr {
const pos = this.span(); const pos = this.span();
const left = this.parseAddSub(); const left = this.parseAddSub(rs);
if (this.test("<")) { if (
return this.parBinTail(left, pos, this.parseAddSub, "<"); this.test("<") || this.test(">") || this.test("<=") ||
} this.test(">=")
if (this.test(">")) { ) {
return this.parBinTail(left, pos, this.parseAddSub, ">"); const op = this.current().type as BinaryType;
} return this.parBinTail(left, pos, rs, this.parseAddSub, op);
if (this.test("<=")) {
return this.parBinTail(left, pos, this.parseAddSub, "<=");
}
if (this.test(">=")) {
return this.parBinTail(left, pos, this.parseAddSub, ">=");
} }
return left; return left;
} }
private parseAddSub(): Expr { private parseAddSub(rs: ExprRestricts): Expr {
const pos = this.span(); const pos = this.span();
let left = this.parseMulDiv(); let left = this.parseMulDiv(rs);
while (true) { while (true) {
if (this.test("+")) { if (this.test("+") || this.test("-")) {
left = this.parBinTail(left, pos, this.parseMulDiv, "+"); const op = this.current().type as BinaryType;
continue; left = this.parBinTail(left, pos, rs, this.parseMulDiv, op);
}
if (this.test("-")) {
left = this.parBinTail(left, pos, this.parseMulDiv, "-");
continue; continue;
} }
break; break;
@ -798,16 +788,13 @@ export class Parser {
return left; return left;
} }
private parseMulDiv(): Expr { private parseMulDiv(rs: ExprRestricts): Expr {
const pos = this.span(); const pos = this.span();
let left = this.parsePrefix(); let left = this.parsePrefix(rs);
while (true) { while (true) {
if (this.test("*")) { if (this.test("*") || this.test("/")) {
left = this.parBinTail(left, pos, this.parsePrefix, "*"); const op = this.current().type as BinaryType;
continue; left = this.parBinTail(left, pos, rs, this.parsePrefix, op);
}
if (this.test("/")) {
left = this.parBinTail(left, pos, this.parsePrefix, "/");
continue; continue;
} }
break; break;
@ -818,23 +805,24 @@ export class Parser {
private parBinTail( private parBinTail(
left: Expr, left: Expr,
span: Span, span: Span,
parseRight: (this: Parser) => Expr, rs: ExprRestricts,
parseRight: (this: Parser, rs: ExprRestricts) => Expr,
binaryType: BinaryType, binaryType: BinaryType,
): Expr { ): Expr {
this.step(); this.step();
const right = parseRight.call(this); const right = parseRight.call(this, rs);
return this.expr( return this.expr(
{ tag: "binary", binaryType, left, right }, { tag: "binary", binaryType, left, right },
span, span,
); );
} }
private parsePrefix(): Expr { private parsePrefix(rs: ExprRestricts): Expr {
const pos = this.span(); const pos = this.span();
if (this.test("not") || this.test("-")) { if (this.test("not") || this.test("-")) {
const unaryType = this.current().type as UnaryType; const unaryType = this.current().type as UnaryType;
this.step(); this.step();
const expr = this.parsePrefix(); const expr = this.parsePrefix(rs);
return this.expr({ tag: "unary", unaryType, expr }, pos); return this.expr({ tag: "unary", unaryType, expr }, pos);
} }
if (this.test("&")) { if (this.test("&")) {
@ -849,19 +837,19 @@ export class Parser {
this.step(); this.step();
mut = true; mut = true;
} }
const expr = this.parsePrefix(); const expr = this.parsePrefix(rs);
return this.expr({ tag: "ref", expr, mut, refType }, pos); return this.expr({ tag: "ref", expr, mut, refType }, pos);
} }
if (this.test("*")) { if (this.test("*")) {
this.step(); this.step();
const expr = this.parsePrefix(); const expr = this.parsePrefix(rs);
return this.expr({ tag: "deref", expr }, pos); return this.expr({ tag: "deref", expr }, pos);
} }
return this.parsePostfix(); return this.parsePostfix(rs);
} }
private parsePostfix(): Expr { private parsePostfix(rs: ExprRestricts): Expr {
let subject = this.parseOperand(); let subject = this.parseOperand(rs);
while (true) { while (true) {
if (this.test(".")) { if (this.test(".")) {
subject = this.parseFieldTail(subject); subject = this.parseFieldTail(subject);
@ -917,14 +905,14 @@ export class Parser {
return Res.Ok(this.parseExpr()); return Res.Ok(this.parseExpr());
} }
private parseOperand(): Expr { private parseOperand(rs: ExprRestricts): Expr {
const pos = this.span(); const pos = this.span();
if (this.test("ident")) { if (this.test("ident")) {
const pathRes = this.parsePath(); const pathRes = this.parsePath();
if (!pathRes.ok) { if (!pathRes.ok) {
return this.expr({ tag: "error" }, pos); return this.expr({ tag: "error" }, pos);
} }
if (this.test("{")) { if (this.test("{") && !(rs & ExprRestricts.NoStructs)) {
this.step(); this.step();
const fields = this.parseDelimitedList( const fields = this.parseDelimitedList(
this.parseExprField, this.parseExprField,
@ -1264,3 +1252,9 @@ export class Parser {
return this.cx.ty(kind, span); return this.cx.ty(kind, span);
} }
} }
const ExprRestricts = {
NoStructs: 1 << 0,
} as const;
type ExprRestricts = (typeof ExprRestricts)[keyof typeof ExprRestricts];

View File

@ -4,12 +4,12 @@ fn add(lhs: int, rhs: int) -> int {
} }
fn main() { fn main() {
let a = 5; let foo = 5;
let b = 7 + a; let bar = 7 + foo;
if true {} if foo {} else {}
let c = add(a, b); let c = add(foo, bar);
} }

View File

@ -1,10 +1,11 @@
import * as ast from "@slige/ast"; import * as ast from "@slige/ast";
import { exhausted, todo } from "@slige/common"; import { Ctx, exhausted, todo } from "@slige/common";
import { Checker } from "@slige/check"; import { Checker } from "@slige/check";
import { Ty } from "@slige/ty"; import { Ty, tyToString } from "@slige/ty";
export class HirStringifyer { export class HirStringifyer {
public constructor( public constructor(
private ctx: Ctx,
private ch: Checker, private ch: Checker,
) {} ) {}
@ -14,7 +15,7 @@ export class HirStringifyer {
).join("\n"); ).join("\n");
} }
public stmt(stmt: ast.Stmt, depth = 0): string { public stmt(stmt: ast.Stmt, d = 0): string {
const k = stmt.kind; const k = stmt.kind;
switch (k.tag) { switch (k.tag) {
case "error": case "error":
@ -23,18 +24,18 @@ export class HirStringifyer {
return this.item(k.item); return this.item(k.item);
case "let": case "let":
return `let ${this.pat(k.pat)}${ return `let ${this.pat(k.pat)}${
k.expr && ` = ${this.expr(k.expr)}` || "" k.expr && ` = ${this.expr(k.expr, d)}` || ""
};`; };`;
case "return": case "return":
return `return${k.expr && ` ${this.expr(k.expr)}` || ""};`; return `return${k.expr && ` ${this.expr(k.expr, d)}` || ""};`;
case "break": case "break":
return `break${k.expr && ` ${this.expr(k.expr)}` || ""};`; return `break${k.expr && ` ${this.expr(k.expr, d)}` || ""};`;
case "continue": case "continue":
return `continue;`; return `continue;`;
case "assign": case "assign":
return `${this.expr(k.subject)} = ${this.expr(k.value)};`; return `${this.expr(k.subject, d)} = ${this.expr(k.value, d)};`;
case "expr": case "expr":
return `${this.expr(k.expr)};`; return `${this.expr(k.expr, d)};`;
} }
exhausted(k); exhausted(k);
} }
@ -76,7 +77,7 @@ export class HirStringifyer {
exhausted(k); exhausted(k);
} }
public expr(expr: ast.Expr, depth = 0): string { public expr(expr: ast.Expr, d: number): string {
const k = expr.kind; const k = expr.kind;
switch (k.tag) { switch (k.tag) {
case "error": case "error":
@ -102,19 +103,21 @@ export class HirStringifyer {
case "index": case "index":
return todo(k.tag); return todo(k.tag);
case "call": case "call":
return `${this.expr(k.expr)}(${ return `${this.expr(k.expr, d)}(${
k.args.map((arg) => this.expr(arg)).join(", ") k.args.map((arg) => this.expr(arg, d)).join(", ")
})`; })`;
case "unary": case "unary":
return todo(k.tag); return todo(k.tag);
case "binary": case "binary":
return `${this.expr(k.left)} ${k.binaryType} ${ return `${this.expr(k.left, d)} ${k.binaryType} ${
this.expr(k.right) this.expr(k.right, d)
}`; }`;
case "block": case "block":
return todo(k.tag); return this.block(k.block, d);
case "if": case "if":
return `if ${this.expr(k.cond)}`; return `if ${this.expr(k.cond, d)} ${this.expr(k.truthy, d)}${
k.falsy && ` else ${this.expr(k.falsy, d)}` || ""
}`;
case "loop": case "loop":
case "while": case "while":
case "for": case "for":
@ -124,7 +127,7 @@ export class HirStringifyer {
exhausted(k); exhausted(k);
} }
public pat(pat: ast.Pat, depth = 0): string { public pat(pat: ast.Pat): string {
const k = pat.kind; const k = pat.kind;
switch (k.tag) { switch (k.tag) {
case "error": case "error":
@ -139,41 +142,29 @@ export class HirStringifyer {
exhausted(k); exhausted(k);
} }
public block(block: ast.Block, depth = 0): string { public block(block: ast.Block, d: number): string {
if (block.stmts.length === 0 && !block.expr) {
return "{}";
}
return `{\n${ return `{\n${
[ [
...block.stmts ...block.stmts
.map((stmt) => this.stmt(stmt, depth + 1)), .map((stmt) => this.stmt(stmt, d + 1)),
...(block.expr ? [this.expr(block.expr)] : []), ...(block.expr ? [this.expr(block.expr, d + 1)] : []),
] ]
.map((str) => indent(depth + 1) + str) .map((str) => indent(d + 1) + str)
.join("\n") .join("\n")
}\n${indent(depth)}}`; }\n${indent(d)}}`;
} }
public path(path: ast.Path): string { public path(path: ast.Path): string {
return path.segments.map((seg) => seg.ident.text).join("::"); return path.segments
.map((seg) => seg.ident.text)
.join("::");
} }
public ty(ty: Ty): string { public ty(ty: Ty): string {
const k = ty.kind; return tyToString(this.ctx, ty);
switch (k.tag) {
case "error":
return "<error>";
case "unknown":
return "<unknown>";
case "null":
return "null";
case "int":
return "int";
case "bool":
return "bool";
case "fn":
return `fn ${k.item.ident}(${
k.params.map((param) => this.ty(param)).join(", ")
}) -> ${this.ty(k.returnTy)}`;
}
exhausted(k);
} }
} }

View File

@ -10,11 +10,9 @@ import {
ProjElem, ProjElem,
RVal, RVal,
Stmt, Stmt,
StmtKind,
Ter, Ter,
} from "@slige/middle"; } from "@slige/middle";
import { Ctx, exhausted, IdMap, todo } from "@slige/common"; import { Ctx, exhausted, IdMap, todo } from "@slige/common";
import { Checker } from "@slige/check";
import { Ty, tyToString } from "@slige/ty"; import { Ty, tyToString } from "@slige/ty";
export class MirFnStringifyer { export class MirFnStringifyer {