; Iter 17a — first fixture exercising the per-fn arena (alloca for ; non-escaping allocations). ; ; `peek` builds a Box(n) and immediately matches on it, returning ; a literal Int that does not depend on the box's payload. The ; box's value is fully consumed by the local match — it never ; flows to a fn arg, never becomes a ctor field, never returns. ; Iter 17a's escape analysis flags the `(term-ctor Box MkBox n)` ; allocation as non-escaping; codegen lowers it to LLVM `alloca` ; instead of `@GC_malloc`. Boehm's heap is not touched at all by ; this fn. ; ; `count` repeats the same pattern under recursion: each call to ; `count` builds a fresh Box(_), matches it, and returns either 0 ; or 1 + (count rest). Without the alloca optimisation, each ; recursive frame leaks a Box onto the GC heap until collection; ; with the optimisation, each frame's Box dies with its frame. ; ; Expected stdout (one per line): ; 42 — peek(0) returns the literal 42 from the only arm ; 42 — peek(123) ditto (the input value never reaches stdout) ; 5 — count(5) ; 0 — count(0) (module escape_local_demo (data Box (vars a) (ctor MkBox a)) (fn peek (doc "Build a Box(n), discard the payload, return 42. The Box is non-escaping.") (type (fn-type (params (con Int)) (ret (con Int)))) (params n) (body (let b (term-ctor Box MkBox n) (match b (case (pat-ctor MkBox _) 42))))) (fn count (doc "Recurse n times; each call builds a temporary Box, discards its payload via _, and returns 0 if n<=0, else 1 + count(n-1).") (type (fn-type (params (con Int)) (ret (con Int)))) (params n) (body (if (app <= n 0) 0 (let b (term-ctor Box MkBox n) (match b (case (pat-ctor MkBox _) (app + 1 (app count (app - n 1))))))))) (fn main (type (fn-type (params) (ret (con Unit)) (effects IO))) (params) (body (seq (do io/print_int (app peek 0)) (seq (do io/print_int (app peek 123)) (seq (do io/print_int (app count 5)) (do io/print_int (app count 0))))))))