owned fields (25.5e): @owns(Kind) on a record's field, and owned_leaks
A field marked @owns(PhysShape) holds a handle its record owns. A function that releases one owned field of a record (body_free(w, s.body)) and neither releases nor hands on another owned field of the same record type (s.shape) gives the first back and loses the second - the phys_remove bug, at compile time. ludic deps --resources (or --owned) lists them; owned_leaks is a number --check ratchets. A test: the function that frees a solid's body alone is the one found; the one that frees both is not. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
a0c6f8ce7f
commit
b87ee96805
6 changed files with 74859 additions and 72140 deletions
|
|
@ -229,6 +229,7 @@ function deps_keeps(f: pointer, seen: []bool) -> void {
|
|||
if g_arena_strict and n > 0 { exit(1) }
|
||||
deps_births(f, seen)
|
||||
deps_resources(f) # 25.5e
|
||||
deps_owned(f)
|
||||
}
|
||||
|
||||
function fr_consumed(n: Node) -> bool {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@ var g_res_free_fn: []pointer = new []pointer
|
|||
var g_res_free_kind: []pointer = new []pointer
|
||||
var g_res_pending_make: pointer = null
|
||||
var g_res_pending_free: pointer = null
|
||||
var g_own_type: []pointer = new []pointer # @owns(Kind) fields: the record's type ...
|
||||
var g_own_field: []pointer = new []pointer # ... the field ...
|
||||
var g_own_kind: []pointer = new []pointer # ... and the kind of handle it owns
|
||||
var g_own_pending: pointer = null
|
||||
|
||||
function res_kind_made_by(name: pointer) -> pointer {
|
||||
var i = 0
|
||||
|
|
@ -98,3 +102,86 @@ function deps_resources(f: pointer) -> void {
|
|||
}
|
||||
deps_line(f, `resource_drops {itoa(g_res_drops)}`)
|
||||
}
|
||||
|
||||
function res_is_release(name: pointer) -> bool {
|
||||
var i = 0
|
||||
while i < len(g_res_free_fn) { if (g_res_free_fn[i] == name) { return true }; i += 1 }
|
||||
return false
|
||||
}
|
||||
|
||||
# every `base.field` handed to a call in n: into rel when the callee @releases, into all always
|
||||
function own_collect(n: Node, rel_b: []pointer, rel_f: []pointer, all_b: []pointer, all_f: []pointer, rel_line: []int) -> void {
|
||||
if n == null { return }
|
||||
if n.kind == E_CALL and n.a != null and n.a.kind == E_ID and n.kids != null {
|
||||
let releasing = res_is_release(n.a.s)
|
||||
var i = 0
|
||||
while i < len(n.kids) {
|
||||
let a = n.kids[i]
|
||||
if a != null and a.kind == E_MEMBER and a.a != null and a.a.kind == E_ID and a.s != null {
|
||||
push(all_b, a.a.s)
|
||||
push(all_f, a.s)
|
||||
if releasing {
|
||||
push(rel_b, a.a.s)
|
||||
push(rel_f, a.s)
|
||||
push(rel_line, n.line)
|
||||
}
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
own_collect(n.a, rel_b, rel_f, all_b, all_f, rel_line)
|
||||
own_collect(n.b, rel_b, rel_f, all_b, all_f, rel_line)
|
||||
own_collect(n.c, rel_b, rel_f, all_b, all_f, rel_line)
|
||||
if n.kids != null {
|
||||
var j = 0
|
||||
while j < len(n.kids) { own_collect(n.kids[j], rel_b, rel_f, all_b, all_f, rel_line); j += 1 }
|
||||
}
|
||||
}
|
||||
|
||||
function own_has(bs: []pointer, fs: []pointer, b: pointer, f: pointer) -> bool {
|
||||
var i = 0
|
||||
while i < len(bs) { if (bs[i] == b) and (fs[i] == f) { return true }; i += 1 }
|
||||
return false
|
||||
}
|
||||
|
||||
# 25.5e's owned fields: a function that releases one @owns field of a record (x.body) and neither
|
||||
# releases nor hands on another @owns field of the same record type (x.shape) gives the first back
|
||||
# and loses the second - phys_remove freeing the body and not its shape. `oleak` lines, owned_leaks
|
||||
var g_own_leaks: int = 0
|
||||
function deps_owned(f: pointer) -> void {
|
||||
g_own_leaks = 0
|
||||
if len(g_own_field) > 0 {
|
||||
var i = 0
|
||||
while i < len(prog) {
|
||||
let d = prog[i]
|
||||
if (d.kind == N_FN or d.kind == N_SYS) and d.file != null and not is_runtime_file(d.file) {
|
||||
let rb = new []pointer
|
||||
let rf = new []pointer
|
||||
let rl = new []int
|
||||
let ab = new []pointer
|
||||
let af = new []pointer
|
||||
own_collect(d.a, rb, rf, ab, af, rl)
|
||||
var r = 0
|
||||
while r < len(rb) {
|
||||
var t = 0
|
||||
while t < len(g_own_field) {
|
||||
if (g_own_field[t] == rf[r]) {
|
||||
var o = 0
|
||||
while o < len(g_own_field) {
|
||||
if o != t and (g_own_type[o] == g_own_type[t]) and not own_has(ab, af, rb[r], g_own_field[o]) {
|
||||
deps_line(f, `oleak {g_own_type[o]}.{g_own_field[o]} {d.file}:{itoa(rl[r])} {d.s} {rb[r]}.{rf[r]}`)
|
||||
g_own_leaks += 1
|
||||
}
|
||||
o += 1
|
||||
}
|
||||
}
|
||||
t += 1
|
||||
}
|
||||
r += 1
|
||||
}
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
deps_line(f, `owned_leaks {itoa(g_own_leaks)}`)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,11 @@ function parse_component() -> Node {
|
|||
pi += 1
|
||||
let ann = eat_id()
|
||||
if ann == "Computed" { is_computed = true } else if ann == "Sync" { is_sync = true } else if ann == "frame" { is_frame = true }
|
||||
else if ann == "owns" { # @owns(PhysShape) — this field holds a handle its record owns (25.5e)
|
||||
eat_op("(")
|
||||
g_own_pending = eat_id()
|
||||
eat_op(")")
|
||||
}
|
||||
else if ann == "max" {
|
||||
eat_op("(")
|
||||
let ce = expr()
|
||||
|
|
@ -27,6 +32,7 @@ function parse_component() -> Node {
|
|||
let f = node(N_FIELD); f.s = eat_id(); eat_op(":"); f.ty = ptype()
|
||||
if is_frame { push(g_frame_fields, `{n.s}.{f.s}`) }
|
||||
if cap > 0 { push(g_cap_keys, `{n.s}.{f.s}`); push(g_cap_vals, cap) }
|
||||
if g_own_pending != null { push(g_own_type, n.s); push(g_own_field, f.s); push(g_own_kind, g_own_pending); g_own_pending = null }
|
||||
if is_op("=") { pi += 1; f.a = expr() }
|
||||
if is_sync { f.ival = 1 } # mark the field replicable (read by emit_net)
|
||||
if is_computed { register_computed(n.s, f.s, f.ty, f.a) } # derived: no storage
|
||||
|
|
|
|||
73459
selfhost/ludicc.seed.ll
73459
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
Loading…
Add table
Add a link
Reference in a new issue