Single iter shipping the post-milestone-24 follow-up named in
docs/specs/2026-05-14-retire-per-type-print-effects.md. After this
iter the only surviving direct-output effect-op is `io/print_str`;
all per-type print primitives are replaced by the polymorphic
`print` helper (prelude, iter 24.3).
Components:
- 92 examples/*.ail fixtures migrated (do io/print_<T> x) →
(app print x); 6 .prose.txt snapshots regenerated via `ail prose`.
- Four-site lockstep compiler deletion: crates/ailang-check/src/builtins.rs
(3 effect_ops.insert blocks + 3 list() rows + the
install_io_print_float_signature test + module + EffectOpSig
doc-comments); crates/ailang-codegen/src/lib.rs lower_app
(3 arms + lowers_io_print_float test); crates/ailang-codegen/src/synth.rs
builtin_effect_op_ret match-arm pattern. Dead `intern_string`
helper removed as a follow-up.
- Five incidental test-body migrations (ailang-check x2, ailang-core
spec_drift + design_schema_drift, ailang-surface/src/lex.rs,
ailang-prose/src/lib.rs round-trip test).
- Cat B test-harness patch: six IR-shape tests in
crates/ail/tests/e2e.rs gained a monomorphise_workspace call
before lower_workspace_with_alloc so they follow the same
pipeline as `ail build` (the home-rolled desugar+lift loop
stayed because mono's precondition is "already lifted"; mono
inserts after lift).
- Six doc-comment touch-ups (lex.rs module doc, parse.rs
diagnostic example, ail/src/main.rs x2, runtime/str.c %g anchor,
crates/ailang-core/specs/form_a.md surface-spec example).
- DESIGN.md seven-site sweep (Decision 11 example, Polymorphic
print past-tense, Heap-Str output sentence, effect-op
invocation comment, Float NaN paragraph re-anchored on
float_to_str, two "What is supported" lists).
- Three E2E test-comment polish + four IR-snapshot refresh + one
canonical-hash pin update (plan-unanticipated downstream
consequences of the corpus migration).
- bench/{check,compile_check,cross_lang}.py: all exit 0; no
ratification needed.
- Roadmap entry struck through; per-iter journal at
docs/journals/2026-05-14-iter-rpe.1.md.
Tests 564/0/3. cargo clippy and cargo doc: zero warnings.
Two upstream codegen bugs surfaced during the first BLOCKED
attempt and were fixed in separate iters before this retry:
- 1fb225e bugfix: mono cursor misalignment at poly-free-fn Var
with class-constrained Forall.
- feb9413 bugfix: print leak — propagate ret_mode through rigid
substitution + prelude Show.show ret_mode.
Known debt (carried forward for next /audit):
- Emitter.strings field is functionally dead post-iter (orphan
after intern_string removal); cycles over empty map harmlessly.
8.2 KiB
iter rpe.1 — Retire per-type print effect-ops
Date: 2026-05-14
Started from: 8b455bee4c
Status: DONE
Tasks completed: 10 of 10
Summary
The retire-per-type-print-effect-ops milestone shipped in one iter,
covering: a RED test pinning the deletion gate (crates/ailang-check/ tests/no_per_type_print_ops.rs); 92 examples/*.ail fixture
migrations from (do io/print_<T> x) → (app print x) plus 6
.prose.txt snapshot regenerations; four-site lockstep compiler
deletion (crates/ailang-check/src/builtins.rs × 5 regions,
crates/ailang-codegen/src/lib.rs::lower_app × 3 arms + 1 test,
crates/ailang-codegen/src/synth.rs::builtin_effect_op_ret × 1
match arm), with the dead intern_string helper removed as a
clean follow-up; six test-body migrations (ailang-check × 2,
ailang-core tests × 2, ailang-surface/src/lex.rs, ailang-prose);
the Cat B test-harness patch in crates/ail/tests/e2e.rs (6
IR-shape tests gained a monomorphise_workspace call so they
follow the same desugar → lift → mono → lower pipeline as ail build); 6 doc-comment touch-ups; 7-site DESIGN.md sweep; 3 E2E
test-comment polish; bench-run-and-no-ratification (all 3 scripts
exit 0 against existing baselines, drift envelope within
tolerance); plus IR-snapshot refresh (4 .ll files: sum,
list, max3, ws_main) and 1 canonical-hash pin update
(ordering_match::main) as plan-unanticipated downstream
consequences of the corpus migration. Tests 564/564 green
post-iter (was 563 at start; +1 from the new RED test).
Per-task notes
- iter rpe.1.1: RED test
no_per_type_print_ops.rs— assertsio/print_int|bool|floatabsent fromenv.effect_ops,io/print_strpresent. - iter rpe.1.2: 92
.ailcorpus migrated (89 single-line + 3 multi-line shapes); 5 ancillary comment touch-ups inborrow_own_demo,eq_demo,int_to_print_int_borrow,floats_1_newton_sqrt,floats_3_safe_divisionto satisfy the §H acceptance criteriongrep -rl ... examples/ == 0. - iter rpe.1.3: 6
.prose.txtsnapshots regenerated viacargo run -p ail -- prose. - iter rpe.1.4: builtins.rs deletes (3 effect_ops.insert blocks
- 3
list()rows + 1 unit test + 2 doc-comment swaps); codegen lib.rs deletes (3lower_apparms + 1 test); synth.rsbuiltin_effect_op_retmatch-arm narrowed.intern_stringremoved as orphan helper.Emitter.stringsfield left in place (still consumed by emit-time loop; cycles over empty map; non-functional). RED test flips GREEN.
- 3
- iter rpe.1.5: 6 test-body sites swapped to
io/print_str+Strargs; Cat B harness patch at all 6 e2e.rs sites insertsailang_check::monomorphise_workspace(&lifted_ws)between the existing lift loop and the lower call (the plan said "replace the desugar+lift loop with mono", but mono's precondition is "already lifted" — interpreting per spirit and matching theail buildpipeline). E2E 85/85 green after this step. - iter rpe.1.6: doc-comment swaps in 6 sites (
lex.rs,parse.rs,main.rs× 2,runtime/str.c,form_a.md). - iter rpe.1.7: DESIGN.md 7 sites swept; the iter's past-tense paragraph at line 1990-1992 anchors the milestone close.
- iter rpe.1.8: E2E test-comment polish at
eq_ord_e2e.rs× 2,floats_e2e.rs. Plus three plan-unanticipated sites:int_arg_to_effect_op_does_not_rc_tracktest assertions shifted from0/0/0to1/1/0(post-iterprint nfor Int now goes via Show Int → int_to_str → io/print_str, exactly one heap-Str slab cycle per spec §E); 4 IR snapshots refreshed viaUPDATE_SNAPSHOTS=1because the format-string globals (@.str_<module>_fmt_int_0/%lld\n) no longer appear in IR; 1 canonical-hash pin update forordering_match::main(body migration changes the def's canonical-form hash). - iter rpe.1.9: bench run — all 3 scripts exit 0 against
existing baselines. No ratification needed; spec §C4 (a)
anticipated possible latency drift but actual fell within
tolerance. Largest single deltas:
latency.explicit_at_rc. max_us+154.84% (a tail metric in the noise-class envelope observed across the last ~12 audits);throughput. bench_list_sum.bump_s+11.85% (REGRESSION, single bench, 3rd-iter-recurring; tracked as background noise per audit-24 lineage). Baseline pristine for the 13th consecutive audit-equivalent. - iter rpe.1.10: roadmap entry struck
[x]; journal written; stats written.
Concerns
- Cat B harness patch deviates from plan-literal: plan said
"replace the desugar+lift loop with monomorphise_workspace";
I inserted mono after the existing lift loop. Mono's
documented precondition is "already lifted", so the literal
reading would have broken mono's contract. Spirit-reading
was unambiguous: match
ail build's pipeline. - Plan Task 8 under-scoped: it named 3 E2E-comment sites,
but the corpus migration also forced (a) one assertion
update on
int_arg_to_effect_op_does_not_rc_track(allocs 0→1, frees 0→1, fully documented in spec §E), (b) IR snapshot refresh × 4, (c) one canonical-hash pin update. All three are mechanical downstream consequences of Task 2. Emitter.stringsfield incrates/ailang-codegen/src/lib.rsis now functionally dead (no caller afterintern_stringremoval) but the field + the per-module collection loop + the emit-time globals loop are all still in place. They cycle over an empty map and are harmless. Cleaning them up cascades throughemit_workspace_with_allocand parallelstr_literalsinfrastructure; out of scope for this iter.
Known debt
- The
int_arg_to_effect_op_does_not_rc_tracktest name now describes a pre-iter property; the body pins the post-iter shape (one Show-Int slab cycle). Renaming to e.g.print_int_one_slab_cyclewas floated and explicitly declined (plan Task 8 step 2 said "lean toward keep-name- update-doc"). If a future iter wants the rename, the test body is already a clean reference. - The
intern_stringhelper is gone butEmitter.stringsfield remains — see Concerns above.
Files touched
121 files in the working tree (1 new + 120 modified).
Code (compiler):
crates/ailang-check/src/builtins.rs(3 effect_ops + 3 list() rows + 1 test + 2 doc-comments deleted/swapped)crates/ailang-check/src/lib.rs(2 test-body sites swapped)crates/ailang-codegen/src/lib.rs(3 lower_app arms + 1 test +intern_stringhelper removed)crates/ailang-codegen/src/synth.rs(builtin_effect_op_ret match-arm narrowed)crates/ailang-surface/src/lex.rs(1 test-body + 1 module doc swap)crates/ailang-surface/src/parse.rs(1 diagnostic example swap)crates/ailang-prose/src/lib.rs(1 round-trip test-body swap)crates/ail/src/main.rs(2 doc-comments swapped)runtime/str.c(1 comment re-anchored on %g)
Tests:
crates/ailang-check/tests/no_per_type_print_ops.rs(NEW)crates/ailang-core/tests/spec_drift.rs(1 swap)crates/ailang-core/tests/design_schema_drift.rs(1 swap)crates/ailang-core/tests/hash_pin.rs(1 hash pin updated)crates/ail/tests/e2e.rs(6 Cat B harness patches + 1 assertion update + 1 doc-comment)crates/ail/tests/eq_ord_e2e.rs(2 doc-comments)crates/ail/tests/floats_e2e.rs(1 doc-comment)crates/ail/tests/snapshots/{sum,list,max3,ws_main}.ll(regenerated; format-string globals removed)
Docs:
docs/DESIGN.md(7 sites)docs/roadmap.md(P2 entry struck [x])crates/ailang-core/specs/form_a.md(1 example swap)
Examples: 92 .ail migrations + 6 .prose.txt regenerations + 5 ancillary comment touch-ups.
Verification
cargo test --workspace: 564 passed, 0 failed (was 563 at start; +1 = new RED test, no test counts shifted otherwise).cargo build --workspace: clean.python3 bench/check.pyexit 0 (drift within tolerance, no ratification).python3 bench/compile_check.pyexit 0.python3 bench/cross_lang.pyexit 0 (25/25 stable).grep -rl "io/print_int\|io/print_bool\|io/print_float" examples/returns 0 results (acceptance criterion met).grep -n "io/print_int\|io/print_bool\|io/print_float" crates/ailang-check/src/builtins.rsreturns 0 lines.crates/ailang-codegen/src/lib.rs::lower_apphas no arm for any of the three retired op names.
Stats
bench/orchestrator-stats/2026-05-14-iter-rpe.1.json