mirror of
https://git.sfja.dk/Mikkel/slige.git
synced 2025-01-18 07:46:32 +00:00
add borrow checker
This commit is contained in:
parent
a7349890d0
commit
5d6b1abefc
@ -63,6 +63,9 @@ export type ExprKind =
|
||||
sym: Sym;
|
||||
}
|
||||
| { type: "group"; expr: Expr }
|
||||
| { type: "ref"; subject: Expr }
|
||||
| { type: "ref_mut"; subject: Expr }
|
||||
| { type: "deref"; subject: Expr }
|
||||
| { type: "array"; exprs: Expr[] }
|
||||
| { type: "struct"; fields: Field[] }
|
||||
| { type: "field"; subject: Expr; ident: string }
|
||||
@ -117,6 +120,7 @@ export type Param = {
|
||||
id: number;
|
||||
index?: number;
|
||||
ident: string;
|
||||
mut: boolean;
|
||||
etype?: EType;
|
||||
pos: Pos;
|
||||
sym?: Sym;
|
||||
@ -157,7 +161,11 @@ export type ETypeKind =
|
||||
ident: string;
|
||||
sym: Sym;
|
||||
}
|
||||
| { type: "array"; inner: EType }
|
||||
| { type: "ref"; subject: EType }
|
||||
| { type: "ref_mut"; subject: EType }
|
||||
| { type: "ptr"; subject: EType }
|
||||
| { type: "ptr_mut"; subject: EType }
|
||||
| { type: "array"; subject: EType }
|
||||
| { type: "struct"; fields: Param[] }
|
||||
| { type: "type_of"; expr: Expr };
|
||||
|
||||
@ -227,3 +235,7 @@ export class AnnoView {
|
||||
return anno;
|
||||
}
|
||||
}
|
||||
|
||||
export function forceType(v: unknown): { type: string } {
|
||||
return v as { type: string };
|
||||
}
|
||||
|
@ -22,6 +22,9 @@ export interface AstVisitor<Args extends unknown[] = []> {
|
||||
visitStringExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitIdentExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitGroupExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitRefExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitRefMutExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitDerefExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitArrayExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitStructExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitFieldExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
@ -50,6 +53,10 @@ export interface AstVisitor<Args extends unknown[] = []> {
|
||||
visitStringEType?(etype: EType, ...args: Args): VisitRes;
|
||||
visitIdentEType?(etype: EType, ...args: Args): VisitRes;
|
||||
visitSymEType?(etype: EType, ...args: Args): VisitRes;
|
||||
visitRefEType?(etype: EType, ...args: Args): VisitRes;
|
||||
visitRefMutEType?(etype: EType, ...args: Args): VisitRes;
|
||||
visitPtrEType?(etype: EType, ...args: Args): VisitRes;
|
||||
visitPtrMutEType?(etype: EType, ...args: Args): VisitRes;
|
||||
visitArrayEType?(etype: EType, ...args: Args): VisitRes;
|
||||
visitStructEType?(etype: EType, ...args: Args): VisitRes;
|
||||
visitTypeOfEType?(etype: EType, ...args: Args): VisitRes;
|
||||
@ -152,6 +159,18 @@ export function visitExpr<Args extends unknown[] = []>(
|
||||
if (v.visitGroupExpr?.(expr, ...args) == "stop") return;
|
||||
visitExpr(expr.kind.expr, v, ...args);
|
||||
break;
|
||||
case "ref":
|
||||
if (v.visitRefExpr?.(expr, ...args) == "stop") return;
|
||||
visitExpr(expr.kind.subject, v, ...args);
|
||||
break;
|
||||
case "ref_mut":
|
||||
if (v.visitRefMutExpr?.(expr, ...args) == "stop") return;
|
||||
visitExpr(expr.kind.subject, v, ...args);
|
||||
break;
|
||||
case "deref":
|
||||
if (v.visitDerefExpr?.(expr, ...args) == "stop") return;
|
||||
visitExpr(expr.kind.subject, v, ...args);
|
||||
break;
|
||||
case "field":
|
||||
if (v.visitFieldExpr?.(expr, ...args) == "stop") return;
|
||||
visitExpr(expr.kind.subject, v, ...args);
|
||||
@ -289,9 +308,25 @@ export function visitEType<Args extends unknown[] = []>(
|
||||
case "sym":
|
||||
if (v.visitSymEType?.(etype, ...args) == "stop") return;
|
||||
break;
|
||||
case "ref":
|
||||
if (v.visitRefEType?.(etype, ...args) == "stop") return;
|
||||
visitEType(etype.kind.subject, v, ...args);
|
||||
break;
|
||||
case "ref_mut":
|
||||
if (v.visitRefMutEType?.(etype, ...args) == "stop") return;
|
||||
visitEType(etype.kind.subject, v, ...args);
|
||||
break;
|
||||
case "ptr":
|
||||
if (v.visitPtrEType?.(etype, ...args) == "stop") return;
|
||||
visitEType(etype.kind.subject, v, ...args);
|
||||
break;
|
||||
case "ptr_mut":
|
||||
if (v.visitPtrMutEType?.(etype, ...args) == "stop") return;
|
||||
visitEType(etype.kind.subject, v, ...args);
|
||||
break;
|
||||
case "array":
|
||||
if (v.visitArrayEType?.(etype, ...args) == "stop") return;
|
||||
if (etype.kind.inner) visitEType(etype.kind.inner, v, ...args);
|
||||
visitEType(etype.kind.subject, v, ...args);
|
||||
break;
|
||||
case "struct":
|
||||
if (v.visitStructEType?.(etype, ...args) == "stop") return;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { AnnoView, EType, Expr, Stmt, Sym } from "./ast.ts";
|
||||
import { AnnoView, EType, Expr, forceType, Stmt, Sym } from "./ast.ts";
|
||||
import { printStackTrace, Reporter } from "./info.ts";
|
||||
import { Pos } from "./token.ts";
|
||||
import {
|
||||
@ -286,7 +286,7 @@ export class Checker {
|
||||
}
|
||||
if (
|
||||
subject.type == "array" &&
|
||||
!vtypesEqual(subject.inner, value)
|
||||
!vtypesEqual(subject.subject, value)
|
||||
) {
|
||||
this.report(
|
||||
`cannot assign incompatible type to array ` +
|
||||
@ -348,6 +348,12 @@ export class Checker {
|
||||
return { type: "string" };
|
||||
case "group":
|
||||
return this.checkExpr(expr.kind.expr);
|
||||
case "ref":
|
||||
return this.checkRefExpr(expr);
|
||||
case "ref_mut":
|
||||
return this.checkRefMutExpr(expr);
|
||||
case "deref":
|
||||
return this.checkDerefExpr(expr);
|
||||
case "array":
|
||||
throw new Error("should have been desugared");
|
||||
case "struct":
|
||||
@ -421,6 +427,89 @@ export class Checker {
|
||||
}
|
||||
}
|
||||
|
||||
public checkRefExpr(expr: Expr): VType {
|
||||
if (expr.kind.type !== "ref") {
|
||||
throw new Error();
|
||||
}
|
||||
const subject = this.checkExpr(expr.kind.subject);
|
||||
if (expr.kind.subject.kind.type === "sym") {
|
||||
const sym = expr.kind.subject.kind.sym;
|
||||
if (sym.type === "let" || sym.type === "fn_param") {
|
||||
return { type: "ref", subject };
|
||||
}
|
||||
this.report(
|
||||
`taking reference to symbol type '${sym.type}' not supported`,
|
||||
expr.pos,
|
||||
);
|
||||
return { type: "error" };
|
||||
}
|
||||
this.report(
|
||||
`taking reference to expression type '${
|
||||
forceType(expr.kind.subject.kind).type
|
||||
}' not supported`,
|
||||
expr.pos,
|
||||
);
|
||||
return { type: "error" };
|
||||
}
|
||||
|
||||
public checkRefMutExpr(expr: Expr): VType {
|
||||
if (expr.kind.type !== "ref_mut") {
|
||||
throw new Error();
|
||||
}
|
||||
const subject = this.checkExpr(expr.kind.subject);
|
||||
if (expr.kind.subject.kind.type === "sym") {
|
||||
const sym = expr.kind.subject.kind.sym;
|
||||
if (sym.type === "let" || sym.type === "fn_param") {
|
||||
if (!sym.param.mut) {
|
||||
this.report(
|
||||
`symbol '${sym.ident}' it not declared mutable`,
|
||||
expr.pos,
|
||||
);
|
||||
this.reporter.addNote({
|
||||
reporter: "checker",
|
||||
msg: "symbol defined here",
|
||||
pos: sym.param.pos,
|
||||
});
|
||||
return { type: "error" };
|
||||
}
|
||||
return { type: "ref_mut", subject };
|
||||
}
|
||||
this.report(
|
||||
`taking reference to symbol type '${sym.type}' not supported`,
|
||||
expr.pos,
|
||||
);
|
||||
return { type: "error" };
|
||||
}
|
||||
this.report(
|
||||
`taking mutable reference to expression type '${
|
||||
forceType(expr.kind.subject.kind).type
|
||||
}' not supported`,
|
||||
expr.pos,
|
||||
);
|
||||
return { type: "error" };
|
||||
}
|
||||
public checkDerefExpr(expr: Expr): VType {
|
||||
if (expr.kind.type !== "deref") {
|
||||
throw new Error();
|
||||
}
|
||||
const subject = this.checkExpr(expr.kind.subject);
|
||||
switch (subject.type) {
|
||||
case "ref":
|
||||
return subject.subject;
|
||||
case "ref_mut":
|
||||
return subject.subject;
|
||||
case "ptr":
|
||||
return subject.subject;
|
||||
case "ptr_mut":
|
||||
return subject.subject;
|
||||
}
|
||||
this.report(
|
||||
`dereferenced type is neither a reference nor a pointer`,
|
||||
expr.pos,
|
||||
);
|
||||
return { type: "error" };
|
||||
}
|
||||
|
||||
public checkStructExpr(expr: Expr): VType {
|
||||
if (expr.kind.type !== "struct") {
|
||||
throw new Error();
|
||||
@ -472,7 +561,7 @@ export class Checker {
|
||||
return { type: "error" };
|
||||
}
|
||||
if (subject.type === "array") {
|
||||
return subject.inner;
|
||||
return subject.subject;
|
||||
}
|
||||
return { type: "int" };
|
||||
}
|
||||
@ -653,7 +742,7 @@ export class Checker {
|
||||
return { a, b };
|
||||
}
|
||||
if (a.type === "array" && b.type === "array") {
|
||||
return this.reduceToSignificant(a.inner, b.inner);
|
||||
return this.reduceToSignificant(a.subject, b.subject);
|
||||
}
|
||||
if (a.type === "generic" && b.type === "generic") {
|
||||
return { a, b };
|
||||
@ -670,8 +759,13 @@ export class Checker {
|
||||
case "int":
|
||||
case "bool":
|
||||
return false;
|
||||
case "ref":
|
||||
case "ref_mut":
|
||||
case "ptr":
|
||||
case "ptr_mut":
|
||||
return this.vtypeContainsGeneric(vtype.subject);
|
||||
case "array":
|
||||
return this.vtypeContainsGeneric(vtype.inner);
|
||||
return this.vtypeContainsGeneric(vtype.subject);
|
||||
case "struct":
|
||||
return vtype.fields.some((field) =>
|
||||
this.vtypeContainsGeneric(field.vtype)
|
||||
@ -749,10 +843,14 @@ export class Checker {
|
||||
case "int":
|
||||
case "bool":
|
||||
return vtype;
|
||||
case "ref":
|
||||
case "ref_mut":
|
||||
case "ptr":
|
||||
case "ptr_mut":
|
||||
case "array":
|
||||
return {
|
||||
type: "array",
|
||||
inner: this.concretizeVType(vtype.inner, generics),
|
||||
type: vtype.type,
|
||||
subject: this.concretizeVType(vtype.subject, generics),
|
||||
};
|
||||
case "struct":
|
||||
return {
|
||||
@ -994,9 +1092,25 @@ export class Checker {
|
||||
this.report(`sym type '${etype.kind.sym.type}' used as type`, pos);
|
||||
return { type: "error" };
|
||||
}
|
||||
if (etype.kind.type === "ref") {
|
||||
const subject = this.checkEType(etype.kind.subject);
|
||||
return { type: "ref", subject };
|
||||
}
|
||||
if (etype.kind.type === "ref_mut") {
|
||||
const subject = this.checkEType(etype.kind.subject);
|
||||
return { type: "ref", subject };
|
||||
}
|
||||
if (etype.kind.type === "ptr") {
|
||||
const subject = this.checkEType(etype.kind.subject);
|
||||
return { type: "ptr", subject };
|
||||
}
|
||||
if (etype.kind.type === "ptr_mut") {
|
||||
const subject = this.checkEType(etype.kind.subject);
|
||||
return { type: "ptr_mut", subject };
|
||||
}
|
||||
if (etype.kind.type === "array") {
|
||||
const inner = this.checkEType(etype.kind.inner);
|
||||
return { type: "array", inner };
|
||||
const subject = this.checkEType(etype.kind.subject);
|
||||
return { type: "array", subject };
|
||||
}
|
||||
if (etype.kind.type === "struct") {
|
||||
const noTypeTest = etype.kind.fields.reduce(
|
||||
|
@ -21,7 +21,8 @@ import {
|
||||
eliminateOnlyChildsBlocks,
|
||||
eliminateUnreachableBlocks,
|
||||
} from "./middle/elim_blocks.ts";
|
||||
import { eliminateTransientVals } from "./middle/elim_transient_vals.ts";
|
||||
import { checkBorrows } from "./middle/borrow_checker.ts";
|
||||
import { makeMoveCopyExplicit } from "./middle/explicit_move_copy.ts";
|
||||
|
||||
export type CompileResult = {
|
||||
program: number[];
|
||||
@ -53,44 +54,52 @@ export class Compiler {
|
||||
|
||||
new Checker(this.reporter).check(ast);
|
||||
|
||||
const mir = lowerAst(ast);
|
||||
//const mir = lowerAst(ast);
|
||||
//
|
||||
//console.log("Before optimizations:");
|
||||
//printMir(mir);
|
||||
|
||||
console.log("Before optimizations:");
|
||||
printMir(mir);
|
||||
|
||||
const mirHistory = [mirOpCount(mir)];
|
||||
for (let i = 0; i < 1; ++i) {
|
||||
eliminateUnusedLocals(mir, this.reporter, mirHistory.length === 1);
|
||||
eliminateOnlyChildsBlocks(mir);
|
||||
eliminateUnreachableBlocks(mir);
|
||||
eliminateTransientVals(mir);
|
||||
|
||||
const opCount = mirOpCount(mir);
|
||||
const histOccurence = mirHistory
|
||||
.filter((v) => v === opCount).length;
|
||||
if (histOccurence >= 2) {
|
||||
break;
|
||||
}
|
||||
mirHistory.push(opCount);
|
||||
}
|
||||
|
||||
console.log("After optimizations:");
|
||||
printMir(mir);
|
||||
//const mirHistory = [mirOpCount(mir)];
|
||||
//for (let i = 0; i < 1; ++i) {
|
||||
// eliminateUnusedLocals(mir, this.reporter, mirHistory.length === 1);
|
||||
// eliminateOnlyChildsBlocks(mir);
|
||||
// eliminateUnreachableBlocks(mir);
|
||||
// eliminateTransientVals(mir);
|
||||
//
|
||||
// const opCount = mirOpCount(mir);
|
||||
// const histOccurence = mirHistory
|
||||
// .filter((v) => v === opCount).length;
|
||||
// if (histOccurence >= 2) {
|
||||
// break;
|
||||
// }
|
||||
// mirHistory.push(opCount);
|
||||
//}
|
||||
//
|
||||
//console.log("After optimizations:");
|
||||
//printMir(mir);
|
||||
|
||||
if (this.reporter.errorOccured()) {
|
||||
console.error("Errors occurred, stopping compilation.");
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
const { monoFns, callMap } = new Monomorphizer(ast).monomorphize();
|
||||
const mir = lowerAst(ast);
|
||||
|
||||
const lastPos = await lastPosInTextFile(this.startFilePath);
|
||||
makeMoveCopyExplicit(mir);
|
||||
checkBorrows(mir, this.reporter);
|
||||
|
||||
const lowerer = new Lowerer(monoFns, callMap, lastPos);
|
||||
const { program, fnNames } = lowerer.lower();
|
||||
//lowerer.printProgram();
|
||||
printMir(mir);
|
||||
|
||||
return { program, fnNames };
|
||||
//const { monoFns, callMap } = new Monomorphizer(ast).monomorphize();
|
||||
//
|
||||
//const lastPos = await lastPosInTextFile(this.startFilePath);
|
||||
//
|
||||
//const lowerer = new Lowerer(monoFns, callMap, lastPos);
|
||||
//const { program, fnNames } = lowerer.lower();
|
||||
////lowerer.printProgram();
|
||||
//
|
||||
//return { program, fnNames };
|
||||
return { program: [], fnNames: {} };
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,7 @@ export class ArrayLiteralDesugarer implements AstVisitor {
|
||||
type: "let",
|
||||
param: this.astCreator.param({
|
||||
ident: "::value",
|
||||
mut: true,
|
||||
pos: npos,
|
||||
}),
|
||||
value: Expr({
|
||||
|
@ -72,6 +72,7 @@ export class SpecialLoopDesugarer implements AstVisitor {
|
||||
type: "let",
|
||||
param: this.astCreator.param({
|
||||
ident: "::values",
|
||||
mut: true,
|
||||
pos: npos,
|
||||
}),
|
||||
value: expr.kind.value,
|
||||
@ -80,6 +81,7 @@ export class SpecialLoopDesugarer implements AstVisitor {
|
||||
type: "let",
|
||||
param: this.astCreator.param({
|
||||
ident: "::length",
|
||||
mut: false,
|
||||
pos: npos,
|
||||
}),
|
||||
value: Expr({
|
||||
@ -104,6 +106,7 @@ export class SpecialLoopDesugarer implements AstVisitor {
|
||||
type: "let",
|
||||
param: this.astCreator.param({
|
||||
ident: "::index",
|
||||
mut: true,
|
||||
pos: npos,
|
||||
}),
|
||||
value: Expr({ type: "int", value: 0 }),
|
||||
|
@ -54,6 +54,7 @@ export class StructLiteralDesugarer implements AstVisitor {
|
||||
type: "let",
|
||||
param: this.astCreator.param({
|
||||
ident: "::value",
|
||||
mut: true,
|
||||
pos: npos,
|
||||
}),
|
||||
value: Expr({
|
||||
|
@ -36,6 +36,7 @@ export class Lexer {
|
||||
"break",
|
||||
"return",
|
||||
"let",
|
||||
"mut",
|
||||
"fn",
|
||||
"loop",
|
||||
"if",
|
||||
@ -128,7 +129,7 @@ export class Lexer {
|
||||
this.step();
|
||||
return { ...this.token("string", pos), stringValue: value };
|
||||
}
|
||||
if (this.test(/[\+\{\};=\-\*\(\)\.,:;\[\]><!0#]/)) {
|
||||
if (this.test(/[\+\{\};=\-\*\(\)\.,:;\[\]><!0#&]/)) {
|
||||
const first = this.current();
|
||||
this.step();
|
||||
if (first === "=" && !this.done() && this.test("=")) {
|
||||
|
240
compiler/middle/borrow_checker.ts
Normal file
240
compiler/middle/borrow_checker.ts
Normal file
@ -0,0 +1,240 @@
|
||||
import { Reporter } from "../info.ts";
|
||||
import { Pos } from "../token.ts";
|
||||
import { createCfg } from "./cfg.ts";
|
||||
import { Cfg } from "./cfg.ts";
|
||||
import { Block, BlockId, Fn, Local, LocalId, Mir, RValue } from "./mir.ts";
|
||||
|
||||
export function checkBorrows(
|
||||
mir: Mir,
|
||||
reporter: Reporter,
|
||||
) {
|
||||
for (const fn of mir.fns) {
|
||||
new BorrowCheckerFnPass(fn, reporter).pass();
|
||||
}
|
||||
}
|
||||
|
||||
class BorrowCheckerFnPass {
|
||||
private cfg: Cfg;
|
||||
|
||||
public constructor(
|
||||
private fn: Fn,
|
||||
private reporter: Reporter,
|
||||
) {
|
||||
this.cfg = createCfg(this.fn);
|
||||
}
|
||||
|
||||
public pass() {
|
||||
for (const local of this.fn.locals) {
|
||||
new LocalChecker(local, this.fn, this.cfg, this.reporter).check();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LocalChecker {
|
||||
private visitedBlocks = new Set<BlockId>();
|
||||
|
||||
private assignedTo = false;
|
||||
private moved = false;
|
||||
private borrowed = false;
|
||||
private borrowedMut = false;
|
||||
|
||||
private movedPos?: Pos;
|
||||
private borrowedPos?: Pos;
|
||||
|
||||
public constructor(
|
||||
private local: Local,
|
||||
private fn: Fn,
|
||||
private cfg: Cfg,
|
||||
private reporter: Reporter,
|
||||
) {}
|
||||
|
||||
public check() {
|
||||
this.checkBlock(this.cfg.entry());
|
||||
}
|
||||
|
||||
private checkBlock(block: Block) {
|
||||
if (this.visitedBlocks.has(block.id)) {
|
||||
return;
|
||||
}
|
||||
this.visitedBlocks.add(block.id);
|
||||
for (const op of block.ops) {
|
||||
const ok = op.kind;
|
||||
switch (ok.type) {
|
||||
case "error":
|
||||
break;
|
||||
case "assign":
|
||||
this.markDst(ok.dst);
|
||||
this.markSrc(ok.src);
|
||||
break;
|
||||
case "ref":
|
||||
case "ptr":
|
||||
this.markDst(ok.dst);
|
||||
this.markBorrow(ok.src);
|
||||
break;
|
||||
case "ref_mut":
|
||||
case "ptr_mut":
|
||||
this.markDst(ok.dst);
|
||||
this.markBorrowMut(ok.src);
|
||||
break;
|
||||
case "deref":
|
||||
this.markDst(ok.dst);
|
||||
this.markSrc(ok.src);
|
||||
break;
|
||||
case "assign_deref":
|
||||
this.markSrc(ok.subject);
|
||||
this.markSrc(ok.src);
|
||||
break;
|
||||
case "field":
|
||||
this.markDst(ok.dst);
|
||||
this.markSrc(ok.subject);
|
||||
break;
|
||||
case "assign_field":
|
||||
this.markSrc(ok.subject);
|
||||
this.markSrc(ok.src);
|
||||
break;
|
||||
case "index":
|
||||
this.markDst(ok.dst);
|
||||
this.markSrc(ok.subject);
|
||||
this.markSrc(ok.index);
|
||||
break;
|
||||
case "assign_index":
|
||||
this.markSrc(ok.subject);
|
||||
this.markSrc(ok.index);
|
||||
this.markSrc(ok.src);
|
||||
break;
|
||||
case "call_val":
|
||||
this.markDst(ok.dst);
|
||||
this.markSrc(ok.subject);
|
||||
for (const arg of ok.args) {
|
||||
this.markSrc(arg);
|
||||
}
|
||||
break;
|
||||
case "binary":
|
||||
this.markDst(ok.dst);
|
||||
this.markSrc(ok.left);
|
||||
this.markSrc(ok.right);
|
||||
break;
|
||||
}
|
||||
}
|
||||
const tk = block.ter.kind;
|
||||
switch (tk.type) {
|
||||
case "error":
|
||||
break;
|
||||
case "return":
|
||||
break;
|
||||
case "jump":
|
||||
break;
|
||||
case "if":
|
||||
this.markSrc(tk.cond);
|
||||
break;
|
||||
}
|
||||
for (const child of this.cfg.children(block)) {
|
||||
this.checkBlock(child);
|
||||
}
|
||||
}
|
||||
|
||||
private markDst(localId: LocalId) {
|
||||
if (localId !== this.local.id) {
|
||||
return;
|
||||
}
|
||||
if (!this.assignedTo) {
|
||||
this.assignedTo = true;
|
||||
return;
|
||||
}
|
||||
if (!this.local.mut) {
|
||||
this.reportReassignToNonMut();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private markBorrow(localId: LocalId) {
|
||||
if (localId !== this.local.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.assignedTo) {
|
||||
this.assignedTo = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private markBorrowMut(localId: LocalId) {
|
||||
if (localId !== this.local.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.assignedTo) {
|
||||
this.assignedTo = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private markSrc(src: RValue) {
|
||||
if (src.type === "local") {
|
||||
throw new Error("should be 'copy' or 'move'");
|
||||
}
|
||||
if (
|
||||
(src.type !== "copy" && src.type !== "move") ||
|
||||
src.id !== this.local.id
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (src.type === "move") {
|
||||
if (this.moved) {
|
||||
this.reportUseMoved();
|
||||
return;
|
||||
}
|
||||
if (this.borrowed) {
|
||||
this.reportUseBorrowed();
|
||||
return;
|
||||
}
|
||||
this.moved = true;
|
||||
}
|
||||
}
|
||||
|
||||
private reportReassignToNonMut() {
|
||||
const ident = this.local.sym!.ident;
|
||||
this.reporter.reportError({
|
||||
reporter: "borrow checker",
|
||||
msg: `cannot re-assign to '${ident}' as it was not declared mutable`,
|
||||
pos: this.local.sym!.pos!,
|
||||
});
|
||||
this.reporter.addNote({
|
||||
reporter: "borrow checker",
|
||||
msg: `declared here`,
|
||||
pos: this.local.sym!.pos!,
|
||||
});
|
||||
}
|
||||
|
||||
private reportUseMoved() {
|
||||
const ident = this.local.sym!.ident;
|
||||
this.reporter.reportError({
|
||||
reporter: "borrow checker",
|
||||
msg: `cannot use '${ident}' as it has been moved`,
|
||||
pos: this.local.sym!.pos!,
|
||||
});
|
||||
if (this.movedPos) {
|
||||
this.reporter.addNote({
|
||||
reporter: "borrow checker",
|
||||
msg: `moved here`,
|
||||
pos: this.movedPos,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private reportUseBorrowed() {
|
||||
const ident = this.local.sym!.ident;
|
||||
this.reporter.reportError({
|
||||
reporter: "borrow checker",
|
||||
msg: `cannot use '${ident}' as it has been borrowed`,
|
||||
pos: this.local.sym!.pos!,
|
||||
});
|
||||
if (this.borrowedPos) {
|
||||
this.reporter.addNote({
|
||||
reporter: "borrow checker",
|
||||
msg: `borrowed here`,
|
||||
pos: this.movedPos,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { Block, BlockId, Fn, Mir } from "./mir.ts";
|
||||
import { Block, BlockId, Fn } from "./mir.ts";
|
||||
|
||||
export function createCfg(fn: Fn): Cfg {
|
||||
return new CfgBuilder(fn).build();
|
||||
@ -7,10 +7,14 @@ export function createCfg(fn: Fn): Cfg {
|
||||
export class Cfg {
|
||||
public constructor(
|
||||
private graph: Map<BlockId, CfgNode>,
|
||||
private entry: BlockId,
|
||||
private entry_: BlockId,
|
||||
private exit: BlockId,
|
||||
) {}
|
||||
|
||||
public entry(): Block {
|
||||
return this.graph.get(this.entry_)!.block;
|
||||
}
|
||||
|
||||
public parents(block: Block): Block[] {
|
||||
return this.graph
|
||||
.get(block.id)!.parents
|
||||
|
@ -1,111 +0,0 @@
|
||||
import { FnStmtKind } from "../ast.ts";
|
||||
import {
|
||||
Block,
|
||||
Fn,
|
||||
Local,
|
||||
LocalId,
|
||||
Mir,
|
||||
replaceBlockSrcs,
|
||||
RValue,
|
||||
} from "./mir.ts";
|
||||
|
||||
export function eliminateTransientLocals(mir: Mir) {
|
||||
for (const fn of mir.fns) {
|
||||
const otherLocals = fn.locals
|
||||
.slice(1 + (fn.stmt.kind as FnStmtKind).params.length)
|
||||
.map((local) => local.id);
|
||||
|
||||
for (const block of fn.blocks) {
|
||||
new EliminateTransientLocalsBlockPass(block, otherLocals)
|
||||
.pass();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Candidate = {
|
||||
dst: LocalId;
|
||||
src: LocalId;
|
||||
};
|
||||
|
||||
class EliminateTransientLocalsBlockPass {
|
||||
private candidates: Candidate[] = [];
|
||||
|
||||
public constructor(
|
||||
private block: Block,
|
||||
private readonly locals: LocalId[],
|
||||
) {
|
||||
}
|
||||
|
||||
public pass() {
|
||||
this.findCandidatesInBlock(this.block);
|
||||
|
||||
this.candidates = this.candidates
|
||||
.filter((cand) => this.locals.includes(cand.dst));
|
||||
|
||||
this.eliminateCandidatesInBlock(this.block);
|
||||
}
|
||||
|
||||
private eliminateCandidatesInBlock(block: Block) {
|
||||
replaceBlockSrcs(block, (src) => this.replaceMaybe(src));
|
||||
}
|
||||
|
||||
private replaceMaybe(src: RValue): RValue {
|
||||
if (src.type !== "local") {
|
||||
return src;
|
||||
}
|
||||
const candidate = this.candidates
|
||||
.find((cand) => cand.dst === src.id)?.src;
|
||||
return candidate !== undefined ? { type: "local", id: candidate } : src;
|
||||
}
|
||||
|
||||
private findCandidatesInBlock(block: Block) {
|
||||
for (const op of block.ops) {
|
||||
const ok = op.kind;
|
||||
switch (ok.type) {
|
||||
case "error":
|
||||
break;
|
||||
case "assign":
|
||||
this.markDst(ok.dst, ok.src);
|
||||
break;
|
||||
case "assign_error":
|
||||
case "assign_null":
|
||||
case "assign_bool":
|
||||
case "assign_int":
|
||||
case "assign_string":
|
||||
case "assign_fn":
|
||||
case "field":
|
||||
case "assign_field":
|
||||
case "index":
|
||||
case "assign_index":
|
||||
case "call_val":
|
||||
case "binary":
|
||||
break;
|
||||
default:
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
const tk = block.ter.kind;
|
||||
switch (tk.type) {
|
||||
case "error":
|
||||
break;
|
||||
case "return":
|
||||
break;
|
||||
case "jump":
|
||||
break;
|
||||
case "if":
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private markDst(dst: LocalId, src: RValue) {
|
||||
if (src.type !== "local") {
|
||||
return;
|
||||
}
|
||||
|
||||
this.candidates = this.candidates
|
||||
.filter((cand) => cand.dst !== dst);
|
||||
this.candidates = this.candidates
|
||||
.filter((cand) => cand.src !== dst);
|
||||
this.candidates.push({ dst, src: src.id });
|
||||
}
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
import { Mir, Op, RValue, visitBlockSrcs } from "./mir.ts";
|
||||
|
||||
export function eliminateTransientVals(mir: Mir) {
|
||||
for (const fn of mir.fns) {
|
||||
for (const block of fn.blocks) {
|
||||
const cands: { src: RValue; consumer: Op; definition: Op }[] = [];
|
||||
|
||||
visitBlockSrcs(block, (src, op, i) => {
|
||||
if (src.type !== "local") {
|
||||
return;
|
||||
}
|
||||
const found = block.ops.find((op, fi) =>
|
||||
op.kind.type === "assign" &&
|
||||
op.kind.dst === src.id &&
|
||||
fi < i!
|
||||
);
|
||||
if (!found) {
|
||||
return;
|
||||
}
|
||||
cands.push({ src, consumer: op!, definition: found! });
|
||||
});
|
||||
|
||||
//console.log(cands);
|
||||
|
||||
for (const { src: oldsrc, consumer, definition } of cands) {
|
||||
if (oldsrc.type !== "local") {
|
||||
throw new Error();
|
||||
}
|
||||
if (definition.kind.type !== "assign") {
|
||||
throw new Error();
|
||||
}
|
||||
const src = definition.kind.src;
|
||||
|
||||
const k = consumer.kind;
|
||||
switch (k.type) {
|
||||
case "error":
|
||||
break;
|
||||
case "assign":
|
||||
k.src = src;
|
||||
break;
|
||||
case "field":
|
||||
if (same(k.subject, oldsrc)) {
|
||||
k.subject = src;
|
||||
}
|
||||
break;
|
||||
case "assign_field":
|
||||
if (same(k.subject, oldsrc)) {
|
||||
k.subject = src;
|
||||
}
|
||||
if (same(k.src, oldsrc)) {
|
||||
k.src = src;
|
||||
}
|
||||
break;
|
||||
case "index":
|
||||
if (same(k.subject, oldsrc)) {
|
||||
k.subject = src;
|
||||
}
|
||||
if (same(k.index, oldsrc)) {
|
||||
k.index = src;
|
||||
}
|
||||
break;
|
||||
case "assign_index":
|
||||
if (same(k.subject, oldsrc)) {
|
||||
k.subject = src;
|
||||
}
|
||||
if (same(k.index, oldsrc)) {
|
||||
k.index = src;
|
||||
}
|
||||
if (same(k.src, oldsrc)) {
|
||||
k.src = src;
|
||||
}
|
||||
break;
|
||||
case "call_val":
|
||||
if (same(k.subject, oldsrc)) {
|
||||
k.subject = src;
|
||||
}
|
||||
for (let i = 0; i < k.args.length; ++i) {
|
||||
if (same(k.args[i], oldsrc)) {
|
||||
k.args[i] = src;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "binary":
|
||||
if (same(k.left, oldsrc)) {
|
||||
k.left = src;
|
||||
}
|
||||
if (same(k.right, oldsrc)) {
|
||||
k.right = src;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function same(a: RValue, b: RValue): boolean {
|
||||
return a.type === "local" && a.type === b.type && a.id === b.id;
|
||||
}
|
58
compiler/middle/explicit_move_copy.ts
Normal file
58
compiler/middle/explicit_move_copy.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { VType } from "../vtype.ts";
|
||||
import { Fn, Local, Mir, replaceBlockSrcs, RValue } from "./mir.ts";
|
||||
|
||||
export function makeMoveCopyExplicit(mir: Mir) {
|
||||
for (const fn of mir.fns) {
|
||||
for (const local of fn.locals) {
|
||||
new LocalExpliciter(fn, local).pass();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LocalExpliciter {
|
||||
private copyable: boolean;
|
||||
|
||||
public constructor(private fn: Fn, private local: Local) {
|
||||
this.copyable = copyableIsType(local.vtype);
|
||||
}
|
||||
|
||||
public pass() {
|
||||
for (const block of this.fn.blocks) {
|
||||
replaceBlockSrcs(block, (src) => this.explicitSrc(src));
|
||||
}
|
||||
}
|
||||
|
||||
private explicitSrc(src: RValue): RValue {
|
||||
if (src.type !== "local") {
|
||||
return src;
|
||||
}
|
||||
return this.copyable
|
||||
? { type: "copy", id: src.id }
|
||||
: { type: "move", id: src.id };
|
||||
}
|
||||
}
|
||||
|
||||
function copyableIsType(vtype: VType): boolean {
|
||||
switch (vtype.type) {
|
||||
case "error":
|
||||
case "unknown":
|
||||
throw new Error();
|
||||
case "null":
|
||||
case "int":
|
||||
case "bool":
|
||||
case "string":
|
||||
case "ref":
|
||||
case "ref_mut":
|
||||
case "ptr":
|
||||
case "ptr_mut":
|
||||
return true;
|
||||
case "array":
|
||||
case "struct":
|
||||
case "fn":
|
||||
return false;
|
||||
case "generic":
|
||||
return false;
|
||||
case "generic_spec":
|
||||
throw new Error();
|
||||
}
|
||||
}
|
@ -103,7 +103,7 @@ class FnAstLowerer {
|
||||
const val = this.lowerExpr(stmt.kind.expr);
|
||||
this.addOp({ type: "assign", dst, src: local(val) });
|
||||
} else {
|
||||
this.addOp({ type: "assign_null", dst });
|
||||
this.addOp({ type: "assign", dst, src: { type: "null" } });
|
||||
}
|
||||
this.setTer({ type: "jump", target: block });
|
||||
this.pushBlock();
|
||||
@ -227,6 +227,24 @@ class FnAstLowerer {
|
||||
return this.lowerSymExpr(expr);
|
||||
case "group":
|
||||
return this.lowerExpr(expr.kind.expr);
|
||||
case "ref": {
|
||||
const src = this.lowerExpr(expr.kind.subject);
|
||||
const dst = this.locals.alloc(expr.vtype!);
|
||||
this.addOp({ type: "ref", dst, src });
|
||||
return dst;
|
||||
}
|
||||
case "ref_mut": {
|
||||
const src = this.lowerExpr(expr.kind.subject);
|
||||
const dst = this.locals.alloc(expr.vtype!);
|
||||
this.addOp({ type: "ref_mut", dst, src });
|
||||
return dst;
|
||||
}
|
||||
case "deref": {
|
||||
const src = local(this.lowerExpr(expr.kind.subject));
|
||||
const dst = this.locals.alloc(expr.kind.subject.vtype!);
|
||||
this.addOp({ type: "deref", dst, src });
|
||||
return dst;
|
||||
}
|
||||
case "array":
|
||||
throw new Error("incomplete desugar");
|
||||
case "struct":
|
||||
@ -274,7 +292,7 @@ class FnAstLowerer {
|
||||
throw new Error();
|
||||
}
|
||||
const dst = this.locals.alloc(sym.stmt.kind.vtype!);
|
||||
this.addOp({ type: "assign_fn", dst, stmt });
|
||||
this.addOp({ type: "assign", dst, src: { type: "fn", stmt } });
|
||||
return dst;
|
||||
}
|
||||
case "fn_param": {
|
||||
@ -320,7 +338,7 @@ class FnAstLowerer {
|
||||
const dstVType = ((): VType => {
|
||||
const outer = expr.kind.subject.vtype!;
|
||||
if (outer.type === "array") {
|
||||
return outer.inner;
|
||||
return outer.subject;
|
||||
}
|
||||
if (outer.type === "string") {
|
||||
return { type: "int" };
|
||||
@ -442,7 +460,7 @@ class FnAstLowerer {
|
||||
return this.lowerExpr(expr.kind.expr);
|
||||
} else {
|
||||
const local = this.locals.alloc({ type: "null" });
|
||||
this.addOp({ type: "assign_null", dst: local });
|
||||
this.addOp({ type: "assign", dst: local, src: { type: "null" } });
|
||||
return local;
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,13 @@ type R = RValue;
|
||||
export type OpKind =
|
||||
| { type: "error" }
|
||||
| { type: "assign"; dst: L; src: R }
|
||||
| { type: "ref"; dst: L; src: L }
|
||||
| { type: "ref_mut"; dst: L; src: L }
|
||||
| { type: "ptr"; dst: L; src: L }
|
||||
| { type: "ptr_mut"; dst: L; src: L }
|
||||
| { type: "drop"; src: L }
|
||||
| { type: "deref"; dst: L; src: R }
|
||||
| { type: "assign_deref"; subject: R; src: R }
|
||||
| { type: "field"; dst: L; subject: R; ident: string }
|
||||
| { type: "assign_field"; subject: R; ident: string; src: R }
|
||||
| { type: "index"; dst: L; subject: R; index: R }
|
||||
@ -61,6 +68,8 @@ export type TerKind =
|
||||
export type RValue =
|
||||
| { type: "error" }
|
||||
| { type: "local"; id: BlockId }
|
||||
| { type: "copy"; id: BlockId }
|
||||
| { type: "move"; id: BlockId }
|
||||
| { type: "null" }
|
||||
| { type: "bool"; val: boolean }
|
||||
| { type: "int"; val: number }
|
||||
@ -77,12 +86,18 @@ export function visitBlockDsts(
|
||||
case "error":
|
||||
break;
|
||||
case "assign":
|
||||
case "ref":
|
||||
case "ref_mut":
|
||||
case "ptr":
|
||||
case "ptr_mut":
|
||||
case "deref":
|
||||
case "field":
|
||||
case "index":
|
||||
case "call_val":
|
||||
case "binary":
|
||||
visit(ok.dst, i);
|
||||
break;
|
||||
case "assign_deref":
|
||||
case "assign_field":
|
||||
case "assign_index":
|
||||
break;
|
||||
@ -104,6 +119,19 @@ export function replaceBlockSrcs(
|
||||
case "assign":
|
||||
ok.src = replace(ok.src);
|
||||
break;
|
||||
case "ref":
|
||||
case "ref_mut":
|
||||
case "ptr":
|
||||
case "ptr_mut":
|
||||
case "drop":
|
||||
break;
|
||||
case "deref":
|
||||
ok.src = replace(ok.src);
|
||||
break;
|
||||
case "assign_deref":
|
||||
ok.subject = replace(ok.subject);
|
||||
ok.src = replace(ok.src);
|
||||
break;
|
||||
case "field":
|
||||
ok.subject = replace(ok.subject);
|
||||
break;
|
||||
@ -158,6 +186,19 @@ export function visitBlockSrcs(
|
||||
case "assign":
|
||||
visitor(ok.src, op, i);
|
||||
break;
|
||||
case "ref":
|
||||
case "ref_mut":
|
||||
case "ptr":
|
||||
case "ptr_mut":
|
||||
case "drop":
|
||||
break;
|
||||
case "deref":
|
||||
visitor(ok.src, op, i);
|
||||
break;
|
||||
case "assign_deref":
|
||||
visitor(ok.src, op, i);
|
||||
visitor(ok.subject, op, i);
|
||||
break;
|
||||
case "field":
|
||||
visitor(ok.subject, op, i);
|
||||
break;
|
||||
@ -254,6 +295,27 @@ export function printMir(mir: Mir) {
|
||||
case "assign":
|
||||
l(`_${k.dst} = ${r(k.src)};`);
|
||||
break;
|
||||
case "ref":
|
||||
l(`_${k.dst} = &_${k.src};`);
|
||||
break;
|
||||
case "ref_mut":
|
||||
l(`_${k.dst} = &mut _${k.src};`);
|
||||
break;
|
||||
case "ptr":
|
||||
l(`_${k.dst} = *_${k.src};`);
|
||||
break;
|
||||
case "ptr_mut":
|
||||
l(`_${k.dst} = *mut _${k.src};`);
|
||||
break;
|
||||
case "drop":
|
||||
l(`drop _${k.src};`);
|
||||
break;
|
||||
case "deref":
|
||||
l(`_${k.dst} = *${r(k.src)};`);
|
||||
break;
|
||||
case "assign_deref":
|
||||
l(`*${r(k.subject)} = ${r(k.src)};`);
|
||||
break;
|
||||
case "field":
|
||||
l(`_${k.dst} = ${r(k.subject)}.${k.ident};`);
|
||||
break;
|
||||
@ -277,6 +339,8 @@ export function printMir(mir: Mir) {
|
||||
};`);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
const tk = block.ter.kind;
|
||||
@ -295,6 +359,8 @@ export function printMir(mir: Mir) {
|
||||
r(tk.cond)
|
||||
}, true: bb${tk.truthy}, false: bb${tk.falsy};`);
|
||||
break;
|
||||
default:
|
||||
throw new Error();
|
||||
}
|
||||
console.log(" }");
|
||||
}
|
||||
@ -308,6 +374,10 @@ export function rvalueToString(rvalue: RValue): string {
|
||||
return `<error>`;
|
||||
case "local":
|
||||
return `_${rvalue.id}`;
|
||||
case "copy":
|
||||
return `copy _${rvalue.id}`;
|
||||
case "move":
|
||||
return `move _${rvalue.id}`;
|
||||
case "null":
|
||||
return "null";
|
||||
case "bool":
|
||||
|
@ -163,8 +163,16 @@ function vtypeNameGenPart(vtype: VType): string {
|
||||
case "null":
|
||||
case "unknown":
|
||||
return vtype.type;
|
||||
case "ref":
|
||||
return `&${vtypeNameGenPart(vtype.subject)}`;
|
||||
case "ref_mut":
|
||||
return `&mut ${vtypeNameGenPart(vtype.subject)}`;
|
||||
case "ptr":
|
||||
return `*${vtypeNameGenPart(vtype.subject)}`;
|
||||
case "ptr_mut":
|
||||
return `*mut ${vtypeNameGenPart(vtype.subject)}`;
|
||||
case "array":
|
||||
return `[${vtypeNameGenPart(vtype.inner)}]`;
|
||||
return `[${vtypeNameGenPart(vtype.subject)}]`;
|
||||
case "struct": {
|
||||
const fields = vtype.fields
|
||||
.map((field) =>
|
||||
|
@ -388,7 +388,12 @@ export class Parser {
|
||||
|
||||
private parseParam(index?: number): Res<Param> {
|
||||
const pos = this.pos();
|
||||
if (this.test("ident")) {
|
||||
if (this.test("ident") || this.test("mut")) {
|
||||
let mut = false;
|
||||
if (this.test("mut")) {
|
||||
mut = true;
|
||||
this.step();
|
||||
}
|
||||
const ident = this.current().identValue!;
|
||||
this.step();
|
||||
if (this.test(":")) {
|
||||
@ -396,12 +401,18 @@ export class Parser {
|
||||
const etype = this.parseEType();
|
||||
return {
|
||||
ok: true,
|
||||
value: this.astCreator.param({ index, ident, etype, pos }),
|
||||
value: this.astCreator.param({
|
||||
index,
|
||||
ident,
|
||||
mut,
|
||||
etype,
|
||||
pos,
|
||||
}),
|
||||
};
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
value: this.astCreator.param({ index, ident, pos }),
|
||||
value: this.astCreator.param({ index, ident, mut, pos }),
|
||||
};
|
||||
}
|
||||
this.report("expected param");
|
||||
@ -779,6 +790,21 @@ export class Parser {
|
||||
const subject = this.parsePrefix();
|
||||
return this.expr({ type: "unary", unaryType, subject }, pos);
|
||||
}
|
||||
if (this.test("&")) {
|
||||
this.step();
|
||||
let type: "ref" | "ref_mut" = "ref";
|
||||
if (this.test("mut")) {
|
||||
this.step();
|
||||
type = "ref_mut";
|
||||
}
|
||||
const subject = this.parsePrefix();
|
||||
return this.expr({ type, subject }, pos);
|
||||
}
|
||||
if (this.test("*")) {
|
||||
this.step();
|
||||
const subject = this.parsePrefix();
|
||||
return this.expr({ type: "deref", subject }, pos);
|
||||
}
|
||||
return this.parsePostfix();
|
||||
}
|
||||
|
||||
@ -955,13 +981,13 @@ export class Parser {
|
||||
}
|
||||
if (this.test("[")) {
|
||||
this.step();
|
||||
const inner = this.parseEType();
|
||||
const subject = this.parseEType();
|
||||
if (!this.test("]")) {
|
||||
this.report("expected ']'", pos);
|
||||
return this.etype({ type: "error" }, pos);
|
||||
}
|
||||
this.step();
|
||||
return this.etype({ type: "array", inner }, pos);
|
||||
return this.etype({ type: "array", subject }, pos);
|
||||
}
|
||||
if (this.test("struct")) {
|
||||
this.step();
|
||||
@ -972,6 +998,26 @@ export class Parser {
|
||||
const fields = this.parseETypeStructFields();
|
||||
return this.etype({ type: "struct", fields }, pos);
|
||||
}
|
||||
if (this.test("&")) {
|
||||
this.step();
|
||||
let type: "ref" | "ref_mut" = "ref";
|
||||
if (this.test("mut")) {
|
||||
this.step();
|
||||
type = "ref_mut";
|
||||
}
|
||||
const subject = this.parseEType();
|
||||
return this.etype({ type, subject }, pos);
|
||||
}
|
||||
if (this.test("*")) {
|
||||
this.step();
|
||||
let type: "ptr" | "ptr_mut" = "ptr";
|
||||
if (this.test("mut")) {
|
||||
this.step();
|
||||
type = "ptr_mut";
|
||||
}
|
||||
const subject = this.parseEType();
|
||||
return this.etype({ type, subject }, pos);
|
||||
}
|
||||
this.report("expected type");
|
||||
return this.etype({ type: "error" }, pos);
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ export class Resolver implements AstVisitor<[Syms]> {
|
||||
return "stop";
|
||||
}
|
||||
|
||||
visitTypeAliasStmt(stmt: Stmt, syms: Syms): VisitRes {
|
||||
visitTypeAliasStmt(stmt: Stmt, _syms: Syms): VisitRes {
|
||||
if (stmt.kind.type !== "type_alias") {
|
||||
throw new Error("expected type_alias statement");
|
||||
}
|
||||
|
@ -5,7 +5,11 @@ export type VType =
|
||||
| { type: "int" }
|
||||
| { type: "string" }
|
||||
| { type: "bool" }
|
||||
| { type: "array"; inner: VType }
|
||||
| { type: "ref"; subject: VType }
|
||||
| { type: "ref_mut"; subject: VType }
|
||||
| { type: "ptr"; subject: VType }
|
||||
| { type: "ptr_mut"; subject: VType }
|
||||
| { type: "array"; subject: VType }
|
||||
| { type: "struct"; fields: VTypeParam[] }
|
||||
| {
|
||||
type: "fn";
|
||||
@ -45,8 +49,20 @@ export function vtypesEqual(
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (a.type === "ref" && b.type === "ref") {
|
||||
return vtypesEqual(a.subject, b.subject, generics);
|
||||
}
|
||||
if (a.type === "ref_mut" && b.type === "ref_mut") {
|
||||
return vtypesEqual(a.subject, b.subject, generics);
|
||||
}
|
||||
if (a.type === "ptr" && b.type === "ptr") {
|
||||
return vtypesEqual(a.subject, b.subject, generics);
|
||||
}
|
||||
if (a.type === "ptr_mut" && b.type === "ptr_mut") {
|
||||
return vtypesEqual(a.subject, b.subject, generics);
|
||||
}
|
||||
if (a.type === "array" && b.type === "array") {
|
||||
return vtypesEqual(a.inner, b.inner, generics);
|
||||
return vtypesEqual(a.subject, b.subject, generics);
|
||||
}
|
||||
if (a.type === "struct" && b.type === "struct") {
|
||||
if (a.fields.length !== b.fields.length) {
|
||||
@ -117,8 +133,20 @@ export function vtypeToString(vtype: VType): string {
|
||||
) {
|
||||
return vtype.type;
|
||||
}
|
||||
if (vtype.type === "ref") {
|
||||
return `&${vtypeToString(vtype.subject)}`;
|
||||
}
|
||||
if (vtype.type === "ref_mut") {
|
||||
return `&mut ${vtypeToString(vtype.subject)}`;
|
||||
}
|
||||
if (vtype.type === "ptr") {
|
||||
return `*${vtypeToString(vtype.subject)}`;
|
||||
}
|
||||
if (vtype.type === "ptr_mut") {
|
||||
return `*mut ${vtypeToString(vtype.subject)}`;
|
||||
}
|
||||
if (vtype.type === "array") {
|
||||
return `[${vtypeToString(vtype.inner)}]`;
|
||||
return `[${vtypeToString(vtype.subject)}]`;
|
||||
}
|
||||
if (vtype.type === "struct") {
|
||||
const fields = vtype.fields
|
||||
|
@ -4,7 +4,14 @@ fn add(a: int, b: int) -> int {
|
||||
|
||||
fn main() -> int {
|
||||
let result = 0;
|
||||
let i = 0;
|
||||
|
||||
let a = 0;
|
||||
let b = a;
|
||||
let c = b;
|
||||
let d = c;
|
||||
|
||||
|
||||
let i = c;
|
||||
loop {
|
||||
if i >= 10 {
|
||||
break;
|
||||
|
7
examples/refs.slg
Normal file
7
examples/refs.slg
Normal file
@ -0,0 +1,7 @@
|
||||
//
|
||||
|
||||
fn main() {
|
||||
let a = 5;
|
||||
let b: &int = &a;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user