Files
AILang/docs/journals/2026-05-14-iter-bugfix-mono-cursor-print-with-class-method-arg.md
T
Brummel 1fb225ee25 bugfix: mono cursor misalignment at poly-free-fn Var with class-constrained Forall
When `synth` visits a `Term::Var v` where v is a polymorphic free fn
whose `Type::Forall` carries class constraints (e.g. `print : forall a.
Show a => ...`, `lt`/`le`/`gt`/`ge : forall a. Ord a => ...`), it
pushes BOTH a class `ResidualConstraint` for each constraint AND a
`FreeFnCall` observation at the same Var site. The mono walkers
(`interleave_slots` collection-side and `rewrite_mono_calls`
rewrite-side) consumed only ONE slot per such Var, leaving the
class-residual cursor misaligned for every subsequent class-method
call in the same body. The next `eq`/`compare` Var in the body then
consumed the leftover `Show T` / `Ord T` slot and was mis-rewritten —
codegen reported `call prelude.show__Bool arg type mismatch: expected
i1, got i64` (or the Int dual).

Fix: new `poly_free_fn_constraint_counts_for_module` registry in
mono.rs (sibling to `poly_free_fn_names_for_module`) maps each
synth-visible poly-free-fn name to its declared constraint count.
Threaded into `collect_residuals_ordered`, `interleave_slots`, and
`rewrite_mono_calls`. Both walkers now advance the cursor by 1 + N
(FreeFn slot + N class-residual fillers) at every poly-free-fn Var.

RED test `print_with_class_method_arg_does_not_misalign_mono_cursor`
(crates/ail/tests/show_print_e2e.rs) + fixture
examples/print_eq_arg_repro.ail pin the bug — body `(app print (app
eq 1 2))` previously crashed at codegen, now prints "false".

Bug surfaced 2026-05-14 during the rpe.1 BLOCKED orchestrator run;
existed in latent form since iter 24.3 (the poly-free-fn-with-class-
constraint synth shape was new there). No schema / codegen / DESIGN.md
changes. cargo test --workspace: 564 / 0 / 3.
2026-05-14 01:38:41 +02:00

96 lines
4.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# iter bugfix-mono-cursor-print-with-class-method-arg — `(app print (app eq …))` no longer rewrites the inner class-method to a stale `Show T` symbol
**Date:** 2026-05-14
**Started from:** 05e4c04e3ba74f3382216ff3ea285e3fb1e3a45d
**Status:** DONE
**Tasks completed:** 1 of 1
## Summary
The iter 24.3 `synth` Var-arm pushes BOTH a class `ResidualConstraint`
for each declared constraint AND a `FreeFnCall` observation at the
same poly-free-fn `Var` site (`crates/ailang-check/src/lib.rs` ≈ 2908
2966). The two mono walkers in `crates/ailang-check/src/mono.rs`,
however, were each consuming exactly one slot per `Var`: at a
poly-free-fn `Var` they popped one `free_fn_slot` and zero
`class_slot`s. Every constraint on a poly-free-fn-with-constraints
left a leftover class slot, so the next class-method `Var` in the
same body consumed the wrong slot — the residual that belonged to
`print`'s `Show T` constraint got cursor-aligned with the inner `eq`
`Var`, and codegen reported `call prelude.show__Bool arg type
mismatch: expected i1, got i64`.
The fix advances both walkers' cursors by `1 + N` at a poly-free-fn
`Var` whose source `Type::Forall` carries `N` class constraints. A
new sibling helper `poly_free_fn_constraint_counts_for_module`
mirrors `poly_free_fn_names_for_module`'s three-source spelling
contract (same-module / implicit-import bare / dot-qualified
`alias.name`) and produces a `BTreeMap<String, usize>` from synth-
visible name to declared-constraint count. The map is threaded into
the rewrite-phase loop alongside `poly_free_fns` and passed to both
`rewrite_mono_calls` and `interleave_slots`.
In `interleave_slots`, a poly-free-fn `Var` now pushes the `FreeFn`
slot first (this is what the rewrite walker reads for the rename),
then `N` filler entries drawn in order from `class_slots` (consumed
only to keep `class_cur` aligned). In `rewrite_mono_calls`, the
`Var`-arm captures `n_filler` from the count map BEFORE the rename
mutates `name` (lookup keys are the synth-visible spelling), reads
the slot at `*cursor` as before, then advances by `1 + n_filler`.
Class-method `Var`s stay at advance-by-one (synth pushes exactly one
residual and no `FreeFnCall`).
## Per-task notes
- task 1: write/verify the cursor-alignment fix.
- Reproduced RED at `print_with_class_method_arg_does_not_misalign_mono_cursor`
with the exact failure mode stated in the carrier:
`call prelude.show__Bool arg type mismatch: expected i1, got i64`.
- Added `poly_free_fn_constraint_counts_for_module` helper
immediately after `poly_free_fn_names_for_module`; identical
name-source contract, returning constraint counts instead of a
presence set.
- Threaded `poly_free_fn_ccounts: &BTreeMap<String, usize>` into
`collect_residuals_ordered`, `interleave_slots`, and
`rewrite_mono_calls` (signatures + every recursive call site).
- `interleave_slots::Term::Var` poly-free-fn branch: after pushing
the `FreeFn` slot, drain `N` entries from `class_slots` and push
them as fillers in `out`, advancing `class_cur` by `N`.
- `rewrite_mono_calls::Term::Var`: compute `n_filler` from the
count map before the rename mutation, then `*cursor += 1 +
n_filler`. Class-method `Var`s are guarded with `if
is_poly_free_fn { … } else { 0 }`.
- RED `print_with_class_method_arg_does_not_misalign_mono_cursor`
flips to GREEN with stdout `false` as predicted by the fixture
docstring.
- Full `cargo test --workspace` passes 564 / 564 (= 563 baseline
from `clippy-sweep` + the 1 new mini-mode RED). Zero
regressions across any of the existing show / mono / typeclass
test suites.
## Concerns
(none)
## Known debt
(none — the constraint set is a single new sibling helper plus a
new threaded argument; the two-walker symmetry remains the invariant
the existing comments already document.)
## Files touched
- `crates/ailang-check/src/mono.rs` — the fix (helper + two walker
signatures + Var-arm cursor advancement + recursive-call
threading).
- `crates/ail/tests/show_print_e2e.rs` — RED pin
`print_with_class_method_arg_does_not_misalign_mono_cursor`
(committed by `/debug` upstream of this iter; flips RED → GREEN
here).
- `examples/print_eq_arg_repro.ail` — RED fixture (committed by
`/debug` upstream; consumed by the pin).
## Stats
bench/orchestrator-stats/2026-05-14-iter-bugfix-mono-cursor-print-with-class-method-arg.json