iter design-md-rolesplit.tidy (DONE 7/7): resolve milestone-close audit drift
Gate-first TDD: widened design_index_pin.rs clause-3 to a hand-rolled
FAITHFUL Sweep-1 superset (case-sensitive digit-anchored line anchors
+ Sweep-1's ^[^/]* path-excluded date + the audit-named
decision-record phrases, case-insensitive); no regex dep; the blanket
iter-detector rejected as unworkable. Sentence-level strip of
faithfully-migrated history/decision-record prose out of 5 contract
files (the audit's 3 spot-checked + roundtrip-invariant.md +
data-model.md the exhaustive scan found) into the decision-record
journal, each replaced by its present-tense contract equivalent;
float-semantics.md stale 'see Str ABI below' -> str-abi.md;
architect_sweeps honesty sweeps re-scoped to design/contracts only
(models/ is the narrative tier) + ailang-architect.md lockstep.
Invariant: clause-3 GREEN => Sweep-1 clean in contracts/.
The prior dispatch correctly BLOCKED on a real plan defect (iso_date
lacked Sweep-1's path-exclusion, over-firing on legit
docs/specs/2026-.. citations); per the two+-defects-in-one-iteration
discipline the audit Resolution mechanism+scope were corrected
upstream in lockstep (f2cdd67) before re-dispatch, not patched a
third time.
Boss-verified independently: cargo test --workspace 646/0,
design_index_pin 4/4 (clause-3 RED->GREEN), architect_sweeps.sh exit
0 'All five sweeps clean' (acceptance criterion 9 met), acceptance
grep CLEAN, 3 docs_honesty_pin pinned runs each exactly 1 contiguous
match. Zero spec/quality re-loops. FINAL design-md-rolesplit
iteration — milestone functionally complete, audited, drift-resolved,
hard gate enforces the honesty spirit.
This commit is contained in:
@@ -146,7 +146,7 @@ are real surface forms.
|
||||
// lowers as in-place rewrite under `--alloc=rc`.
|
||||
{ "t": "reuse-as", "source": Term, "body": Term }
|
||||
|
||||
// loop-recur iter 1: strict iteration block. `binders` declares
|
||||
// loop: strict iteration block. `binders` declares
|
||||
// one or more loop parameters (name, type, init), evaluated in
|
||||
// order on loop entry; `body` is in scope of all binders. The
|
||||
// loop's value is `body`'s value on the iteration that exits via a
|
||||
@@ -158,7 +158,7 @@ are real surface forms.
|
||||
"binders": [ { "name": "<id>", "type": Type, "init": Term }, ... ],
|
||||
"body": Term }
|
||||
|
||||
// loop-recur iter 1: re-enter the lexically innermost enclosing
|
||||
// recur: re-enter the lexically innermost enclosing
|
||||
// `loop`, rebinding its binders positionally to `args`. Transfers
|
||||
// control (no fall-through); valid only in tail position of its
|
||||
// enclosing loop (enforced at typecheck, `recur-not-in-tail-position`).
|
||||
|
||||
@@ -97,8 +97,8 @@ LLM-author wants. Use ordering operators (`<`, `>`, ...) and
|
||||
|
||||
`float_to_str` (Float → Str) and `int_to_str` (Int → Str) are
|
||||
fully wired through checker, codegen, and runtime. Both allocate
|
||||
a fresh heap-Str slab at the call site (see "Str ABI" below for
|
||||
the dual realisation) and carry `ret_mode: Own` so the let-binder
|
||||
a fresh heap-Str slab at the call site (see design/contracts/str-abi.md
|
||||
for the dual realisation) and carry `ret_mode: Own` so the let-binder
|
||||
for the call result is RC-tracked and the slab is freed at scope
|
||||
close.
|
||||
|
||||
|
||||
@@ -69,8 +69,7 @@ inherit the gate automatically):
|
||||
Pins drift internal tests cannot see.
|
||||
- `crates/ailang-core/tests/schema_coverage.rs::every_ast_variant_is_observed_in_the_fixture_corpus`
|
||||
— every variant of `Def`, `Term`, `Pattern`, `Literal`, `Type`,
|
||||
and `ParamMode` appears in at least one `.ail` fixture (corpus
|
||||
flipped from `.ail.json` to `.ail` at iter form-a.1). New AST
|
||||
and `ParamMode` appears in at least one `.ail` fixture. New AST
|
||||
variants fail compile until the visitor and corpus are extended
|
||||
in lockstep.
|
||||
- `crates/ailang-core/tests/carve_out_inventory.rs::examples_ail_json_inventory_matches_carve_outs`
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
## What is not (yet) supported
|
||||
|
||||
Snapshot of the current boundary. Items move out of this list
|
||||
as iterations land; the JOURNAL records when.
|
||||
Snapshot of the current boundary.
|
||||
|
||||
- No effect handlers — only the built-in `IO` op (`io/print_str`).
|
||||
`Diverge` is a reserved effect name with no op and no codegen.
|
||||
@@ -13,8 +12,7 @@ as iterations land; the JOURNAL records when.
|
||||
a body, lambdas check monomorphically against their declared type.
|
||||
- Polymorphic fns must be **directly called** at the use site.
|
||||
Passing a polymorphic fn as a value (`let f = id in f(42)`) is
|
||||
not yet supported — it would need one closure-pair global per
|
||||
instantiation, deferred.
|
||||
not yet supported.
|
||||
- No higher-rank polymorphism. Passing a polymorphic fn to another
|
||||
polymorphic fn (`apply(id, 42)`) is not supported.
|
||||
- No recursive `let` for non-fn values. Plain `let x = … in …` only
|
||||
@@ -32,10 +30,8 @@ What **is** supported (and used as the smoke test for the pipeline):
|
||||
- Int, Bool, Unit, **Str**, **Float** as primitive types.
|
||||
- `if`, `let`, function calls, recursion.
|
||||
- Effects on function signatures, with `do op(args)` for direct effect
|
||||
ops (`io/print_str`). The per-type print ops
|
||||
`io/print_int`, `io/print_bool`, `io/print_float` were retired in
|
||||
iter rpe.1; the polymorphic `print` (§"Polymorphic print") is the
|
||||
canonical output path for non-Str values.
|
||||
ops (`io/print_str`). The polymorphic `print` (§"Polymorphic print")
|
||||
is the canonical output path for non-Str values.
|
||||
- **Builtins.** Arithmetic operators (`+`, `-`, `*`, `/`) of type
|
||||
`forall a. (a, a) -> a` (codegen-restricted to `{Int, Float}`);
|
||||
`%` of type `(Int, Int) -> Int` (Int-only — `fmod` semantics for
|
||||
|
||||
+12
-16
@@ -11,16 +11,16 @@ and return an owned `Str`. Each is registered as a builtin in
|
||||
`crates/ailang-codegen/src/lib.rs::lower_app` to a `call ptr @ailang_<name>`,
|
||||
and backed by a `runtime/str.c` C helper.
|
||||
|
||||
- `int_to_str : (borrow Int) -> Str` (iter 24.1) — decimal rendering
|
||||
- `int_to_str : (borrow Int) -> Str` — decimal rendering
|
||||
of an `Int`. Backs `Show Int` in the prelude.
|
||||
- `bool_to_str : (borrow Bool) -> Str` (iter 24.1) — `"true"`/`"false"`.
|
||||
- `bool_to_str : (borrow Bool) -> Str` — `"true"`/`"false"`.
|
||||
Backs `Show Bool` in the prelude.
|
||||
- `float_to_str : (borrow Float) -> Str` (iter 24.1) —
|
||||
- `float_to_str : (borrow Float) -> Str` —
|
||||
type-installed; codegen is reserved and not yet shipped.
|
||||
- `str_clone : (borrow Str) -> Str` (iter 24.1) — allocates a fresh
|
||||
- `str_clone : (borrow Str) -> Str` — allocates a fresh
|
||||
heap-Str copy of the input's bytes. Backs `Show Str` in the prelude.
|
||||
- `str_concat : (borrow Str, borrow Str) -> Str` (iter str-concat,
|
||||
2026-05-13) — combines two `Str` values into a single owned `Str`.
|
||||
- `str_concat : (borrow Str, borrow Str) -> Str` — combines two `Str`
|
||||
values into a single owned `Str`.
|
||||
General-purpose; commonly used in Show bodies for labelled output
|
||||
(`(app str_concat "label=" (app int_to_str x))`).
|
||||
|
||||
@@ -35,16 +35,12 @@ directly; values of other primitive types route through the
|
||||
polymorphic `print` helper (§"Polymorphic print"), which feeds
|
||||
the heap-Str result of `show x` into `io/print_str`.
|
||||
|
||||
`==`, `<`, `<=`, `>`, `>=` REMAIN primitive operators (unchanged
|
||||
from the original draft). Class methods are accessed by name (`eq x y`,
|
||||
`lt x y`, …), not via these operators. Routing operators through
|
||||
classes is deliberately deferred — it would require migrating every
|
||||
existing fixture and would re-baseline the bench corpus, which is a
|
||||
new-baseline decision rather than an iter detail.
|
||||
`==`, `<`, `<=`, `>`, `>=` REMAIN primitive operators. Class methods
|
||||
are accessed by name (`eq x y`, `lt x y`, …), not via these
|
||||
operators.
|
||||
|
||||
`Num` is NOT in milestone 22. Arithmetic operators (`+`, `-`, `*`,
|
||||
`/`) stay primitive and per-type. Class-based numeric overloading
|
||||
would invoke literal-defaulting which axis-7 already excluded.
|
||||
Arithmetic operators (`+`, `-`, `*`, `/`) stay primitive and
|
||||
per-type.
|
||||
|
||||
**Str ABI.** A `Str` is a pointer to a structure with `i64 len` at
|
||||
offset 0 followed by `len` bytes plus a trailing `NUL` at offset 8.
|
||||
@@ -71,7 +67,7 @@ the previous global in `.rodata` and reading them is undefined.
|
||||
**The static-Str non-RC invariant is enforced at codegen.** Two
|
||||
mechanisms keep static-Str pointers out of `ailang_rc_dec` along
|
||||
every shipping execution path: (1) the non-escape lowering pass
|
||||
(iter 18b) and the move-tracking partial-drop logic (iter 18d.3)
|
||||
and the move-tracking partial-drop logic
|
||||
prevent let-binders or pattern-binders for static-Str literals
|
||||
from reaching scope-close drop emission; (2) the `Type::Con { name: "Str" }`
|
||||
carve-outs in `field_drop_call` and in the `Term::App` arm of
|
||||
|
||||
@@ -74,8 +74,7 @@ user-ADT call site `print (MkIntBox 7)` causes synthesis of
|
||||
user-defining module per Decision 11 coherence). The synthesised body
|
||||
crosses a module boundary the source template did not.
|
||||
|
||||
Three invariants make this work, all installed in milestone 24's iter
|
||||
24.3 and worth keeping load-bearing:
|
||||
Three invariants make this work:
|
||||
|
||||
1. **`MonoTarget::FreeFn::type_args` carries canonical types
|
||||
post-collection.** At every site where `subst.apply(m)` produces a
|
||||
@@ -179,11 +178,10 @@ wording is fixed; the categories and their triggers are:
|
||||
- `OverridingNonExistentMethod` — instance specifies a method not in
|
||||
the class.
|
||||
|
||||
(`MethodNameCollision` was retired at iter mq.3. Cross-class method
|
||||
sharing is now structurally legal; ambiguity surfaces at the call
|
||||
site via `AmbiguousMethodResolution` or, for class-fn name overlap,
|
||||
via the `class-method-shadowed-by-fn` warning. See §"Method
|
||||
dispatch" below.)
|
||||
Cross-class method sharing is structurally legal; ambiguity surfaces
|
||||
at the call site via `AmbiguousMethodResolution` or, for class-fn
|
||||
name overlap, via the `class-method-shadowed-by-fn` warning. See
|
||||
§"Method dispatch" below.
|
||||
|
||||
**Class-schema diagnostics** (validation of class declarations):
|
||||
|
||||
@@ -196,8 +194,7 @@ A class param appearing in applied position (e.g., `f a` where `f`
|
||||
is the class param) is rejected earlier by the canonical-form
|
||||
validator as `BareCrossModuleTypeRef` — `f` is a bare,
|
||||
non-primitive name not declared as a `TypeDef` in the owning
|
||||
module — so the dedicated `KindMismatch` diagnostic was retired
|
||||
at iter ctt.3.
|
||||
module.
|
||||
|
||||
**Typecheck diagnostics** (per function body):
|
||||
|
||||
@@ -276,40 +273,23 @@ warning surfaces the shadow.
|
||||
|
||||
## Prelude (built-in) classes
|
||||
|
||||
Milestone 22 ships **no built-in Prelude classes**.
|
||||
An earlier draft committed to a fixed Prelude (Show / Eq / Ord on the
|
||||
primitives), but the implementation work to wire `int_to_str` as a
|
||||
heap-allocated-string runtime primitive proved substantively
|
||||
separable from the typeclass machinery itself, and the LLM-utility
|
||||
case for primitive `Show` is weak (LLM-natural form is `int_to_str
|
||||
x`, not `show x` through a single-instance class). The user-class
|
||||
end-to-end path is the milestone's typeclass acceptance gate
|
||||
(the user-defined-class fixture: `class Foo a` + `data IntBox` +
|
||||
`instance Foo IntBox`); see `docs/specs/2026-05-09-22-typeclasses.md`
|
||||
"Amendments" for the substantive rationale.
|
||||
The prelude ships the `Ordering` ADT, the `Eq` and `Ord` classes,
|
||||
primitive `Eq Int/Bool/Str` and `Ord Int/Bool/Str` instances, and
|
||||
the five polymorphic free-fn helpers `ne`/`lt`/`le`/`gt`/`ge`. Float
|
||||
has neither `Eq` nor `Ord` instance per §"Float semantics"; a
|
||||
polymorphic helper invoked at Float fires `NoInstance` at typecheck
|
||||
with a Float-aware diagnostic cross-referencing this section.
|
||||
|
||||
Milestone 23 amends the above: the prelude now ships the `Ordering`
|
||||
ADT, the `Eq` and `Ord` classes, primitive `Eq Int/Bool/Str` and
|
||||
`Ord Int/Bool/Str` instances, and the five polymorphic free-fn helpers
|
||||
`ne` / `lt` / `le` / `gt` / `ge`. Operator routing through `Eq`/`Ord`
|
||||
and `print`-rewire remain out of scope per their original substantive
|
||||
reasons (bench rebaseline, milestone-24 dependency on the post-mq
|
||||
dispatcher); `Show` itself ships in milestone 24. Float has neither
|
||||
`Eq` nor `Ord` instance per §"Float semantics"; a polymorphic helper
|
||||
invoked at Float fires `NoInstance` at typecheck with a Float-aware
|
||||
diagnostic cross-referencing this section.
|
||||
|
||||
Milestone 24 amends the above further: the prelude ships `class Show
|
||||
a where show : (a borrow) -> Str` and primitive `Show Int`,
|
||||
`Show Bool`, `Show Str`, `Show Float` instances (iter 24.2). Float
|
||||
**is** included in Show (unlike Eq/Ord) — IEEE-754 makes structural
|
||||
equality and total ordering semantically dubious, but textual
|
||||
representation of a Float is well-defined modulo the NaN-spelling
|
||||
caveat in §"Float semantics". Each `Show <T>` instance body is a
|
||||
single-application lambda invoking the corresponding runtime primitive
|
||||
(`int_to_str`, `bool_to_str`, `str_clone`, `float_to_str`); no codegen
|
||||
intercept is required. The polymorphic helper `print : forall a. Show
|
||||
a => a -> () !IO` shipped in iter 24.3 with body
|
||||
The prelude ships `class Show a where show : (a borrow) -> Str` and
|
||||
primitive `Show Int`, `Show Bool`, `Show Str`, `Show Float`
|
||||
instances. Float **is** included in Show (unlike Eq/Ord) — IEEE-754
|
||||
makes structural equality and total ordering semantically dubious,
|
||||
but textual representation of a Float is well-defined modulo the
|
||||
NaN-spelling caveat in §"Float semantics". Each `Show <T>` instance
|
||||
body is a single-application lambda invoking the corresponding
|
||||
runtime primitive (`int_to_str`, `bool_to_str`, `str_clone`,
|
||||
`float_to_str`); no codegen intercept is required. The polymorphic
|
||||
helper `print : forall a. Show a => a -> () !IO` has body
|
||||
`\x -> let s = show x in do io/print_str s` (explicit let-binder for
|
||||
heap-Str RC discipline per eob.1 Str carve-out). The let-binder is
|
||||
structurally pinned by `crates/ail/tests/print_mono_body_shape.rs`.
|
||||
|
||||
Reference in New Issue
Block a user