Commit Graph

239 Commits

Author SHA1 Message Date
Brummel a1692a4859 iter 23.4: mono-pass unification — class methods + polymorphic free fns in one fixpoint
Restructure ailang-check/src/mono.rs::monomorphise_workspace into a
single specialiser over every Type::Forall-quantified Def::Fn, covering
both class-method residuals AND polymorphic free-fn call sites in one
fixpoint pass. Remove the codegen-time specialiser (lower_polymorphic_call,
module_polymorphic_fns, mono_queue, mono_emitted, emit_specialised_fn,
plus the now-dead helpers apply_subst_to_term, descriptor_for_subst,
type_descriptor) entirely. Codegen sees only monomorphic Def::Fns after
this iter — no polymorphic call sites, no codegen-time queue.

Architecture changes:
- MonoTarget: struct → enum with ClassMethod / FreeFn variants; dedup
  keys are guaranteed-disjoint via 'class'/'free' first component.
- collect_mono_targets: new FreeFnCall channel through synth (cleaner
  than the plan's separate-walker proposal — one less data flow,
  substitution tracking comes 'for free' via fresh metavars +
  post-synth subst.apply). Plan Step 4.4 (polymorphic_free_fns env
  field) consequently obsolete.
- synthesise_mono_fn_for_free_fn: new free-fn entry point; substitutes
  rigid vars in BOTH the type AND the body (body substitution required
  because free-fn bodies carry inner Lams whose param_tys reference
  outer Forall vars — adapted from plan's 'body unchanged' sketch).
- mono_symbol_n: N-ary extension of mono_symbol; bit-stable for the
  single-type-var case (hash-stability pin Task 1 fires zero times
  through all eight refactor tasks).
- rewrite_class_method_calls → rewrite_mono_calls; interleaved-slot
  collector preserves the positional-cursor invariant across both
  channels.
- workspace_has_typeclasses → workspace_has_specialisable_targets
  (principled correctness; old predicate happened to already be true
  for every user workspace because the prelude is auto-injected).

Codegen cleanup:
- Two call sites of lower_polymorphic_call deleted (codegen/src/lib.rs
  lines ~1939-1945, ~1965-1973).
- lower_polymorphic_call body, module_polymorphic_fns field +
  population + Emitter wiring, mono_queue / mono_emitted, drain loop,
  emit_specialised_fn — all removed.
- is_static_callee collapsed to module_user_fns-only.
- subst.rs apply_subst_to_term + descriptor_for_subst removed; synth.rs
  type_descriptor removed (all dead post-removal).
- ail emit-ir command pipeline gains lift_letrecs + monomorphise_workspace
  pre-passes (pre-iter-23.4 the codegen-time poly path was masking
  this dependency — emit-ir is now consistent with build).
- Net codegen reduction: -285 lines lib.rs, -93 lines subst.rs.

Tests:
- mono_hash_stability.rs (new): regression pin for six primitive Eq/Ord
  mono-symbol body hashes; stayed bit-stable through all eight refactor
  tasks.
- mono_unification.rs (new): six tests covering variant-key disjointness,
  free-fn target emission, free-fn synthesis with rigid substitution,
  rewrite walker on free fns, identity for poly-free-fn-only workspaces,
  end-to-end cmp_max_smoke runtime correctness.
- typeclass_22b3.rs: three test sites updated to MonoTarget::ClassMethod
  enum form.
- 73 e2e + 18 typeclass + 81 codegen tests all green workspace-wide.

DESIGN.md §'Monomorphisation (post-typecheck, pre-codegen)' amended:
two-arm fixpoint described, disjoint-keyed dedup, cursor-aligned
walker, removed codegen-time path.

Bench check (all exit 0, 112 metrics stable): bench/check.py +
compile_check.py + cross_lang.py.

Plan parent: docs/plans/2026-05-11-iter-23.4.md (fab1685).
Spec parent: docs/specs/2026-05-11-23-eq-ord-prelude.md (841d65d).
Per-iter journal documents two adapted deviations from the plan
(synth-channel vs separate walker; body substitution; emit-ir fix);
no spec defects.
2026-05-11 23:59:45 +02:00
Brummel 923dd8c02f iter 23.4-prep.2: check — bare-name fall-through to implicit-imported free fns 2026-05-11 22:01:52 +02:00
Brummel 0caaced7e8 iter 23.4-prep.1: linearity — register Def::Class method types in globals 2026-05-11 22:01:52 +02:00
Brummel ba48349052 iter ct.4.4: compare_primitives_smoke E2E fixture (closes iter 23.3 Task 4) 2026-05-11 10:09:48 +02:00
Brummel b651b1e8fc iter ct.4.3-tidy: drop dated prefixes from new prose comments 2026-05-11 10:01:29 +02:00
Brummel 06bc5f017c iter ct.4.3: prose qualifier-trim-on-print + ordering_match snapshot 2026-05-11 09:58:22 +02:00
Brummel 1d039b782e iter ct.4.2: pin canonical-form hashes for migrated fixtures + unmigrated sanity 2026-05-11 09:51:19 +02:00
Brummel 666c78468f iter ct.3.3: retire obsolete ambiguous-type paragraph in migrate-canonical-types docstring 2026-05-11 09:39:47 +02:00
Brummel 91498016b2 iter ct.3.2: mono overlay narrowed to env.types only; rename to apply_per_module_types_overlay 2026-05-11 09:37:07 +02:00
Brummel 05d0cce5c7 iter ct.3.1-tidy: drop dated comment prefix 2026-05-11 09:34:17 +02:00
Brummel a8a58cc41e iter ct.3.1: codegen lookup_ctor_by_type direct lookup; delete imports-fallback 2026-05-11 09:31:48 +02:00
Brummel 97bb5e71fd iter ct.2.3: Term::Ctor synth direct lookup; delete imports-fallback + dead AmbiguousType 2026-05-11 09:21:36 +02:00
Brummel edb694fdce iter ct.2.2-doctidy: refresh stale doc-comments on UnknownCtorInPattern and mono_xmod_ctor_pattern 2026-05-11 09:16:20 +02:00
Brummel 0044467425 iter ct.2.2-tidy: remove unused Bag fixture from cross_module_ws helper 2026-05-11 09:14:17 +02:00
Brummel 0ca5b3d567 iter ct.2.2-followup: delete dead AmbiguousCtor variant + refresh stale comment + fold redundant re-match
Quality-review follow-up on ct.2.2 (b3c3b60).

- Delete CheckError::AmbiguousCtor: its sole producer (the Pattern::Ctor
  imports-fallback) was removed in ct.2.2, leaving the variant, the
  code()/ctx() arms, and the diagnostic.rs doc-comment as dead surface.
  Workspace grep confirms no remaining Rust consumers. AmbiguousType
  stays (it still has a Term::Ctor producer, scheduled for symmetric
  removal in ct.2.3).
- Refresh the per-module-overlay rationale comment in check_in_workspace:
  it still cited the now-defunct Pattern::Ctor local-first / imports-
  fallback path. The overlay's current job is duplicate detection plus
  bare-name lookups for local Term::Ctor synth.
- Fold the awkward re-match in type_check_pattern: the first match on
  expected now binds scrutinee_args alongside resolved_type_name /
  resolved_td / resolved_owning_module, deleting the second match +
  unreachable!(). Collapse the any-then-expect ctor lookup into a single
  find().ok_or(UnknownCtorInPattern).

Build green, 449+ tests green across the workspace.
2026-05-11 09:13:12 +02:00
Brummel b3c3b6088e iter ct.2.2: Pattern::Ctor type-driven lookup; delete imports-fallback 2026-05-11 09:07:59 +02:00
Brummel 78ccbcee9c iter ct.2.1: qualify_local_types on class-method channel + Forall constraints recursion 2026-05-11 09:00:06 +02:00
Brummel c1783e01bc iter ct.1.8: E2E coverage for canonical-form diagnostic surface + happy path
Five tests in crates/ail/tests/ct1_check_cli.rs, each pinning one named property:

- check_json_emits_bare_cross_module_type_ref: --json mode surfaces
  `bare-cross-module-type-ref` with non-zero exit on the
  test_ct1_bare_xmod_rejected fixture.
- check_json_emits_bad_cross_module_type_ref: same for
  `bad-cross-module-type-ref`.
- check_json_emits_qualified_class_name: same for `qualified-class-name`.
- check_human_mode_emits_actionable_message_to_stderr: human (non-JSON)
  mode prints both the offending type name and the migration-command
  hint to stderr — this path goes through the anyhow/thiserror Display
  impl, not workspace_error_to_diagnostic, so it needs its own pin.
- check_ordering_match_post_migration_is_clean: post-migration
  ordering_match.ail.json typechecks via the CLI with zero diagnostics,
  guarding against revert of the ct.1.5 migration or a regression in
  Registry::normalize_type_for_lookup that would reject the canonical form.
2026-05-11 01:50:29 +02:00
Brummel f22f88b547 iter ct.1.7: on-disk fixture tests for canonical-form diagnostics 2026-05-11 01:46:58 +02:00
Brummel 727d2c13ec iter ct.1.6: wire validator into load_workspace; refresh kind-mismatch test 2026-05-11 01:42:45 +02:00
Brummel 5e653ff536 iter ct.1.5a-followup: forall-constraints recursion + docstring + drop iteration-tag comments
Three Minor items from the ct.1.5a quality review:

1. normalize_type_for_registry now recurses into
   Type::Forall.constraints[].type_, symmetric to walk_type. Closes a
   latent gap: a Forall with bare cross-module constraint types reaching
   the registry would have produced a key disagreeing with the qualified
   form. RED test: ct1_5a_normalize_recurses_into_forall_constraints.

2. Documented the bare-name limitation on Registry::type_def_module:
   two modules each defining 'type Foo' collide at insert; the proper
   fix (re-key as (owning_module, bare_name)) is out of ct.1's scope.

3. Removed the four iter-tag explanatory paragraphs at the
   normalize_type_for_lookup call sites (check/lib.rs check_fn,
   mono.rs three sites). Replaced each with a one-line pointer to the
   helper's docstring per project comment policy.
2026-05-11 01:39:26 +02:00
Brummel 7872ccfc23 iter ct.1.5a: registry-side normalize-before-hash 2026-05-11 01:32:21 +02:00
Brummel a73f7d3213 iter ct.1.4-followup: type-con rewrite test + doc clarity 2026-05-11 01:21:18 +02:00
Brummel d45977f9bb iter ct.1.4: dev-only ail migrate-canonical-types subcommand 2026-05-11 01:16:55 +02:00
Brummel 219908ed54 iter ct.1.3-followup: test for Constraint.class in ClassDef-method Forall 2026-05-11 01:11:47 +02:00
Brummel 9e2e84332f iter ct.1.3: validator rejects qualified class-name fields 2026-05-11 01:08:50 +02:00
Brummel 3723495d8a iter ct.1.2: validator extends to Term::Ctor.type_name 2026-05-11 01:04:39 +02:00
Brummel 000b84dda7 iter ct.1.1: validator scaffolding + Type::Con canonical-form rules
Adds BareCrossModuleTypeRef / BadCrossModuleTypeRef / QualifiedClassName
to WorkspaceLoadError plus the validate_canonical_type_names function
that enforces the canonical-form rule on every Type::Con name reachable
from any def (including Term::Lam.param_tys / .ret_ty and Term::LetRec.ty).
Term::Ctor.type_name and class-name fields land in Tasks 2 + 3; the
validator is not yet wired into load_workspace (Task 6). Seven RED-first
unit tests cover the cases enumerated in spec section Testing strategy
plus a Lam-embedded-type smuggle-prevention test.

main.rs gains diagnostic arms for the three new variants to keep the
exhaustive match compiling.
2026-05-11 00:58:37 +02:00
Brummel 12d9130567 iter 23.3.3: prelude — class Ord a extends Eq + Ord Int/Bool/Str instances 2026-05-10 23:13:02 +02:00
Brummel 119632812d iter 23.3.2-fixup: emit_compare_ladder helper + drop unused test param 2026-05-10 23:10:02 +02:00
Brummel d865f8a618 iter 23.3.2: codegen — three compare__T intercept arms + emit_ordering_arm helper 2026-05-10 23:05:45 +02:00
Brummel 94893bfb9d iter 23.3.1: runtime ail_str_compare + IR header declaration 2026-05-10 23:00:22 +02:00
Brummel a11cb2f144 iter 23.2.4-fixup: symmetrize check_workspace error assertion across three IR-shape tests 2026-05-10 22:50:00 +02:00
Brummel 559e591ab2 iter 23.2.4: e2e smoke fixture + ir-shape integration tests for eq__T mono symbols 2026-05-10 22:46:51 +02:00
Brummel be882c4ef8 fix: codegen lower_workspace_inner implicit prelude import (4th lockstep site) 2026-05-10 22:44:37 +02:00
Brummel 7172e7e8c9 iter 23.2.3: prelude — class Eq a + Eq Int/Bool/Str instances 2026-05-10 22:35:51 +02:00
Brummel 7289bbc9d4 iter 23.2.3-prep: reconcile workspace tests with auto-loaded prelude class Eq
The auto-loaded prelude (iter 23.2 work-in-progress) injects
`class Eq` plus three primitive instances (Eq Int, Eq Bool, Eq Str)
into every workspace. Five workspace::tests assertions broke against
this new baseline:

- iter22b1_missing_method_fires_diagnostic
- instance_without_superclass_instance_fires
- instance_overriding_nonexistent_method_fires
  Their fixtures redeclare `class Eq` (and `class Ord` in one case),
  which now collides with the prelude. Renamed each fixture's
  `Eq`/`eq` -> `TEq`/`teq` and `Ord`/`lt` -> `TOrd`/`tlt`, matching
  the scheme already used in the two earlier-renamed test_22b2
  fixtures. Method `ne` stays (does not collide). Assertion strings
  in workspace.rs updated to match.

- iter22b1_workspace_with_no_classes_has_empty_registry
- iter22b1_instance_in_class_module_loads_clean
  Their asserted registry counts assumed an otherwise-empty registry.
  Rewritten to filter by `defining_module != "prelude"`: the property
  is "no NON-prelude entries" / "exactly one fixture-owned entry",
  not raw count.

Doc comments updated alongside each assertion change so the rationale
for TEq/TOrd (collision avoidance) is local. No production code
touched.
2026-05-10 22:34:30 +02:00
Brummel 84dcc46645 fix: mono.rs per-module env.ctor_index overlay (same family as 13b36cc / 5c5180f)
The mono pass re-runs synth on every fn body to recover residual class
constraints. build_workspace_env (delegating to build_check_env) leaves
env.types and env.ctor_index workspace-flat. The synth path for cross-
module Pattern::Ctor / Term::Ctor resolution depends on a per-module
shape — local-first lookup, then imports-fallback that produces a
qualified type name. Without the overlay, the local-flat hit short-
circuits the fallback and yields a bare type name, mismatching the
qualified scrutinee/pattern from the sibling path with
CheckError::PatternTypeMismatch.

Add a per-module overlay helper that clears and rebuilds both
env.types and env.ctor_index from the current module's Def::Type list
— mirroring check_in_workspace at lib.rs:1257-1287 — and apply it at
the two mono entry points that re-walk bodies: the Phase 3 rewrite
loop in monomorphise_workspace and collect_targets_workspace_wide.

Pinned by tests/mono_xmod_ctor_pattern.rs (e580f75); also unblocks
the latent E2E regressions nested_ctor_pattern_first_two_sum,
std_either_list_demo, and ordering_match_via_prelude_prints_1 that
surfaced once the typeclass gate in workspace_has_typeclasses flipped
to true.
2026-05-10 22:23:53 +02:00
Brummel e580f75adf test: red for mono pass mis-resolving cross-module ctor patterns
`monomorphise_workspace` re-runs `synth` on every fn body to recover
residual class constraints; the env it uses is built by
`mono::build_workspace_env`, which delegates to `build_check_env` and
produces a workspace-flat `ctor_index` (every Def::Type ctor across
every module, keyed by bare ctor name → bare type name).

`check_in_workspace` (lib.rs:1247-1258) explicitly clears that flat
index after `build_check_env` and rebuilds it per-module so that
`Pattern::Ctor`'s local-first / imports-fallback resolution at
lib.rs:2486-2521 keeps the qualified-type-name comparison at
lib.rs:2526 intact: imports-fallback yields a qualified
`resolved_type_name` (`Mod.Type`), local-hit yields a bare one.

The mono pass never does the per-module overlay, so when a body in
module B pattern-matches a ctor `C` whose Def::Type lives in imported
module A, the workspace-flat index resolves `C` locally and yields the
bare type name. The scrutinee, however, was typed against the
qualified name, and the comparison fails with
`PatternTypeMismatch { ctor: "Cons", ty: "A.Type<...>" }`.

Surfaced by iter 23.2 Task 3, which adds `class Eq a` + Eq Int/Bool/Str
instances to the prelude. Pre-Task-3 the prelude is class-free, so
`workspace_has_typeclasses(ws) == false` and the mono pass early-outs
at `mono.rs:73`. Task 3 flips the gate; every workspace now traverses
bodies, which brings the latent bug to the surface for the
pre-existing `nested_ctor_pattern_first_two_sum` and
`std_either_list_demo` E2E tests.

Sibling regressions in the same family: `mono_xmod_qualified_ref.rs`
(env.imports not seeded), commit 13b36cc (env.globals not seeded for
self-recursive fns), commit 5c5180f (env.types / env.ctor_index not
seeded for user ADTs — the original "flat ctor_index" decision that
this bug now exposes as wrong-by-construction for cross-module ctor
pattern resolution).

The minimal fixture is two modules with five defs total:
test_mono_ctor_listmod (data List a = Nil | Cons a (List a)) and
test_mono_ctor_main (class Trivial a + instance Trivial Int + a fn
that pattern-matches Cons against the imported List<Int> + a main).
The test pins the inner cause: matches against the specific
`CheckError::PatternTypeMismatch` variant with the qualified
scrutinee type and bare ctor name, so it stays RED regardless of the
prelude's typeclass content.
2026-05-10 22:16:51 +02:00
Brummel 1618182220 iter 23.2.2-fixup-doc: tighten try_emit_primitive_instance_body contract doc 2026-05-10 22:02:29 +02:00
Brummel c6168ad5d1 iter 23.2.2-fixup: emit closure adapter for primitive-instance-bodied fns 2026-05-10 22:01:00 +02:00
Brummel 736064adf4 iter 23.2.2: codegen — declare @ail_str_eq + eq__Str body intercept 2026-05-10 21:55:19 +02:00
Brummel cc2d6944c1 iter 23.2.1: runtime/str.c with ail_str_eq + unconditional link 2026-05-10 21:49:45 +02:00
Brummel 1f244379de iter 23.1.4 fixup: local-hit with mismatching type_name falls through to imports-fallback 2026-05-10 21:35:43 +02:00
Brummel aace5e3ce2 iter 23.1.4: E2E fixture — bare LT match via implicit prelude import 2026-05-10 21:32:05 +02:00
Brummel 47d95d0c60 iter 23.1.3 fixup: clarify imports-fallback anchor + comment on env.imports vs env.module_imports duplication 2026-05-10 21:28:53 +02:00
Brummel 24af13e7e0 iter 23.1.3 fixup: cover AmbiguousType branch with cross-module test 2026-05-10 21:25:26 +02:00
Brummel 842df380a4 iter 23.1.3: implicit prelude import + symmetric bare-type-name imports-fallback in Term::Ctor synth 2026-05-10 21:24:16 +02:00
Brummel 927f7ea38f iter 23.1.2 fixup: align workspace-root idiom + add collision test for ReservedModuleName 2026-05-10 21:16:56 +02:00
Brummel 3742583924 iter 23.1.2: load_workspace auto-injects prelude module 2026-05-10 21:13:40 +02:00