escape (fix): kept memory is HEAP, so what is pushed through an alias of it is kept

render3d's stream_new holds its pool through a local (`let live = s.chunks; push(live, new
Chunk)`): the flow edge from s.chunks to live carried ESC to nothing, the Chunk records were
LOCAL, and the arena reset them under the stream - the row and horse scenarios' crash at
0xdddd... in fn_stream_update. An ESC class is now HEAP too, so every alias of kept memory is,
and a value stored through it is kept. Bidirectional alias edges were tried first and over-kept
through returns (el_place, rim).

- examples/lang/arena_alias.ludic: the stream_new shape; poisoned it read 3 3000, now 3 1518
- examples/modules/alloc_ok_private.ludic: @alloc_ok on a module's private function and on a
  statement in its private generic, declared at run time (it already passes: a guard)
- the game: frame_allocs, frame_keeps, owned_leaks 0; the lab builds under `arena strict`; the row
  scenario poisoned (R3D_ARENA_CHECK=1, 2400 frames) runs clean, bad 0 kept 0

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-28 21:15:32 +03:00
parent 4d3ecfb72c
commit de6d78bef5
7 changed files with 45874 additions and 45571 deletions

View file

@ -835,6 +835,14 @@ function arena_fnval_case() -> void {
if (poisoned == "5 1053") { ok(lbl) } else { bad2(lbl, `poisoned [{poisoned}]`) }
}
function arena_alias_case() -> void {
let lbl = "arena_alias.ludic (--arena: what is pushed through an alias of kept memory (let live = s.cells) is kept; 0xDD poison changes nothing)"
let b = `{tmp_dir()}/e_arena_alias`
if not shq(`bin/ludicc --arena examples/lang/arena_alias.ludic -o {b} 2>/dev/null`) { bad2(lbl, "did not build"); return }
let poisoned = capture_line(`R3D_ARENA_CHECK=1 R3D_ALLOC_FENCE=off {b} < /dev/null`)
if (poisoned == "3 1518") { ok(lbl) } else { bad2(lbl, `poisoned [{poisoned}]`) }
}
function arena_pool_case() -> void {
let lbl = "arena_pool.ludic (--arena: a generic push into a kept pool keeps its records and its buffer on the heap; 0xDD poison changes nothing)"
let b = `{tmp_dir()}/e_arena_pool`
@ -1146,6 +1154,8 @@ function cmd_dev_test() -> int {
alloc_fence_auto_case()
arena_pool_case()
arena_fnval_case()
arena_alias_case()
feat_case("modules/alloc_ok_private", "", "bad 0 kept 0", "alloc_ok_private.ludic (25.1: @alloc_ok on a module's private function and its private generic's statement is declared at run time)")
arena_defaults_case()
feat_case("lang/nested_templates", "", "outer [inner 3 {x}] \"`}\" end", "nested_templates.ludic (a template literal inside another's hole)")
feat_case("lang/aliases", "", "10 5 3", "aliases.ludic (L6: a namespace method declared as an alias of a function, labelled or by its parameters)")