3b3c8c4f07
Post-ship field test of the shipped `series` library extension as a downstream Form-A author working from the public surface only. Five fixtures under examples/fieldtest/ across the construction/auto-import, ring-buffer/financial-indexing, len/total_count bookkeeping, param-in error-quality, and operator-naming axes; spec at docs/specs/0067-fieldtest-series-step1.md. Wins (carry-on): auto-import works with no `(import series)`; the financial index (0 = newest) maps directly onto the bounded-buffer mental model and stays correct after multiple wraps; the param-in diagnostic for a disallowed element type names the type, the type-variable, the qualified type, and the allowed set. Findings routed: - [bug] An owned ADT threaded through tail-recursion whose base case neither returns nor consumes it is never dropped — a general drop-soundness leak (a plain `Box` control with no Series/RawBuf also leaks). Series surfaces it heavily on the whitepaper's headline streaming pattern, and the committed reference examples/series_sma.ail itself leaks (live=8). Minimal repro: series-step1_5 (live=4). - [friction] `ail describe ... Series.push` (the canonical type-scoped form) reports module-not-found; only `series.push` / bare `push` resolve, and no command lists a type's op set. - [spec_gap] referencing a kernel base type (`RawBuf`) from a user ADT ctor field requires full qualification (`raw_buf.RawBuf`) while bare `Series` works in type-position via auto-import — the asymmetry is undocumented. The fixtures that leak still produce correct output; the leak is a separate observable under AILANG_RC_STATS and an orthogonal, pre-existing bug, not a Series defect. refs #61
56 lines
2.2 KiB
Plaintext
56 lines
2.2 KiB
Plaintext
; Fieldtest series-step1.5 (LEAK REPRO) — owned Series threaded through a
|
|
; tail-recursive stream loop is not dropped, leaking the RawBuf slab.
|
|
;
|
|
; This is the minimal form of a leak that surfaces on the CANONICAL
|
|
; streaming pattern the whitepaper itself uses (design/models/0007 §Series,
|
|
; the SMA worked example, and examples/series_sma.ail): a `run`-style fn
|
|
; takes (own (con Series ...)) plus a worklist, and on each element does
|
|
; (Series.push s v) then tail-recurses; the base case returns Unit.
|
|
;
|
|
; Under AILANG_RC_STATS the program reports `live > 0` (here live=4):
|
|
; the intermediate Series values produced by push, and/or the final Series
|
|
; alive at the base-case arm, are never freed. The committed reference
|
|
; example examples/series_sma.ail exhibits the same leak (live=8), as do
|
|
; series-step1_1_rolling_max.ail (live=8) and
|
|
; series-step1_3_warmup_rolling_sum.ail (live=8).
|
|
;
|
|
; The leak is NOT present when the owned Series is RETURNED from the
|
|
; recursion (ret own Series) and dropped at the final caller's scope close
|
|
; (see series-step1_2_last_n_events.ail, which is live=0): the trigger is
|
|
; an owned Series reaching a base case whose return type is not Series and
|
|
; that does not explicitly consume it.
|
|
;
|
|
; This fixture is the smallest reproduction: push two Ints into a size-3
|
|
; Series via tail recursion, base case prints "done".
|
|
;
|
|
; Expected stdout: "done".
|
|
; Observed under AILANG_RC_STATS: `ailang_rc_stats: allocs=7 frees=3 live=4`.
|
|
|
|
(module series-step1_5_drop_leak_repro
|
|
|
|
(data IntList
|
|
(ctor INil)
|
|
(ctor ICons (con Int) (con IntList)))
|
|
|
|
(fn run
|
|
(type (fn-type
|
|
(params (own (con Series (con Int))) (own (con IntList)))
|
|
(ret (own (con Unit))) (effects IO)))
|
|
(params s input)
|
|
(body
|
|
(match input
|
|
(case (pat-ctor INil) (do io/print_str "done\n"))
|
|
(case (pat-ctor ICons v rest)
|
|
(let s_new (app Series.push s v)
|
|
(tail-app run s_new rest))))))
|
|
|
|
(fn main
|
|
(type (fn-type (params) (ret (own (con Unit))) (effects IO)))
|
|
(params)
|
|
(body
|
|
(let s (new Series (con Int) 3)
|
|
(app run s
|
|
(term-ctor IntList ICons 1
|
|
(term-ctor IntList ICons 2
|
|
(term-ctor IntList INil))))))))
|