4fc65ccb99643c4c276c42e812a72468d2e6755d
4 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5170b6abd1 |
iter operator-routing-eq-ord.1 (DONE 13/13): drop comparator builtins, route through Eq/Ord
Closes Gitea #1. Realises the "P2 follow-up" called out in examples/prelude.ail:9 — removes the surface comparator names `==` / `!=` / `<` / `<=` / `>` / `>=` from the language entirely, routes the LLM-author's `(app eq …)` / `(app compare …)` / `(app ne|lt|le|gt|ge …)` through prelude.Eq / prelude.Ord class- dispatch, ships six named Float-comparison fns (`float_eq`/`float_ne`/`float_lt`/`float_le`/`float_gt`/`float_ge`) so Float keeps comparability without an Eq/Ord instance, and emits primitive instance bodies with `alwaysinline` so the -O0 IR shape stays at one instruction per comparison. Plan-task journal (13 tasks, single atomic iter per Approach A): Task 1 — Bootstrap: 4 new fixtures (eq_user_adt_smoke.ail, eq_float_must_fail.ail, float_compare_smoke.ail, operator_unbound_check.ail) + 5 new E2E/pin tests as RED starting state. All 5 confirmed RED at start: north-star fixed `NoInstance Eq Unit` (preserved by Unit-eq opening line intentionally added in plan-self-review); float_compare unknown `float_eq`; operator-name typechecked (== still polymorphic); must-fail diagnostic lacked `float_eq`; alwaysinline absent from IR. Task 2 — Codegen alwaysinline + intercept arms: introduced `intercept_emit_wants_alwaysinline` allowlist + appended ` alwaysinline` between `)` and `{` of the `define` line in `emit_fn`; added 9 new intercept arms to `try_emit_primitive_instance_body` — `eq__Int`/`eq__Bool`/ `eq__Unit` + the six `float_*` arms. Task 3 — Prelude reshape: `instance Eq Unit` added; Eq Int/Bool bodies become placeholder-`false` (intercept overrides); six `float_*` free fns added; line-9 P2-follow-up comment removed. Lockstep hash re-pins: prelude_module_hash_pin (3abe0d3fa3c11c99 → new), mono_hash_stability six body-hash literals (eq__Int/Bool/Str + compare__Int/Bool/Str all shifted because placeholder body changes the canonical hash). IR-snapshot regen (hello/list/max3/sum/ws_main) rolled forward into this task at orchestrator's pragmatic call — prelude shift forces the snapshots immediately. Task 4 — Fixture migration: 58 .ail fixtures rewritten (`(app == …)` → `(app eq …)`, etc.; Float-typed operand sites to `(app float_eq …)` / `(app float_lt …)`). eq_ord_user_adt.ail:21 inner `==` → `eq` migration with lockstep eq_ord_e2e.rs:134 body-hash re-pin (3c4cf040cb4e8bb2 → new). Two prose snapshots accepted; deps test + 5 hash_pin literals updated as cascading consequences. Task 5 — Test-scaffold migration: all 8 in-source `#[cfg(test)] mod tests` AST-literal sites threaded (desugar.rs:2414, check/lib.rs:5656/6092/6220, codegen/lib.rs sites). 7 obsolete in-source tests deleted (5 eq_typechecks + 2 lower_eq ADT/Fn rejection — coverage moves to E2E eq_user_adt_smoke + eq_float_must_fail). 2 additional letrec tests deleted (single-module check env can't resolve eq/ge without prelude auto-import; covered by workspace E2E). Task 6 — Lit-pattern desugar: build_eq emits `Term::Var { name: "eq" }` instead of `"=="` at desugar.rs:1109; doc-comment rewritten to describe class-dispatch. Task 7 — Dead-machinery deletion sweep (compile-gated): typchecker builtins (install + list comparator entries deleted); codegen builtin_binop_typed (10 comparator arms deleted from synth.rs, table reduced to arithmetic core); lower_eq fn entirely deleted; `==` short-circuit in lower_app deleted; `is_static_callee` `==` clause deleted; `is_arithmetic_or_comparison_op` renamed to `is_arithmetic_op` + caller-update at codegen/lib.rs:2152 + :2529. Dead `poly_a_a_to_bool` helpers also removed. Workspace build green after compile gate — every caller of the deleted surface was migrated by Tasks 4-6. Task 8 — Float-aware NoInstance diagnostic: check/lib.rs:856-880 addendum extended to name `float_eq` / `float_lt` as the explicit alternative for Eq/Ord at Float; eq_float_noinstance.rs:32-44 assertion extended. Task 9 — IR snapshot regen: subsumed by Task 3 (prelude shift forced immediate snapshot regen; deferring to Task 9 would have left the workspace red between tasks). Task 10 — Prose-projection cleanup: 6 comparator arms deleted from binop_info; 3 in-source mod-tests updated/deleted (comparator-infix rendering would be dishonest now that the operators are no longer language identifiers). Task 11 — Contract updates: 5 design files rewritten to the class-dispatch present — float-semantics.md (arithmetic guarantees retained on +/-/*/; comparison guarantees transferred from ==/!=/< to float_eq/float_ne/float_lt/etc.); prelude-classes.md (Eq Unit added to instance list; new paragraph on six float_* fns; Float-no-Eq/Ord clause gains `→ use float_eq` cross-reference); str-abi.md (clause "REMAIN primitive operators" rewritten to describe class-method dispatch); scope-boundaries.md (multiple operator-name and Pattern::Lit-desugar clauses rewritten); authoring-surface.md (==, <= dropped from operator-example list). Task 12 — Initial acceptance gate: workspace 638/0 GREEN; bench/compile_check + cross_lang 0 regressed; bench/check.py flagged 4 regressions on bench_closure_chain (+29% bump_s, +47% rc_s, +29% bump_rss_kb, +48% rc_rss_kb). Orchestrator initially classified as DONE-with-concerns; Boss reclassified as PARTIAL-via-acceptance-#8-fail after independent re-run, extended iter scope to Task 13. Task 13 — Direct icmp intercept arms (Boss-extension): try_emit_primitive_instance_body gains direct-icmp arms for lt__Int / le__Int / gt__Int / ge__Int / ne__Int, bypassing the compare__Int → Ordering → match indirection that allocated one Ordering ctor per call in tight loops. Each new arm inherits `alwaysinline` via the existing allowlist (extended accordingly). New IR pin test `ord_int_intercept_ir_pin.rs` + smoke fixture `ord_int_intercept_smoke.ail` ratify the optimization — opt -O2 -S confirms zero `call @ail_prelude_lt__Int` in optimized IR; the icmp folds directly at every use site. Bool variants (lt__Bool/etc.) deliberately NOT added — no bench/example calls Ord at Bool; spec-extension permits skipping if unreachable at bench level. Family can be extended symmetrically when first Bool-ordered perf workload appears. Bench-gate post-Task-13: bench_closure_chain bump_s -6.05%, rc_s -2.67%, bump_rss_kb -0.77%, rc_rss_kb +0.46% — all four previously-regressed metrics back inside tolerance. Full bench corpus: 36 metrics, 0 regressed, 0 improved beyond tolerance, 36 stable. bench/check.py exit 0 ✓; bench/compile_check.py exit 0 ✓; bench/cross_lang.py exit 0 ✓. Workspace: 640 passed, 0 failed across all binaries (delta vs. milestone start: +5 new E2E/pin tests added in Task 1 + 1 IR pin added in Task 13 − 9 in-source mod tests deleted as obsolete net ≈ −3). The eq_user_adt_smoke fixture's Unit-opening line was the deliberate RED-first device added in planner self-review so the north-star wouldn't accidentally GREEN at start (user-ADT-Eq on Point with hand-written instance was already operable today; the milestone's actual delivery is operator-name death + Eq Unit + Float-named-fns + cleanup of the two-pathy primitive comparator machinery). Net delta: 96 files changed, 1760 insertions, 1101 deletions; 12 net-new files (6 new fixtures, 5 new E2E/pin tests, 1 stats file). main passes-test-count: 640 (was 633 pre-iter, accounting for the deletions). Spec-vs-acceptance addendum: spec §Testing strategy anticipated the bench-gate-regression case with two recovery paths (`alwaysinline` investigation or "spec needs revisiting toward α"). Task 13 is the third path — direct intercept arms for `lt`/etc. that bypass the compare→match path entirely. The spec's contingency clause was thus generous enough to absorb Task 13 without spec revision, but a future iter that hits a class-method primitive where the body shape introduces a similar codegen cost (Ordering allocation, RC tax, deferred-init) should expect a parallel extension. The pattern is "primitive instance whose canonical body indirects through other class methods that allocate" → add a direct-emit intercept arm + alwaysinline + IR-shape pin. Concerns absorbed: - Codegen lower_app / resolve_top_level_fn gained a prelude-fallback lookup so bare monomorphic prelude fns (`float_eq` etc.) resolve from non-prelude modules without explicit `prelude.float_eq` qualifier. Mirrors the typechecker's implicit prelude import — not in plan but necessary infra for the named-fn surface to be callable. - Recon's "≈10 fixtures" estimate was 6× too low — 58 actual. Mechanical migration; no design impact. - Recon caught 4 in-source AST-literal sites the spec missed (lib.rs:5656 `>=` site + three codegen/lib.rs sites); Task 5 covered all. - Recon caught 2 contract files outside the spec's update set that directly contradicted the milestone (str-abi.md + scope-boundaries.md "REMAIN primitive operators" / "Pattern::Lit desugar to ==" clauses); Task 11 covered all 5. Stats file: `bench/orchestrator-stats/2026-05-21-iter-operator-routing-eq-ord.1.json`. closes #1 |
||
|
|
19dc42f5ca |
design/ ledger: dense cross-linking via three topical splits + 88-link sweep
The first formal-links milestone shipped clause-5 + 8 links across the
existing file layout. Browsing surfaced that file-only granularity is
only as precise as the file boundaries — three files mixed two or
three navigation targets under one address, so the 8 links could not
multiply without ambiguity. This commit fixes the substrate, then
applies the sweep the original milestone deferred.
Splits (each extracts an already-self-contained section into its own
file so links land on the topic, not the parent doc's TOC):
contracts/typeclasses.md
→ +contracts/prelude-classes.md (Eq/Ord/Show ships, polymorphic `print`)
→ +contracts/method-dispatch.md (5-step dispatch rule, candidate index)
contracts/memory-model.md
→ +contracts/language-constraints.md (the 4 binding constraints
making RC sound without a
cycle collector)
models/authoring-surface.md
→ +models/prose-projection.md (Form-B / `ail prose` / merge-prose)
Each new file enters design/INDEX.md as its own row (three contracts
share show_no_instance_e2e.rs / uniqueness.rs as ratifying tests;
prose-projection is a model). Two pre-existing links rebind to the
new topic-files (memory-model.md → method-dispatch.md;
float-semantics.md → prelude-classes.md).
Link sweep: 8 → 88 formal Markdown links over 23 files. Every file
in design/contracts/ + design/models/ now has at least one outgoing
link; the tree is fully connected. Links are file-relative
`[label](path)` per the established convention, fenced code blocks
are skipped (a `](` inside ```jsonc``` is literal text), the durable
tier (design/ + crates/ + runtime/) is enforced by clause-5.
Tests:
- design_index_pin.rs (5/5 clauses): clean-cut, INDEX resolution,
ratifying-test resolution, no decision-record prose in contracts/,
body links durable + resolving.
- docs_honesty_pin.rs (5/5): one assertion rebinds from typeclasses.md
to prelude-classes.md (where the gated sentence now lives);
design_corpus widens to include the 4 new files so the Wunschdenken
/ doc-archaeology sweeps continue to cover everything that used to
live in the parents.
No spec/plan/journal for this batch — interactive collaboration after
the milestone closed; the user gated the splits explicitly before the
sweep.
|
||
|
|
8ad91e7f24 |
iter design-ledger-formal-links.1 (DONE 5/5): clause-5 hard gate + 7 prose-ref conversions + 2 disposition-(b) homeless removals + honesty-rule positive-half (whole milestone in one iter)
Positive-half completion of the DESIGN.md -> design/ split: design/
body cross-references are now formal, file-relative Markdown links
into the durable tier (design/ or source), and a new in-tree hard
gate (design_index_pin.rs clause-5,
design_body_links_are_durable_and_resolve) walks every
design/contracts/*.md + design/models/*.md, strips fenced code
(strip_fences toggles on ```/~~~ lines so a ]( inside a fence is not
treated as a link), extracts every ](path), and asserts the target
resolves file-relative to a real file under design/-or-crates/-or-
runtime/; never docs/, never an in-file #anchor.
RED-first via identity-stubbed strip_fences (four embedded synthetic
vectors -- first one FAILS); replacing the stub with the real
toggle-on-fence impl turns the test GREEN. clause-5 composes with
clause-3 into the complete invariant the milestone establishes:
every contract cross-reference is EITHER a resolving durable
file-link OR clause-3-forbidden decision-record prose.
Conversions (recon-and-corpus-verified closed set):
Task 2 (7 prose refs, 8 link tokens):
float-semantics.md:69 Prelude classes -> [..](typeclasses.md)
float-semantics.md:100 bare-path -> [Str ABI](str-abi.md)
embedding-abi.md:45 "Frozen value layout" -> [..](frozen-value-layout.md) (drop stale "below")
memory-model.md:44 Data model -> [..](data-model.md)
memory-model.md:105-106 Method dispatch -> [..](typeclasses.md) (drop stale "below"; the target heading lives in typeclasses.md:227, not in this file)
scope-boundaries.md:48 Str ABI -> [..](str-abi.md)
scope-boundaries.md:88 mixed split: ailang-core::desugar -> source link + Pipeline -> ../models/pipeline.md (drop stale "above")
Task 3 (2 disposition-(b) homeless removals):
pipeline.md:60-61 (see docs/PROSE_ROUNDTRIP.md) pointer removed, CLI prose preserved
authoring-surface.md:178-181 cross-tier pointer clause removed, ail merge-prose sentence preserved
Task 4: honesty-rule.md positive-half paragraph inserted between L14 and the existing L15-blank-L16; both docs_honesty_pin.rs-pinned phrases byte-identical at L14/L19 (now shifted to L19 -> L25 by the +6 lines).
Out of scope, preserved (asserted independently): every intra-file
"above/below"; embedding-abi.md:51 "frozen value layout below
specifies" (no quoted title, no (see) form); data-model.md
38/66/79/206/226 (in-fence ```jsonc schema annotations -- the inline
analog of the nominal-mention carve-out). INDEX.md and the
decision-records journal byte-unchanged; clauses 1-4 of
design_index_pin.rs source byte-unchanged (the only `-` lines in
the diff are the two-line //! header rewrite Task 1 Step 5 itself
delivers).
Boss-verified independently (not on agent report alone):
cargo test --workspace 647 passed / 0 failed
(+1 vs pre-milestone 646:
the new clause-5)
cargo test --test design_index_pin 5 / 5 passed
cargo test --test docs_honesty_pin 5 / 5 passed (additive
paragraph is pin-safe)
grep ](.../docs/.../) under design/ zero
grep ](#) under design/ zero
](-link count under design/ 8 (closed convert-set)
git diff --quiet design/INDEX.md ok
git diff --quiet decision-records ok
embedding-abi.md:48 pinned phrase byte-identical
One Concerns item: Task-5 Step-7's plan-predicted "`-` line count = 1"
was actually 2 because Task 1 Step 5 rewrote the //! header 5 -> 8
lines (removing the original L4 + L5, not just L5). Planner self-
review-item-8 miss on my part -- a verification-arithmetic error in
the plan, NOT an implementation defect. The substantive assertion
(clauses 1-4 source byte-unchanged) is fully satisfied; the
implementer correctly flagged it and proceeded. The plan stands as
written; the assertion's `1` should have been `2`. Lesson noted for
future header-rewrite tasks.
Spec: docs/specs/2026-05-19-design-ledger-formal-links.md
(grounding-check PASS x3 across two corpus-grounded amendments --
clause-6 + cross-ref definition; clause-5 fence-skip + closed
convert-set enumeration).
Next: mandatory milestone-close audit (no fieldtest -- zero
authoring-surface change, reasoned exclusion).
|
||
|
|
176821c2e7 |
iter design-md-rolesplit.1 (DONE 9/9): DESIGN.md -> design/ ledger role-split
The 3020-line docs/DESIGN.md is replaced by the design/ ledger:
design/INDEX.md (sole addressable spine, typed Contracts+Models tables,
polymorphic links — prose file OR authoritative source //!), 14
design/contracts/*.md test-linked invariants + 3 source-link-only
contracts (mangling/env-construction/qualified-xref, no prose file —
code is SoT), 5 design/models/*.md whitepapers, and
docs/journals/2026-05-19-design-decision-records.md (the
relitigation-guard archive — every why/rejected/does-not-do/rollback/
empirical ### moved out at ###-granularity). Clean cut: git rm
docs/DESIGN.md, no stub.
RED-first crates/ailang-core/tests/design_index_pin.rs — the 4-clause
anti-regrowth spine (DESIGN.md-gone / every-INDEX-link-resolves /
every-contract-names-a-resolvable-ratifier /
contracts-carry-no-decision-record-prose) — demonstrably RED before,
GREEN after. Build-atomic by task ordering: design_schema_drift.rs's
include_str! (the only compile-time consumer) retargeted to
design/contracts/data-model.md BEFORE the deletion; its
## Data model/## Pipeline slicer dropped (a simplification the split
enables). 2 NoInstance diagnostics + 2 lockstep E2Es retargeted to
design/contracts/{float-semantics,typeclasses}.md. ~12 agent reading
lists + 5 SKILL bodies + CLAUDE.md + skills/README.md + ~25
code/C/.ail/spec comment xrefs retargeted; OQ7 dangling 'Iter 13b'
cite deleted (no forward target — a pointer would be fiction).
honesty-rule.md rewritten so the rule names the new home
(rationale->journals), resolving the recon-found internal
contradiction; the two docs_honesty_pin.rs:70,72 pinned phrases kept
verbatim+contiguous.
Boss-verified independently: cargo test --workspace 646 passed /
0 failed; design_index_pin 4/4; acceptance grep CLEAN of live
DESIGN.md refs (residuals = only the spec-mandated clause-4
deletion-enforcer). 2 DONE_WITH_CONCERNS routed to the mandatory
milestone-close audit: (a) str-abi.md:23 '(iter str-concat,
2026-05-13)' provenance stamp trips advisory architect_sweeps Sweep-1
— Boss-confirmed byte-identical to DESIGN.md@deeffb1:2062-2065, a
faithfully-migrated PRE-EXISTING anchor (regexes verbatim, only path
retargeted), NOT split-introduced — RATIFY-or-tidy at audit; (b) a
now stale-direction intra-prose 'see Str ABI below' cross-ref in
float-semantics.md — audit-adjudication candidate. Plan defect noted:
Task 9 Step 4's verbatim acceptance grep used a ^./ anchor not
matching the system's grep -rIn output; substance re-verified CLEAN.
Spec grounding-check PASS x2. Journals INDEX + decision-records
pointer appended (Boss-only).
|