mirror of
				https://git.sfja.dk/Mikkel/slige.git
				synced 2025-10-31 11:38:19 +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({ | ||||
|  | ||||
| @ -31,7 +31,7 @@ export class Reporter { | ||||
|     private printReport({ reporter, type, pos, msg }: Report) { | ||||
|         console.error( | ||||
|             `${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", | ||||
|                 "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