From 4e22f464387725a5a282f3d72e11cfb694a31a5a Mon Sep 17 00:00:00 2001 From: _ov4 Date: Sat, 17 Oct 2026 05:33:33 +0000 Subject: feat(memory): add checked REALLOC with failure preservation Co-Authored-By: Claude Fable 5.1 --- tests/i/memory-reallocation/case.json | 1 + tests/i/memory-reallocation/expected.txt | 17 +++++++ tests/i/memory-reallocation/notes.md | 5 ++ tests/i/memory-reallocation/program.cps | 81 ++++++++++++++++++++++++++++++++ tests/i/memory-uncaught/case.json | 1 + tests/i/memory-uncaught/expected.txt | 2 + tests/i/memory-uncaught/notes.md | 5 ++ tests/i/memory-uncaught/program.cps | 5 ++ tests/iii/memory-size-type/case.json | 4 ++ tests/iii/memory-size-type/program.cps | 3 ++ 10 files changed, 124 insertions(+) create mode 100644 tests/i/memory-reallocation/case.json create mode 100644 tests/i/memory-reallocation/expected.txt create mode 100644 tests/i/memory-reallocation/notes.md create mode 100644 tests/i/memory-reallocation/program.cps create mode 100644 tests/i/memory-uncaught/case.json create mode 100644 tests/i/memory-uncaught/expected.txt create mode 100644 tests/i/memory-uncaught/notes.md create mode 100644 tests/i/memory-uncaught/program.cps (limited to 'tests') 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) -- cgit v1.3.1-4-g156e