iter ctt.1: env-overlay shape ratification + DuplicateCtor pin
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"iter_id": "ctt.1",
|
||||
"date": "2026-05-12",
|
||||
"mode": "standard",
|
||||
"outcome": "DONE",
|
||||
"tasks_total": 3,
|
||||
"tasks_completed": 3,
|
||||
"reloops_per_task": {
|
||||
"1": 0,
|
||||
"2": 0,
|
||||
"3": 0
|
||||
},
|
||||
"review_loops_spec": 0,
|
||||
"review_loops_quality": 0,
|
||||
"blocked_reason": null
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
//! Pin for `CheckError::DuplicateCtor` — same-named ctors in two
|
||||
//! different ADTs within one module. Ratifies the load-bearing
|
||||
//! consumer of the per-module `env.ctor_index` rebuild in
|
||||
//! `check_in_workspace` that DESIGN.md §"Env construction" rests
|
||||
//! on as the asymmetry-with-mono-side argument. If the
|
||||
//! per-module rebuild is ever removed without an equivalent
|
||||
//! replacement path, this test goes red.
|
||||
|
||||
use ailang_check::check_workspace;
|
||||
use ailang_core::ast::{Ctor, Def, Module, TypeDef};
|
||||
use ailang_core::workspace::{Registry, Workspace};
|
||||
use ailang_core::SCHEMA;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[test]
|
||||
fn duplicate_ctor_across_two_adts_in_one_module() {
|
||||
let m = Module {
|
||||
schema: SCHEMA.into(),
|
||||
name: "M".into(),
|
||||
imports: vec![],
|
||||
defs: vec![
|
||||
Def::Type(TypeDef {
|
||||
name: "Cat".into(),
|
||||
vars: vec![],
|
||||
ctors: vec![Ctor {
|
||||
name: "Twins".into(),
|
||||
fields: vec![],
|
||||
}],
|
||||
doc: None,
|
||||
drop_iterative: false,
|
||||
}),
|
||||
Def::Type(TypeDef {
|
||||
name: "Dog".into(),
|
||||
vars: vec![],
|
||||
ctors: vec![Ctor {
|
||||
name: "Twins".into(),
|
||||
fields: vec![],
|
||||
}],
|
||||
doc: None,
|
||||
drop_iterative: false,
|
||||
}),
|
||||
],
|
||||
};
|
||||
let mut modules = BTreeMap::new();
|
||||
modules.insert(m.name.clone(), m.clone());
|
||||
let ws = Workspace {
|
||||
entry: m.name.clone(),
|
||||
modules,
|
||||
root_dir: std::path::PathBuf::from("."),
|
||||
registry: Registry::default(),
|
||||
};
|
||||
|
||||
let diags = check_workspace(&ws);
|
||||
let dup = diags
|
||||
.iter()
|
||||
.find(|d| d.code == "duplicate-ctor")
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"expected at least one `duplicate-ctor` diagnostic, got: {diags:?}"
|
||||
)
|
||||
});
|
||||
assert!(
|
||||
dup.message.contains("Twins"),
|
||||
"expected message to name the duplicate ctor `Twins`, got: {}",
|
||||
dup.message
|
||||
);
|
||||
assert!(
|
||||
dup.message.contains("Cat") && dup.message.contains("Dog"),
|
||||
"expected message to name both ADTs `Cat` and `Dog`, got: {}",
|
||||
dup.message
|
||||
);
|
||||
}
|
||||
@@ -1956,6 +1956,35 @@ with exactly one dot in the name is a qualified reference: `<prefix>.<def>`.
|
||||
Hash stability: no new AST node, no renamed fields — all previous module
|
||||
hashes stay bit-identical.
|
||||
|
||||
## Env construction
|
||||
|
||||
The check environment carries two parallel per-module overlays for
|
||||
ADT definitions: `env.types: IndexMap<String, TypeDef>` is the
|
||||
**owning index** — it stores the full definition keyed by bare type
|
||||
name. `env.ctor_index: IndexMap<String, CtorRef>` is the **reverse
|
||||
index** — it maps each constructor name back to its owning type, so
|
||||
ctor-by-name lookups run in O(1) rather than searching every type's
|
||||
ctor list.
|
||||
|
||||
The split is not a refactor wart. The two maps have distinct
|
||||
semantic roles: an owning data index, and a derived lookup
|
||||
accelerator over the same data. Collapsing them into one
|
||||
variant-keyed map (`Map<Name, EntryKind>`) would force every
|
||||
type-iterator and every ctor-lookup site to discriminate the variant
|
||||
tag, reversing the optimisation the reverse index exists to
|
||||
provide.
|
||||
|
||||
The check-side per-module rebuild of both overlays at
|
||||
`crates/ailang-check/src/lib.rs:1342-1384` is intentionally
|
||||
retained: the in-band `DuplicateCtor` diagnostic at the same site
|
||||
consumes the rebuilt per-module `env.ctor_index`. The mono-side
|
||||
overlay was narrowed to types-only at iter ct.3.2 because its
|
||||
consumer is the runtime ctor lookup, which became type-driven
|
||||
post-ct.2.2 — a different consumer story, hence a different
|
||||
overlay shape. The asymmetry between the check side and the mono
|
||||
side is by design and is pinned by
|
||||
`crates/ailang-check/tests/duplicate_ctor_pin.rs`.
|
||||
|
||||
## Data model
|
||||
|
||||
The on-disk JSON-AST is what the toolchain hashes, typechecks, and
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# iter ctt.1 — Env-overlay shape ratification
|
||||
|
||||
**Date:** 2026-05-12
|
||||
**Started from:** 0a36294e61f08d8de85c084415d694d3736e0c67
|
||||
**Status:** DONE
|
||||
**Tasks completed:** 3 of 3
|
||||
|
||||
## Summary
|
||||
|
||||
Ratifies the two-overlay shape of the check environment (`env.types`
|
||||
+ `env.ctor_index`) and pins its load-bearing consumer. The split
|
||||
decision now has a DESIGN.md anchor (§"Env construction") explaining
|
||||
the owning-index / reverse-index roles, the rationale for not
|
||||
collapsing into one variant-keyed map, and the intentional asymmetry
|
||||
with the mono side (narrowed to types-only at ct.3.2, because its
|
||||
runtime-ctor-lookup consumer story differs from the check side's
|
||||
in-band `DuplicateCtor` diagnostic). A new behavioural-pin test
|
||||
(`crates/ailang-check/tests/duplicate_ctor_pin.rs`) asserts
|
||||
`code == "duplicate-ctor"` plus message-substring coverage for a
|
||||
two-ADT-same-ctor fixture, going red if the per-module rebuild at
|
||||
`crates/ailang-check/src/lib.rs:1342-1384` is ever silently removed.
|
||||
Two P2 roadmap todos struck `[x]` with one-line forward-references to
|
||||
this iter. No production-code edits. `cargo test --workspace` green.
|
||||
|
||||
## Per-task notes
|
||||
|
||||
- iter ctt.1.1: New pinning test `crates/ailang-check/tests/duplicate_ctor_pin.rs` — builds a `Workspace` inline with one module declaring `type Cat = Twins` and `type Dog = Twins`, calls `check_workspace`, asserts a `Diagnostic` with `code == "duplicate-ctor"` plus message substring for `Twins` / `Cat` / `Dog`. PASS on first run, as the plan predicted (behaviour pre-exists).
|
||||
- iter ctt.1.2: Inserted new top-level section `## Env construction` in `docs/DESIGN.md` between `## Convention: qualified cross-module references` (line 1941) and `## Data model` (now line 1988, was 1959 pre-edit). Three paragraphs: split definition, rationale, check-side / mono-side asymmetry. Cross-references the pinning test.
|
||||
- iter ctt.1.3: Two `[ ]` → `[x]` strikes in `docs/roadmap.md` at lines 79 (overlay shape question) and 183 (per-module overlay narrowing). Each gains a one-line forward-reference suffix on its `context:` line pointing at iter ctt.1 and the closing artefact.
|
||||
|
||||
## Concerns
|
||||
|
||||
(none)
|
||||
|
||||
## Known debt
|
||||
|
||||
(none)
|
||||
|
||||
## Files touched
|
||||
|
||||
- `crates/ailang-check/tests/duplicate_ctor_pin.rs` (new)
|
||||
- `docs/DESIGN.md` (modified — `## Env construction` section inserted)
|
||||
- `docs/roadmap.md` (modified — two strikes)
|
||||
|
||||
## Stats
|
||||
|
||||
bench/orchestrator-stats/2026-05-12-iter-ctt.1.json
|
||||
@@ -37,3 +37,4 @@
|
||||
- 2026-05-12 — iter hs.4: IR + checker + linker wiring — `int_to_str : (Int) -> Str` installed in checker + synth.rs lockstep partner; IR-header preamble unconditionally declares both `@ailang_int_to_str` / `@ailang_float_to_str`; `Emitter::lower_app` gets new `int_to_str` arm and replaces `float_to_str`'s `CodegenError::Internal` with the actual call emission; `is_static_callee` whitelist extends to `int_to_str`; `runtime/rc.c` hoisted out of `AllocStrategy::Rc` arm to the unconditional link path (the `__attribute__((weak))` on str.c's `ailang_rc_alloc` extern becomes the documented no-op); 2 new IR-shape pins + 4 new E2E (2 stdout-smoke + 2 RC-stats) + 4 `.ail.json` fixtures; `drop.rs` Str-arm comment refreshed to dual-realisation framing; 5 IR snapshots regen for the 2 new declare lines. Status: DONE_WITH_CONCERNS — heap-Str RC-discipline incomplete (slabs leak at program end) because plan's literal `ret_mode: Implicit` interacts with uniqueness analyser walking `Term::Do` args as `Position::Consume`. Test asserts weakened from `allocs == frees && live == 0` to `allocs >= 1`. Substantive fix requires spec-level effect-op arg-mode decision (deferred, bounce-back to user) → 2026-05-12-iter-hs.4.md
|
||||
- 2026-05-12 — iter eob.1: Effect-op args walked as Borrow (uniqueness.rs + linearity.rs + linearity.rs doc-comment); heap-Str RC discipline closes (int_to_str / float_to_str `ret_mode: Implicit` → `Own` at 4 lockstep sites across builtins.rs + synth.rs; `drop_symbol_for_binder` App-arm gets `Str` carve-out symmetric to `field_drop_call`'s existing one); 2 pre-existing RED tests at e2e.rs (commit 592d87b) flipped to GREEN unchanged with predicted concrete numbers (`allocs==1,frees==1,live==0` and `allocs==2,frees==2,live==0`); 2 new positive tests + fixtures (primitive-Int through `io/print_int`: zero RC traffic; heap-Str repeated borrow through 2× `io/print_str`: `allocs==1,frees==1,live==0`); DESIGN.md §Decision 10 anchors both arg-position rules together (Ctor=Consume, Do=Borrow, plus a paragraph linking Do=Borrow to ret_mode==Own letbinder-trackability); WhatsNew entry shipped, roadmap P1 `[milestone] Heap-Str ABI` checked off, Post-22-Prelude `depends on:` line removed; 7/7 tasks first-shot, zero review re-loops; lint side-effect surface (over-strict-mode on (own T) params used solely via effect-ops) was empty for the current corpus → 2026-05-12-iter-eob.1.md
|
||||
- 2026-05-12 — audit-eob: milestone close (heap-str-abi) — architect drift fixed inline as `eob.tidy` (3 DESIGN.md edits: float_to_str / int_to_str caveats dropped, "Str ABI" anchor added documenting both heap-Str and static-Str realisations with their shared consumer ABI and codegen-level non-RC invariant for static-Str); bench mixed (latency.explicit_at_rc improvement cluster reappears for the 3rd consecutive audit + new marginal bump_s regressions on list_sum/hof_pipeline 12% over 10% tol), baseline pristine for 3rd consecutive audit (conservative call: latency cluster has no identified cause across 3 audits — ratify-without-attribution would obscure future signal; bump_s cluster is first-sighting, observe next audit) → 2026-05-12-audit-eob.md
|
||||
- 2026-05-12 — iter ctt.1: env-overlay shape ratification — new DESIGN.md top-level section `## Env construction` anchors the `env.types` (owning) / `env.ctor_index` (reverse-index) split with the semantic rationale, plus the intentional check-side / mono-side asymmetry (mono-side narrowed at ct.3.2, check-side retains both halves because in-band `DuplicateCtor` consumes the per-module `env.ctor_index` rebuild); new behavioural-pin test `crates/ailang-check/tests/duplicate_ctor_pin.rs` ratifies the consumer; two P2 roadmap todos struck `[x]` (overlay shape question, per-module overlay narrowing); 3/3 tasks first-shot, zero review re-loops, no production-code edits → 2026-05-12-iter-ctt.1.md
|
||||
|
||||
+4
-4
@@ -76,11 +76,11 @@ context. Pick the next milestone from P1.)_
|
||||
to make sure the indirection doesn't tank latency.
|
||||
- context: JOURNAL 2026-05-09
|
||||
- depends on: Post-22 Prelude — Eq/Ord (shipped 23.5)
|
||||
- [ ] **\[todo\]** `types` / `ctor_index` overlay shape question —
|
||||
- [x] **\[todo\]** `types` / `ctor_index` overlay shape question —
|
||||
decide whether the env's two parallel ctor maps should collapse
|
||||
into one overlay, or stay split. Surfaced during the
|
||||
env-construction unify audit.
|
||||
- context: JOURNAL 2026-05-10 ("Audit close: env-construction unify")
|
||||
- context: JOURNAL 2026-05-10 ("Audit close: env-construction unify"); closed by iter ctt.1 — DESIGN.md §"Env construction" anchors the split decision.
|
||||
- [ ] **\[todo\]** CLI human-mode diagnostic surface for `WorkspaceLoadError` —
|
||||
non-JSON `ail check` routes ct.1 errors via `anyhow`/`thiserror`
|
||||
Display (`Error: <message>`) instead of going through
|
||||
@@ -180,7 +180,7 @@ context. Pick the next milestone from P1.)_
|
||||
- context: fieldtest 2026-05-11 — fieldtest fixtures could not be
|
||||
placed under `examples/fieldtest/` because of this; predates the
|
||||
canonical-type-names milestone but surfaces every time.
|
||||
- [ ] **\[todo\]** `check_in_workspace` per-module overlay narrowing —
|
||||
- [x] **\[todo\]** `check_in_workspace` per-module overlay narrowing —
|
||||
`crates/ailang-check/src/lib.rs:1234` still clears+rebuilds
|
||||
`env.ctor_index` per-module; ct.3.2 narrowed the analogous mono
|
||||
overlay to types-only. The typecheck-side overlay's `env.ctor_index`
|
||||
@@ -189,7 +189,7 @@ context. Pick the next milestone from P1.)_
|
||||
Narrowing is mechanical but needs a careful read to confirm no
|
||||
other consumer survives.
|
||||
- context: JOURNAL 2026-05-11 ("Iteration ct.4") — milestone close
|
||||
follow-up.
|
||||
follow-up; closed by iter ctt.1 — recon confirmed the rebuild is the load-bearing consumer of in-band DuplicateCtor (pinned by `crates/ailang-check/tests/duplicate_ctor_pin.rs`); the asymmetry with the mono side is intentional.
|
||||
|
||||
## P3 — Ideas
|
||||
|
||||
|
||||
Reference in New Issue
Block a user