mirror of
https://git.sfja.dk/Mikkel/slige.git
synced 2025-01-18 10:06:31 +00:00
add borrow checker
This commit is contained in:
parent
a7349890d0
commit
5d6b1abefc
@ -63,6 +63,9 @@ export type ExprKind =
|
|||||||
sym: Sym;
|
sym: Sym;
|
||||||
}
|
}
|
||||||
| { type: "group"; expr: Expr }
|
| { type: "group"; expr: Expr }
|
||||||
|
| { type: "ref"; subject: Expr }
|
||||||
|
| { type: "ref_mut"; subject: Expr }
|
||||||
|
| { type: "deref"; subject: Expr }
|
||||||
| { type: "array"; exprs: Expr[] }
|
| { type: "array"; exprs: Expr[] }
|
||||||
| { type: "struct"; fields: Field[] }
|
| { type: "struct"; fields: Field[] }
|
||||||
| { type: "field"; subject: Expr; ident: string }
|
| { type: "field"; subject: Expr; ident: string }
|
||||||
@ -117,6 +120,7 @@ export type Param = {
|
|||||||
id: number;
|
id: number;
|
||||||
index?: number;
|
index?: number;
|
||||||
ident: string;
|
ident: string;
|
||||||
|
mut: boolean;
|
||||||
etype?: EType;
|
etype?: EType;
|
||||||
pos: Pos;
|
pos: Pos;
|
||||||
sym?: Sym;
|
sym?: Sym;
|
||||||
@ -157,7 +161,11 @@ export type ETypeKind =
|
|||||||
ident: string;
|
ident: string;
|
||||||
sym: Sym;
|
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: "struct"; fields: Param[] }
|
||||||
| { type: "type_of"; expr: Expr };
|
| { type: "type_of"; expr: Expr };
|
||||||
|
|
||||||
@ -227,3 +235,7 @@ export class AnnoView {
|
|||||||
return anno;
|
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;
|
visitStringExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||||
visitIdentExpr?(expr: Expr, ...args: Args): VisitRes;
|
visitIdentExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||||
visitGroupExpr?(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;
|
visitArrayExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||||
visitStructExpr?(expr: Expr, ...args: Args): VisitRes;
|
visitStructExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||||
visitFieldExpr?(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;
|
visitStringEType?(etype: EType, ...args: Args): VisitRes;
|
||||||
visitIdentEType?(etype: EType, ...args: Args): VisitRes;
|
visitIdentEType?(etype: EType, ...args: Args): VisitRes;
|
||||||
visitSymEType?(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;
|
visitArrayEType?(etype: EType, ...args: Args): VisitRes;
|
||||||
visitStructEType?(etype: EType, ...args: Args): VisitRes;
|
visitStructEType?(etype: EType, ...args: Args): VisitRes;
|
||||||
visitTypeOfEType?(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;
|
if (v.visitGroupExpr?.(expr, ...args) == "stop") return;
|
||||||
visitExpr(expr.kind.expr, v, ...args);
|
visitExpr(expr.kind.expr, v, ...args);
|
||||||
break;
|
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":
|
case "field":
|
||||||
if (v.visitFieldExpr?.(expr, ...args) == "stop") return;
|
if (v.visitFieldExpr?.(expr, ...args) == "stop") return;
|
||||||
visitExpr(expr.kind.subject, v, ...args);
|
visitExpr(expr.kind.subject, v, ...args);
|
||||||
@ -289,9 +308,25 @@ export function visitEType<Args extends unknown[] = []>(
|
|||||||
case "sym":
|
case "sym":
|
||||||
if (v.visitSymEType?.(etype, ...args) == "stop") return;
|
if (v.visitSymEType?.(etype, ...args) == "stop") return;
|
||||||
break;
|
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":
|
case "array":
|
||||||
if (v.visitArrayEType?.(etype, ...args) == "stop") return;
|
if (v.visitArrayEType?.(etype, ...args) == "stop") return;
|
||||||
if (etype.kind.inner) visitEType(etype.kind.inner, v, ...args);
|
visitEType(etype.kind.subject, v, ...args);
|
||||||
break;
|
break;
|
||||||
case "struct":
|
case "struct":
|
||||||
if (v.visitStructEType?.(etype, ...args) == "stop") return;
|
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 { printStackTrace, Reporter } from "./info.ts";
|
||||||
import { Pos } from "./token.ts";
|
import { Pos } from "./token.ts";
|
||||||
import {
|
import {
|
||||||
@ -286,7 +286,7 @@ export class Checker {
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
subject.type == "array" &&
|
subject.type == "array" &&
|
||||||
!vtypesEqual(subject.inner, value)
|
!vtypesEqual(subject.subject, value)
|
||||||
) {
|
) {
|
||||||
this.report(
|
this.report(
|
||||||
`cannot assign incompatible type to array ` +
|
`cannot assign incompatible type to array ` +
|
||||||
@ -348,6 +348,12 @@ export class Checker {
|
|||||||
return { type: "string" };
|
return { type: "string" };
|
||||||
case "group":
|
case "group":
|
||||||
return this.checkExpr(expr.kind.expr);
|
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":
|
case "array":
|
||||||
throw new Error("should have been desugared");
|
throw new Error("should have been desugared");
|
||||||
case "struct":
|
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 {
|
public checkStructExpr(expr: Expr): VType {
|
||||||
if (expr.kind.type !== "struct") {
|
if (expr.kind.type !== "struct") {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
@ -472,7 +561,7 @@ export class Checker {
|
|||||||
return { type: "error" };
|
return { type: "error" };
|
||||||
}
|
}
|
||||||
if (subject.type === "array") {
|
if (subject.type === "array") {
|
||||||
return subject.inner;
|
return subject.subject;
|
||||||
}
|
}
|
||||||
return { type: "int" };
|
return { type: "int" };
|
||||||
}
|
}
|
||||||
@ -653,7 +742,7 @@ export class Checker {
|
|||||||
return { a, b };
|
return { a, b };
|
||||||
}
|
}
|
||||||
if (a.type === "array" && b.type === "array") {
|
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") {
|
if (a.type === "generic" && b.type === "generic") {
|
||||||
return { a, b };
|
return { a, b };
|
||||||
@ -670,8 +759,13 @@ export class Checker {
|
|||||||
case "int":
|
case "int":
|
||||||
case "bool":
|
case "bool":
|
||||||
return false;
|
return false;
|
||||||
|
case "ref":
|
||||||
|
case "ref_mut":
|
||||||
|
case "ptr":
|
||||||
|
case "ptr_mut":
|
||||||
|
return this.vtypeContainsGeneric(vtype.subject);
|
||||||
case "array":
|
case "array":
|
||||||
return this.vtypeContainsGeneric(vtype.inner);
|
return this.vtypeContainsGeneric(vtype.subject);
|
||||||
case "struct":
|
case "struct":
|
||||||
return vtype.fields.some((field) =>
|
return vtype.fields.some((field) =>
|
||||||
this.vtypeContainsGeneric(field.vtype)
|
this.vtypeContainsGeneric(field.vtype)
|
||||||
@ -749,10 +843,14 @@ export class Checker {
|
|||||||
case "int":
|
case "int":
|
||||||
case "bool":
|
case "bool":
|
||||||
return vtype;
|
return vtype;
|
||||||
|
case "ref":
|
||||||
|
case "ref_mut":
|
||||||
|
case "ptr":
|
||||||
|
case "ptr_mut":
|
||||||
case "array":
|
case "array":
|
||||||
return {
|
return {
|
||||||
type: "array",
|
type: vtype.type,
|
||||||
inner: this.concretizeVType(vtype.inner, generics),
|
subject: this.concretizeVType(vtype.subject, generics),
|
||||||
};
|
};
|
||||||
case "struct":
|
case "struct":
|
||||||
return {
|
return {
|
||||||
@ -994,9 +1092,25 @@ export class Checker {
|
|||||||
this.report(`sym type '${etype.kind.sym.type}' used as type`, pos);
|
this.report(`sym type '${etype.kind.sym.type}' used as type`, pos);
|
||||||
return { type: "error" };
|
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") {
|
if (etype.kind.type === "array") {
|
||||||
const inner = this.checkEType(etype.kind.inner);
|
const subject = this.checkEType(etype.kind.subject);
|
||||||
return { type: "array", inner };
|
return { type: "array", subject };
|
||||||
}
|
}
|
||||||
if (etype.kind.type === "struct") {
|
if (etype.kind.type === "struct") {
|
||||||
const noTypeTest = etype.kind.fields.reduce(
|
const noTypeTest = etype.kind.fields.reduce(
|
||||||
|
@ -21,7 +21,8 @@ import {
|
|||||||
eliminateOnlyChildsBlocks,
|
eliminateOnlyChildsBlocks,
|
||||||
eliminateUnreachableBlocks,
|
eliminateUnreachableBlocks,
|
||||||
} from "./middle/elim_blocks.ts";
|
} 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 = {
|
export type CompileResult = {
|
||||||
program: number[];
|
program: number[];
|
||||||
@ -53,44 +54,52 @@ export class Compiler {
|
|||||||
|
|
||||||
new Checker(this.reporter).check(ast);
|
new Checker(this.reporter).check(ast);
|
||||||
|
|
||||||
const mir = lowerAst(ast);
|
//const mir = lowerAst(ast);
|
||||||
|
//
|
||||||
|
//console.log("Before optimizations:");
|
||||||
|
//printMir(mir);
|
||||||
|
|
||||||
console.log("Before optimizations:");
|
//const mirHistory = [mirOpCount(mir)];
|
||||||
printMir(mir);
|
//for (let i = 0; i < 1; ++i) {
|
||||||
|
// eliminateUnusedLocals(mir, this.reporter, mirHistory.length === 1);
|
||||||
const mirHistory = [mirOpCount(mir)];
|
// eliminateOnlyChildsBlocks(mir);
|
||||||
for (let i = 0; i < 1; ++i) {
|
// eliminateUnreachableBlocks(mir);
|
||||||
eliminateUnusedLocals(mir, this.reporter, mirHistory.length === 1);
|
// eliminateTransientVals(mir);
|
||||||
eliminateOnlyChildsBlocks(mir);
|
//
|
||||||
eliminateUnreachableBlocks(mir);
|
// const opCount = mirOpCount(mir);
|
||||||
eliminateTransientVals(mir);
|
// const histOccurence = mirHistory
|
||||||
|
// .filter((v) => v === opCount).length;
|
||||||
const opCount = mirOpCount(mir);
|
// if (histOccurence >= 2) {
|
||||||
const histOccurence = mirHistory
|
// break;
|
||||||
.filter((v) => v === opCount).length;
|
// }
|
||||||
if (histOccurence >= 2) {
|
// mirHistory.push(opCount);
|
||||||
break;
|
//}
|
||||||
}
|
//
|
||||||
mirHistory.push(opCount);
|
//console.log("After optimizations:");
|
||||||
}
|
//printMir(mir);
|
||||||
|
|
||||||
console.log("After optimizations:");
|
|
||||||
printMir(mir);
|
|
||||||
|
|
||||||
if (this.reporter.errorOccured()) {
|
if (this.reporter.errorOccured()) {
|
||||||
console.error("Errors occurred, stopping compilation.");
|
console.error("Errors occurred, stopping compilation.");
|
||||||
Deno.exit(1);
|
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);
|
printMir(mir);
|
||||||
const { program, fnNames } = lowerer.lower();
|
|
||||||
//lowerer.printProgram();
|
|
||||||
|
|
||||||
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",
|
type: "let",
|
||||||
param: this.astCreator.param({
|
param: this.astCreator.param({
|
||||||
ident: "::value",
|
ident: "::value",
|
||||||
|
mut: true,
|
||||||
pos: npos,
|
pos: npos,
|
||||||
}),
|
}),
|
||||||
value: Expr({
|
value: Expr({
|
||||||
|
@ -72,6 +72,7 @@ export class SpecialLoopDesugarer implements AstVisitor {
|
|||||||
type: "let",
|
type: "let",
|
||||||
param: this.astCreator.param({
|
param: this.astCreator.param({
|
||||||
ident: "::values",
|
ident: "::values",
|
||||||
|
mut: true,
|
||||||
pos: npos,
|
pos: npos,
|
||||||
}),
|
}),
|
||||||
value: expr.kind.value,
|
value: expr.kind.value,
|
||||||
@ -80,6 +81,7 @@ export class SpecialLoopDesugarer implements AstVisitor {
|
|||||||
type: "let",
|
type: "let",
|
||||||
param: this.astCreator.param({
|
param: this.astCreator.param({
|
||||||
ident: "::length",
|
ident: "::length",
|
||||||
|
mut: false,
|
||||||
pos: npos,
|
pos: npos,
|
||||||
}),
|
}),
|
||||||
value: Expr({
|
value: Expr({
|
||||||
@ -104,6 +106,7 @@ export class SpecialLoopDesugarer implements AstVisitor {
|
|||||||
type: "let",
|
type: "let",
|
||||||
param: this.astCreator.param({
|
param: this.astCreator.param({
|
||||||
ident: "::index",
|
ident: "::index",
|
||||||
|
mut: true,
|
||||||
pos: npos,
|
pos: npos,
|
||||||
}),
|
}),
|
||||||
value: Expr({ type: "int", value: 0 }),
|
value: Expr({ type: "int", value: 0 }),
|
||||||
|
@ -54,6 +54,7 @@ export class StructLiteralDesugarer implements AstVisitor {
|
|||||||
type: "let",
|
type: "let",
|
||||||
param: this.astCreator.param({
|
param: this.astCreator.param({
|
||||||
ident: "::value",
|
ident: "::value",
|
||||||
|
mut: true,
|
||||||
pos: npos,
|
pos: npos,
|
||||||
}),
|
}),
|
||||||
value: Expr({
|
value: Expr({
|
||||||
|
@ -31,7 +31,7 @@ export class Reporter {
|
|||||||
private printReport({ reporter, type, pos, msg }: Report) {
|
private printReport({ reporter, type, pos, msg }: Report) {
|
||||||
console.error(
|
console.error(
|
||||||
`${reporter} ${type}: ${msg}${
|
`${reporter} ${type}: ${msg}${
|
||||||
pos ? `\n at ${this.filePath}:${pos.line}:${pos.col}` : ""
|
pos ? `\n at ${this.filePath}:${pos.line}:${pos.col}` : ""
|
||||||
}`,
|
}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ export class Lexer {
|
|||||||
"break",
|
"break",
|
||||||
"return",
|
"return",
|
||||||
"let",
|
"let",
|
||||||
|
"mut",
|
||||||
"fn",
|
"fn",
|
||||||
"loop",
|
"loop",
|
||||||
"if",
|
"if",
|
||||||
@ -128,7 +129,7 @@ export class Lexer {
|
|||||||
this.step();
|
this.step();
|
||||||
return { ...this.token("string", pos), stringValue: value };
|
return { ...this.token("string", pos), stringValue: value };
|
||||||
}
|
}
|
||||||
if (this.test(/[\+\{\};=\-\*\(\)\.,:;\[\]><!0#]/)) {
|
if (this.test(/[\+\{\};=\-\*\(\)\.,:;\[\]><!0#&]/)) {
|
||||||
const first = this.current();
|
const first = this.current();
|
||||||
this.step();
|
this.step();
|
||||||
if (first === "=" && !this.done() && this.test("=")) {
|
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 {
|
export function createCfg(fn: Fn): Cfg {
|
||||||
return new CfgBuilder(fn).build();
|
return new CfgBuilder(fn).build();
|
||||||
@ -7,10 +7,14 @@ export function createCfg(fn: Fn): Cfg {
|
|||||||
export class Cfg {
|
export class Cfg {
|
||||||
public constructor(
|
public constructor(
|
||||||
private graph: Map<BlockId, CfgNode>,
|
private graph: Map<BlockId, CfgNode>,
|
||||||
private entry: BlockId,
|
private entry_: BlockId,
|
||||||
private exit: BlockId,
|
private exit: BlockId,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
public entry(): Block {
|
||||||
|
return this.graph.get(this.entry_)!.block;
|
||||||
|
}
|
||||||
|
|
||||||
public parents(block: Block): Block[] {
|
public parents(block: Block): Block[] {
|
||||||
return this.graph
|
return this.graph
|
||||||
.get(block.id)!.parents
|
.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);
|
const val = this.lowerExpr(stmt.kind.expr);
|
||||||
this.addOp({ type: "assign", dst, src: local(val) });
|
this.addOp({ type: "assign", dst, src: local(val) });
|
||||||
} else {
|
} else {
|
||||||
this.addOp({ type: "assign_null", dst });
|
this.addOp({ type: "assign", dst, src: { type: "null" } });
|
||||||
}
|
}
|
||||||
this.setTer({ type: "jump", target: block });
|
this.setTer({ type: "jump", target: block });
|
||||||
this.pushBlock();
|
this.pushBlock();
|
||||||
@ -227,6 +227,24 @@ class FnAstLowerer {
|
|||||||
return this.lowerSymExpr(expr);
|
return this.lowerSymExpr(expr);
|
||||||
case "group":
|
case "group":
|
||||||
return this.lowerExpr(expr.kind.expr);
|
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":
|
case "array":
|
||||||
throw new Error("incomplete desugar");
|
throw new Error("incomplete desugar");
|
||||||
case "struct":
|
case "struct":
|
||||||
@ -274,7 +292,7 @@ class FnAstLowerer {
|
|||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
const dst = this.locals.alloc(sym.stmt.kind.vtype!);
|
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;
|
return dst;
|
||||||
}
|
}
|
||||||
case "fn_param": {
|
case "fn_param": {
|
||||||
@ -320,7 +338,7 @@ class FnAstLowerer {
|
|||||||
const dstVType = ((): VType => {
|
const dstVType = ((): VType => {
|
||||||
const outer = expr.kind.subject.vtype!;
|
const outer = expr.kind.subject.vtype!;
|
||||||
if (outer.type === "array") {
|
if (outer.type === "array") {
|
||||||
return outer.inner;
|
return outer.subject;
|
||||||
}
|
}
|
||||||
if (outer.type === "string") {
|
if (outer.type === "string") {
|
||||||
return { type: "int" };
|
return { type: "int" };
|
||||||
@ -442,7 +460,7 @@ class FnAstLowerer {
|
|||||||
return this.lowerExpr(expr.kind.expr);
|
return this.lowerExpr(expr.kind.expr);
|
||||||
} else {
|
} else {
|
||||||
const local = this.locals.alloc({ type: "null" });
|
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;
|
return local;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,13 @@ type R = RValue;
|
|||||||
export type OpKind =
|
export type OpKind =
|
||||||
| { type: "error" }
|
| { type: "error" }
|
||||||
| { type: "assign"; dst: L; src: R }
|
| { 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: "field"; dst: L; subject: R; ident: string }
|
||||||
| { type: "assign_field"; subject: R; ident: string; src: R }
|
| { type: "assign_field"; subject: R; ident: string; src: R }
|
||||||
| { type: "index"; dst: L; subject: R; index: R }
|
| { type: "index"; dst: L; subject: R; index: R }
|
||||||
@ -61,6 +68,8 @@ export type TerKind =
|
|||||||
export type RValue =
|
export type RValue =
|
||||||
| { type: "error" }
|
| { type: "error" }
|
||||||
| { type: "local"; id: BlockId }
|
| { type: "local"; id: BlockId }
|
||||||
|
| { type: "copy"; id: BlockId }
|
||||||
|
| { type: "move"; id: BlockId }
|
||||||
| { type: "null" }
|
| { type: "null" }
|
||||||
| { type: "bool"; val: boolean }
|
| { type: "bool"; val: boolean }
|
||||||
| { type: "int"; val: number }
|
| { type: "int"; val: number }
|
||||||
@ -77,12 +86,18 @@ export function visitBlockDsts(
|
|||||||
case "error":
|
case "error":
|
||||||
break;
|
break;
|
||||||
case "assign":
|
case "assign":
|
||||||
|
case "ref":
|
||||||
|
case "ref_mut":
|
||||||
|
case "ptr":
|
||||||
|
case "ptr_mut":
|
||||||
|
case "deref":
|
||||||
case "field":
|
case "field":
|
||||||
case "index":
|
case "index":
|
||||||
case "call_val":
|
case "call_val":
|
||||||
case "binary":
|
case "binary":
|
||||||
visit(ok.dst, i);
|
visit(ok.dst, i);
|
||||||
break;
|
break;
|
||||||
|
case "assign_deref":
|
||||||
case "assign_field":
|
case "assign_field":
|
||||||
case "assign_index":
|
case "assign_index":
|
||||||
break;
|
break;
|
||||||
@ -104,6 +119,19 @@ export function replaceBlockSrcs(
|
|||||||
case "assign":
|
case "assign":
|
||||||
ok.src = replace(ok.src);
|
ok.src = replace(ok.src);
|
||||||
break;
|
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":
|
case "field":
|
||||||
ok.subject = replace(ok.subject);
|
ok.subject = replace(ok.subject);
|
||||||
break;
|
break;
|
||||||
@ -158,6 +186,19 @@ export function visitBlockSrcs(
|
|||||||
case "assign":
|
case "assign":
|
||||||
visitor(ok.src, op, i);
|
visitor(ok.src, op, i);
|
||||||
break;
|
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":
|
case "field":
|
||||||
visitor(ok.subject, op, i);
|
visitor(ok.subject, op, i);
|
||||||
break;
|
break;
|
||||||
@ -254,6 +295,27 @@ export function printMir(mir: Mir) {
|
|||||||
case "assign":
|
case "assign":
|
||||||
l(`_${k.dst} = ${r(k.src)};`);
|
l(`_${k.dst} = ${r(k.src)};`);
|
||||||
break;
|
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":
|
case "field":
|
||||||
l(`_${k.dst} = ${r(k.subject)}.${k.ident};`);
|
l(`_${k.dst} = ${r(k.subject)}.${k.ident};`);
|
||||||
break;
|
break;
|
||||||
@ -277,6 +339,8 @@ export function printMir(mir: Mir) {
|
|||||||
};`);
|
};`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
throw new Error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const tk = block.ter.kind;
|
const tk = block.ter.kind;
|
||||||
@ -295,6 +359,8 @@ export function printMir(mir: Mir) {
|
|||||||
r(tk.cond)
|
r(tk.cond)
|
||||||
}, true: bb${tk.truthy}, false: bb${tk.falsy};`);
|
}, true: bb${tk.truthy}, false: bb${tk.falsy};`);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error();
|
||||||
}
|
}
|
||||||
console.log(" }");
|
console.log(" }");
|
||||||
}
|
}
|
||||||
@ -308,6 +374,10 @@ export function rvalueToString(rvalue: RValue): string {
|
|||||||
return `<error>`;
|
return `<error>`;
|
||||||
case "local":
|
case "local":
|
||||||
return `_${rvalue.id}`;
|
return `_${rvalue.id}`;
|
||||||
|
case "copy":
|
||||||
|
return `copy _${rvalue.id}`;
|
||||||
|
case "move":
|
||||||
|
return `move _${rvalue.id}`;
|
||||||
case "null":
|
case "null":
|
||||||
return "null";
|
return "null";
|
||||||
case "bool":
|
case "bool":
|
||||||
|
@ -163,8 +163,16 @@ function vtypeNameGenPart(vtype: VType): string {
|
|||||||
case "null":
|
case "null":
|
||||||
case "unknown":
|
case "unknown":
|
||||||
return vtype.type;
|
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":
|
case "array":
|
||||||
return `[${vtypeNameGenPart(vtype.inner)}]`;
|
return `[${vtypeNameGenPart(vtype.subject)}]`;
|
||||||
case "struct": {
|
case "struct": {
|
||||||
const fields = vtype.fields
|
const fields = vtype.fields
|
||||||
.map((field) =>
|
.map((field) =>
|
||||||
|
@ -388,7 +388,12 @@ export class Parser {
|
|||||||
|
|
||||||
private parseParam(index?: number): Res<Param> {
|
private parseParam(index?: number): Res<Param> {
|
||||||
const pos = this.pos();
|
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!;
|
const ident = this.current().identValue!;
|
||||||
this.step();
|
this.step();
|
||||||
if (this.test(":")) {
|
if (this.test(":")) {
|
||||||
@ -396,12 +401,18 @@ export class Parser {
|
|||||||
const etype = this.parseEType();
|
const etype = this.parseEType();
|
||||||
return {
|
return {
|
||||||
ok: true,
|
ok: true,
|
||||||
value: this.astCreator.param({ index, ident, etype, pos }),
|
value: this.astCreator.param({
|
||||||
|
index,
|
||||||
|
ident,
|
||||||
|
mut,
|
||||||
|
etype,
|
||||||
|
pos,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
ok: true,
|
ok: true,
|
||||||
value: this.astCreator.param({ index, ident, pos }),
|
value: this.astCreator.param({ index, ident, mut, pos }),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
this.report("expected param");
|
this.report("expected param");
|
||||||
@ -779,6 +790,21 @@ export class Parser {
|
|||||||
const subject = this.parsePrefix();
|
const subject = this.parsePrefix();
|
||||||
return this.expr({ type: "unary", unaryType, subject }, pos);
|
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();
|
return this.parsePostfix();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -955,13 +981,13 @@ export class Parser {
|
|||||||
}
|
}
|
||||||
if (this.test("[")) {
|
if (this.test("[")) {
|
||||||
this.step();
|
this.step();
|
||||||
const inner = this.parseEType();
|
const subject = this.parseEType();
|
||||||
if (!this.test("]")) {
|
if (!this.test("]")) {
|
||||||
this.report("expected ']'", pos);
|
this.report("expected ']'", pos);
|
||||||
return this.etype({ type: "error" }, pos);
|
return this.etype({ type: "error" }, pos);
|
||||||
}
|
}
|
||||||
this.step();
|
this.step();
|
||||||
return this.etype({ type: "array", inner }, pos);
|
return this.etype({ type: "array", subject }, pos);
|
||||||
}
|
}
|
||||||
if (this.test("struct")) {
|
if (this.test("struct")) {
|
||||||
this.step();
|
this.step();
|
||||||
@ -972,6 +998,26 @@ export class Parser {
|
|||||||
const fields = this.parseETypeStructFields();
|
const fields = this.parseETypeStructFields();
|
||||||
return this.etype({ type: "struct", fields }, pos);
|
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");
|
this.report("expected type");
|
||||||
return this.etype({ type: "error" }, pos);
|
return this.etype({ type: "error" }, pos);
|
||||||
}
|
}
|
||||||
|
@ -107,7 +107,7 @@ export class Resolver implements AstVisitor<[Syms]> {
|
|||||||
return "stop";
|
return "stop";
|
||||||
}
|
}
|
||||||
|
|
||||||
visitTypeAliasStmt(stmt: Stmt, syms: Syms): VisitRes {
|
visitTypeAliasStmt(stmt: Stmt, _syms: Syms): VisitRes {
|
||||||
if (stmt.kind.type !== "type_alias") {
|
if (stmt.kind.type !== "type_alias") {
|
||||||
throw new Error("expected type_alias statement");
|
throw new Error("expected type_alias statement");
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,11 @@ export type VType =
|
|||||||
| { type: "int" }
|
| { type: "int" }
|
||||||
| { type: "string" }
|
| { type: "string" }
|
||||||
| { type: "bool" }
|
| { 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: "struct"; fields: VTypeParam[] }
|
||||||
| {
|
| {
|
||||||
type: "fn";
|
type: "fn";
|
||||||
@ -45,8 +49,20 @@ export function vtypesEqual(
|
|||||||
) {
|
) {
|
||||||
return true;
|
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") {
|
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.type === "struct" && b.type === "struct") {
|
||||||
if (a.fields.length !== b.fields.length) {
|
if (a.fields.length !== b.fields.length) {
|
||||||
@ -117,8 +133,20 @@ export function vtypeToString(vtype: VType): string {
|
|||||||
) {
|
) {
|
||||||
return vtype.type;
|
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") {
|
if (vtype.type === "array") {
|
||||||
return `[${vtypeToString(vtype.inner)}]`;
|
return `[${vtypeToString(vtype.subject)}]`;
|
||||||
}
|
}
|
||||||
if (vtype.type === "struct") {
|
if (vtype.type === "struct") {
|
||||||
const fields = vtype.fields
|
const fields = vtype.fields
|
||||||
|
@ -4,7 +4,14 @@ fn add(a: int, b: int) -> int {
|
|||||||
|
|
||||||
fn main() -> int {
|
fn main() -> int {
|
||||||
let result = 0;
|
let result = 0;
|
||||||
let i = 0;
|
|
||||||
|
let a = 0;
|
||||||
|
let b = a;
|
||||||
|
let c = b;
|
||||||
|
let d = c;
|
||||||
|
|
||||||
|
|
||||||
|
let i = c;
|
||||||
loop {
|
loop {
|
||||||
if i >= 10 {
|
if i >= 10 {
|
||||||
break;
|
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