Commit Graph

10 Commits

Author SHA1 Message Date
Brummel 7ae92d3e60 refactor(test): hoist duplicated test helpers into ailang-test-support
closes #60

The fixture-corpus filter (NON_PARSEABLE_FIXTURES + list_ail_fixtures)
was copy-pasted across three test crates and drifted out of sync as
new reject fixtures landed; the examples_dir / workspace_root path
walk was reimplemented ~30 more times across the suite, under two
spellings (workspace_root / ws_root).

Introduce crates/ailang-test-support — a zero-dependency, dev-only
leaf crate — as the single home for these helpers:
- NON_PARSEABLE_FIXTURES, list_ail_fixtures
- workspace_root, examples_dir
- canonical_workspace_root (symlink-resolved variant, for the tsan/
  race tests that feed the root into native build steps)

All integration-test copies across ailang-core, ailang-surface,
ailang-prose, ailang-check, and ail now import from the shared crate;
the local definitions and their now-orphaned path imports are removed.
Path helpers resolve via the support crate's own CARGO_MANIFEST_DIR,
so they return the correct absolute paths from any caller.

ail_bin helpers are intentionally left in place: they depend on
env!("CARGO_BIN_EXE_ail"), which Cargo only defines when compiling the
ail crate's own integration tests, so they cannot move to a shared
crate.

Behaviour-identical: same paths, same fixture lists; full workspace
test suite green. Net -177 LOC.
2026-06-02 01:54:37 +02:00
Brummel a163c8c34a GREEN: pre-pass/resolver asymmetry — same-module bare + kernel-tier qualifier acceptance (closes F1, F3)
Two minimal edits in ailang-check that bring the resolver's
acceptance set into line with what prep.1's workspace pre-pass
(`prepare_workspace_for_check`) actually produces. The pre-pass
itself is unchanged — its qualification rules are correct per
spec § "Realisation mechanism — workspace pre-pass". RED tests
landed in 4d39fdc; both turn GREEN, both fieldtest fixtures
that surfaced the bugs now `ail check` clean.

F1 — same-module type-scoped call (kem_2b_min_repro.ail).
The dot-qualified `Term::Var` arm in `synth_term`
(`crates/ailang-check/src/lib.rs` ~line 3425) was
unconditionally rewriting the resolved fn signature via
`qualify_local_types`, so a call like `(app Counter.value c)`
from inside Counter's home module produced a return type
`<this>.Counter` — but the matching `Term::Ctor` arm
(~line 3644) and the pre-pass itself both leave own-module
type-names bare. The two halves of the resolver disagreed.

Fix: branch on `target_module == env.current_module` — when
true, skip the qualification (the raw type is already in the
form the rest of the resolver speaks); when false, qualify
as before. `owner_types` lookup moved inside the else-branch
since it's only consulted when qualification runs. A short
comment names the pre-pass and the sibling Term::Ctor arm so
a future reader sees the three-way symmetry.

F3 — kernel-tier qualifier acceptance (kem_3_stub_consumer.ail).
The env-builder paths `build_check_env` (~line 1653) and
`check_in_workspace` (~line 1807) force-injected only
`"prelude"` into `env.imports` / `env.module_imports`. The
loader meanwhile derives the implicit-imports list from
`modules.values().filter(|m| m.kernel)` (since prep.3) — so
`kernel_stub` flows into runtime resolution but not into
the checker's qualifier-validity check (~line 1968). Bare
`StubT` got qualified by the pre-pass to `kernel_stub.StubT`,
then bounced off the validity check as "unknown module
prefix".

Fix at both sites: keep the unconditional `"prelude"` literal
injection AND additively insert every workspace module with
`kernel: true` into the per-module import map (skip
self-injection). Idempotent for production (where prelude
also carries `kernel: true`) and preserves the
`bare_name_resolves_through_implicit_import_to_free_fn`
unit-test contract — that test deliberately constructs a
prelude module with `kernel: false` to pin the legacy
"prelude is always implicit" guarantee per se, independent
of the kernel flag. Mirrors the loader's kernel-flag filter,
making the env-builder and the loader speak the same set.

