d64031c234
Decision 8 ships. Term::App and Term::Do gain tail: bool with serde-default false and skip-when-false serialisation. New typecheck pass verify_tail_positions enforces tail-position rules (Scheme-style propagation through match arms, seq.rhs, let body, lam body). Codegen emits musttail call for marked App calls. Hash invariance verified: only the two migrated print_list defs (list_map_poly.print_list, sort.print_list) changed hashes; all other defs across all 18 fixtures kept bit-identical hashes — confirms the skip-when-false serialisation rule works. Tests 76 -> 79: tail_call_in_non_tail_position_is_rejected, tail_call_in_tail_position_is_accepted, plus an IR-grep e2e test asserting that print_list's recursive call site emits musttail in the lowered IR. Existing 25 e2e tests unchanged in behaviour (map -> [2,3,4], sort -> sorted list). IR evidence at the recursive site: %v7 = musttail call i8 @ail_list_map_poly_print_list(ptr %v6) ret i8 %v7 Two deviations called out in the implementer report and JOURNAL: 1. tail-do uses tail call, not musttail. Cross-type return (runtime helpers return i32, AILang Unit is i8) would have LLVM reject musttail. Path is implemented but not exercised by any current fixture; proper fix is runtime-helper signature change, punted. 2. block_terminated flag in codegen so tail-call emit (musttail call + ret) doesn't get a duplicate trailing ret from surrounding code (match-arm phi, fn-body, lambda thunk). Internal plumbing; required for IR well-formedness. Form (A) productions now at ~30, exactly the constraint-1 budget. Future surface additions need to retire something or explicit-budget-rebalance in DESIGN.md. GC notes from implementer survey land in JOURNAL: - Allocations cluster in lower_ctor; every term-ctor does malloc(8+8n). - Tail recursion does not reduce alloc pressure, only stack. For map-style ctor-blocked recursions, allocation IS the bottleneck. - Per-fn arena is sound only when fn return type contains no boxed ADT. Most current fixtures violate this. Plan 14f: Boehm conservative GC (GC_malloc, -lgc) as a first cut. Single-iter integration, no AST/schema change. Stress test: build a 100k Cons list, observe RSS doesn't blow up. After 14f the language is feature-complete enough for stdlib work (15a). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
63 lines
1.6 KiB
Plaintext
63 lines
1.6 KiB
Plaintext
; Iter 14b design exhibit, finalised in Iter 14c.
|
|
; Hash-equivalent to list_map_poly.ail.json (round-trip-tested).
|
|
|
|
(module list_map_poly
|
|
|
|
(data List (vars a)
|
|
(doc "Polymorphic singly-linked list.")
|
|
(ctor Nil)
|
|
(ctor Cons a (con List a)))
|
|
|
|
(fn inc
|
|
(doc "Add 1 to an Int.")
|
|
(type (fn-type (params (con Int)) (ret (con Int))))
|
|
(params x)
|
|
(body (app + x 1)))
|
|
|
|
(fn map
|
|
(doc "Polymorphic map: apply f to every element, recursing on the tail.")
|
|
(type
|
|
(forall (vars a b)
|
|
(fn-type
|
|
(params (fn-type (params a) (ret b))
|
|
(con List a))
|
|
(ret (con List b)))))
|
|
(params f xs)
|
|
(body
|
|
(match xs
|
|
(case (pat-ctor Nil)
|
|
(term-ctor List Nil))
|
|
(case (pat-ctor Cons h t)
|
|
(term-ctor List Cons
|
|
(app f h)
|
|
(app map f t))))))
|
|
|
|
(fn print_list
|
|
(doc "Print each Int on its own line.")
|
|
(type
|
|
(fn-type
|
|
(params (con List (con Int)))
|
|
(ret (con Unit))
|
|
(effects IO)))
|
|
(params xs)
|
|
(body
|
|
(match xs
|
|
(case (pat-ctor Nil)
|
|
(lit-unit))
|
|
(case (pat-ctor Cons h t)
|
|
(seq
|
|
(do io/print_int h)
|
|
(tail-app print_list t))))))
|
|
|
|
(fn main
|
|
(doc "Map inc over [1,2,3] via polymorphic List, then print: 2,3,4.")
|
|
(type (fn-type (params) (ret (con Unit)) (effects IO)))
|
|
(params)
|
|
(body
|
|
(app print_list
|
|
(app map inc
|
|
(term-ctor List Cons 1
|
|
(term-ctor List Cons 2
|
|
(term-ctor List Cons 3
|
|
(term-ctor List Nil)))))))))
|