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.
This commit is contained in:
+88
-87
@@ -37,7 +37,7 @@ fn build_and_run(example: &str) -> String {
|
||||
String::from_utf8(output.stdout).expect("stdout utf8")
|
||||
}
|
||||
|
||||
/// Iter 18b: build with an explicit `--alloc=<alloc>` and run.
|
||||
/// build with an explicit `--alloc=<alloc>` and run.
|
||||
/// Mirrors [`build_and_run`] but threads the allocator selector through
|
||||
/// to `ail build`. Used by the alloc-equivalence tests that assert the
|
||||
/// `gc` and `rc` paths produce byte-identical stdout.
|
||||
@@ -140,7 +140,7 @@ fn list_sum_via_match() {
|
||||
assert_eq!(stdout.trim(), "42");
|
||||
}
|
||||
|
||||
/// Iter 7: first-class function references — `apply(inc, 41)` must
|
||||
/// first-class function references — `apply(inc, 41)` must
|
||||
/// produce 42, exercising fn-typed parameters and indirect call.
|
||||
#[test]
|
||||
fn higher_order_apply_inc() {
|
||||
@@ -148,7 +148,7 @@ fn higher_order_apply_inc() {
|
||||
assert_eq!(stdout.trim(), "42");
|
||||
}
|
||||
|
||||
/// Iter 8b: lambdas with capture. `let n = 3 in apply(\x. x + n, 39)`
|
||||
/// lambdas with capture. `let n = 3 in apply(\x. x + n, 39)`
|
||||
/// must print 42 — the lambda captures `n` from the enclosing `let`,
|
||||
/// the env struct is malloc'd, the closure pair is passed to `apply`,
|
||||
/// and apply's indirect call unpacks both halves.
|
||||
@@ -158,7 +158,7 @@ fn closure_captures_let_n() {
|
||||
assert_eq!(stdout.trim(), "42");
|
||||
}
|
||||
|
||||
/// Iter 9 dogfood: a non-trivial program that combines ADTs, recursion,
|
||||
/// a non-trivial program that combines ADTs, recursion,
|
||||
/// closure-without-capture, fn-typed parameters, IO effects, and `let`-
|
||||
/// sequencing of effectful sub-expressions in a pattern-match arm.
|
||||
/// `map (\x. x * 2)` over `[1, 2, 3]` then print each element.
|
||||
@@ -169,7 +169,7 @@ fn list_map_doubles_then_prints() {
|
||||
assert_eq!(lines, vec!["2", "4", "6"]);
|
||||
}
|
||||
|
||||
/// Iter 14a: end-to-end exercise of parameterised ADTs through a
|
||||
/// end-to-end exercise of parameterised ADTs through a
|
||||
/// polymorphic higher-order fn. `data List a` plus
|
||||
/// `map : forall a b. ((a) -> b, List<a>) -> List<b>` recursive,
|
||||
/// instantiated at `(Int, Int)`. Guards: forall with two type vars +
|
||||
@@ -183,7 +183,7 @@ fn list_map_poly_inc_then_prints() {
|
||||
assert_eq!(lines, vec!["2", "3", "4"]);
|
||||
}
|
||||
|
||||
/// Iter 14f: stress the Boehm conservative GC integration end-to-end.
|
||||
/// stress the Boehm conservative GC integration end-to-end.
|
||||
/// `build 50` performs 50 `Cons` allocations via `GC_malloc`; `sum_list`
|
||||
/// walks the resulting `List Int` and prints the sum (1275 = 50*51/2).
|
||||
/// If GC is wired wrongly — missing `-lgc`, missing
|
||||
@@ -202,7 +202,7 @@ fn gc_handles_recursive_list_construction() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 17a: per-fn arena via stack `alloca` for non-escaping
|
||||
/// per-fn arena via stack `alloca` for non-escaping
|
||||
/// allocations. The fixture's `peek` and `count` fns each build a
|
||||
/// `Box(_)` whose payload is dropped via a wildcard pattern; the
|
||||
/// allocation is fully consumed locally and never flows to a fn
|
||||
@@ -264,7 +264,7 @@ fn iter17a_local_box_alloca() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Iter 14e: `tail: true` annotation on `print_list`'s recursive
|
||||
/// `tail: true` annotation on `print_list`'s recursive
|
||||
/// call must reach LLVM as a `musttail call`. Asserted by emitting IR
|
||||
/// for list_map_poly and grepping for the exact instruction. This is
|
||||
/// the only direct evidence that the type-system marker actually
|
||||
@@ -320,7 +320,7 @@ fn iter14e_print_list_recursion_emits_musttail() {
|
||||
panic!("musttail call line not found (sanity check)");
|
||||
}
|
||||
|
||||
/// Iter 11 dogfood: insertion sort over an 11-element IntList.
|
||||
/// insertion sort over an 11-element IntList.
|
||||
/// Exercises `<=`, `if`, mutual-leaf recursion (`insert` and `sort`),
|
||||
/// nested ctor construction, and the Iter 10 seq operator inside
|
||||
/// print_list. Validates that the language handles deeper ADT
|
||||
@@ -335,7 +335,7 @@ fn insertion_sort_orders_list() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 12b: polymorphism end-to-end. `id : forall a. (a) -> a`
|
||||
/// polymorphism end-to-end. `id : forall a. (a) -> a`
|
||||
/// gets called with `42` (Int) and `true` (Bool); each instantiation
|
||||
/// triggers a separate specialised LLVM fn. Output: 42, then "true".
|
||||
#[test]
|
||||
@@ -345,7 +345,7 @@ fn polymorphic_id_at_int_and_bool() {
|
||||
assert_eq!(lines, vec!["42", "true"]);
|
||||
}
|
||||
|
||||
/// Iter 12b: polymorphism with a function-typed parameter.
|
||||
/// polymorphism with a function-typed parameter.
|
||||
/// `apply : forall a b. ((a) -> b, a) -> b` invoked as
|
||||
/// `apply(succ, 41) == 42`. Exercises the closure-pair ABI inside a
|
||||
/// monomorphised body (a fn-typed param survives substitution).
|
||||
@@ -355,7 +355,7 @@ fn polymorphic_apply_with_fn_param() {
|
||||
assert_eq!(stdout.trim(), "42");
|
||||
}
|
||||
|
||||
/// Iter 13b: round-trip a primitive through a parameterised ADT and a
|
||||
/// round-trip a primitive through a parameterised ADT and a
|
||||
/// polymorphic-fn boundary. `type Box[a] = MkBox(a)` plus
|
||||
/// `unbox : forall a. (Box<a>) -> a` plus `print_int(unbox(MkBox(42)))`.
|
||||
/// Guards: ctor lower with substituted LLVM field types,
|
||||
@@ -367,7 +367,7 @@ fn parameterised_box_round_trip() {
|
||||
assert_eq!(stdout.trim(), "42");
|
||||
}
|
||||
|
||||
/// Iter 13b: pattern-match on `Maybe<Int>`. `or_else(Some(7), 99) == 7`
|
||||
/// pattern-match on `Maybe<Int>`. `or_else(Some(7), 99) == 7`
|
||||
/// then `or_else(None, 99) == 99`. Guards: match-arm field
|
||||
/// substitution at a parameterised ctor (the `Some(x)` arm must bind
|
||||
/// `x : Int`, not `x : a`).
|
||||
@@ -378,13 +378,14 @@ fn parameterised_maybe_match() {
|
||||
assert_eq!(lines, vec!["7", "99"]);
|
||||
}
|
||||
|
||||
/// Iter 15a: cross-module reference to a parameterised ADT, including
|
||||
/// cross-module reference to a parameterised ADT, including
|
||||
/// its ctors and a polymorphic combinator instantiated at `(Int, Int)`.
|
||||
/// `std_maybe_demo` imports `std_maybe`, qualifies the type-name slot
|
||||
/// of every `term-ctor` (`std_maybe.Maybe`), and exercises
|
||||
/// `from_maybe`, `is_some`, `is_none`, and `map_maybe` once each.
|
||||
/// Property protected: the qualified-only convention (Decision 6's
|
||||
/// architectural pin extended to types and ctors per the brief)
|
||||
/// Property protected: the qualified-only convention (the
|
||||
/// authoring surface's architectural pin extended to types and
|
||||
/// ctors per the brief)
|
||||
/// flows end-to-end through check, codegen, and runtime.
|
||||
#[test]
|
||||
fn cross_module_maybe_demo() {
|
||||
@@ -393,7 +394,7 @@ fn cross_module_maybe_demo() {
|
||||
assert_eq!(lines, vec!["7", "99", "true", "true", "42"]);
|
||||
}
|
||||
|
||||
/// Iter 15b: drives the polymorphic `std_list` combinators end-to-end.
|
||||
/// drives the polymorphic `std_list` combinators end-to-end.
|
||||
/// Exercises a recursive cross-module ADT (`std_list.List<a>`) consumed
|
||||
/// from a separate module that also imports `std_maybe`. Guards the
|
||||
/// `qualify_local_types` propagation in both `Term::Ctor` synth and
|
||||
@@ -412,7 +413,7 @@ fn std_list_demo() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 15c: empirical stress test for `std_list`'s folds at N=1000.
|
||||
/// empirical stress test for `std_list`'s folds at N=1000.
|
||||
///
|
||||
/// Property protected: `fold_left`'s tail-call marker actually
|
||||
/// survives monomorphisation and lowers to `musttail call` in the
|
||||
@@ -450,7 +451,7 @@ fn std_list_stress_1000_element_folds() {
|
||||
// `crates/ail/tests/roundtrip_cli.rs` (added in T1), which is strictly
|
||||
// stronger (whole-corpus vs. one-fixture).
|
||||
|
||||
/// Iter 16a: nested constructor patterns. Property protected:
|
||||
/// nested constructor patterns. Property protected:
|
||||
/// `ailang_core::desugar` flattens `Cons a (Cons b _)` to a chain of
|
||||
/// single-level matches before the checker/codegen sees it. Without
|
||||
/// the desugar pass the checker would emit
|
||||
@@ -463,7 +464,7 @@ fn nested_ctor_pattern_first_two_sum() {
|
||||
assert_eq!(lines, vec!["30"]);
|
||||
}
|
||||
|
||||
/// Iter 15f: `std_pair` end-to-end. Polymorphic product type with
|
||||
/// `std_pair` end-to-end. Polymorphic product type with
|
||||
/// two type vars and a single constructor; six combinators including
|
||||
/// the 3-type-var `map_first` / `map_second` reshapers. Property
|
||||
/// protected: `Pair<a, b> -> Pair<c, b>` and `Pair<a, b> -> Pair<a, c>`
|
||||
@@ -477,7 +478,7 @@ fn std_pair_demo() {
|
||||
assert_eq!(lines, vec!["7", "9", "9", "7", "8", "18"]);
|
||||
}
|
||||
|
||||
/// Iter 15d: `std_either` end-to-end. First stdlib ADT with two type
|
||||
/// `std_either` end-to-end. First stdlib ADT with two type
|
||||
/// parameters (`Either<e, a>`); first combinator with three type vars
|
||||
/// (`either : (e -> c) -> (a -> c) -> Either<e, a> -> c`).
|
||||
///
|
||||
@@ -497,7 +498,7 @@ fn std_either_demo() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 15g: `std_either_list` end-to-end. First stdlib fn set that
|
||||
/// `std_either_list` end-to-end. First stdlib fn set that
|
||||
/// imports three other stdlib modules (`std_list`, `std_either`,
|
||||
/// `std_pair`) and returns a compound polymorphic ADT tree
|
||||
/// (`partition_eithers : List<Either<e,a>> -> Pair<List<e>, List<a>>`).
|
||||
@@ -519,7 +520,7 @@ fn std_either_list_demo() {
|
||||
assert_eq!(lines, vec!["2", "3", "2", "3"]);
|
||||
}
|
||||
|
||||
/// Iter 15h: `std_list.take` and `std_list.drop` end-to-end. Both are
|
||||
/// `std_list.take` and `std_list.drop` end-to-end. Both are
|
||||
/// index-driven recursive ADT-pattern fns — the first `std_list`
|
||||
/// combinators that pair `if (<= n 0)` Int-arithmetic guards with a
|
||||
/// recursive Cons/Nil match. Property protected: the `if` base-case
|
||||
@@ -534,7 +535,7 @@ fn std_list_more_demo() {
|
||||
assert_eq!(lines, vec!["0", "3", "5", "5", "3", "0"]);
|
||||
}
|
||||
|
||||
/// Iter 16b.1: local recursive `let`. Property protected: a
|
||||
/// local recursive `let`. Property protected: a
|
||||
/// `(let-rec ...)` term in a fn body whose recursive body has no
|
||||
/// captures from the enclosing scope is lifted by the 16a desugar
|
||||
/// pass to a synthetic top-level fn (`<name>$lr_N`), and the
|
||||
@@ -552,7 +553,7 @@ fn local_rec_factorial_demo() {
|
||||
assert_eq!(lines, vec!["1", "6", "120"]);
|
||||
}
|
||||
|
||||
/// Iter 16b.2: LetRec capture of a fn-param (path-1 safe subset).
|
||||
/// LetRec capture of a fn-param (path-1 safe subset).
|
||||
/// Property protected: the desugar pass lifts a LetRec whose body
|
||||
/// captures one or more enclosing-scope names (here: `n` from
|
||||
/// `sum_below`'s params) into a synthetic top-level fn whose
|
||||
@@ -569,7 +570,7 @@ fn local_rec_capture_demo() {
|
||||
assert_eq!(lines, vec!["0", "10", "45"]);
|
||||
}
|
||||
|
||||
/// Iter 16b.3: LetRec capture of a `Term::Let`-bound name. Property
|
||||
/// LetRec capture of a `Term::Let`-bound name. Property
|
||||
/// protected: the desugar pass leaves a LetRec whose only outside-
|
||||
/// scope captures are Let-bound (type unknown until typecheck) in
|
||||
/// place; the post-typecheck `lift_letrecs` pass in `ailang-check`
|
||||
@@ -587,7 +588,7 @@ fn local_rec_let_capture_demo() {
|
||||
assert_eq!(lines, vec!["0", "5", "9"]);
|
||||
}
|
||||
|
||||
/// Iter 16b.4: LetRec capture of a Match-arm pattern binding.
|
||||
/// LetRec capture of a Match-arm pattern binding.
|
||||
/// Property protected: the desugar pass leaves a LetRec whose
|
||||
/// captures are `Pattern::Ctor`-Var-bound (here `threshold` and
|
||||
/// `n`, both fields of `MkPair` in `Pair Int Int`) in place; the
|
||||
@@ -607,7 +608,7 @@ fn local_rec_match_capture_demo() {
|
||||
assert_eq!(lines, vec!["0", "5", "9"]);
|
||||
}
|
||||
|
||||
/// Iter 16b.5: LetRec name as a value in the in-clause (no capture).
|
||||
/// LetRec name as a value in the in-clause (no capture).
|
||||
/// Property protected: the desugar pass detects a non-callee use of
|
||||
/// the LetRec name in `in_term` and wraps the rewritten in-term in
|
||||
/// `(let factorial (lam ...) ...)` whose lam eta-expands the lifted
|
||||
@@ -623,7 +624,7 @@ fn local_rec_as_value_demo() {
|
||||
assert_eq!(lines, vec!["120"]);
|
||||
}
|
||||
|
||||
/// Iter 16b.5: LetRec name as a value in the in-clause WITH capture.
|
||||
/// LetRec name as a value in the in-clause WITH capture.
|
||||
/// Property protected: the eta-Lam wrap composes correctly with the
|
||||
/// 16b.2 capture-augmentation. The lifted fn has signature
|
||||
/// `factorial_plus$lr_N(n: Int, base: Int) -> Int`; the eta-Lam
|
||||
@@ -639,7 +640,7 @@ fn local_rec_as_value_capture_demo() {
|
||||
assert_eq!(lines, vec!["1320", "12120"]);
|
||||
}
|
||||
|
||||
/// Iter 16b.6: LetRec inside a polymorphic enclosing fn.
|
||||
/// LetRec inside a polymorphic enclosing fn.
|
||||
/// Property protected: when the enclosing fn is `Forall(a). Fn(...)`,
|
||||
/// the desugar/lift pipeline now (a) enters fn-params as `KnownType`
|
||||
/// (not `LetBound`) so a LetRec inside CAN capture them, and (b)
|
||||
@@ -667,7 +668,7 @@ fn poly_rec_capture_demo() {
|
||||
assert_eq!(lines, vec!["5", "false"]);
|
||||
}
|
||||
|
||||
/// Iter 16b.7: nested LetRec where the inner one captures the OUTER
|
||||
/// nested LetRec where the inner one captures the OUTER
|
||||
/// LetRec's PARAMS (not its name).
|
||||
///
|
||||
/// Property protected: when an inner LetRec lifts inside an outer
|
||||
@@ -868,7 +869,7 @@ fn workspace_lists_imported_modules() {
|
||||
);
|
||||
|
||||
let modules = v["modules"].as_array().expect("modules must be array");
|
||||
// Iter 23.1: the loader auto-injects the `prelude` module, so
|
||||
// the loader auto-injects the `prelude` module, so
|
||||
// the count is the user's two modules plus prelude.
|
||||
assert_eq!(modules.len(), 3, "expected 3 modules: {stdout}");
|
||||
|
||||
@@ -930,7 +931,7 @@ fn ordering_match_via_prelude_prints_1() {
|
||||
assert_eq!(stdout, "1\n");
|
||||
}
|
||||
|
||||
/// Iter 23.3 Task 4 (resumed in ct.4): the end-to-end
|
||||
/// the end-to-end
|
||||
/// demonstration that the iter-23.1 cross-module Type::Con
|
||||
/// mismatch bug is closed. `examples/compare_primitives_smoke.ail.json`
|
||||
/// imports prelude, calls `compare` on three pairs each of
|
||||
@@ -949,7 +950,7 @@ fn compare_primitives_smoke_prints_1_2_3_thrice() {
|
||||
assert_eq!(stdout, "1\n2\n3\n1\n2\n3\n1\n2\n3\n");
|
||||
}
|
||||
|
||||
/// Iter 23.2: end-to-end coverage for the auto-loaded `Eq` class
|
||||
/// end-to-end coverage for the auto-loaded `Eq` class
|
||||
/// and its three primitive instances. The fixture calls `eq` on
|
||||
/// Int / Bool / Str with both an equal and an unequal pair each;
|
||||
/// the monomorphiser synthesises `eq__Int` / `eq__Bool` /
|
||||
@@ -1272,7 +1273,7 @@ fn check_json_unbound_var() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 16c: literal patterns at top level and inside Ctor
|
||||
/// literal patterns at top level and inside Ctor
|
||||
/// sub-patterns. Property protected: the 16a desugar pass rewrites
|
||||
/// every `Pattern::Lit` to a `Term::If` against the scrutinee/field
|
||||
/// before either typecheck or codegen sees it. Without 16c, the
|
||||
@@ -1281,7 +1282,7 @@ fn check_json_unbound_var() {
|
||||
/// the path. The fixture exercises both sites: `classify` (top-
|
||||
/// level lit arms) and `categorize_first` (nested lit-in-Ctor).
|
||||
///
|
||||
/// Iter 16d update: the trailing `(case _ 0)` arm of
|
||||
/// the trailing `(case _ 0)` arm of
|
||||
/// `categorize_first` was removed when the chain machinery's
|
||||
/// terminator switched from a `Unit` literal to the polymorphic
|
||||
/// `__unreachable__` builtin. Output is unchanged — this test
|
||||
@@ -1294,7 +1295,7 @@ fn lit_pat_demo() {
|
||||
assert_eq!(lines, vec!["100", "200", "999", "-1", "0", "7"]);
|
||||
}
|
||||
|
||||
/// Iter 16d: `__unreachable__` as an explicit user-callable
|
||||
/// `__unreachable__` as an explicit user-callable
|
||||
/// primitive. Property protected: a polymorphic `forall a. a` value
|
||||
/// reference (a) typechecks against any expected type at the use
|
||||
/// site (here `Int`), and (b) codegen lowers it to LLVM
|
||||
@@ -1309,7 +1310,7 @@ fn unreachable_demo() {
|
||||
assert_eq!(lines, vec!["4", "5"]);
|
||||
}
|
||||
|
||||
/// Iter 18a: `(borrow T)` and `(own T)` mode annotations on
|
||||
/// `(borrow T)` and `(own T)` mode annotations on
|
||||
/// fn-type parameters. Properties protected:
|
||||
/// (1) the parser accepts `(borrow ...)` and `(own ...)` only as
|
||||
/// wrappers in fn-type param/ret slots and round-trips them
|
||||
@@ -1366,7 +1367,7 @@ fn borrow_own_demo_modes_are_metadata_only() {
|
||||
assert_eq!(lines, vec!["3", "6"]);
|
||||
}
|
||||
|
||||
/// Iter 16e: polymorphic `==`. Properties protected:
|
||||
/// polymorphic `==`. Properties protected:
|
||||
/// (1) `==` typechecks at Int / Bool / Str / Unit (the fixture
|
||||
/// uses every supported case directly via `(app == ...)`);
|
||||
/// (2) codegen dispatches each arg-type to the right LLVM shape
|
||||
@@ -1392,7 +1393,7 @@ fn eq_demo() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 18b: --alloc=rc routes allocation through ailang_rc_alloc
|
||||
/// --alloc=rc routes allocation through ailang_rc_alloc
|
||||
/// (8-byte refcount header, libc malloc backing). With no inc/dec
|
||||
/// emission yet (18c work), programs leak under this mode but must
|
||||
/// still produce correct stdout — that's the validation 18b ships.
|
||||
@@ -1406,7 +1407,7 @@ fn alloc_rc_produces_same_stdout_as_gc() {
|
||||
assert_eq!(stdout_rc.trim(), "42");
|
||||
}
|
||||
|
||||
/// Iter 18c.1: `Term::Clone` is a pure schema addition. In 18c.1 the
|
||||
/// `Term::Clone` is a pure schema addition. In 18c.1 the
|
||||
/// wrapper is identity for both typechecker (same type as inner) and
|
||||
/// codegen (same SSA reg, no extra IR) — programs that contain
|
||||
/// `(clone X)` produce the same stdout they would have produced
|
||||
@@ -1418,7 +1419,7 @@ fn clone_demo_is_identity_in_18c1() {
|
||||
assert_eq!(stdout.trim(), "42");
|
||||
}
|
||||
|
||||
/// Iter 18d.2: under `--alloc=rc`, `Term::ReuseAs { source, body =
|
||||
/// under `--alloc=rc`, `Term::ReuseAs { source, body =
|
||||
/// Term::Ctor }` lowers to a runtime refcount-1 dispatch — when the
|
||||
/// source's box is unique we overwrite it in place (skipping the
|
||||
/// alloc + cascade-dec round-trip); otherwise we fall back to a
|
||||
@@ -1497,7 +1498,7 @@ fn reuse_as_demo_under_rc_uses_inplace_rewrite() {
|
||||
root_dir: ws.root_dir.clone(),
|
||||
registry: ws.registry.clone(),
|
||||
};
|
||||
// Iter rpe.1: post-print-migration, fixtures use `(app print x)`
|
||||
// post-print-migration, fixtures use `(app print x)`
|
||||
// which monomorphises to `print__<T>`. Adding the mono pass here
|
||||
// mirrors `ail build`'s pipeline (desugar -> lift -> mono ->
|
||||
// lower); without it, lowering errors with `UnknownVar("print")`.
|
||||
@@ -1537,7 +1538,7 @@ fn reuse_as_demo_under_rc_uses_inplace_rewrite() {
|
||||
!reuse_arm.contains("@ailang_rc_alloc"),
|
||||
"reuse arm must not call @ailang_rc_alloc (the slot is reused in place). Reuse arm IR was:\n{reuse_arm}"
|
||||
);
|
||||
// Iter 18d.3: in this canonical fixture the pattern `(Cons h t)`
|
||||
// in this canonical fixture the pattern `(Cons h t)`
|
||||
// moves the only pointer-typed slot of the source's old ctor
|
||||
// (slot 1, the tail; slot 0 is Int and never a dec candidate).
|
||||
// The reuse arm therefore must emit NEITHER a per-field drop
|
||||
@@ -1556,7 +1557,7 @@ fn reuse_as_demo_under_rc_uses_inplace_rewrite() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 18b: extends `alloc_rc_produces_same_stdout_as_gc` to a larger
|
||||
/// extends `alloc_rc_produces_same_stdout_as_gc` to a larger
|
||||
/// fixture (`std_list_demo`) so more allocation sites — folds, maps,
|
||||
/// cross-module ctors — are exercised under `--alloc=rc`. Same
|
||||
/// invariant: stdout must be byte-identical to the `gc` build.
|
||||
@@ -1571,7 +1572,7 @@ fn alloc_rc_matches_gc_on_std_list_demo() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 18c.3: codegen actually emits `ailang_rc_dec` at end-of-scope
|
||||
/// codegen actually emits `ailang_rc_dec` at end-of-scope
|
||||
/// for trackable RC binders under `--alloc=rc`. This is the first
|
||||
/// fixture where the `dec` path runs at runtime — `b` is bound to a
|
||||
/// heap-allocated `MkBox`, the box is read via `match` (a borrow,
|
||||
@@ -1605,7 +1606,7 @@ fn alloc_rc_emits_dec_for_unique_let_bound_box() {
|
||||
assert_eq!(stdout_gc, stdout_rc, "alloc=rc must match alloc=gc on rc_box_drop");
|
||||
}
|
||||
|
||||
/// Iter 18c.4: per-type drop fns + recursive `dec` cascade. Builds a
|
||||
/// per-type drop fns + recursive `dec` cascade. Builds a
|
||||
/// 5-element `IntList` and reduces it via `sum_list`. Under
|
||||
/// `--alloc=rc` the codegen emits `define void @drop_<m>_IntList`
|
||||
/// whose `Cons` arm recursively calls itself on the `tail` field —
|
||||
@@ -1634,7 +1635,7 @@ fn alloc_rc_recursive_list_sum() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 18c.4 / 18d.4: exercise the recursive drop cascade at
|
||||
/// exercise the recursive drop cascade at
|
||||
/// runtime AND the arm-close pattern-binder dec.
|
||||
///
|
||||
/// 18c.4 originally used this fixture to lock in the recursive
|
||||
@@ -1679,8 +1680,8 @@ fn alloc_rc_borrow_only_recursive_list_drop() {
|
||||
"alloc=rc must match alloc=gc on rc_list_drop_borrow"
|
||||
);
|
||||
|
||||
// Iter 18d.4 IR-shape assertion: the Cons arm of main's match
|
||||
// emits a drop on `t` after lowering the body. We re-lower under
|
||||
// IR-shape assertion: the Cons arm of main's match emits a drop
|
||||
// on `t` after lowering the body. We re-lower under
|
||||
// rc and inspect main's IR — the arm sequence must contain
|
||||
// call void @ai/print_int(...) (the `print h`)
|
||||
// call void @drop_rc_list_drop_borrow_IntList(ptr %v...) (NEW: 18d.4 t-drop)
|
||||
@@ -1703,7 +1704,7 @@ fn alloc_rc_borrow_only_recursive_list_drop() {
|
||||
root_dir: ws.root_dir.clone(),
|
||||
registry: ws.registry.clone(),
|
||||
};
|
||||
// Iter rpe.1: post-print-migration, fixtures use `(app print x)`
|
||||
// post-print-migration, fixtures use `(app print x)`
|
||||
// which monomorphises to `print__<T>`. Adding the mono pass here
|
||||
// mirrors `ail build`'s pipeline (desugar -> lift -> mono ->
|
||||
// lower); without it, lowering errors with `UnknownVar("print")`.
|
||||
@@ -1731,7 +1732,7 @@ fn alloc_rc_borrow_only_recursive_list_drop() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 18d.3: move-aware pattern bindings — let-close inlines a
|
||||
/// move-aware pattern bindings — let-close inlines a
|
||||
/// per-field dec sequence that skips moved slots and dec's non-moved
|
||||
/// (e.g. wildcarded) pointer-typed slots.
|
||||
///
|
||||
@@ -1785,7 +1786,7 @@ fn alloc_rc_partial_drop_skips_moved_keeps_wildcarded() {
|
||||
root_dir: ws.root_dir.clone(),
|
||||
registry: ws.registry.clone(),
|
||||
};
|
||||
// Iter rpe.1: post-print-migration, fixtures use `(app print x)`
|
||||
// post-print-migration, fixtures use `(app print x)`
|
||||
// which monomorphises to `print__<T>`. Adding the mono pass here
|
||||
// mirrors `ail build`'s pipeline (desugar -> lift -> mono ->
|
||||
// lower); without it, lowering errors with `UnknownVar("print")`.
|
||||
@@ -1828,7 +1829,7 @@ fn alloc_rc_partial_drop_skips_moved_keeps_wildcarded() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 18d.4: Own-param dec at fn return. Symmetric to 18c.3/18c.4's
|
||||
/// Own-param dec at fn return. Symmetric to 18c.3/18c.4's
|
||||
/// `Term::Let`-scope-close drop emission and 18d.4's arm-close
|
||||
/// pattern-binder dec, fired at the lexical close of a fn body. The
|
||||
/// `head_or_zero` fn declares its only parameter `xs` as
|
||||
@@ -1881,7 +1882,7 @@ fn alloc_rc_own_param_dec_at_fn_return() {
|
||||
root_dir: ws.root_dir.clone(),
|
||||
registry: ws.registry.clone(),
|
||||
};
|
||||
// Iter rpe.1: post-print-migration, fixtures use `(app print x)`
|
||||
// post-print-migration, fixtures use `(app print x)`
|
||||
// which monomorphises to `print__<T>`. Adding the mono pass here
|
||||
// mirrors `ail build`'s pipeline (desugar -> lift -> mono ->
|
||||
// lower); without it, lowering errors with `UnknownVar("print")`.
|
||||
@@ -1947,7 +1948,7 @@ fn alloc_rc_own_param_dec_at_fn_return() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 18e: `(drop-iterative)` opt-in annotation. The fixture
|
||||
/// `(drop-iterative)` opt-in annotation. The fixture
|
||||
/// declares `IntList` with `(drop-iterative)` and runs an N=1,000,000
|
||||
/// cell list through `head_or_zero`. The fn signature is
|
||||
/// `(own (con IntList))` — 18d.4 emits a drop on the param at fn
|
||||
@@ -1987,7 +1988,7 @@ fn alloc_rc_drop_iterative_handles_million_cell_list() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 18e: IR-shape signature of `(drop-iterative)`. The drop fn
|
||||
/// IR-shape signature of `(drop-iterative)`. The drop fn
|
||||
/// for the annotated type contains a worklist loop (`br label
|
||||
/// %loop_head`, the runtime-helper calls, a backedge from each ctor
|
||||
/// arm) and does NOT contain a recursive `call void @drop_<m>_<T>(...)`
|
||||
@@ -2013,7 +2014,7 @@ fn iter18e_drop_iterative_emits_worklist_body_no_self_recursion() {
|
||||
root_dir: ws.root_dir.clone(),
|
||||
registry: ws.registry.clone(),
|
||||
};
|
||||
// Iter rpe.1: post-print-migration, fixtures use `(app print x)`
|
||||
// post-print-migration, fixtures use `(app print x)`
|
||||
// which monomorphises to `print__<T>`. Adding the mono pass here
|
||||
// mirrors `ail build`'s pipeline (desugar -> lift -> mono ->
|
||||
// lower); without it, lowering errors with `UnknownVar("print")`.
|
||||
@@ -2071,7 +2072,7 @@ fn iter18e_drop_iterative_emits_worklist_body_no_self_recursion() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 18e: control — the same fixture WITHOUT the
|
||||
/// control — the same fixture WITHOUT the
|
||||
/// `(drop-iterative)` annotation must still emit the recursive
|
||||
/// 18c.4 body. We synthesise the unannotated module on the fly
|
||||
/// (mutating the workspace's parsed AST) so this test is independent
|
||||
@@ -2103,7 +2104,7 @@ fn iter18e_no_annotation_keeps_recursive_drop_body() {
|
||||
root_dir: ws.root_dir.clone(),
|
||||
registry: ws.registry.clone(),
|
||||
};
|
||||
// Iter rpe.1: post-print-migration, fixtures use `(app print x)`
|
||||
// post-print-migration, fixtures use `(app print x)`
|
||||
// which monomorphises to `print__<T>`. Adding the mono pass here
|
||||
// mirrors `ail build`'s pipeline (desugar -> lift -> mono ->
|
||||
// lower); without it, lowering errors with `UnknownVar("print")`.
|
||||
@@ -2137,7 +2138,7 @@ fn iter18e_no_annotation_keeps_recursive_drop_body() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 18d.4 regression: pattern-binder dec at arm close (Iter A in
|
||||
/// pattern-binder dec at arm close (Iter A in
|
||||
/// the 18d.4 emission seam) was firing on pattern-bound pointer
|
||||
/// fields whose enclosing fn is Implicit-mode. The dec freed memory
|
||||
/// the caller still referenced, producing refcount underflow or
|
||||
@@ -2174,7 +2175,7 @@ fn alloc_rc_pattern_bind_in_implicit_fn_does_not_dec_borrowed_children() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 18g.0: build the example under `--alloc=rc`, run with
|
||||
/// build the example under `--alloc=rc`, run with
|
||||
/// `AILANG_RC_STATS=1`, and return the parsed `(allocs, frees, live)`
|
||||
/// triple from the runtime's atexit summary on stderr. The summary
|
||||
/// line shape is fixed by `runtime/rc.c`'s `ailang_rc_stats_atexit`:
|
||||
@@ -2246,7 +2247,7 @@ fn build_and_run_with_rc_stats(example: &str) -> (String, u64, u64, i64) {
|
||||
)
|
||||
}
|
||||
|
||||
/// Iter 18g.1 regression: explicit-mode tail-recursive list-sum must
|
||||
/// explicit-mode tail-recursive list-sum must
|
||||
/// not leak the LCons outer cells.
|
||||
///
|
||||
/// Background: 18f.2's tail-latency bench found that
|
||||
@@ -2287,7 +2288,7 @@ fn alloc_rc_explicit_mode_tail_sum_does_not_leak_outer_cells() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 18g.2 regression: a `let`-binder whose value is the result of
|
||||
/// a `let`-binder whose value is the result of
|
||||
/// an Own-returning function call must be dropped at let-scope close.
|
||||
///
|
||||
/// Background: 18c.3's `is_rc_heap_allocated` returns `false` for any
|
||||
@@ -2324,7 +2325,7 @@ fn alloc_rc_let_binder_for_owned_returning_app_drops_at_scope_close() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 18g tidy negative-coverage: a let-binder whose value is the
|
||||
/// a let-binder whose value is the
|
||||
/// result of an `Implicit`-ret-mode (default, unannotated) call must
|
||||
/// NOT be trackable. Implicit is the back-compat lane that 18c.3
|
||||
/// documented as "params don't get any dec — leak rather than mis-
|
||||
@@ -2364,7 +2365,7 @@ fn alloc_rc_let_binder_for_implicit_returning_app_does_not_drop() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 18g tidy follow-up: let-alias-aware mode propagation.
|
||||
/// let-alias-aware mode propagation.
|
||||
///
|
||||
/// 18d.4 Iter A's fix gated arm-close pattern-binder dec on the
|
||||
/// scrutinee's `current_param_modes` lookup, but only fn-params
|
||||
@@ -2402,8 +2403,8 @@ fn alloc_rc_let_alias_of_implicit_param_does_not_dec_borrowed_children() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 18g.tidy.fu2 regression — RED-then-GREEN for the
|
||||
/// dynamic-tag partial-drop carve-out at fn-return.
|
||||
/// Regression — RED-then-GREEN for the dynamic-tag partial-drop
|
||||
/// carve-out at fn-return.
|
||||
///
|
||||
/// Three carve-out sites previously fell back to a shallow
|
||||
/// `ailang_rc_dec` when a binder's `moved_slots` was non-empty
|
||||
@@ -2451,8 +2452,7 @@ fn alloc_rc_partial_drop_at_fn_return_does_not_leak_unmoved_fields() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 18g.tidy.fu2 regression — site 2: Iter A arm-close
|
||||
/// pattern-binder dec (match_lower.rs).
|
||||
/// Arm-close pattern-binder dec (match_lower.rs).
|
||||
///
|
||||
/// An outer match's arm-bound pattern-binder may itself be the
|
||||
/// scrutinee of an inner match that moves out some of its fields.
|
||||
@@ -2489,7 +2489,7 @@ fn alloc_rc_partial_drop_at_arm_close_does_not_leak_unmoved_fields() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 18g.tidy.fu2 regression — site 3: let-close for an
|
||||
/// let-close for an
|
||||
/// App-bound binder (drop.rs `emit_inlined_partial_drop`
|
||||
/// non-Ctor branch).
|
||||
///
|
||||
@@ -2523,7 +2523,7 @@ fn alloc_rc_partial_drop_at_app_let_close_does_not_leak_unmoved_fields() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 20d: `ail merge-prose` reads two files and prints a prompt
|
||||
/// `ail merge-prose` reads two files and prints a prompt
|
||||
/// that frames the prose-round-trip task for an external LLM. Smoke
|
||||
/// test: writes two temp inputs, invokes the binary, asserts exit 0
|
||||
/// and that the captured stdout carries the role-statement landmark
|
||||
@@ -2537,7 +2537,7 @@ fn merge_prose_prints_framed_prompt() {
|
||||
std::fs::create_dir_all(&tmp).unwrap();
|
||||
let orig_path = tmp.join("orig.ail.json");
|
||||
let prose_path = tmp.join("edited.prose.txt");
|
||||
// Iter 20f: merge-prose now loads the module via ailang_core,
|
||||
// merge-prose now loads the module via ailang_core,
|
||||
// renders to Form-A, and embeds *that* in the prompt — so the
|
||||
// input must be a well-formed ailang/v0 JSON-AST that
|
||||
// load_module accepts. The schema, name, and an empty defs list
|
||||
@@ -2681,7 +2681,7 @@ fn ail_run_accepts_ail_source_with_same_stdout_as_ail_json() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter hs.4: `int_to_str(42)` prints "42\n" through the standard
|
||||
/// `int_to_str(42)` prints "42\n" through the standard
|
||||
/// `do io/print_str(...)` path. Smoke pin for the heap-Str builtin
|
||||
/// wired in hs.4 — exercises checker (signature install), codegen
|
||||
/// (lower_app arm + IR-header declare + is_static_callee whitelist),
|
||||
@@ -2696,7 +2696,7 @@ fn int_to_str_smoke() {
|
||||
assert_eq!(out, "42\n");
|
||||
}
|
||||
|
||||
/// Iter hs.4: `float_to_str(3.5)` prints a libc-%g-conformant
|
||||
/// `float_to_str(3.5)` prints a libc-%g-conformant
|
||||
/// rendering of 3.5. The runtime's `ailang_float_to_str` uses
|
||||
/// `snprintf(..., "%g", x)` with no locale call, so the C locale
|
||||
/// default applies and the dev target's glibc produces the three
|
||||
@@ -2744,11 +2744,11 @@ fn str_field_in_adt_drops_heap_str_correctly() {
|
||||
assert_eq!(live, 0, "no leaked RC slabs allowed; live={live}");
|
||||
}
|
||||
|
||||
/// Iter eob.1 / rpe.1: primitive-Int passed to the polymorphic
|
||||
/// `print` helper. Pre-rpe.1 the fixture used `(do io/print_int n)`
|
||||
/// directly and the iter-eob.1 "Term::Do args = Borrow" rule meant
|
||||
/// primitive-Int passed to the polymorphic
|
||||
/// `print` helper. Pre-per-type-print-retirement the fixture used `(do io/print_int n)`
|
||||
/// directly and the heap-Str-ABI "Term::Do args = Borrow" rule meant
|
||||
/// zero RC traffic on the unboxed Int (allocs == 0, frees == 0).
|
||||
/// Post-rpe.1, `print n` desugars through `Show Int.show` →
|
||||
/// Post-per-type-print-retirement, `print n` desugars through `Show Int.show` →
|
||||
/// `int_to_str` (heap-Str alloc, `ret_mode: Own`) → `io/print_str`
|
||||
/// → slab drops at scope close. Pin shifts to the new canonical
|
||||
/// post-iter shape: exactly one heap-Str slab cycle per `print`
|
||||
@@ -2761,14 +2761,15 @@ fn int_arg_to_effect_op_does_not_rc_track() {
|
||||
let (stdout, allocs, frees, live) =
|
||||
build_and_run_with_rc_stats("int_to_print_int_borrow.ail");
|
||||
assert_eq!(stdout, "7\n");
|
||||
// Post-rpe.1: print n for n : Int allocates one heap-Str slab
|
||||
// via Show Int → int_to_str, frees it at scope close. Spec §E.
|
||||
// After the per-type-print-op retirement, `print n` for n : Int
|
||||
// allocates one heap-Str slab via Show Int → int_to_str, frees
|
||||
// it at scope close. Spec §E.
|
||||
assert_eq!(allocs, 1, "expected exactly one Show-Int heap-Str slab; got allocs={allocs}");
|
||||
assert_eq!(frees, 1, "expected exactly one free; got frees={frees}");
|
||||
assert_eq!(live, 0, "no leaked RC slabs allowed; live={live}");
|
||||
}
|
||||
|
||||
/// Iter eob.1: heap-Str let-binder consumed by two `io/print_str`
|
||||
/// heap-Str let-binder consumed by two `io/print_str`
|
||||
/// calls in sequence. Pins the linearity-side consequence of the new
|
||||
/// rule: under the old Position::Consume walk for Term::Do args, the
|
||||
/// second print would have triggered `use-after-consume`. Under the
|
||||
@@ -2786,7 +2787,7 @@ fn heap_str_repeated_print_balances_rc_stats() {
|
||||
assert_eq!(live, 0, "no leaked heap-Str slabs allowed; live={live}");
|
||||
}
|
||||
|
||||
/// Iter 24.1: `bool_to_str(true)` bound to a let, consumed by
|
||||
/// `bool_to_str(true)` bound to a let, consumed by
|
||||
/// `io/print_str`. The heap-Str slab from `ailang_bool_to_str`
|
||||
/// rides the same rc_header + ailang_rc_dec path as int_to_str's
|
||||
/// output — `allocs == 1, frees == 1, live == 0` and stdout is
|
||||
@@ -2802,7 +2803,7 @@ fn bool_to_str_drop_balances_rc_stats() {
|
||||
assert_eq!(live, 0, "no leaked heap-Str slabs allowed; live={live}");
|
||||
}
|
||||
|
||||
/// Iter 24.1: stdout-smoke for the true branch — same fixture as
|
||||
/// stdout-smoke for the true branch — same fixture as
|
||||
/// `bool_to_str_drop_balances_rc_stats` but checked here without
|
||||
/// the RC-stats overhead. Pins the byte content of `ailang_bool_to_str`'s
|
||||
/// "true" slab.
|
||||
@@ -2812,7 +2813,7 @@ fn bool_to_str_emits_true_branch() {
|
||||
assert_eq!(out, "true\n");
|
||||
}
|
||||
|
||||
/// Iter 24.1: stdout-smoke for the false branch. Pins the byte
|
||||
/// stdout-smoke for the false branch. Pins the byte
|
||||
/// content of `ailang_bool_to_str`'s "false" slab.
|
||||
#[test]
|
||||
fn bool_to_str_emits_false_branch() {
|
||||
@@ -2820,7 +2821,7 @@ fn bool_to_str_emits_false_branch() {
|
||||
assert_eq!(out, "false\n");
|
||||
}
|
||||
|
||||
/// Iter 24.1: `str_clone("hello")` bound to a let, consumed by
|
||||
/// `str_clone("hello")` bound to a let, consumed by
|
||||
/// `io/print_str`. The heap-Str clone allocates a fresh slab (via
|
||||
/// str_alloc) and the let-binder drops it at scope close.
|
||||
/// `allocs == 1, frees == 1, live == 0`; stdout is `hello\n` (puts
|
||||
@@ -2835,7 +2836,7 @@ fn str_clone_drop_balances_rc_stats() {
|
||||
assert_eq!(live, 0, "no leaked heap-Str slabs allowed; live={live}");
|
||||
}
|
||||
|
||||
/// Iter 24.1: cross-realisation invariant — `str_clone` works
|
||||
/// cross-realisation invariant — `str_clone` works
|
||||
/// uniformly on heap-Str input (`int_to_str 42`'s output) AND on
|
||||
/// static-Str input (literal `"abc"`). Both clones print their
|
||||
/// input bytes; RC stats account for three heap-Str slabs (one
|
||||
|
||||
Reference in New Issue
Block a user