Sibling pin `crates/ailang-check/tests/env_construction_pin.rs`
(the frozen-reproduction copy of `build_check_env`'s
`module_imports` loop) updated in lockstep so it remains a
meaningful drift detector after the env-builder change. The
pin is intentionally designed to track legitimate behavioural
changes.

Verification:
  - tests::type_scoped_call_in_same_module_resolves      GREEN
  - tests::kernel_tier_module_qualifier_resolves_…       GREEN
  - examples/fieldtest/kem_2b_min_repro.ail              ail check ok
  - examples/fieldtest/kem_3_stub_consumer.ail           ail check ok
  - cargo test --workspace                               666 / 0
  (664 prior baseline + 2 RED→GREEN this iter)

Implementer notes: one re-loop on the F3 site after the first
edit replaced the legacy "prelude" literal injection wholesale
with the kernel-flag filter — that broke the unit-test contract
above. Restored the literal AND added the filter on top per the
"don't adapt tests to bugs" memory. No spec-review or
quality-review loops.

prep.1's pre-pass design (spec § "Realisation mechanism") was
correct as written — the symmetry between owner-side
`qualify_local_types` and consumer-side `prepare_workspace_for_check`
holds. What was missing was uniform resolver-side acceptance of
the qualifiers the pre-pass produces. With these two edits, the
following equivalences hold throughout the checker:

  bare T  ≡  <this-module>.T            (F1, same-module rule)
  bare T  ≡  <kernel-module>.T          (F3, kernel-tier rule)

via the loader-driven implicit-imports list that now drives
both the checker's import map and the type-validity check.
2026-05-28 19:36:36 +02:00
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
Brummel 77b28ad64d iter form-a.1 (Tasks 1-5): additive phase + relocation
First half of the form-a-default-authoring milestone-close iter
(Boss-decided strategy C, big-bang). All five tasks DONE; cargo
test --workspace green at every per-task boundary.

