diff options
| -rw-r--r-- | include/cps/CodeGen.h | 2 | ||||
| -rw-r--r-- | include/cps/MemoryHandler.h | 1 | ||||
| -rw-r--r-- | lib/CodeGen/ConstructorGen.cc | 27 | ||||
| -rw-r--r-- | lib/CodeGen/MemoryHandler.cc | 5 | ||||
| -rw-r--r-- | lib/CodeGen/StatementGen.cc | 14 | ||||
| -rw-r--r-- | tests/i/class-delete/case.json | 1 | ||||
| -rw-r--r-- | tests/i/class-delete/expected.txt | 10 | ||||
| -rw-r--r-- | tests/i/class-delete/notes.md | 5 | ||||
| -rw-r--r-- | tests/i/class-delete/program.cps | 35 | ||||
| -rw-r--r-- | tests/iii/class-delete-type-error/case.json | 8 | ||||
| -rw-r--r-- | tests/iii/class-delete-type-error/program.cps | 2 |
11 files changed, 110 insertions, 0 deletions
diff --git a/include/cps/CodeGen.h b/include/cps/CodeGen.h index a70d191..9d71df7 100644 --- a/include/cps/CodeGen.h +++ b/include/cps/CodeGen.h @@ -40,6 +40,8 @@ class CodeGen { llvm::Value *emitMethodCall(const std::string &Class, llvm::Value *Receiver, const std::string &Name, const std::vector<std::unique_ptr<ExprAST>> &Args, int Line); + bool typeNeedsDestruction(const std::string &TypeName) const; + bool emitDestroy(const std::string &TypeName, llvm::Value *Addr, int Line); bool typeNeedsConstruction(const std::string &TypeName) const; bool emitConstruct(const std::string &TypeName, llvm::Value *Addr, const std::vector<std::unique_ptr<ExprAST>> &Args, int Line); diff --git a/include/cps/MemoryHandler.h b/include/cps/MemoryHandler.h index 2768ebe..45c24dc 100644 --- a/include/cps/MemoryHandler.h +++ b/include/cps/MemoryHandler.h @@ -38,6 +38,7 @@ public: : Context(C), Builder(B), Module(M), Types(TS), Checker(RC), Exceptions(EH) {} llvm::Value *emitAllocate(llvm::Value *Size, int Line); + void emitValidate(llvm::Value *Address, int Line); llvm::Value *emitRelease(llvm::Value *Address, int Line); llvm::Value *emitResize(llvm::Value *Address, llvm::Value *Size, int Line); }; diff --git a/lib/CodeGen/ConstructorGen.cc b/lib/CodeGen/ConstructorGen.cc index 99f21df..9e6b0b3 100644 --- a/lib/CodeGen/ConstructorGen.cc +++ b/lib/CodeGen/ConstructorGen.cc @@ -80,3 +80,30 @@ void CodeGen::emitAssignment(const LValueInfo &Target, ExprAST *Expr, const Symb Value *Val = emitCoercedExpr(Expr, Info); if (Val) Builder->CreateStore(Val, Target.Addr); } + +bool CodeGen::typeNeedsDestruction(const std::string &TypeName) const { + if (findMethod(TypeName, "DELETE")) return true; + const TypeInfo *Info = resolveType(TypeName); + if (Info && Info->asRecord()) { + for (const auto &[Name, Field] : Info->Record.Fields) + if (typeNeedsDestruction(Field.TypeName)) return true; + } + return false; +} + +bool CodeGen::emitDestroy(const std::string &TypeName, Value *Addr, int Line) { + SaveAndRestore<int> Location(DiagnosticLine, Line); + const TypeInfo *Info = resolveType(TypeName); + if (!Info) return false; + if (findMethod(TypeName, "DELETE") && + !emitMethodCall(TypeName, Addr, "DELETE", {}, Line)) return false; + if (Info->asRecord()) { + for (auto It = Info->Record.Fields.rbegin(); It != Info->Record.Fields.rend(); ++It) { + const auto &Field = It->second; + if (!typeNeedsDestruction(Field.TypeName)) continue; + Value *FieldAddr = Builder->CreateStructGEP(Info->LLVMType, Addr, Field.Index); + if (!emitDestroy(Field.TypeName, FieldAddr, Line)) return false; + } + } + return true; +} diff --git a/lib/CodeGen/MemoryHandler.cc b/lib/CodeGen/MemoryHandler.cc index 07fe6f6..fa25eb9 100644 --- a/lib/CodeGen/MemoryHandler.cc +++ b/lib/CodeGen/MemoryHandler.cc @@ -152,3 +152,8 @@ Value *MemoryHandler::emitResize(Value *Address, Value *Size, int Line) { ensureRuntime(); return Exceptions.emitCall(ResizeFn, {Address, Size, Builder.getInt32(Line)}, "resized"); } + +void MemoryHandler::emitValidate(Value *Address, int Line) { + ensureRuntime(); + Exceptions.emitCall(FindFn, {Address, Builder.getInt32(Line)}); +} diff --git a/lib/CodeGen/StatementGen.cc b/lib/CodeGen/StatementGen.cc index bd1f7c5..a7c8eda 100644 --- a/lib/CodeGen/StatementGen.cc +++ b/lib/CodeGen/StatementGen.cc @@ -2,6 +2,7 @@ #include "cps/ArrayHandler.h" #include "cps/FileAST.h" #include "cps/FileHandler.h" +#include "cps/MemoryHandler.h" #include "cps/ExceptionHandler.h" #include "cps/TypeAST.h" @@ -49,6 +50,19 @@ void CodeGen::emitStmt(StmtAST *Stmt) { Builder->SetInsertPoint(BasicBlock::Create(*TheContext, "unreachable_code", F)); } + if (auto *Delete = dynamic_cast<DeleteStmtAST*>(Stmt)) { + LValueInfo Object; + if (!emitLValue(Delete->getTarget(), Object)) return; + const TypeInfo *Info = resolveType(Object.TypeName); + if (!Info || !Info->IsClass) { + reportError("DELETE requires a class object"); + return; + } + Memory->emitValidate(Object.Addr, Stmt->getLine()); + if (emitDestroy(Object.TypeName, Object.Addr, Stmt->getLine())) + Memory->emitRelease(Object.Addr, Stmt->getLine()); + return; + } if (auto *Try = dynamic_cast<TryStmtAST*>(Stmt)) { emitTryStmt(Try); return; diff --git a/tests/i/class-delete/case.json b/tests/i/class-delete/case.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tests/i/class-delete/case.json @@ -0,0 +1 @@ +{} diff --git a/tests/i/class-delete/expected.txt b/tests/i/class-delete/expected.txt new file mode 100644 index 0000000..85c4753 --- /dev/null +++ b/tests/i/class-delete/expected.txt @@ -0,0 +1,10 @@ +pair +pair +pair +leaf:2 +leaf:1 +[Fatal] line 24: Invalid memory address (not an active allocation) +double caught +[Fatal] line 30: Invalid memory address (not an active allocation) +stack caught +leaf:2 diff --git a/tests/i/class-delete/notes.md b/tests/i/class-delete/notes.md new file mode 100644 index 0000000..c4e2638 --- /dev/null +++ b/tests/i/class-delete/notes.md @@ -0,0 +1,5 @@ +# Keyword DELETE + +Human review: pending. Expected output was independently traced by AI. + +Explicit calls print only the outer method. Keyword DELETE prints outer, right, left. Invalid stack and stale heap addresses throw before any method runs. The array element uses an ordinary explicit method call. diff --git a/tests/i/class-delete/program.cps b/tests/i/class-delete/program.cps new file mode 100644 index 0000000..492c032 --- /dev/null +++ b/tests/i/class-delete/program.cps @@ -0,0 +1,35 @@ +CLASS Leaf + Value : INTEGER + PROCEDURE DELETE() + OUTPUT "leaf:", Value + ENDPROCEDURE +ENDCLASS +CLASS Pair + Left : Leaf + Right : Leaf + PROCEDURE NEW() + Left.Value <- 1 + Right.Value <- 2 + ENDPROCEDURE + PROCEDURE DELETE() + OUTPUT "pair" + ENDPROCEDURE +ENDCLASS +DECLARE Obj : Pair +Obj <- NEW Pair() +Obj.DELETE() +CALL Obj.DELETE() +DELETE Obj +TRY + DELETE Obj +CATCH + OUTPUT "double caught" +ENDTRY +DECLARE Stack : Pair +TRY + DELETE Stack +CATCH + OUTPUT "stack caught" +ENDTRY +DECLARE Items : ARRAY[1:2] OF Pair +Items[2].Right.DELETE() diff --git a/tests/iii/class-delete-type-error/case.json b/tests/iii/class-delete-type-error/case.json new file mode 100644 index 0000000..efbbafb --- /dev/null +++ b/tests/iii/class-delete-type-error/case.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "reason": "DELETE requires a class object", + "line": 2 + } + ] +} diff --git a/tests/iii/class-delete-type-error/program.cps b/tests/iii/class-delete-type-error/program.cps new file mode 100644 index 0000000..d468f19 --- /dev/null +++ b/tests/iii/class-delete-type-error/program.cps @@ -0,0 +1,2 @@ +DECLARE N : INTEGER +DELETE N |
