Hypothesis-driven measurement of "did monomorphisation actually
buy us performance?" on a 100M-iter LCG hot loop, AILang mono'd
code vs. four C reference variants (direct-inlinable, direct-
noinline, indirect-monomorphic, indirect-polymorphic). Zen 3,
clang -O2, median-of-15.
Headline: H1 supported, but the mechanism is inlining, not
dispatch shape. AILang mono = hand-C direct (1.000x). Indirect-
monomorphic = direct-noinline (1.000x) — saturating branch
predictor makes the indirect-call cost vanish on this hardware.
Inlining is the actual 3.31x win; polymorphic indirect adds
another 21% predictor-miss penalty.
DESIGN.md Decision 11 gains a rationale paragraph reframing mono
as inlining-enabler rather than indirect-call-eliminator, with
explicit pointer to the bench. JOURNAL entry records the full
methodology, ratios, limitations, and the side-effect mono-pass
env.globals-seeding bug surfaced while building the AILang fixture
(separate RED-first debug iter to follow).
Adds three end-to-end tests for the 22b.3 mono pass, each with a
matching one-property fixture under examples/. Defends mono-pass
invariants 3, 4, and 6 from docs/specs/2026-05-09-22-typeclasses.md
at the binary level (stdout-asserting, not AST-asserting):
- coherence_two_instances_pick_correct_body_at_runtime — class R
with instances at Int and Bool, both reachable from one main.
Stdout `11\n22` proves both mono fns are synthesised AND each
call site picks the matching body (no collapse).
- class_default_method_runs_and_prints_default_value — empty
instance body + class-level `default` clause must lower, link,
and print the default value (`99`). Promotes the existing
synthesise_mono_fn unit test to a binary-level guarantee.
- cross_module_class_method_runs_and_prints_correct_value —
class+instance in module A, called from module B's main. The
qualified-name rewrite + cross-module synth-def placement was
AST-tested in 22b.3.6; this pins down the emit-and-link path.
All 18 typeclass_22b3 tests pass; full workspace green; 3 bench
gates 0/0/0.
Quality-review follow-up to 89311ee:
- Revert `substitute_rigids` to private. Task 3 does not call it; the
visibility bump was forward-looking for Task 4 (synthesise_mono_fn).
Task 4 will re-bump it in the same diff that uses it.
- Promote `build_module_types` to pub(crate); drop the inline
reimplementation in `mono::build_workspace_env`. Removes drift risk
between the two paths.
- Replace defensive `unwrap_or_default()` on `build_module_globals`
with `expect(...)` naming the pre-condition. Silent default violated
CLAUDE.md "no defensive validation in internal code paths".
- Add a one-line drift-risk reminder at the top of `build_workspace_env`
flagging that it and `check_in_workspace` both populate `Env`.
Build clean; all 5 typeclass_22b3 tests green; no workspace regressions.