diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-17 05:33:33 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-17 05:33:33 +0000 |
| commit | 4e22f464387725a5a282f3d72e11cfb694a31a5a (patch) | |
| tree | 17fed09b30633e6aae583f490a0ad9c0c95fb045 | |
| parent | 4dd85fd6ecd9a67e5c2b68380873e6e114714355 (diff) | |
feat(memory): add checked REALLOC with failure preservation
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
| -rw-r--r-- | AGENTS/ARCH.md | 2 | ||||
| -rw-r--r-- | AGENTS/CONVENTIONS.md | 2 | ||||
| -rw-r--r-- | AGENTS/SUBSYSTEMS.md | 2 | ||||
| -rw-r--r-- | doc/language/cpsc-language-overview.7.md | 1 | ||||
| -rw-r--r-- | doc/language/function/cpsc-function-free.7.md | 4 | ||||
| -rw-r--r-- | doc/language/function/cpsc-function-malloc.7.md | 8 | ||||
| -rw-r--r-- | doc/language/function/cpsc-function-realloc.7.md | 41 | ||||
| -rw-r--r-- | include/cps/MemoryHandler.h | 7 | ||||
| -rw-r--r-- | lib/CodeGen/Builtins.h | 1 | ||||
| -rw-r--r-- | lib/CodeGen/ExpressionGen.cc | 7 | ||||
| -rw-r--r-- | lib/CodeGen/MemoryHandler.cc | 34 | ||||
| -rw-r--r-- | tests/i/memory-reallocation/case.json | 1 | ||||
| -rw-r--r-- | tests/i/memory-reallocation/expected.txt | 17 | ||||
| -rw-r--r-- | tests/i/memory-reallocation/notes.md | 5 | ||||
| -rw-r--r-- | tests/i/memory-reallocation/program.cps | 81 | ||||
| -rw-r--r-- | tests/i/memory-uncaught/case.json | 1 | ||||
| -rw-r--r-- | tests/i/memory-uncaught/expected.txt | 2 | ||||
| -rw-r--r-- | tests/i/memory-uncaught/notes.md | 5 | ||||
| -rw-r--r-- | tests/i/memory-uncaught/program.cps | 5 | ||||
| -rw-r--r-- | tests/iii/memory-size-type/case.json | 4 | ||||
| -rw-r--r-- | tests/iii/memory-size-type/program.cps | 3 |
21 files changed, 222 insertions, 11 deletions
diff --git a/AGENTS/ARCH.md b/AGENTS/ARCH.md index edfa281..27bb70a 100644 --- a/AGENTS/ARCH.md +++ b/AGENTS/ARCH.md @@ -297,7 +297,7 @@ do not modify the real release for local verification. | `lib/CodeGen/Designator.cc`, `TypeCheckGen.cc` | TYPE pre-pass and lvalue access; type admission and user-kind binary operators | | `lib/CodeGen/CTypeGen.cc` | CodeGen methods for native C scalar conversions, casts, arithmetic and input/output | | `lib/CodeGen/NativeTypeSystem.cc` | Builtin registration, native C type imports/aliases and scalar promotion | -| `lib/CodeGen/MemoryHandler.cc` | Explicit raw memory allocation and release with a live-address tracking list | +| `lib/CodeGen/MemoryHandler.cc` | Explicit raw memory allocation, resizing and release with a live-address tracking list | | `lib/CodeGen/FileHandler.cc`, `FileRuntimeIO.cc` | File runtime setup/lookup/open and call entry points; read/write/close/EOF helper bodies | | `lib/CodeGen/ArrayHandler.cc`, `ArrayParameters.cc`, `ArrayOutput.cc` | Array allocation/indexing, parameter binding/arguments and whole/subarray output | | `lib/CodeGen/*Handler.cc`, `FunctionGen.cc`, `RuntimeCheck.cc`, `TypeSystem.cc` | Helpers owned by CodeGen (including exception emission) | diff --git a/AGENTS/CONVENTIONS.md b/AGENTS/CONVENTIONS.md index 75289da..8ce9cb2 100644 --- a/AGENTS/CONVENTIONS.md +++ b/AGENTS/CONVENTIONS.md @@ -158,7 +158,7 @@ callback CodeGen injects into FunctionGen). BYVAL arrays are whole malloc+memcpy copies at callee entry; BYREF binds the caller's buffer. **Builtins are plain identifiers backed by one table.** Builtins -(LENGTH/MID/RIGHT/LEFT/LCASE/UCASE/ASC/CHR/IS_NUM/NUM_TO_STR/STR_TO_NUM/EOF/MALLOC/FREE) parse as +(LENGTH/MID/RIGHT/LEFT/LCASE/UCASE/ASC/CHR/IS_NUM/NUM_TO_STR/STR_TO_NUM/EOF/MALLOC/FREE/REALLOC) parse as ordinary calls — no lexer/parser support, and the names are not reserved words. The inline constexpr `Builtins` table in the internal `lib/CodeGen/Builtins.h` ({Name, Arity, ReturnTypeName}) is the single source of truth: `emitCall` checks arity against it diff --git a/AGENTS/SUBSYSTEMS.md b/AGENTS/SUBSYSTEMS.md index 6b4793c..e564c1f 100644 --- a/AGENTS/SUBSYSTEMS.md +++ b/AGENTS/SUBSYSTEMS.md @@ -108,4 +108,4 @@ malloc/realloc buffer per line and strips one trailing `\r`; fopen modes are tex nothing is declared or emitted eagerly. Helper bodies are built with the *shared* Builder (RuntimeCheck emits through the same one), saving/restoring the insert block. -**Explicit memory** (`MemoryHandler`): lazily emits internal allocation/find/release helpers and a linked list of live allocation start addresses. Only explicit MALLOC allocations enter the list; STRING/ARRAY internals retain their existing ownership. FREE validates non-null addresses, unlinks and frees the tracking node, releases the block and returns the original address. Zero-byte allocation returns null. Node allocation precedes the user allocation; if the latter fails, its node is freed before the diagnostic and anonymous throw. Helpers use RuntimeCheck and ExceptionHandler, including source-line arguments and per-function unwind preparation. Sizes accept INTEGER and C integers and convert to native size_t. Address reuse can make stale aliases indistinguishable from current allocations; dereferences are not checked against this list. +**Explicit memory** (`MemoryHandler`): lazily emits internal allocation/find/release/resize helpers and a linked list of live allocation start addresses. Only explicit MALLOC/REALLOC allocations enter the list; STRING/ARRAY internals retain their existing ownership. FREE validates non-null addresses, unlinks and frees the tracking node, releases the block and returns the original address. Zero-byte allocation returns null. REALLOC validates non-null addresses before its zero-size release path; null behaves as MALLOC. Positive-size resizing updates the node only after success, leaving the old block and record intact on failure. Node allocation precedes the user allocation; if the latter fails, its node is freed before the diagnostic and anonymous throw. Helpers use RuntimeCheck and ExceptionHandler, including source-line arguments and per-function unwind preparation. Sizes accept INTEGER and C integers and convert to native size_t. Address reuse can make stale aliases indistinguishable from current allocations; dereferences are not checked against this list. diff --git a/doc/language/cpsc-language-overview.7.md b/doc/language/cpsc-language-overview.7.md index 1c77bb5..d2d3617 100644 --- a/doc/language/cpsc-language-overview.7.md +++ b/doc/language/cpsc-language-overview.7.md @@ -65,6 +65,7 @@ Closing and auxiliary keywords are documented with their main constructs: THEN/E - [cpsc-function-str_to_num(7)](function/cpsc-function-str_to_num.7.md): convert text to a real number. - [cpsc-function-eof(7)](function/cpsc-function-eof.7.md): test for the end of a text file. - [cpsc-function-malloc(7)](function/cpsc-function-malloc.7.md): allocate raw memory. +- [cpsc-function-realloc(7)](function/cpsc-function-realloc.7.md): resize an explicit allocation. - [cpsc-function-free(7)](function/cpsc-function-free.7.md): release an explicit allocation. # UNSUPPORTED FEATURES diff --git a/doc/language/function/cpsc-function-free.7.md b/doc/language/function/cpsc-function-free.7.md index 298199d..e2c8f12 100644 --- a/doc/language/function/cpsc-function-free.7.md +++ b/doc/language/function/cpsc-function-free.7.md @@ -12,7 +12,7 @@ FREE(pointer) # DESCRIPTION -FREE is a compiler extension. Its argument and return type are C void*. Explicitly cast other pointer types to a void* alias before passing them. A null pointer is accepted and returned. A non-null pointer must be the start of an active explicit MALLOC allocation; internal STRING/ARRAY storage, stack addresses, interior addresses and already released allocations are rejected with a source-line diagnostic on stdout and an anonymous exception. +FREE is a compiler extension. Its argument and return type are C `void*`. Explicitly cast other pointer types to a `void*` alias before passing them. A null pointer is accepted and returned. A non-null pointer must be the start of an active explicit MALLOC or REALLOC allocation; internal STRING/ARRAY storage, stack addresses, interior addresses and already released allocations are rejected with a source-line diagnostic on stdout and an anonymous exception. FREE returns its input address after releasing the allocation. It does not clear the caller's variable. The returned address and all aliases are invalid for memory access. Calling FREE again on that address throws unless a subsequent allocation has reused the address. Tracking cannot distinguish stale aliases once an address is reused. @@ -29,4 +29,4 @@ FREE(Memory) # SEE ALSO -[cpsc-function-malloc(7)](cpsc-function-malloc.7.md), [cpsc-keyword-function(7)](../keyword/cpsc-keyword-function.7.md), [cpsc-keyword-try(7)](../keyword/cpsc-keyword-try.7.md). +[cpsc-function-malloc(7)](cpsc-function-malloc.7.md), [cpsc-function-realloc(7)](cpsc-function-realloc.7.md), [cpsc-keyword-function(7)](../keyword/cpsc-keyword-function.7.md), [cpsc-keyword-try(7)](../keyword/cpsc-keyword-try.7.md). diff --git a/doc/language/function/cpsc-function-malloc.7.md b/doc/language/function/cpsc-function-malloc.7.md index 742367c..909903a 100644 --- a/doc/language/function/cpsc-function-malloc.7.md +++ b/doc/language/function/cpsc-function-malloc.7.md @@ -12,11 +12,11 @@ MALLOC(bytes) # DESCRIPTION -MALLOC is a compiler extension returning a C void* to the beginning of an uninitialized allocation. The size is in bytes and accepts INTEGER or a C integer, converted to the host size_t using C integer conversion rules. Negative values are not separately rejected. MALLOC(0) returns null without allocating. A nonzero allocation failure prints the source line and reason to stdout, then throws an anonymous exception catchable with TRY/CATCH. Allocation tracking failures behave the same way. +MALLOC is a compiler extension returning a C `void*` to the beginning of an uninitialized allocation. The size is in bytes and accepts INTEGER or a C integer, converted to the host size_t using C integer conversion rules. Negative values are not separately rejected. MALLOC(0) returns null without allocating. A nonzero allocation failure prints the source line and reason to stdout, then throws an anonymous exception catchable with TRY/CATCH. Allocation tracking failures behave the same way. -Declare a void* alias with C_TYPE and explicitly cast the result to a C or pseudocode pointer before dereferencing. MALLOC does not initialize fields or call constructors; it is unrelated to the CIE class NEW construct. The caller must supply enough bytes and initialize values before reading them. User memory is not automatically released. +Declare a `void*` alias with C_TYPE and explicitly cast the result to a C or pseudocode pointer before dereferencing. MALLOC does not initialize fields or call constructors; it is unrelated to the CIE class NEW construct. The caller must supply enough bytes and initialize values before reading them. User memory is not automatically released. -Only allocations from explicit memory builtins are tracked. Internal STRING and ARRAY buffers remain outside this ownership system. Tracking validates FREE addresses, not memory accesses, bounds or stale aliases after address reuse. +Only allocations from explicit memory builtins are tracked. Internal STRING and ARRAY buffers remain outside this ownership system. Tracking validates FREE/REALLOC addresses, not memory accesses, bounds or stale aliases after address reuse. # EXAMPLE @@ -34,4 +34,4 @@ FREE(Memory) # SEE ALSO -[cpsc-function-free(7)](cpsc-function-free.7.md), [cpsc-keyword-c_type(7)](../keyword/cpsc-keyword-c_type.7.md), [cpsc-keyword-try(7)](../keyword/cpsc-keyword-try.7.md). +[cpsc-function-free(7)](cpsc-function-free.7.md), [cpsc-function-realloc(7)](cpsc-function-realloc.7.md), [cpsc-keyword-c_type(7)](../keyword/cpsc-keyword-c_type.7.md), [cpsc-keyword-try(7)](../keyword/cpsc-keyword-try.7.md). diff --git a/doc/language/function/cpsc-function-realloc.7.md b/doc/language/function/cpsc-function-realloc.7.md new file mode 100644 index 0000000..19fcfdf --- /dev/null +++ b/doc/language/function/cpsc-function-realloc.7.md @@ -0,0 +1,41 @@ +% CPSC-FUNCTION-REALLOC(7) + +# NAME + +cpsc-function-realloc - resize an explicit memory allocation + +# SYNOPSIS + +```text +REALLOC(pointer, bytes) +``` + +# DESCRIPTION + +REALLOC is a compiler extension returning C `void*`. The pointer argument must also be C `void*`; explicitly cast other pointer types. The size accepts INTEGER or a C integer and converts to host size_t, just as MALLOC does. + +A null pointer behaves as MALLOC(bytes). A non-null pointer must start an active explicit MALLOC or REALLOC allocation, even when bytes is zero. Invalid addresses print the source line and reason to stdout and throw an anonymous exception before touching the memory or allocation record. + +For a valid pointer and zero bytes, REALLOC releases the block and returns null. This rule is independent of libc's zero-size behavior. For a nonzero size, successful resizing preserves the smaller of the old and new sizes; added bytes are uninitialized. The returned address may differ from the input. Use the returned pointer afterward; previous aliases must not be used. + +On nonzero allocation failure, REALLOC prints the source line and reason and throws an anonymous exception. The original allocation and its tracking record remain valid. In `p <- REALLOC(p, bytes)`, assignment occurs only on success, so a CATCH handler can still use or release the old p. A successful resize updates the existing record without allocating a new tracking node. Internal STRING/ARRAY buffers cannot be resized with this function. + +# EXAMPLE + +```text +TYPE Raw = C_TYPE("void*") +TYPE IntPointer = ^INTEGER +DECLARE Memory : Raw +DECLARE Pointer : IntPointer +Memory <- MALLOC(8) +Pointer <- (IntPointer)Memory +Pointer^ <- 42 +Memory <- REALLOC(Memory, 16) +Pointer <- (IntPointer)Memory +OUTPUT Pointer^ +FREE(Memory) +``` + +# SEE ALSO + +[cpsc-function-malloc(7)](cpsc-function-malloc.7.md), [cpsc-function-free(7)](cpsc-function-free.7.md), [cpsc-keyword-try(7)](../keyword/cpsc-keyword-try.7.md). diff --git a/include/cps/MemoryHandler.h b/include/cps/MemoryHandler.h index fd2c93e..ded838b 100644 --- a/include/cps/MemoryHandler.h +++ b/include/cps/MemoryHandler.h @@ -20,8 +20,9 @@ class MemoryHandler { llvm::IntegerType *SizeTy = nullptr; llvm::StructType *NodeTy = nullptr; llvm::GlobalVariable *Head = nullptr; - llvm::FunctionCallee Malloc, Free; - llvm::Function *AllocateFn = nullptr, *ReleaseFn = nullptr, *FindFn = nullptr; + llvm::FunctionCallee Malloc, Free, Realloc; + llvm::Function *AllocateFn = nullptr, *ReleaseFn = nullptr, *FindFn = nullptr, + *ResizeFn = nullptr; llvm::Value *AllocationError = nullptr, *TrackingError = nullptr, *InvalidAddress = nullptr; void ensureRuntime(); @@ -29,6 +30,7 @@ class MemoryHandler { void buildFind(); void buildAllocate(); void buildRelease(); + void buildResize(); public: MemoryHandler(llvm::LLVMContext &C, llvm::IRBuilder<> &B, llvm::Module &M, @@ -37,6 +39,7 @@ public: llvm::Value *emitAllocate(llvm::Value *Size, int Line); llvm::Value *emitRelease(llvm::Value *Address, int Line); + llvm::Value *emitResize(llvm::Value *Address, llvm::Value *Size, int Line); }; } // namespace cps diff --git a/lib/CodeGen/Builtins.h b/lib/CodeGen/Builtins.h index 69361cf..b74afc4 100644 --- a/lib/CodeGen/Builtins.h +++ b/lib/CodeGen/Builtins.h @@ -27,6 +27,7 @@ inline constexpr BuiltinInfo Builtins[] = { {"STR_TO_NUM", 1, "REAL"}, {"EOF", 1, "BOOLEAN"}, {"MALLOC", 1, "C_TYPE(\"void*\")"}, + {"REALLOC", 2, "C_TYPE(\"void*\")"}, {"FREE", 1, "C_TYPE(\"void*\")"}, }; diff --git a/lib/CodeGen/ExpressionGen.cc b/lib/CodeGen/ExpressionGen.cc index 0a24c8d..9ea79d8 100644 --- a/lib/CodeGen/ExpressionGen.cc +++ b/lib/CodeGen/ExpressionGen.cc @@ -192,6 +192,13 @@ Value *CodeGen::emitCall(const std::string &Callee, if (!Size) return nullptr; return Memory->emitAllocate(Size, Line); } + if (Name == "REALLOC") { + Value *Address = emitCoercedExpr(ArgExprs[0].get(), resolveType("C_TYPE(\"void*\")")); + if (!Address) return nullptr; + Value *Size = emitAllocationSize(ArgExprs[1].get()); + if (!Size) return nullptr; + return Memory->emitResize(Address, Size, Line); + } if (Name == "FREE") { Value *Address = emitCoercedExpr(ArgExprs[0].get(), resolveType("C_TYPE(\"void*\")")); if (!Address) return nullptr; diff --git a/lib/CodeGen/MemoryHandler.cc b/lib/CodeGen/MemoryHandler.cc index fc6d982..07fe6f6 100644 --- a/lib/CodeGen/MemoryHandler.cc +++ b/lib/CodeGen/MemoryHandler.cc @@ -27,6 +27,7 @@ void MemoryHandler::ensureRuntime() { ConstantPointerNull::get(Ptr), "__cps_memory_head"); Malloc = Module.getOrInsertFunction("malloc", FunctionType::get(Ptr, {SizeTy}, false)); Free = Module.getOrInsertFunction("free", FunctionType::get(Type::getVoidTy(Context), {Ptr}, false)); + Realloc = Module.getOrInsertFunction("realloc", FunctionType::get(Ptr, {Ptr, SizeTy}, false)); AllocationError = Builder.CreateGlobalString("[Fatal] line %d: Memory allocation failed\n", "err_allocation", 0, &Module); TrackingError = Builder.CreateGlobalString("[Fatal] line %d: Allocation tracking failed\n", @@ -39,6 +40,8 @@ void MemoryHandler::ensureRuntime() { buildAllocate(); ReleaseFn = createHelper("__cps_memory_release", {Ptr, I32}); buildRelease(); + ResizeFn = createHelper("__cps_memory_resize", {Ptr, SizeTy, I32}); + buildResize(); } // Return the address of the list link that owns the matching node. This also @@ -118,3 +121,34 @@ Value *MemoryHandler::emitRelease(Value *Address, int Line) { ensureRuntime(); return Exceptions.emitCall(ReleaseFn, {Address, Builder.getInt32(Line)}, "released"); } + +void MemoryHandler::buildResize() { + auto *Ptr = PointerType::getUnqual(Context); + Value *Address = ResizeFn->getArg(0), *Size = ResizeFn->getArg(1), *Line = ResizeFn->getArg(2); + auto *Allocate = BasicBlock::Create(Context, "allocate", ResizeFn); + auto *Validate = BasicBlock::Create(Context, "validate", ResizeFn); + auto *Release = BasicBlock::Create(Context, "release", ResizeFn); + auto *Resize = BasicBlock::Create(Context, "resize", ResizeFn); + Builder.CreateCondBr(Builder.CreateIsNull(Address), Allocate, Validate); + Builder.SetInsertPoint(Allocate); + Builder.CreateRet(Exceptions.emitCall(AllocateFn, {Size, Line}, "allocation")); + Builder.SetInsertPoint(Validate); + Value *Link = Exceptions.emitCall(FindFn, {Address, Line}, "link"); + Value *Node = Builder.CreateLoad(Ptr, Link); + Builder.CreateCondBr(Builder.CreateICmpEQ(Size, ConstantInt::get(SizeTy, 0)), Release, Resize); + Builder.SetInsertPoint(Release); + Exceptions.emitCall(ReleaseFn, {Address, Line}); + Builder.CreateRet(ConstantPointerNull::get(Ptr)); + Builder.SetInsertPoint(Resize); + Value *NewAddress = Builder.CreateCall(Realloc, {Address, Size}, "resized"); + // libc preserves the original block on nonzero-size failure. Leave the + // tracking entry untouched until the check has succeeded as well. + Checker.emitCheck(Builder.CreateIsNull(NewAddress), AllocationError, {Line}); + Builder.CreateStore(NewAddress, Builder.CreateStructGEP(NodeTy, Node, 0)); + Builder.CreateRet(NewAddress); +} + +Value *MemoryHandler::emitResize(Value *Address, Value *Size, int Line) { + ensureRuntime(); + return Exceptions.emitCall(ResizeFn, {Address, Size, Builder.getInt32(Line)}, "resized"); +} diff --git a/tests/i/memory-reallocation/case.json b/tests/i/memory-reallocation/case.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tests/i/memory-reallocation/case.json @@ -0,0 +1 @@ +{} diff --git a/tests/i/memory-reallocation/expected.txt b/tests/i/memory-reallocation/expected.txt new file mode 100644 index 0000000..9e9ccc9 --- /dev/null +++ b/tests/i/memory-reallocation/expected.txt @@ -0,0 +1,17 @@ +TRUE +12:34 +12 +[Fatal] line 28: Memory allocation failed +preserved:12 +56 +TRUE +[Fatal] line 40: Invalid memory address (not an active allocation) +released address caught +[Fatal] line 47: Invalid memory address (not an active allocation) +invalid zero resize caught +71 +[Fatal] line 63: Invalid memory address (not an active allocation) +[Fatal] line 69: Invalid memory address (not an active allocation) +nested catch +[Fatal] line 77: Invalid memory address (not an active allocation) +internal buffer caught diff --git a/tests/i/memory-reallocation/notes.md b/tests/i/memory-reallocation/notes.md new file mode 100644 index 0000000..46560f5 --- /dev/null +++ b/tests/i/memory-reallocation/notes.md @@ -0,0 +1,5 @@ +# Trace notes + +Human review: pending. + +This case exercises explicit memory extensions. REALLOC(null, 0) returns null; REALLOC(null, 16) allocates. The record's 12 and 34 survive growth, and 12 survives shrinking to one INTEGER. A SIZE_MAX request fails without changing the pointer or its stored 12; a subsequent write of 56 survives resizing. A zero resize frees the block and returns null. Resizing its released address throws; a stack pointer throws even at zero size. C int 71 survives reallocation after an allocation returned from a function. Invalid resizing inside a function unwinds to its caller; another invalid free is caught inside that catch. A STRING literal is rejected as untracked storage. The final nested allocation/resize/free has no output. diff --git a/tests/i/memory-reallocation/program.cps b/tests/i/memory-reallocation/program.cps new file mode 100644 index 0000000..d119256 --- /dev/null +++ b/tests/i/memory-reallocation/program.cps @@ -0,0 +1,81 @@ +TYPE Raw = C_TYPE("void*") +TYPE NativePointer = C_TYPE("int*") +TYPE Pair + DECLARE First : INTEGER + DECLARE Second : INTEGER +ENDTYPE +TYPE PairPointer = ^Pair +TYPE IntPointer = ^INTEGER +DECLARE Empty : Raw +DECLARE P : Raw +DECLARE Q : Raw +DECLARE Number : INTEGER +DECLARE Pointer : IntPointer +DECLARE PairAddress : PairPointer +DECLARE Native : NativePointer +OUTPUT REALLOC(Empty, 0) = Empty +P <- REALLOC(Empty, 16) +PairAddress <- (PairPointer)P +PairAddress^.First <- 12 +PairAddress^.Second <- 34 +P <- REALLOC(P, 32) +PairAddress <- (PairPointer)P +OUTPUT PairAddress^.First, ":", PairAddress^.Second +P <- REALLOC(P, (C_INT)8) +Pointer <- (IntPointer)P +OUTPUT Pointer^ +TRY + P <- REALLOC(P, -1) +CATCH + Pointer <- (IntPointer)P + OUTPUT "preserved:", Pointer^ +ENDTRY +Pointer^ <- 56 +P <- REALLOC(P, 16) +Pointer <- (IntPointer)P +OUTPUT Pointer^ +Q <- P +OUTPUT REALLOC(P, 0) = Empty +TRY + REALLOC(Q, 8) +CATCH + OUTPUT "released address caught" +ENDTRY +Pointer <- ^Number +Q <- (Raw)Pointer +TRY + REALLOC(Q, 0) +CATCH + OUTPUT "invalid zero resize caught" +ENDTRY +FUNCTION AllocateNative() RETURNS Raw + RETURN MALLOC(16) +ENDFUNCTION +P <- AllocateNative() +Native <- (NativePointer)P +Native^ <- (C_INT)71 +P <- REALLOC(P, 32) +Native <- (NativePointer)P +OUTPUT Native^ +FREE(P) +// A runtime failure raised from a function propagates to the caller. +FUNCTION InvalidResize(Value : Raw) RETURNS Raw + RETURN REALLOC(Value, 8) +ENDFUNCTION +TRY + InvalidResize(Q) +CATCH + TRY + FREE(Q) + CATCH + OUTPUT "nested catch" + ENDTRY +ENDTRY +// STRING storage is deliberately outside the explicit allocation list. +Q <- (Raw)(C_STRING)"internal" +TRY + REALLOC(Q, 16) +CATCH + OUTPUT "internal buffer caught" +ENDTRY +FREE(REALLOC(Empty, (C_INT)8)) diff --git a/tests/i/memory-uncaught/case.json b/tests/i/memory-uncaught/case.json new file mode 100644 index 0000000..2c4afe3 --- /dev/null +++ b/tests/i/memory-uncaught/case.json @@ -0,0 +1 @@ +{"exit_code": 1} diff --git a/tests/i/memory-uncaught/expected.txt b/tests/i/memory-uncaught/expected.txt new file mode 100644 index 0000000..448a57b --- /dev/null +++ b/tests/i/memory-uncaught/expected.txt @@ -0,0 +1,2 @@ +[Fatal] line 5: Invalid memory address (not an active allocation) +Unhandled exception. Program terminated. diff --git a/tests/i/memory-uncaught/notes.md b/tests/i/memory-uncaught/notes.md new file mode 100644 index 0000000..3d7f2df --- /dev/null +++ b/tests/i/memory-uncaught/notes.md @@ -0,0 +1,5 @@ +# Trace notes + +Human review: pending. + +The second free prints its line and reason, then throws. The generated main handler prints the termination message and returns 1. This is an extension, tested through native and configured runner execution. diff --git a/tests/i/memory-uncaught/program.cps b/tests/i/memory-uncaught/program.cps new file mode 100644 index 0000000..85b96a8 --- /dev/null +++ b/tests/i/memory-uncaught/program.cps @@ -0,0 +1,5 @@ +TYPE Raw = C_TYPE("void*") +DECLARE P : Raw +P <- MALLOC(8) +FREE(P) +FREE(P) diff --git a/tests/iii/memory-size-type/case.json b/tests/iii/memory-size-type/case.json index 9753757..6354ba8 100644 --- a/tests/iii/memory-size-type/case.json +++ b/tests/iii/memory-size-type/case.json @@ -3,6 +3,10 @@ { "reason": "Allocation\\ size\\ must\\ be\\ INTEGER\\ or\\ a\\ C\\ integer", "line": 1 + }, + { + "reason": "Allocation size must be INTEGER or a C integer", + "line": 4 } ] } diff --git a/tests/iii/memory-size-type/program.cps b/tests/iii/memory-size-type/program.cps index 0215fef..d76a7bf 100644 --- a/tests/iii/memory-size-type/program.cps +++ b/tests/iii/memory-size-type/program.cps @@ -1 +1,4 @@ MALLOC(1.5) +TYPE Raw = C_TYPE("void*") +DECLARE P : Raw +REALLOC(P, TRUE) |
