Files
AILang/crates/ail/tests/mono_xmod_ctor_pattern.rs
T
Brummel ac4d545570 source: scrub iter-code / Decision-N residue from inline comments
Follow-up to bcd4181: the remaining ~530 inline `//` and `///`
comments still carrying opaque shorthand are now reformulated to
their content phrases. The only surviving `iter-<code>` reference
in source is the literal filename
`docs/journals/2026-05-13-iter-mq.3.md` (a real journal file).

Sweep covered:
  - `// Iter X.Y: <text>` prefixes (Iter 13a / 14a / 14e / 15g-aux /
    16b.x / 16d / 16e / 18b / 18c.x / 18d.x / 18e / 18g.x / 19a /
    19a.1 / 19b / 20a / 20f / 22-floats.x / 22b.x / 22c / 23.x /
    24.1 / cli-diag-human / hs.x / str-concat / etc.) — fully
    removed; the descriptive text that followed each prefix stays.
  - `// (Decision N)` and `per Decision N` and `Decision N axis 3` —
    replaced with the content phrase plus the relevant contract
    file (`design/contracts/tail-calls.md` for Decision 8,
    `design/contracts/memory-model.md` for Decision 10,
    `design/contracts/typeclasses.md` and `design/models/typeclasses.md`
    for Decision 11, `design/contracts/authoring-surface.md` for
    Decision 6, "the transitional dual-allocator" for Decision 9,
    "Effect prose" for Decision 3).
  - `// mq.X / mq.X (Task N) / mq.X journal / mq.X invariant` ->
    "the canonical-class-form rule / Class-class repurpose /
    method-dispatch-refactor journal / canonical-class-form
    invariant".
  - `// ct.X / ct.X (canonical-type-names) / ct.1.5a + ctt.2 /
    ct.2 Task N` -> "the canonical-form rule for type references /
    the canonical-form normalisation step / canonical-type-lookup
    refactor".
  - `// eob.X` -> "heap-Str-ABI" / "the Str carve-out".
  - `// rpe.X` -> "the per-type-print-op retirement".
  - `// post-mq.X / Pre-ct.X / pre-mq.X` -> "post-canonical-class-form" /
    "Pre-canonical-type-form" etc.
  - `/// Iter X regression: / /// Iter X.Y: / /// Iter A arm-close /
    /// Iter ct.4 (...): / /// Iter rpe.1 ...` -> descriptive
    phrases.

The journal filename in `crates/ailang-core/src/workspace.rs:573`
stays verbatim because it points at an actual file under
`docs/journals/`.

Tests: full `cargo test --workspace` green (80/80 test-result blocks
clean, no FAILED line). design_index_pin 5/5 + docs_honesty_pin 5/5
gating tests pass.
2026-05-20 09:58:04 +02:00

105 lines
4.6 KiB
Rust

//! Regression: the workspace-monomorphisation pass must not mis-resolve
//! a cross-module constructor pattern. The mono pass re-runs `synth` on
//! every fn body to recover residual class constraints; that env is built
//! by `mono::build_workspace_env`, which delegates to `crate::build_check_env`
//! and produces a workspace-flat `ctor_index` and `types` map.
//!
//! After the canonical-form / type-driven-ctor-lookup refactor,
//! `Pattern::Ctor` lookup is type-driven — it consults the
//! scrutinee's canonical `Type::Con.name` to find the TypeDef directly
//! in `env.module_types`, then validates the ctor name within it. The
//! mono pass's flat `ctor_index` is no longer consulted by this path;
//! the per-module overlay (lib.rs:1247-1258) is now decorative for the
//! pattern path and remains only for duplicate-type / duplicate-ctor
//! detection at the workspace-build prologue.
//!
//! This test pins the cross-module pattern shape against a minimal
//! 2-module fixture (`test_mono_ctor_main` + `test_mono_ctor_listmod`).
//! Before the refactor the bug surfaced as
//! `PatternTypeMismatch { ctor: "Cons",
//! ty: "test_mono_ctor_listmod.List<Int>" }` because the mono env
//! resolved `Cons` to bare `List` via the flat index. After the
//! refactor the lookup is type-driven and
//! `expected.name == "test_mono_ctor_listmod.List"` directly indexes
//! the right TypeDef.
//!
//! Surfaced when `class Eq a` + Eq Int/Bool/Str instances were added
//! to `examples/prelude.ail.json`, flipping the
//! `workspace_has_typeclasses` gate so every workspace exercises the
//! mono pass.
use std::path::PathBuf;
fn examples_dir() -> PathBuf {
let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
d.pop();
d.pop();
d.join("examples")
}
/// Property: `monomorphise_workspace` must succeed on a workspace that
/// (a) flips the typeclass gate (any `Def::Class` + `Def::Instance` is
/// enough), and (b) contains a fn whose body pattern-matches a
/// constructor whose `Def::Type` lives in an imported module.
///
/// The minimal fixture is two modules:
///
/// * `test_mono_ctor_listmod` — declares `data List a = Nil | Cons a (List a)`.
/// * `test_mono_ctor_main` — imports the listmod, declares
/// `class Trivial a` + `instance Trivial Int` (to flip the
/// `workspace_has_typeclasses` gate), and defines
/// `head_or_zero : test_mono_ctor_listmod.List<Int> -> Int` that
/// matches on `Cons h _ | Nil`.
///
/// Pre-canonical-type-form repro: `monomorphise_workspace` returned
/// `Err(CheckError::PatternTypeMismatch { ctor: "Cons", ty:
/// "test_mono_ctor_listmod.List<Int>" })`. The same workspace
/// typechecked cleanly because the typecheck pass overlaid a
/// per-module `ctor_index` whose imports-fallback produced the
/// qualified `resolved_type_name`, while the mono pass kept the
/// workspace-flat index whose local hit produced the bare one.
///
/// Post-canonical-type-form expectation: `monomorphise_workspace` returns `Ok`
/// because the Pattern::Ctor lookup no longer consults
/// `ctor_index` at all — it walks directly from the scrutinee's
/// canonical `Type::Con.name` to its TypeDef in
/// `env.module_types` and validates the ctor by name there.
#[test]
fn mono_pass_handles_xmod_ctor_pattern() {
let entry = examples_dir().join("test_mono_ctor_main.ail");
let ws = ailang_surface::load_workspace(&entry).expect("load");
// Pre-condition: the workspace typechecks cleanly. The bug is
// localised to the mono pass; the typecheck path does not have it.
let diags = ailang_check::check_workspace(&ws);
assert!(
diags.is_empty(),
"fixture must typecheck before mono runs: {:?}",
diags
);
// Pin the symptom to the inner cause. Pre-fix this returns Err
// with the qualified scrutinee type and the bare `Cons` ctor.
let result = ailang_check::monomorphise_workspace(&ws);
match result {
Ok(_) => { /* post-fix: pass */ }
Err(ailang_check::CheckError::PatternTypeMismatch { ctor, ty }) => {
panic!(
"mono pass mis-resolves cross-module ctor pattern: \
ctor=`{}` ty=`{}` (expected mono to succeed; the bare \
ctor_index in build_workspace_env resolves `Cons` to \
the local bare `List` instead of qualified \
`test_mono_ctor_listmod.List`)",
ctor, ty
);
}
Err(other) => {
panic!(
"mono pass failed with unexpected error variant: {:?} \
(expected PatternTypeMismatch on cross-module Cons)",
other
);
}
}
}