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:
@@ -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
|
||||
Reference in New Issue
Block a user