Iter 15c: empirical TCO + stack-budget validation at N=1000
Small empirical iter confirming the 14e tail-call story works dogfood-practical. New fixture std_list_stress.ailx builds a 1000-element List<Int> via recursive build, folds it with both fold_left (tail-marked in std_list) and fold_right (constructor- blocked, unmarked), prints both sums (500500 each). IR evidence at the monomorphised fold-recursion sites: fold_left__I_I: musttail call i64 @ail_std_list_fold_left__I_I(...) fold_right__I_I: plain call i64 @ail_std_list_fold_right__I_I(...) The tail: true marker on std_list.fold_left's recursive call survives monomorphisation through the __I_I specialisation. fold_right correctly emits a plain call (recursive call is second arg to f, not tail position). Empirical findings at N=1000: - fold_left runs in constant stack (musttail). - fold_right runs in ~1000 frames. No segfault; default 8MB Linux stack absorbs it comfortably. - build (recursive, unmarked) likewise fits. - End-to-end ~40ms wall (build + clang link); program <1ms. - Boehm GC handles 2 × 1000 Cons allocations without symptom. Tests 87 -> 88. Cumulative 30 e2e tests. cargo doc 0 warnings. Cumulative state, post-15c: 2 stdlib modules (std_maybe, std_list), 14 combinators, cross-module recursive ADTs working, TCO surviving monomorphisation, GC at 1000-element scale. Four compiler bugs surfaced + fixed in dogfood since 14a. Natural pause point. Queue for future iters: 15d (std_either), 15e (std_pair), 16a (nested patterns), 16b (local rec let), 17a (per-fn arena, Decision 9 future-iter). None block further stdlib work. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2423,6 +2423,95 @@ non-stdlib feature like nested patterns — at this scale of
|
||||
language, the case for adding a feature can be made directly
|
||||
from a stdlib annoyance.
|
||||
|
||||
## Iter 15c — empirical TCO + stack-budget validation at N=1000
|
||||
|
||||
Small empirical iter to confirm the TCO story works dogfood-
|
||||
practical, not just on the contrived `print_list` recursion
|
||||
that was the 14e regression target.
|
||||
|
||||
Fixture `examples/std_list_stress.ailx` builds a 1000-element
|
||||
`List<Int>` via the recursive `build` fn (also used in 14f's
|
||||
`gc_stress`), then folds it with both `std_list.fold_left` and
|
||||
`std_list.fold_right`, prints both sums. Both should be
|
||||
`500500` (1000 × 1001 / 2). E2E test asserts the two-line
|
||||
output.
|
||||
|
||||
**IR evidence at the monomorphised fold-recursion sites:**
|
||||
|
||||
```
|
||||
%v12 = musttail call i64 @ail_std_list_fold_left__I_I(...) ; tail
|
||||
%v7 = call i64 @ail_std_list_fold_right__I_I(...) ; non-tail
|
||||
```
|
||||
|
||||
The `tail: true` marker on `std_list.fold_left`'s recursive call
|
||||
survives monomorphisation through the `__I_I` specialisation —
|
||||
exactly what 14e's machinery was supposed to do. `fold_right`
|
||||
correctly emits a plain `call` (its recursive call is the second
|
||||
arg to `f`, not in tail position).
|
||||
|
||||
**Empirical findings.**
|
||||
- `fold_left` at N=1000 runs in constant stack depth (musttail).
|
||||
- `fold_right` at N=1000 runs with ~1000 stack frames. No
|
||||
segfault. LLVM's frames at `-O0` are small enough that the
|
||||
default 8MB Linux stack absorbs this comfortably.
|
||||
- `build` (also unmarked, recursive) likewise fits.
|
||||
- End-to-end binary execution time ~40ms wall, of which the
|
||||
bulk is build + clang link; the actual program runs in <1ms.
|
||||
- Two `build 1000` chains plus both folds = ~3000 frames total
|
||||
for the unmarked recursions; still fine.
|
||||
|
||||
**Tests: 88/88 (was 87, +1 e2e).** Cumulative 30 e2e tests.
|
||||
|
||||
**Cumulative state, post-15c.**
|
||||
|
||||
- Stdlib modules: 2 (`std_maybe`, `std_list`).
|
||||
- Combinators: 14.
|
||||
- Cross-module imports: type-side, ctor-side, fn-side, recursive
|
||||
ADT support — all working.
|
||||
- TCO: marker propagates through monomorphisation; `musttail`
|
||||
reaches LLVM at the right call sites.
|
||||
- GC: Boehm runs at 1000-element scale without intervention
|
||||
(no `GC_INIT()` needed, no symptom of conservative-scan
|
||||
over-retention at this scale).
|
||||
- Compiler bugs surfaced and fixed in dogfood since 14a: 4
|
||||
(14a monomorphisation, 14h cross-module ADT, 15b qualify-
|
||||
fields-codegen + non-literal-const-codegen).
|
||||
|
||||
**Natural pause point.** The language is now genuinely useful
|
||||
for small-to-medium programs. The 14b-through-15c arc was
|
||||
substantial: a textual surface, two language-completion iters
|
||||
(tail calls + GC), one revert (14d→14g), a cross-module ADT
|
||||
gap closure, two stdlib modules, and an empirical stress test
|
||||
to validate TCO. Work since the user's "Lege los": 9 commits.
|
||||
|
||||
**Queue for future iters.**
|
||||
|
||||
- **15d (optional)**: `std_either : Either e a = Left e | Right a`
|
||||
for error propagation. Small module (~5 combinators), would
|
||||
exercise the cross-module-with-2-type-vars import path
|
||||
(currently only Maybe<a> exercised at one type var).
|
||||
- **15e (optional)**: `std_pair : Pair a b = MkPair a b` plus
|
||||
`fst`, `snd`, `swap`, `map_first`, `map_second`. 2-type-var
|
||||
parameterised ADT, no recursion. Smaller dogfood than List.
|
||||
- **16a (language)**: nested patterns. Current pattern shape is
|
||||
flat (a `pat-ctor`'s fields are `pat-var | pat-wild | pat-lit`,
|
||||
not nested `pat-ctor`). This becomes painful when stdlib code
|
||||
wants to match `Cons h (Cons h2 _)` directly. Manageable today
|
||||
via nested `match` but would simplify several stdlib idioms.
|
||||
- **16b (language)**: local recursive `let`. Currently must hoist
|
||||
to top-level. Painful for stdlib helpers that are clearly
|
||||
internal to one combinator (e.g. an accumulator-loop inside
|
||||
`reverse`'s body).
|
||||
- **17a (codegen optimisation)**: per-fn arena for non-escaping
|
||||
ADT allocations (Decision 9's flagged future iter). Real win
|
||||
for `map`/`filter`-style combinators where the intermediate
|
||||
list is dropped immediately. Needs escape analysis; multi-iter
|
||||
design pass.
|
||||
|
||||
None of these block further stdlib work. They are quality-of-
|
||||
life improvements; the language is feature-complete enough that
|
||||
the stdlib can grow without them.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user