; Iter 18e fixture: `(drop-iterative)` opt-in annotation drives the ; codegen to emit `drop__IntList` with a worklist body in place ; of the recursive cascade. Without the annotation, freeing a long ; list overflows Linux's default 8MB stack at ~1M cells (one frame ; per recursive `drop__IntList(tail)` call). With it, the chain ; pops through the heap-allocated worklist in O(N) time and O(1) ; stack. ; ; The fixture builds a 100,000-cell IntList tail-recursively, sums ; it into 4_999_950_000 (= N*(N-1)/2 for N=100_000), then `main` ; returns. At process exit the build's `xs` binder is consumed by ; sum_acc (via tail-call), so the iterative drop fires inside ; sum_acc's `Cons` arm — specifically, on every recursive step the ; tail t is bound, then t is consumed by the next iteration's ; tail-app sum_acc, leaving no live cells at termination. ; ; Wait — under the actual emission shape: sum_acc's xs param is ; (own (con IntList)); 18d.4 emits an Own-param drop at fn return. ; On the inductive Cons arm, the fn returns via tail-call into the ; next iteration (musttail), and the Own-param dec on xs at the ; outer fn would normally fire — but tail-call elision means the ; outer frame is gone before any post-tail-call code can run. The ; canonical case for the iterative-drop test is therefore the ; let-close-drop on `xs` in `main` (or the outer-let's drop after ; sum's tail returns). For 18e the load-bearing property is "long ; chains drop without overflowing the stack"; that fires whenever ; ANY drop call against the head IntList runs end-to-end. ; ; To force the issue: `main` builds the list, sums it, prints the ; sum, AND then `let xs = build n in let _ = sum xs in xs` does NOT ; exist as a pattern in AILang. We instead route the list through a ; `head` fn that takes (own IntList) and returns the head Int — ; which transfers ownership and forces the Own-param drop on xs at ; head's return. Because head's body (a match returning Int) cannot ; be tail-called, the drop is emitted in head's epilogue and runs ; in full before head returns to main. ; ; Expected stdout: `1` (the head of [1, 2, ..., 1_000_000]). ; Why 1M and not 100K: at 100K the recursive cascade fits in the 8MB ; default Linux stack (~64B/frame ≈ 6.4MB at 100K), so the test would ; "pass" even without the iterative drop — false-negative on the ; whole point of 18e. At 1M it overflows clean (SIGSEGV in the ; recursive variant; clean exit-0 in the iterative variant). (module rc_drop_iterative_long_list (data IntList (ctor INil) (ctor ICons (con Int) (con IntList)) (drop-iterative)) (fn cons_n_acc (doc "Tail-recursive list builder. acc-prepended.") (type (fn-type (params (con Int) (con IntList)) (ret (con IntList)))) (params n acc) (body (if (app == n 0) acc (tail-app cons_n_acc (app - n 1) (term-ctor IntList ICons n acc))))) (fn cons_n (doc "Build [1, 2, ..., n] :: IntList. Order is reverse of build but immaterial for head/sum.") (type (fn-type (params (con Int)) (ret (con IntList)))) (params n) (body (app cons_n_acc n (term-ctor IntList INil)))) (fn head_or_zero (doc "Take ownership of an IntList; return its head if ICons, else 0. The (own ...) signature signals 18d.4 to emit an Own-param drop at fn return — which under the (drop-iterative) annotation is the iterative-worklist body, freeing the entire chain via the heap-allocated worklist instead of recursive cascade.") (type (fn-type (params (own (con IntList))) (ret (con Int)))) (params xs) (body (match xs (case (pat-ctor INil) 0) (case (pat-ctor ICons h t) h)))) (fn main (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) (body (do io/print_int (app head_or_zero (app cons_n 1000000))))))