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.
This commit is contained in:
2026-05-14 01:38:41 +02:00
parent 05e4c04e3b
commit 1fb225ee25
5 changed files with 295 additions and 41 deletions
@@ -0,0 +1,95 @@
# 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