spec: unique binder names per fn — A2b leg of RawBuf drop-leak (refs #43)
The last open leg of the #43 owned-heap drop-leak cluster is the
UniquenessTable shadow-name collapse: the side-table keys by
(def_name, binder_name), so a fn that shadows a binder name collapses
every shadow onto one key. The outermost binding (records last, on pop)
overwrites the inner ones; codegen's scope-close drop gates then read
the collapsed consume_count for the innermost binding and suppress its
drop, leaking the owned slab.
Spec 0056 resolves this as a compiler-internal naming collision — NOT
the deeper, separate problem of persistent AST provenance back to the
authored form (certain to be needed eventually, deliberately deferred;
conflating the two is a category error). Fix: alpha-rename shadowing
binders during desugar (reusing the existing fresh-name machinery that
already mints $mp_N / $lr_N), making the (def, name) key injective
again. No consumer of the uniqueness table changes — they all key by
name, and the name becomes a per-fn injective identity. IR-neutral:
codegen names heap by fresh SSA, never by source binder name. The
pre-desugar hashed form is untouched, so module identity and hash-pin
tests are unaffected.
RED fixtures securing the binder-kind class (the user's gating
condition before proceeding to the uniform-across-kinds fix):
- Let-shadow: the three raw_buf_{int,float,bool}_shadow_rebind tests
(already in tree, committed f7f4c3b) — assert live == 0.
- Flat-pattern-shadow: a differential test plus its two fixtures
(flat_pat_shadow_leak.ail shadows the outer binder; _control.ail
alpha-renames it to a distinct name). The shadow must reach the
control's live count. Differential by design, to isolate the
shadow-collapse drop from unrelated baseline drop gaps out of
#43's scope.
Grounding-check PASS; ail-check parse-gate green on every fenced block.
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
(module flat_pat_shadow_leak
|
||||
(data IntList
|
||||
(ctor Nil)
|
||||
(ctor Cons (con Int) (con IntList)))
|
||||
(data Box (vars a)
|
||||
(ctor MkBox a))
|
||||
(fn blen
|
||||
(doc "borrow-only length over a borrowed IntList")
|
||||
(type (fn-type (params (borrow (con IntList))) (ret (con Int))))
|
||||
(params xs)
|
||||
(body (match xs
|
||||
(case (pat-ctor Nil) 0)
|
||||
(case (pat-ctor Cons h t) (app + 1 (app blen t))))))
|
||||
(fn csum
|
||||
(doc "consuming sum over an IntList")
|
||||
(type (fn-type (params (con IntList)) (ret (con Int))))
|
||||
(params xs)
|
||||
(body (match xs
|
||||
(case (pat-ctor Nil) 0)
|
||||
(case (pat-ctor Cons h t) (app + h (app csum t))))))
|
||||
(fn main
|
||||
(doc "A2b completeness fixture (refs #43): a flat-match pattern-binder `x` shadows the outer let `x`. The inner `x` (Box payload, a heap IntList) is borrow-only (consume_count 0) and must be dropped at arm close; the outer `x` is consumed once by csum (count 1). Under the (def,name) uniqueness-table collision the arm-close drop gate (match_lower.rs) reads the collapsed outer count 1 and suppresses the inner payload's drop, leaking 2 cells. Pairs with flat_pat_shadow_control.ail (same program, inner binder alpha-renamed to `y`); the live-count delta is exactly the suppressed drop. GREEN once binder names are made unique per fn at desugar.")
|
||||
(type (fn-type (params) (ret (con Unit)) (effects IO)))
|
||||
(params)
|
||||
(body
|
||||
(let x (term-ctor IntList Cons 1 (term-ctor IntList Nil))
|
||||
(seq
|
||||
(match (term-ctor Box MkBox (term-ctor IntList Cons 2 (term-ctor IntList Nil)))
|
||||
(case (pat-ctor MkBox x)
|
||||
(app print (app blen x))))
|
||||
(app print (app csum x)))))))
|
||||
Reference in New Issue
Block a user