From 4dd85fd6ecd9a67e5c2b68380873e6e114714355 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Sat, 17 Oct 2026 05:30:57 +0000 Subject: feat(memory): add tracked MALLOC and FREE builtins Co-Authored-By: Claude Fable 5.1 --- tests/i/memory-allocation/case.json | 1 + tests/i/memory-allocation/expected.txt | 14 +++++++ tests/i/memory-allocation/notes.md | 5 +++ tests/i/memory-allocation/program.cps | 63 +++++++++++++++++++++++++++++++ tests/iii/memory-call-arity/case.json | 8 ++++ tests/iii/memory-call-arity/notes.md | 5 +++ tests/iii/memory-call-arity/program.cps | 1 + tests/iii/memory-pointer-type/case.json | 8 ++++ tests/iii/memory-pointer-type/notes.md | 5 +++ tests/iii/memory-pointer-type/program.cps | 4 ++ tests/iii/memory-size-type/case.json | 8 ++++ tests/iii/memory-size-type/notes.md | 5 +++ tests/iii/memory-size-type/program.cps | 1 + 13 files changed, 128 insertions(+) create mode 100644 tests/i/memory-allocation/case.json create mode 100644 tests/i/memory-allocation/expected.txt create mode 100644 tests/i/memory-allocation/notes.md create mode 100644 tests/i/memory-allocation/program.cps create mode 100644 tests/iii/memory-call-arity/case.json create mode 100644 tests/iii/memory-call-arity/notes.md create mode 100644 tests/iii/memory-call-arity/program.cps create mode 100644 tests/iii/memory-pointer-type/case.json create mode 100644 tests/iii/memory-pointer-type/notes.md create mode 100644 tests/iii/memory-pointer-type/program.cps create mode 100644 tests/iii/memory-size-type/case.json create mode 100644 tests/iii/memory-size-type/notes.md create mode 100644 tests/iii/memory-size-type/program.cps (limited to 'tests') diff --git a/tests/i/memory-allocation/case.json b/tests/i/memory-allocation/case.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tests/i/memory-allocation/case.json @@ -0,0 +1 @@ +{} diff --git a/tests/i/memory-allocation/expected.txt b/tests/i/memory-allocation/expected.txt new file mode 100644 index 0000000..330c9ba --- /dev/null +++ b/tests/i/memory-allocation/expected.txt @@ -0,0 +1,14 @@ +TRUE +TRUE +42 +11:22 +TRUE +[Fatal] line 34: Invalid memory address (not an active allocation) +double free caught +[Fatal] line 40: Invalid memory address (not an active allocation) +stack address caught +[Fatal] line 48: Invalid memory address (not an active allocation) +interior address caught +[Fatal] line 54: Memory allocation failed +allocation failure caught +99 diff --git a/tests/i/memory-allocation/notes.md b/tests/i/memory-allocation/notes.md new file mode 100644 index 0000000..c0d4286 --- /dev/null +++ b/tests/i/memory-allocation/notes.md @@ -0,0 +1,5 @@ +# Trace notes + +Human review: pending. + +Explicit memory allocation is a compiler extension. Zero size and FREE(null) return null. An INTEGER allocation stores 42; a two-INTEGER record stores 11 and 22. FREE returns the input address. Freeing the middle, head and tail nodes empties the allocation list. Repeated free, a stack address and a record field's interior address are caught before libc is called. The negative size converts to SIZE_MAX, whose allocation fails. A final allocation still stores 99. Sizes 8 and 16 cover the fixed i64 representation. diff --git a/tests/i/memory-allocation/program.cps b/tests/i/memory-allocation/program.cps new file mode 100644 index 0000000..85a24dc --- /dev/null +++ b/tests/i/memory-allocation/program.cps @@ -0,0 +1,63 @@ +TYPE Raw = C_TYPE("void*") +TYPE IntPointer = ^INTEGER +TYPE Pair + DECLARE First : INTEGER + DECLARE Second : INTEGER +ENDTYPE +TYPE PairPointer = ^Pair +DECLARE Empty : Raw +DECLARE P : Raw +DECLARE Q : Raw +DECLARE R : Raw +DECLARE Number : INTEGER +DECLARE Pointer : IntPointer +DECLARE PairAddress : PairPointer +DECLARE Result : Raw +OUTPUT MALLOC(0) = Empty +OUTPUT FREE(Empty) = Empty +P <- malloc(8) +Pointer <- (IntPointer)P +Pointer^ <- 42 +OUTPUT Pointer^ +Q <- MALLOC((C_INT)16) +PairAddress <- (PairPointer)Q +PairAddress^.First <- 11 +PairAddress^.Second <- 22 +OUTPUT PairAddress^.First, ":", PairAddress^.Second +R <- MALLOC(1) +// Remove a middle node, then the head, then the tail. +Result <- FREE(Q) +OUTPUT Result = Q +FREE(R) +FREE(P) +TRY + FREE(P) +CATCH + OUTPUT "double free caught" +ENDTRY +Pointer <- ^Number +TRY + FREE((Raw)Pointer) +CATCH + OUTPUT "stack address caught" +ENDTRY +Q <- MALLOC(16) +PairAddress <- (PairPointer)Q +Pointer <- ^PairAddress^.Second +TRY + FREE((Raw)Pointer) +CATCH + OUTPUT "interior address caught" +ENDTRY +FREE(Q) +TRY + MALLOC(-1) +CATCH + OUTPUT "allocation failure caught" +ENDTRY +// Allocation still works after all caught failures. +P <- MALLOC(8) +Pointer <- (IntPointer)P +Pointer^ <- 99 +OUTPUT Pointer^ +FREE(P) diff --git a/tests/iii/memory-call-arity/case.json b/tests/iii/memory-call-arity/case.json new file mode 100644 index 0000000..3159635 --- /dev/null +++ b/tests/iii/memory-call-arity/case.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "reason": "FREE\\ expects\\ 1\\ arg", + "line": 1 + } + ] +} diff --git a/tests/iii/memory-call-arity/notes.md b/tests/iii/memory-call-arity/notes.md new file mode 100644 index 0000000..456445e --- /dev/null +++ b/tests/iii/memory-call-arity/notes.md @@ -0,0 +1,5 @@ +# Rejection notes + +Human review: pending. + +The explicit memory builtin rejects the invalid argument before emitting a runtime call. diff --git a/tests/iii/memory-call-arity/program.cps b/tests/iii/memory-call-arity/program.cps new file mode 100644 index 0000000..2ae44d4 --- /dev/null +++ b/tests/iii/memory-call-arity/program.cps @@ -0,0 +1 @@ +FREE() diff --git a/tests/iii/memory-pointer-type/case.json b/tests/iii/memory-pointer-type/case.json new file mode 100644 index 0000000..557c4b9 --- /dev/null +++ b/tests/iii/memory-pointer-type/case.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "reason": "Cannot\\ implicitly\\ convert", + "line": 4 + } + ] +} diff --git a/tests/iii/memory-pointer-type/notes.md b/tests/iii/memory-pointer-type/notes.md new file mode 100644 index 0000000..456445e --- /dev/null +++ b/tests/iii/memory-pointer-type/notes.md @@ -0,0 +1,5 @@ +# Rejection notes + +Human review: pending. + +The explicit memory builtin rejects the invalid argument before emitting a runtime call. diff --git a/tests/iii/memory-pointer-type/program.cps b/tests/iii/memory-pointer-type/program.cps new file mode 100644 index 0000000..2c1abeb --- /dev/null +++ b/tests/iii/memory-pointer-type/program.cps @@ -0,0 +1,4 @@ +TYPE Raw = C_TYPE("void*") +TYPE P = ^INTEGER +DECLARE Pointer : P +FREE(Pointer) diff --git a/tests/iii/memory-size-type/case.json b/tests/iii/memory-size-type/case.json new file mode 100644 index 0000000..9753757 --- /dev/null +++ b/tests/iii/memory-size-type/case.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "reason": "Allocation\\ size\\ must\\ be\\ INTEGER\\ or\\ a\\ C\\ integer", + "line": 1 + } + ] +} diff --git a/tests/iii/memory-size-type/notes.md b/tests/iii/memory-size-type/notes.md new file mode 100644 index 0000000..456445e --- /dev/null +++ b/tests/iii/memory-size-type/notes.md @@ -0,0 +1,5 @@ +# Rejection notes + +Human review: pending. + +The explicit memory builtin rejects the invalid argument before emitting a runtime call. diff --git a/tests/iii/memory-size-type/program.cps b/tests/iii/memory-size-type/program.cps new file mode 100644 index 0000000..0215fef --- /dev/null +++ b/tests/iii/memory-size-type/program.cps @@ -0,0 +1 @@ +MALLOC(1.5) -- cgit v1.3.1-4-g156e