T1 — Add three new tests:
- parse_is_deterministic_over_every_ail_fixture (round_trip.rs)
- cli_parse_then_render_then_parse_is_idempotent (roundtrip_cli.rs)
- carve_out_inventory.rs (new file; #[ignore]'d until T8 deletion)

T2 — Bulk-render the 99 missing examples/<stem>.ail files via
`ail render`. Corpus 58 .ail (pre-iter) -> 157 .ail. Eight .ail.json
carve-outs (7 §C4(a) subject-matter + 1 §C4(b) prelude) preserved.
One re-loop triggered: load_workspace prefers .ail siblings since
ext-cli.1, so the newly-rendered imports broke seven Group-A entries
whose JSON entry-paths now resolved imports to fresh .ail. Repair:
pre-emptive forward-pull of five T3 migrations + 4 transient
#[ignore]s on workspace.rs mod tests (cleanly relocated in T5).

T3 — 14 Group-A test files migrated from ailang_core::load_workspace
to ailang_surface::load_workspace + .ail paths. Carve-out lines
preserved verbatim (7 sites in typeclass_22b2.rs / typeclass_22b3.rs).

T4 — 12 Group-B test files: bulk regex flip on build_and_run /
build_and_run_with_alloc / build_and_run_with_rc_stats call sites
(~70 e2e.rs invocations + 11 subprocess sites). Four files
mis-classified Group-A as Group-B in plan recon (mono_hash_stability,
prelude_free_fns, print_mono_body_shape, show_mono_synthesis); two
files mis-classified Group-B as Group-A (mono_recursive_fn,
mono_xmod_qualified_ref). Migrated per actual shape, not plan label.

T5 — Relocated #[cfg(test)] mod tests from production source to
integration test crates with ailang-surface dev-dependency:
- crates/ailang-core/tests/hash_pin.rs (10 tests from hash.rs)
- crates/ailang-core/tests/workspace_pin.rs (10 non-carve-out tests
  from workspace.rs)
- crates/ailang-codegen/tests/eq_primitives_pin.rs (3 tests from
  codegen/src/lib.rs:3717-3799)
- ailang-prose/tests/snapshot.rs migrated (helper + 8 fixtures) to
  .ail + ailang_surface::load_module
Carve-out tests in workspace.rs mod tests preserved in-place
(3× 22b2 + 3× ct1 = 6 tests). Tempdir-based loader-mechanism tests
(3 sites) also preserved — they don't consume examples/.

Tests: 560 passed, 0 failed, 4 ignored (was 558 + 3 T1 new -
1 transit carve_out_inventory #[ignore] = 560 active).

Tasks 6-12 (bench-driver suffix flips, e2e diff-test rewrite + 4
additional raw-JSON-inspect handlers, .ail.json deletion, retiring
obsolete roundtrip tests + schema_coverage corpus flip, §C3
DESIGN.md restatement, §A4 doctrine edits, WhatsNew + roadmap
strike) ship in the next dispatch on this iter ID.

Known debt inherited to T6-12: 4 raw-JSON-inspect tests in e2e.rs
(borrow_own_demo / reuse_as_demo / render_parse_round_trip_canonical
/ ail_run_accepts_ail_source_with_same_stdout_as_ail_json dual-form
smoke) need rewrite or #[ignore] before T8 deletion; recorded in
journal Concerns + Known debt sections.
2026-05-13 11:12:48 +02:00
Brummel 0eb33235eb iter mq.1: class-ref canonical-form extension + workspace-internal class-name qualification
Three schema fields move bare → canonical (bare for same-module,
<module>.<Class> for cross-module): InstanceDef.class,
Constraint.class, SuperclassRef.class. Symmetric to ct.1's
Type::Con.name rule. ClassDef.name stays bare.

validate_canonical_type_names gains three field-walks via new
check_class_ref helper + two sibling diagnostics
BareCrossModuleClassRef / BadCrossModuleClassRef.
check_class_name_fields narrowed to ClassDef.name-only.

Workspace-internal class-name keys all qualified:
class_def_module, class_by_name, registry entries.0,
ClassMethodEntry.class_name, class_superclasses, mono's
class_index, Origin::Class.class_name. New qualify_class_ref
helper at workspace.rs scope plus sibling
qualify_class_ref_in_check in ailang-check.

Two new positive on-disk fixtures (mq1_xmod_constraint_class{,_dep})
exercise the qualified Constraint.class shape.

Recon claim that all existing fixtures' class-refs are intra-module
was empirically wrong — 10 fixtures required minimal canonical-form
migration. Duplicate-instance test architecturally restructured:
post-mq.1 orphan-freedom makes two-modules-on-same-(class,type)
structurally impossible; new test_22b1_dup_same_module fixture
lands both instances in the class's module.

Plan defects fixed inline: Origin::Class.class_name single
construction site (plan recon off-by-one); build_class_index in
mono.rs needed qualifying; build_module_globals needed Def::Instance
carve-out; check_fn declared-constraint matching needed inline
canonical-form lifting; NoInstance Float-aware message arm needed
prelude.Eq/prelude.Ord match; coherence type-leg needed qualified-
head split-and-lookup. Tasks 3-6 ran as one coherent fix.

7/7 tasks, 520 tests green. bench/compile_check.py + cross_lang.py
exit 0; bench/check.py exit 1 (4 improvements-beyond-tolerance,
audit-ratifiable, not a regression).

MethodNameCollision + dispatch path unchanged; iters mq.2 + mq.3
land them.
2026-05-13 01:11:56 +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 c3fd1c08a3 iter env-unify.audit: tidy stale Env docs, pin-test docstring; queue types/ctor_index shape 2026-05-10 02:36:27 +02:00
Brummel cd08f53315 test: fix env-pin globals assertion (builtins seed operators workspace-flat) 2026-05-10 02:14:38 +02:00
Brummel 42a7ed7673 test: clean red signal for env-construction pin (inline module_types) 2026-05-10 02:06:46 +02:00
Brummel d7706dc3ed test: red for env-construction drift-shape pin 2026-05-10 02:03:09 +02:00