codegen rc: tag-conditional partial-drop helper closes 3 carve-outs
Three sibling sites previously fell back to shallow `ailang_rc_dec` when a binder had non-empty `moved_slots` and a dynamic runtime ctor tag: Iter B Own-param dec at fn-return (lib.rs), Iter A arm-close pattern-binder dec (match_lower.rs), and emit_inlined_partial_drop's non-Ctor branch (drop.rs). Shallow freed the outer cell but did not walk the unmoved ptr fields — silent leak. Adds `emit_partial_drop_fn_for_type`: a per-ADT helper structurally parallel to `emit_drop_fn_for_type` but taking an i64 moved-slots bitmask. Each ptr-field's dec is gated on the corresponding mask bit; the outer box is dec'd at join. Emitted alongside the per-type drop fn for every ADT under --alloc=rc. Three call sites rewritten to resolve the partial_drop symbol from the binder's static type and build the mask from `moved_slots`. Three RED-then-GREEN fixtures (rc_own_param_/rc_match_arm_/ rc_app_let_partial_drop_leak) pin the carve-outs in regression coverage; live cell counts go from 3/1/3 (pre-fix) to 0/0/0. e2e count: 66 → 69. The pre-existing `alloc_rc_own_param_dec_at_fn_return` IR-shape assertion is widened to accept `partial_drop_<m>_<T>(%arg_xs, i64 mask)` as a third valid drop shape (the canonical fu2 case). Closes the JOURNAL queue's only remaining iter; the "wait for organic fixture" stance from the 18g tidy is retracted — known leaks are bugs, CLAUDE.md's TDD rule covers them autonomously.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
{"defs":[{"ctors":[{"fields":[{"k":"con","name":"Int"}],"name":"MkWrap"}],"kind":"type","name":"Wrap"},{"ctors":[{"fields":[{"k":"con","name":"Wrap"},{"k":"con","name":"Wrap"}],"name":"MkCell"}],"kind":"type","name":"Cell"},{"ctors":[{"fields":[{"k":"con","name":"Cell"},{"k":"con","name":"Cell"}],"name":"MkPair"}],"kind":"type","name":"Pair"},{"body":{"args":[{"args":[{"args":[{"name":"n","t":"var"}],"ctor":"MkWrap","t":"ctor","type":"Wrap"},{"args":[{"lit":{"kind":"int","value":2},"t":"lit"}],"ctor":"MkWrap","t":"ctor","type":"Wrap"}],"ctor":"MkCell","t":"ctor","type":"Cell"},{"args":[{"args":[{"lit":{"kind":"int","value":3},"t":"lit"}],"ctor":"MkWrap","t":"ctor","type":"Wrap"},{"args":[{"lit":{"kind":"int","value":4},"t":"lit"}],"ctor":"MkWrap","t":"ctor","type":"Wrap"}],"ctor":"MkCell","t":"ctor","type":"Cell"}],"ctor":"MkPair","t":"ctor","type":"Pair"},"kind":"fn","name":"build_pair","params":["n"],"type":{"effects":[],"k":"fn","params":[{"k":"con","name":"Int"}],"ret":{"k":"con","name":"Pair"},"ret_mode":"own"}},{"body":{"arms":[{"body":{"lit":{"kind":"int","value":1},"t":"lit"},"pat":{"ctor":"MkCell","fields":[{"name":"w1","p":"var"},{"name":"w2","p":"var"}],"p":"ctor"}}],"scrutinee":{"name":"c","t":"var"},"t":"match"},"kind":"fn","name":"use_cell","params":["c"],"type":{"effects":[],"k":"fn","param_modes":["own"],"params":[{"k":"con","name":"Cell"}],"ret":{"k":"con","name":"Int"}}},{"body":{"body":{"args":[{"arms":[{"body":{"args":[{"name":"a","t":"var"}],"fn":{"name":"use_cell","t":"var"},"t":"app"},"pat":{"ctor":"MkPair","fields":[{"name":"a","p":"var"},{"p":"wild"}],"p":"ctor"}}],"scrutinee":{"name":"p","t":"var"},"t":"match"}],"op":"io/print_int","t":"do"},"name":"p","t":"let","value":{"args":[{"lit":{"kind":"int","value":1},"t":"lit"}],"fn":{"name":"build_pair","t":"var"},"t":"app"}},"kind":"fn","name":"main","params":[],"type":{"effects":["IO"],"k":"fn","params":[],"ret":{"k":"con","name":"Unit"}}}],"imports":[],"name":"rc_app_let_partial_drop_leak","schema":"ailang/v0"}
|
||||
@@ -0,0 +1,69 @@
|
||||
; Iter 18g.tidy.fu2 RED-test fixture 3/3: dynamic-tag partial-drop
|
||||
; carve-out at let-close for an App-bound binder
|
||||
; (drop.rs:580 fallback in `emit_inlined_partial_drop`).
|
||||
;
|
||||
; Shape:
|
||||
; - Same `Pair`/`Cell`/`Wrap` types as fixtures 1 and 2.
|
||||
; - `main` let-binds `p = (app build_pair)` — value is
|
||||
; `Term::App` (Own-returning), `is_rc_heap_allocated` returns
|
||||
; true → trackable.
|
||||
; - Body matches `p` as `MkPair a _` — slot 0 bound, slot 1 wild.
|
||||
; `moved_slots[p] = {0}`.
|
||||
; - `a` is consumed by `use_cell` (Own param) → `consume_count[a]
|
||||
; == 1`, arm-close drop skipped. `use_cell`'s body fully
|
||||
; destructures `a` and returns Int.
|
||||
; - At p's let-close: `consume_count[p] == 0`, `moves[p] = {0}`
|
||||
; non-empty, `value` is `Term::App` (not `Term::Ctor`) →
|
||||
; `emit_inlined_partial_drop` falls into the non-Ctor branch
|
||||
; (drop.rs:580) → shallow `ailang_rc_dec(p)`. p's outer cell
|
||||
; is freed; slot 1 (the second MkCell + its two MkWraps =
|
||||
; 3 cells) leaks.
|
||||
;
|
||||
; Pre-fix: live = 3 (one MkCell + two MkWrap children in slot 1).
|
||||
; Post-fix: live = 0.
|
||||
|
||||
(module rc_app_let_partial_drop_leak
|
||||
|
||||
(data Wrap
|
||||
(ctor MkWrap (con Int)))
|
||||
|
||||
(data Cell
|
||||
(ctor MkCell (con Wrap) (con Wrap)))
|
||||
|
||||
(data Pair
|
||||
(ctor MkPair (con Cell) (con Cell)))
|
||||
|
||||
(fn build_pair
|
||||
(type
|
||||
(fn-type
|
||||
(params (con Int))
|
||||
(ret (own (con Pair)))))
|
||||
(params n)
|
||||
(body
|
||||
(term-ctor Pair MkPair
|
||||
(term-ctor Cell MkCell
|
||||
(term-ctor Wrap MkWrap n)
|
||||
(term-ctor Wrap MkWrap 2))
|
||||
(term-ctor Cell MkCell
|
||||
(term-ctor Wrap MkWrap 3)
|
||||
(term-ctor Wrap MkWrap 4)))))
|
||||
|
||||
(fn use_cell
|
||||
(type
|
||||
(fn-type
|
||||
(params (own (con Cell)))
|
||||
(ret (con Int))))
|
||||
(params c)
|
||||
(body
|
||||
(match c
|
||||
(case (pat-ctor MkCell w1 w2) 1))))
|
||||
|
||||
(fn main
|
||||
(type (fn-type (params) (ret (con Unit)) (effects IO)))
|
||||
(params)
|
||||
(body
|
||||
(let p (app build_pair 1)
|
||||
(do io/print_int
|
||||
(match p
|
||||
(case (pat-ctor MkPair a _)
|
||||
(app use_cell a))))))))
|
||||
@@ -0,0 +1 @@
|
||||
{"defs":[{"ctors":[{"fields":[{"k":"con","name":"Int"}],"name":"MkWrap"}],"kind":"type","name":"Wrap"},{"ctors":[{"fields":[{"k":"con","name":"Wrap"},{"k":"con","name":"Wrap"}],"name":"MkCell"}],"kind":"type","name":"Cell"},{"ctors":[{"fields":[{"k":"con","name":"Cell"},{"k":"con","name":"Cell"}],"name":"MkPair"}],"kind":"type","name":"Pair"},{"body":{"args":[{"args":[{"args":[{"name":"n","t":"var"}],"ctor":"MkWrap","t":"ctor","type":"Wrap"},{"args":[{"lit":{"kind":"int","value":2},"t":"lit"}],"ctor":"MkWrap","t":"ctor","type":"Wrap"}],"ctor":"MkCell","t":"ctor","type":"Cell"},{"args":[{"args":[{"lit":{"kind":"int","value":3},"t":"lit"}],"ctor":"MkWrap","t":"ctor","type":"Wrap"},{"args":[{"lit":{"kind":"int","value":4},"t":"lit"}],"ctor":"MkWrap","t":"ctor","type":"Wrap"}],"ctor":"MkCell","t":"ctor","type":"Cell"}],"ctor":"MkPair","t":"ctor","type":"Pair"},"kind":"fn","name":"build_pair","params":["n"],"type":{"effects":[],"k":"fn","params":[{"k":"con","name":"Int"}],"ret":{"k":"con","name":"Pair"},"ret_mode":"own"}},{"body":{"arms":[{"body":{"arms":[{"body":{"lit":{"kind":"int","value":1},"t":"lit"},"pat":{"ctor":"MkCell","fields":[{"name":"w1","p":"var"},{"p":"wild"}],"p":"ctor"}}],"scrutinee":{"name":"a","t":"var"},"t":"match"},"pat":{"ctor":"MkPair","fields":[{"name":"a","p":"var"},{"name":"b","p":"var"}],"p":"ctor"}}],"scrutinee":{"name":"p","t":"var"},"t":"match"},"kind":"fn","name":"use_first","params":["p"],"type":{"effects":[],"k":"fn","param_modes":["own"],"params":[{"k":"con","name":"Pair"}],"ret":{"k":"con","name":"Int"}}},{"body":{"args":[{"args":[{"args":[{"lit":{"kind":"int","value":1},"t":"lit"}],"fn":{"name":"build_pair","t":"var"},"t":"app"}],"fn":{"name":"use_first","t":"var"},"t":"app"}],"op":"io/print_int","t":"do"},"kind":"fn","name":"main","params":[],"type":{"effects":["IO"],"k":"fn","params":[],"ret":{"k":"con","name":"Unit"}}}],"imports":[],"name":"rc_match_arm_partial_drop_leak","schema":"ailang/v0"}
|
||||
@@ -0,0 +1,71 @@
|
||||
; Iter 18g.tidy.fu2 RED-test fixture 2/3: dynamic-tag partial-drop
|
||||
; carve-out at outer-arm-close pattern-binder dec
|
||||
; (Iter A / match_lower.rs:839).
|
||||
;
|
||||
; Shape:
|
||||
; - `Pair` carries two `Cell` fields; `Cell` carries two `Wrap`
|
||||
; fields. (Same types as fixture 1.)
|
||||
; - `use_first` takes `(own Pair)` and the OUTER match binds both
|
||||
; slots: `MkPair a b`.
|
||||
; - The arm body INNER-matches `a` as `MkCell w1 _` — slot 0 of
|
||||
; a is bound, slot 1 is wildcarded.
|
||||
; - Inner arm-close drops `w1` properly via `drop_<m>_Wrap`.
|
||||
; Inner match adds slot 0 to `moved_slots[a]`.
|
||||
; - Outer arm-close drops `a` and `b`. `a` has `moves={0}`
|
||||
; (non-empty) and `consume_count==0` (only Borrow use as
|
||||
; scrutinee of inner match) → Iter A's dynamic-tag carve-out
|
||||
; fires: shallow `ailang_rc_dec(a)`. a's outer Cell is freed,
|
||||
; but slot 1 (the unbound MkWrap(2) cell) leaks.
|
||||
; - `b` has `moves={}` and `consume_count==0` → routes through
|
||||
; `drop_<m>_Cell(b)` which cascades through both Wraps. No
|
||||
; leak from b.
|
||||
; - At fn return, `p`'s `moves={0,1}` (both slots bound) → Iter
|
||||
; B carve-out fires too, but with all slots moved, the shallow
|
||||
; dec is correct (no additional leak from p).
|
||||
;
|
||||
; Pre-fix: live = 1 (the unbound MkWrap(2) cell in slot 1 of a).
|
||||
; Post-fix: live = 0.
|
||||
|
||||
(module rc_match_arm_partial_drop_leak
|
||||
|
||||
(data Wrap
|
||||
(ctor MkWrap (con Int)))
|
||||
|
||||
(data Cell
|
||||
(ctor MkCell (con Wrap) (con Wrap)))
|
||||
|
||||
(data Pair
|
||||
(ctor MkPair (con Cell) (con Cell)))
|
||||
|
||||
(fn build_pair
|
||||
(type
|
||||
(fn-type
|
||||
(params (con Int))
|
||||
(ret (own (con Pair)))))
|
||||
(params n)
|
||||
(body
|
||||
(term-ctor Pair MkPair
|
||||
(term-ctor Cell MkCell
|
||||
(term-ctor Wrap MkWrap n)
|
||||
(term-ctor Wrap MkWrap 2))
|
||||
(term-ctor Cell MkCell
|
||||
(term-ctor Wrap MkWrap 3)
|
||||
(term-ctor Wrap MkWrap 4)))))
|
||||
|
||||
(fn use_first
|
||||
(type
|
||||
(fn-type
|
||||
(params (own (con Pair)))
|
||||
(ret (con Int))))
|
||||
(params p)
|
||||
(body
|
||||
(match p
|
||||
(case (pat-ctor MkPair a b)
|
||||
(match a
|
||||
(case (pat-ctor MkCell w1 _) 1))))))
|
||||
|
||||
(fn main
|
||||
(type (fn-type (params) (ret (con Unit)) (effects IO)))
|
||||
(params)
|
||||
(body
|
||||
(do io/print_int (app use_first (app build_pair 1))))))
|
||||
@@ -0,0 +1 @@
|
||||
{"defs":[{"ctors":[{"fields":[{"k":"con","name":"Int"}],"name":"MkWrap"}],"kind":"type","name":"Wrap"},{"ctors":[{"fields":[{"k":"con","name":"Wrap"},{"k":"con","name":"Wrap"}],"name":"MkCell"}],"kind":"type","name":"Cell"},{"ctors":[{"fields":[{"k":"con","name":"Cell"},{"k":"con","name":"Cell"}],"name":"MkPair"}],"kind":"type","name":"Pair"},{"body":{"args":[{"args":[{"args":[{"name":"n","t":"var"}],"ctor":"MkWrap","t":"ctor","type":"Wrap"},{"args":[{"lit":{"kind":"int","value":2},"t":"lit"}],"ctor":"MkWrap","t":"ctor","type":"Wrap"}],"ctor":"MkCell","t":"ctor","type":"Cell"},{"args":[{"args":[{"lit":{"kind":"int","value":3},"t":"lit"}],"ctor":"MkWrap","t":"ctor","type":"Wrap"},{"args":[{"lit":{"kind":"int","value":4},"t":"lit"}],"ctor":"MkWrap","t":"ctor","type":"Wrap"}],"ctor":"MkCell","t":"ctor","type":"Cell"}],"ctor":"MkPair","t":"ctor","type":"Pair"},"kind":"fn","name":"build_pair","params":["n"],"type":{"effects":[],"k":"fn","params":[{"k":"con","name":"Int"}],"ret":{"k":"con","name":"Pair"},"ret_mode":"own"}},{"body":{"arms":[{"body":{"lit":{"kind":"int","value":1},"t":"lit"},"pat":{"ctor":"MkPair","fields":[{"name":"a","p":"var"},{"p":"wild"}],"p":"ctor"}}],"scrutinee":{"name":"p","t":"var"},"t":"match"},"kind":"fn","name":"use_first","params":["p"],"type":{"effects":[],"k":"fn","param_modes":["own"],"params":[{"k":"con","name":"Pair"}],"ret":{"k":"con","name":"Int"}}},{"body":{"args":[{"args":[{"args":[{"lit":{"kind":"int","value":1},"t":"lit"}],"fn":{"name":"build_pair","t":"var"},"t":"app"}],"fn":{"name":"use_first","t":"var"},"t":"app"}],"op":"io/print_int","t":"do"},"kind":"fn","name":"main","params":[],"type":{"effects":["IO"],"k":"fn","params":[],"ret":{"k":"con","name":"Unit"}}}],"imports":[],"name":"rc_own_param_partial_drop_leak","schema":"ailang/v0"}
|
||||
@@ -0,0 +1,65 @@
|
||||
; Iter 18g.tidy.fu2 RED-test fixture 1/3: dynamic-tag partial-drop
|
||||
; carve-out at fn-return (Iter B / lib.rs:1093).
|
||||
;
|
||||
; Shape:
|
||||
; - `Pair` carries two `Cell` fields (both pointer-typed).
|
||||
; - `Cell` carries two `Wrap` fields (both pointer-typed).
|
||||
; - `use_first` takes `(own Pair)` and pattern-binds the first
|
||||
; slot only, wildcarding the second.
|
||||
; - The bound slot's `Cell` is dec'd via `drop_<m>_Cell` at the
|
||||
; outer arm-close (moves[a] empty → field_drop_call routes
|
||||
; correctly).
|
||||
; - At fn return, `p`'s `consume_count == 0`, mode = Own, and
|
||||
; `moved_slots[p] = {0}`. Iter B's drop (lib.rs:1093) hits the
|
||||
; dynamic-tag carve-out: moves non-empty → shallow
|
||||
; `ailang_rc_dec(p)`. p's outer cell is freed; slot 1 (the
|
||||
; wildcarded second Cell + its two MkWrap children = 3 cells)
|
||||
; leaks.
|
||||
;
|
||||
; Pre-fix: live = 3 (one Cell + two MkWrap children).
|
||||
; Post-fix: live = 0.
|
||||
;
|
||||
; This fixture is the cleanest of the three because no inner match
|
||||
; is involved: the leak surfaces from Iter B alone.
|
||||
|
||||
(module rc_own_param_partial_drop_leak
|
||||
|
||||
(data Wrap
|
||||
(ctor MkWrap (con Int)))
|
||||
|
||||
(data Cell
|
||||
(ctor MkCell (con Wrap) (con Wrap)))
|
||||
|
||||
(data Pair
|
||||
(ctor MkPair (con Cell) (con Cell)))
|
||||
|
||||
(fn build_pair
|
||||
(type
|
||||
(fn-type
|
||||
(params (con Int))
|
||||
(ret (own (con Pair)))))
|
||||
(params n)
|
||||
(body
|
||||
(term-ctor Pair MkPair
|
||||
(term-ctor Cell MkCell
|
||||
(term-ctor Wrap MkWrap n)
|
||||
(term-ctor Wrap MkWrap 2))
|
||||
(term-ctor Cell MkCell
|
||||
(term-ctor Wrap MkWrap 3)
|
||||
(term-ctor Wrap MkWrap 4)))))
|
||||
|
||||
(fn use_first
|
||||
(type
|
||||
(fn-type
|
||||
(params (own (con Pair)))
|
||||
(ret (con Int))))
|
||||
(params p)
|
||||
(body
|
||||
(match p
|
||||
(case (pat-ctor MkPair a _) 1))))
|
||||
|
||||
(fn main
|
||||
(type (fn-type (params) (ret (con Unit)) (effects IO)))
|
||||
(params)
|
||||
(body
|
||||
(do io/print_int (app use_first (app build_pair 1))))))
|
||||
Reference in New Issue
Block a user