escape (fix): a node walked more than once is scratch only if every walk found it LOCAL
A field's default is one expression, walked at every 'new' of its record: marked LOCAL by a frame's
temporary, it stayed marked when a record a pool keeps was made from it, and that record's list came
from the frame's scratch. The marks are now taken off any node one walk found kept.
examples/lang/arena_defaults.ludic is the case (a pool's record made in frame 3, a temporary of the
same type every frame): foundations 1315baf crashes on it poisoned; this prints '497 124747', as the
heap does. In ludic-dev test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
1315baf332
commit
bad7c4a255
5 changed files with 44624 additions and 44209 deletions
28
examples/lang/arena_defaults.ludic
Normal file
28
examples/lang/arena_defaults.ludic
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# arena_defaults.ludic - a field's default (`items: []int = new []int`) is one expression, made at every
|
||||
# `new` of its record: here for a record a pool keeps (made in frame 3, the arena running) and for a
|
||||
# frame's temporary. It must stay on the heap for both. Built with --arena and poisoned on every
|
||||
# reset, it prints `497 124747`.
|
||||
program ArenaDefaults {
|
||||
property Bag { items: []int = new []int, n: int = 0 }
|
||||
function temp_sum(f: int) -> int {
|
||||
let t = new Bag
|
||||
t.n = f
|
||||
return t.n
|
||||
}
|
||||
function step(held: []Bag, f: int) -> int {
|
||||
if f == 3 { push(held, new Bag) }
|
||||
if len(held) > 0 { push(held[0].items, f) }
|
||||
return temp_sum(f)
|
||||
}
|
||||
entry {
|
||||
let held = new []Bag
|
||||
var t = 0
|
||||
for f in 0 .. 500 {
|
||||
t += step(held, f)
|
||||
Mem.frame()
|
||||
}
|
||||
var sum = 0
|
||||
for i in 0 .. len(held[0].items) { sum += held[0].items[i] }
|
||||
print(`{len(held[0].items)} {sum}`)
|
||||
}
|
||||
}
|
||||
|
|
@ -627,6 +627,9 @@ function escape_analyse() -> void {
|
|||
li += 1
|
||||
}
|
||||
es_solve()
|
||||
# a node analysed more than once (a field's default, walked at every `new` of its record) is
|
||||
# scratch only if every walk found it LOCAL: mark the LOCAL ones, then take the mark off any node
|
||||
# one walk found kept
|
||||
var s = 0
|
||||
while s < len(g_es_site) {
|
||||
let n = g_es_site[s]
|
||||
|
|
@ -648,6 +651,15 @@ function escape_analyse() -> void {
|
|||
}
|
||||
s += 1
|
||||
}
|
||||
var u = 0
|
||||
while u < len(g_es_site) {
|
||||
let n2 = g_es_site[u]
|
||||
let c2 = g_es_site_cls[u]
|
||||
let fi2 = g_es_site_fn[u]
|
||||
let spans2 = fi2 < 0 or g_es_fspans[fi2]
|
||||
if n2.uns == ES_SCRATCH and (c2 < 0 or (g_es_flag[c2] & (ES_ESC | ES_HEAP)) != 0 or spans2) { n2.uns = 0 }
|
||||
u += 1
|
||||
}
|
||||
}
|
||||
|
||||
var g_es_report: bool = false
|
||||
|
|
|
|||
44397
selfhost/ludicc.seed.ll
44397
selfhost/ludicc.seed.ll
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
|
|
@ -819,6 +819,14 @@ function alloc_fence_auto_case() -> void {
|
|||
|
||||
# 25.3: a pool that keeps records across frames through a generic push must not take them from the
|
||||
# frame's scratch - poisoned on every reset it prints what the heap does
|
||||
function arena_defaults_case() -> void {
|
||||
let lbl = "arena_defaults.ludic (--arena: a field's default made for a kept record and a frame's temporary stays on the heap for both)"
|
||||
let b = `{tmp_dir()}/e_arena_defaults`
|
||||
if not shq(`bin/ludicc --arena examples/lang/arena_defaults.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 == "497 124747") { 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`
|
||||
|
|
@ -1127,6 +1135,7 @@ function cmd_dev_test() -> int {
|
|||
alloc_fence_leak_case()
|
||||
alloc_fence_auto_case()
|
||||
arena_pool_case()
|
||||
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)")
|
||||
reject_case("rejected/alias_arity", "this call to Trail.length leaves out to, which has no default", "an alias's arguments are checked against its target")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue