diff --git a/crates/ail/src/main.rs b/crates/ail/src/main.rs index 4db2687..781b9c4 100644 --- a/crates/ail/src/main.rs +++ b/crates/ail/src/main.rs @@ -137,10 +137,10 @@ enum Cmd { /// `runtime/rc.c`'s `@ailang_rc_alloc` (libc-malloc backing + /// 8-byte refcount header) with `ailang_rc_inc`/`_dec` /// instrumentation emitted by codegen; this is the runtime - /// AILang's memory model (RC + uniqueness, Decision 10) is + /// AILang's memory model (RC + uniqueness) is /// designed for. `gc` retains the Boehm conservative GC path /// (`@GC_malloc`, libgc); it is kept as a differential parity - /// oracle for codegen diagnosis (Decision 9). `bump` is a + /// oracle for codegen diagnosis (the transitional dual-allocator). `bump` is a /// bench-only no-free stub from `runtime/bump.c` — it leaks /// every allocation by design. #[arg(long, default_value = "rc", value_parser = ["gc", "bump", "rc"])] @@ -199,7 +199,7 @@ enum Cmd { /// Loads a workspace (entry module + transitive imports) and lists /// all reachable modules with hash and def count. /// - /// Iter 5a: listing only. Cross-module typecheck/codegen follow in + /// listing only. Cross-module typecheck/codegen follow in /// 5b/5c; existing subcommands continue to work per single module. Workspace { entry: PathBuf, @@ -217,14 +217,14 @@ enum Cmd { #[arg(short, long)] output: Option, }, - /// Iter 20a: prints the module as human-readable prose (form B). + /// prints the module as human-readable prose (form B). /// /// One-way projection: the renderer is deterministic, but no /// parser exists for the prose surface. The canonical authoring /// surface remains form (A) (`render` / `parse`) and the canonical /// hashable artefact remains the JSON-AST. Prose { path: PathBuf }, - /// Iter 20d: composes a prompt for the prose-edit round-trip. + /// composes a prompt for the prose-edit round-trip. /// /// Given the original `.ail.json` and the edited `.prose.txt`, /// prints a prompt that asks an external LLM to emit an updated @@ -241,7 +241,7 @@ enum Cmd { /// Edited `.prose.txt` (carries the human's intent). edited: PathBuf, }, - /// ct.1 (dev-only): rewrite every `*.ail.json` under `` so + /// Dev-only: rewrite every `*.ail.json` under `` so /// that bare cross-module `Type::Con` and `Term::Ctor.type_name` /// references gain their owning module's qualifier. Idempotent. /// @@ -268,10 +268,10 @@ enum Cmd { /// string is the role-statement, contract, and output spec that frame /// the LLM's task. /// -/// Iter 20f rewrote this function: the prompt now embeds +/// the prompt now embeds /// [`ailang_core::FORM_A_SPEC`] (the language reference an LLM needs /// to generate AILang from scratch) and the original module as Form-A -/// (the canonical authoring surface, Decision 6) instead of JSON-AST. +/// (the canonical authoring surface) instead of JSON-AST. /// JSON-AST stays the canonical hashable artefact, but no human or /// LLM should write it directly — `ail parse` converts the LLM's /// Form-A output to JSON. @@ -444,15 +444,15 @@ fn main() -> Result<()> { print!("{}", ailang_surface::print(&m)); } Cmd::Prose { path } => { - // Iter 20a: load via `load_module` (single-module mode, + // load via `load_module` (single-module mode, // matching how `render` / `parse` work). Workspace-wide // prose is not in scope for 20a. let m = ailang_surface::load_module(&path)?; print!("{}", ailang_prose::module_to_prose(&m)); } Cmd::MergeProse { original, edited } => { - // Iter 20f: load the original .ail.json, render it as - // Form-A (the canonical authoring surface, Decision 6), + // load the original .ail.json, render it as + // Form-A (the canonical authoring surface), // and embed *that* in the prompt — not the raw JSON-AST. // Form-A is the form an LLM should produce; JSON is the // hashable artefact, not the writing surface. The prose @@ -582,7 +582,7 @@ fn main() -> Result<()> { } } Cmd::Check { path, json } => { - // Iter 5b: `ail check` now **always** loads via + // `ail check` now **always** loads via // `load_workspace` and checks cross-module. For modules // without imports, the workspace loader behaves equivalently // to `load_module` plus a hash consistency check of the @@ -626,7 +626,7 @@ fn main() -> Result<()> { d.message, ); } - // Iter 19a: only Error-severity diagnostics fail the + // only Error-severity diagnostics fail the // command. Warning-severity diagnostics (e.g. the // `over-strict-mode` lint) print to stderr but the // module is still considered to have typechecked. @@ -646,7 +646,7 @@ fn main() -> Result<()> { } } Cmd::EmitIr { path, out, emit } => { - // Iter 5c: workspace lowering. For single-module programs the + // workspace lowering. For single-module programs the // workspace is effectively a trivial workspace with one module. let ws = load_workspace_human(&path)?; let diags = ailang_check::check_workspace(&ws); @@ -666,7 +666,7 @@ fn main() -> Result<()> { d.message, ); } - // Iter 19a: only Error-severity blocks codegen. + // only Error-severity blocks codegen. if diags .iter() .any(|d| matches!(d.severity, ailang_check::Severity::Error)) @@ -674,7 +674,7 @@ fn main() -> Result<()> { std::process::exit(1); } } - // Iter 23.4: emit-ir must run the same pre-codegen pipeline + // emit-ir must run the same pre-codegen pipeline // as `build` — `lift_letrecs` per module, then // `monomorphise_workspace`. Pre-iter-23.4 this was implicit: // codegen's poly-call path handled specialisation internally. @@ -726,7 +726,7 @@ fn main() -> Result<()> { } } Cmd::Run { path, opt, alloc, args } => { - // Iter 9b: build into a fresh tempdir per run, exec, propagate + // build into a fresh tempdir per run, exec, propagate // exit code. The artefact dir is left around (no cleanup) so // it can be inspected in case of a crash; OS temp policy // collects them. @@ -1098,9 +1098,10 @@ fn workspace_error_to_diagnostic( "module": name, })), ), - // Iter 22b.1: typeclass-coherence diagnostics emitted from + // typeclass-coherence diagnostics emitted from // `workspace::build_registry`. The codes follow the JOURNAL - // queue's wording and Decision 11 §"Diagnostic categories". + // queue's wording and the diagnostic-categories section of + // `design/contracts/typeclasses.md`. W::OrphanInstance { class, type_repr, @@ -1231,7 +1232,7 @@ fn workspace_error_to_diagnostic( "type": type_repr, })), ), - // Iter 23.1: the user named a module `prelude`, which the + // the user named a module `prelude`, which the // loader reserves for the auto-injected prelude module. W::ReservedModuleName { name } => Some( ailang_check::Diagnostic::error( @@ -1244,7 +1245,7 @@ fn workspace_error_to_diagnostic( "module": name, })), ), - // ct.1 (canonical-type-names): bare `Type::Con` that does not + // the canonical-form rule for type references: bare `Type::Con` that does not // resolve to a primitive or a local TypeDef of the owning // module. `candidates` lists qualified suggestions scanned // from imports. @@ -1263,7 +1264,7 @@ fn workspace_error_to_diagnostic( "candidates": candidates, })), ), - // ct.1: qualified `.` where `` is not a + // qualified `.` where `` is not a // known module or `` has no `TypeDef `. W::BadCrossModuleTypeRef { module, name } => Some( ailang_check::Diagnostic::error( @@ -1278,7 +1279,7 @@ fn workspace_error_to_diagnostic( "name": name, })), ), - // mq.1 (canonical-class-names): bare class-ref that does not + // the canonical-form rule for class references: bare class-ref that does not // resolve to a local class of the owning module. Sibling of // `BareCrossModuleTypeRef` for class-reference fields. W::BareCrossModuleClassRef { module, name, candidates } => Some( @@ -1295,7 +1296,7 @@ fn workspace_error_to_diagnostic( "candidates": candidates, })), ), - // mq.1: qualified `.` where `` is not a + // qualified `.` where `` is not a // known module or `` has no class ``. W::BadCrossModuleClassRef { module, name } => Some( ailang_check::Diagnostic::error( @@ -1310,7 +1311,7 @@ fn workspace_error_to_diagnostic( "name": name, })), ), - // ct.1: a class-reference field contains a `.` — class names + // a class-reference field contains a `.` — class names // are not module-qualified in this milestone. W::QualifiedClassName { module, name, field } => Some( ailang_check::Diagnostic::error( @@ -1343,7 +1344,7 @@ fn workspace_error_to_diagnostic( } } -/// Iter cli-diag-human (2026-05-14): loads a workspace and, on +/// loads a workspace and, on /// `WorkspaceLoadError`, routes through `workspace_error_to_diagnostic` /// to print a stderr line matching the JSON path's `[code]`-bracketed /// format before exiting non-zero. Pure I/O errors have no diagnostic @@ -1416,7 +1417,7 @@ fn collect_refs(def: &ailang_core::Def) -> std::collections::BTreeSet { } } } - // Iter 22b.1: `ail deps` does not yet trace references inside + // `ail deps` does not yet trace references inside // class/instance defs. Walking method signatures and bodies // (default bodies, instance method bodies) lands in 22b.2 along // with the typecheck arms — until then a class/instance def @@ -1514,7 +1515,7 @@ fn walk_term( walk_term(rhs, out, builtins, scope); } Term::LetRec { name, params, body, in_term, .. } => { - // Iter 16b.1: `deps` walks the on-disk module before the + // `deps` walks the on-disk module before the // desugar pass, so a `Term::LetRec` is reachable here. // Treat it like a fn def for dependency purposes: // `name` shadows for both body and in_term; params shadow @@ -1536,12 +1537,12 @@ fn walk_term( } } Term::Clone { value } => { - // Iter 18c.1: clone is identity for dependency-walk + // clone is identity for dependency-walk // purposes. The wrapper introduces no new symbols. walk_term(value, out, builtins, scope); } Term::ReuseAs { source, body } => { - // Iter 18d.1: walk both children; the wrapper introduces no + // walk both children; the wrapper introduces no // new bindings of its own. walk_term(source, out, builtins, scope); walk_term(body, out, builtins, scope); @@ -1851,7 +1852,7 @@ fn def_summary(d: &ailang_core::Def) -> (&'static str, String, Vec) { .join(" | "); ("type", s, vec![]) } - // Iter 22b.1: a one-line `class C a` / `instance C T` summary + // a one-line `class C a` / `instance C T` summary // for `ail manifest`. The `effects` slot is empty for both — // class methods carry their own effect annotations on each // method signature, but the class itself does not (it is a @@ -2243,13 +2244,13 @@ fn locate_str_runtime() -> Result { ) } -/// Iter 9b: shared build helper for `Cmd::Build` and `Cmd::Run`. +/// shared build helper for `Cmd::Build` and `Cmd::Run`. /// Loads the workspace, runs the typechecker, emits IR, and links via /// clang. On typecheck failure, prints diagnostics to stderr and exits /// the process with code 1. On clang failure, returns a Result error /// (the .ll path is preserved for post-mortem inspection). /// -/// Iter 16b.3: between `check_workspace` and `lower_workspace` we run +/// between `check_workspace` and `lower_workspace` we run /// `ailang_check::lift_letrecs` per module. The lift eliminates any /// `Term::LetRec` that the desugar pass left in place (specifically: /// LetRecs that capture `Term::Let`-bound names, whose types are @@ -2287,7 +2288,7 @@ fn build_to( d.message, ); } - // Iter 19a: only Error-severity blocks the build. Warning- + // only Error-severity blocks the build. Warning- // severity diagnostics (e.g. `over-strict-mode`) print but // do not abort. if diags @@ -2297,7 +2298,7 @@ fn build_to( std::process::exit(1); } } - // Iter 16b.3: run `lift_letrecs` per module on the post-desugar + // run `lift_letrecs` per module on the post-desugar // form. Codegen's internal desugar pass is idempotent on a // module that contains no `Term::LetRec`, so the lifted output // can be handed directly to `lower_workspace`. @@ -2312,13 +2313,13 @@ fn build_to( entry: ws.entry.clone(), modules: lifted_modules, root_dir: ws.root_dir.clone(), - // Iter 22b.1: pass the registry through the lift pass. Lift + // pass the registry through the lift pass. Lift // only rewrites `Term::LetRec` into top-level fns; it does // not touch class/instance defs, so the registry built at // load-time remains valid. registry: ws.registry.clone(), }; - // Iter 22b.3: monomorphisation pass. After `lift_letrecs` and + // monomorphisation pass. After `lift_letrecs` and // before codegen, every (class-method, concrete-type) call-site // pair becomes a synthesised top-level fn; user call sites are // rewritten to target those names. Class-free workspaces flow @@ -2333,7 +2334,7 @@ fn build_to( let out_bin = out.unwrap_or_else(|| Path::new(".").join(&ws.entry).with_extension("")); let mut clang = std::process::Command::new("clang"); clang.arg(opt).arg("-o").arg(&out_bin).arg(&ll_path); - // Iter 23.2: compile and link `runtime/str.c` unconditionally — + // compile and link `runtime/str.c` unconditionally — // `@ail_str_eq` is emitted in the IR header without an alloc-strategy // guard (it backs the prelude `eq__Str` mono symbol). The .o is // cached at /str.o per build invocation; if a program never @@ -2356,7 +2357,7 @@ fn build_to( ); } clang.arg(&str_obj); - // Iter hs.4: compile and link `runtime/rc.c` unconditionally, + // compile and link `runtime/rc.c` unconditionally, // hoisted out of the AllocStrategy::Rc arm. `runtime/str.c`'s // `ailang_int_to_str` / `ailang_float_to_str` call // `ailang_rc_alloc` defined in rc.c; the weak extern declaration @@ -2386,7 +2387,7 @@ fn build_to( clang.arg(&rc_obj); match alloc { ailang_codegen::AllocStrategy::Gc => { - // Boehm conservative GC (Decision 9 / Iter 14f). The lowered + // Boehm conservative GC (the transitional dual-allocator). The lowered // IR calls @GC_malloc; libgc supplies it. Pthread/dl are // pulled in transitively via libgc.so on Linux, so a single // -lgc suffices. @@ -2418,7 +2419,7 @@ fn build_to( clang.arg(&bump_obj); } ailang_codegen::AllocStrategy::Rc => { - // Iter hs.4: rc.c compile-and-link hoisted to the + // rc.c compile-and-link hoisted to the // unconditional path above. `--alloc=rc` is still a // valid CLI flag — it drives codegen's // `@ailang_rc_alloc`-vs-`@GC_malloc` selection in the IR @@ -2547,7 +2548,7 @@ fn run_cmd(bin: &str, args: &[&str], ctx: &str) -> Result<()> { Ok(()) } -/// ct.1 migration helper: rewrite bare cross-module Type::Con names +/// Canonical-form migration helper: rewrite bare cross-module Type::Con names /// and Term::Ctor.type_name to their qualified form. Sets /// `*changed` to `true` if any rewrite happened. Uses the /// first-match-in-imports-order disambiguation rule with prelude as diff --git a/crates/ail/tests/ct1_check_cli.rs b/crates/ail/tests/ct1_check_cli.rs index e932af4..1c07bca 100644 --- a/crates/ail/tests/ct1_check_cli.rs +++ b/crates/ail/tests/ct1_check_cli.rs @@ -86,12 +86,12 @@ fn check_json_emits_bad_cross_module_type_ref() { ); } -/// Property: mq.1 — a qualified class name in an `InstanceDef.class` +/// Property: with class references in canonical form, a qualified class name in an `InstanceDef.class` /// field is the canonical form, not a rejection. The /// `test_ct1_qualified_class_rejected` fixture (declares /// `instance prelude.Eq Int` outside prelude and outside Int's /// defining module) is now rejected by the downstream coherence -/// check with `orphan-instance` instead of the pre-mq.1 +/// check with `orphan-instance` instead of the pre-canonical-class-form /// `qualified-class-name`. Guards against /// `workspace_error_to_diagnostic` losing the OrphanInstance arm /// AND against any regression that would reintroduce @@ -119,12 +119,12 @@ fn check_json_emits_orphan_instance_on_xmod_class_without_coherence_post_mq1() { ); assert!( !arr.iter().any(|d| d["code"] == "qualified-class-name"), - "must NOT fire `qualified-class-name` on a referencing field post-mq.1; got {stdout}" + "must NOT fire `qualified-class-name` on a referencing field post-canonical-class-form; got {stdout}" ); } /// Property: in non-JSON (human) mode `ail check` exits non-zero on a -/// ct.1 validator failure and writes an actionable error message to +/// canonical-type-form validator failure and writes an actionable error message to /// stderr — naming both the offending type and the migration command /// the author should run. Pinning the human-mode path separately /// because in this mode the loader error short-circuits via `anyhow` @@ -160,7 +160,7 @@ fn check_human_mode_emits_actionable_message_to_stderr() { /// Property: the post-migration `examples/ordering_match.ail.json` /// (Term::Ctor `Ordering` -> `prelude.Ordering`) typechecks cleanly /// through the CLI — `ail check` exits 0 with no diagnostics. -/// Guards against (a) a revert of the ct.1.5 migration, (b) a +/// Guards against (a) a revert of the canonical-form migration, (b) a /// regression in `Registry::normalize_type_for_lookup` that would make /// the canonical (qualified) form fail to dispatch where the bare /// form used to succeed. diff --git a/crates/ail/tests/e2e.rs b/crates/ail/tests/e2e.rs index ce964eb..d687241 100644 --- a/crates/ail/tests/e2e.rs +++ b/crates/ail/tests/e2e.rs @@ -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=` and run. +/// build with an explicit `--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) -> List` 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` 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`. `or_else(Some(7), 99) == 7` +/// pattern-match on `Maybe`. `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`) 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 -> Pair` and `Pair -> Pair` @@ -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`); first combinator with three type vars /// (`either : (e -> c) -> (a -> c) -> Either -> 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> -> Pair, List>`). @@ -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 (`$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__`. 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__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__`. 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__`. 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__`. 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__(...)` @@ -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__`. 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__`. 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 diff --git a/crates/ail/tests/mono_xmod_ctor_pattern.rs b/crates/ail/tests/mono_xmod_ctor_pattern.rs index ea1aa19..e80c4b8 100644 --- a/crates/ail/tests/mono_xmod_ctor_pattern.rs +++ b/crates/ail/tests/mono_xmod_ctor_pattern.rs @@ -51,7 +51,7 @@ fn examples_dir() -> PathBuf { /// `head_or_zero : test_mono_ctor_listmod.List -> Int` that /// matches on `Cons h _ | Nil`. /// -/// Pre-ct.2 repro: `monomorphise_workspace` returned +/// Pre-canonical-type-form repro: `monomorphise_workspace` returned /// `Err(CheckError::PatternTypeMismatch { ctor: "Cons", ty: /// "test_mono_ctor_listmod.List" })`. The same workspace /// typechecked cleanly because the typecheck pass overlaid a @@ -59,7 +59,7 @@ fn examples_dir() -> PathBuf { /// qualified `resolved_type_name`, while the mono pass kept the /// workspace-flat index whose local hit produced the bare one. /// -/// Post-ct.2 expectation: `monomorphise_workspace` returns `Ok` +/// Post-canonical-type-form expectation: `monomorphise_workspace` returns `Ok` /// because the Pattern::Ctor lookup no longer consults /// `ctor_index` at all — it walks directly from the scrutinee's /// canonical `Type::Con.name` to its TypeDef in diff --git a/crates/ail/tests/mq3_multi_class_e2e.rs b/crates/ail/tests/mq3_multi_class_e2e.rs index f90cf67..f5aaa1e 100644 --- a/crates/ail/tests/mq3_multi_class_e2e.rs +++ b/crates/ail/tests/mq3_multi_class_e2e.rs @@ -38,7 +38,7 @@ fn run_ail_check_json(entry: &str) -> std::process::Output { .expect("ail binary must launch") } -/// mq.3.6 (Trajectory C): bare `show 42` in a workspace with two +/// Multi-class trajectory C: bare `show 42` in a workspace with two /// `Show` classes (modA and modB) each shipping `Show Int` is /// genuinely ambiguous. `ail check` rejects with /// `ambiguous-method-resolution` and names both candidate classes. @@ -71,7 +71,7 @@ fn mq3_two_show_ambiguous_fires_ambiguous_method_resolution() { ); } -/// mq.3.6 (Trajectory E): explicit qualifier +/// Multi-class trajectory E: explicit qualifier /// `mq3_two_show_ambiguous_a.Show.show 42` disambiguates the same /// workspace as the ambiguous fixture. Typecheck succeeds. #[test] @@ -85,7 +85,7 @@ fn mq3_two_show_qualified_resolves_clean() { ); } -/// mq.3.6 (class-fn shadow): bare `myeq 1 2` in a workspace with +/// Class-fn shadow: bare `myeq 1 2` in a workspace with /// `class MyEq { myeq }` in one module and `fn myeq` in another /// resolves to the fn per lookup precedence. Typecheck succeeds AND /// the structured warning `class-method-shadowed-by-fn` fires so the diff --git a/crates/ail/tests/typeclass_22b2.rs b/crates/ail/tests/typeclass_22b2.rs index 9394961..6be70d0 100644 --- a/crates/ail/tests/typeclass_22b2.rs +++ b/crates/ail/tests/typeclass_22b2.rs @@ -34,19 +34,19 @@ fn class_method_is_in_module_globals() { let mod_globals = globals .get("test_22b2_class_method_lookup") .expect("module globals present"); - // mq.3: ModuleGlobals accessors are tuple-keyed by (class, method). - // Pre-mq.3 callers passed the bare method name only; post-mq.3 they + // ModuleGlobals accessors are tuple-keyed by (class, method). + // Pre-canonical-class-form callers passed the bare method name only; post-canonical-class-form they // must disambiguate by class. assert!( mod_globals.has_class_method("test_22b2_class_method_lookup.TShow", "tshow"), "class method `tshow` (in class `TShow`) must appear in module globals" ); - // mq.1: class_method_class returns the qualified form + // class_method_class returns the qualified form // `.`. assert_eq!( mod_globals.class_method_class("test_22b2_class_method_lookup.TShow", "tshow"), Some("test_22b2_class_method_lookup.TShow"), - "class method `tshow` must remember its class is `TShow` (qualified post-mq.1)" + "class method `tshow` must remember its class is `TShow` (qualified post-canonical-class-form)" ); } @@ -183,7 +183,7 @@ fn fn_with_correct_constraint_typechecks_green() { /// `(Eq, a)` is satisfied because `Ord` extends `Eq`, so the expanded /// declared set must include `(Eq, a)`. Without the superclass walk the /// fn would spuriously fire `missing-constraint`. This is the positive -/// fixture for Decision 11's one-step superclass closure. +/// fixture for the typeclass design's one-step superclass closure. #[test] fn fn_calling_superclass_method_via_subclass_constraint_typechecks_green() { let entry = examples_dir() diff --git a/crates/ail/tests/typeclass_22b3.rs b/crates/ail/tests/typeclass_22b3.rs index d79b808..a3e258b 100644 --- a/crates/ail/tests/typeclass_22b3.rs +++ b/crates/ail/tests/typeclass_22b3.rs @@ -148,7 +148,7 @@ fn collect_mono_targets_single_concrete_call_site() { let t = &targets[0]; match t { ailang_check::mono::MonoTarget::ClassMethod { class, method, type_, defining_module } => { - // mq.1: MonoTarget.class carries the qualified workspace-key shape. + // MonoTarget.class carries the qualified workspace-key shape. assert_eq!(class, "test_22b2_instance_present.TShow"); assert_eq!(method, "tshow"); assert!( @@ -483,7 +483,7 @@ fn rewrite_walker_skips_locally_shadowed_class_method() { let entry = examples_dir().join("test_22b3_shadow_class_method.ail"); let ws = ailang_surface::load_workspace(&entry).expect("load"); let diags = ailang_check::check_workspace(&ws); - // mq.3: this fixture intentionally shadows the class method `tshow` + // this fixture intentionally shadows the class method `tshow` // with a local `let show = "shadow"`, which fires the new // `class-method-shadowed-by-fn` warning. The warning is an // expected correct consequence; filter it out so the assertion @@ -617,7 +617,7 @@ fn rewrite_uses_qualified_name_for_cross_module_call_site() { ); } -/// Iter 22b.3.7 GATING: the synthetic class+instance fixture must +/// the synthetic class+instance fixture must /// typecheck cleanly under `check_workspace`. Pre-condition for the /// end-to-end run gate (`synthetic_fixture_runs_and_prints_five`): /// if the fixture is ill-typed, the run gate's failure mode would @@ -630,7 +630,7 @@ fn synthetic_fixture_typechecks_clean() { assert!(diags.is_empty(), "fixture must typecheck: {:?}", diags); } -/// Iter 22b.3.7 GATING: the synthetic class+instance fixture must +/// the synthetic class+instance fixture must /// build via `ail run` and emit `5\n` to stdout. This is the /// spec's close-out criterion for 22b.3 — proves the full mono /// pipeline (class declaration + instance + class-method call site diff --git a/crates/ailang-check/src/builtins.rs b/crates/ailang-check/src/builtins.rs index bf289b1..9b90e3a 100644 --- a/crates/ailang-check/src/builtins.rs +++ b/crates/ailang-check/src/builtins.rs @@ -48,7 +48,7 @@ pub struct EffectOpSig { /// [`list()`] is resolvable in `env`. Idempotent for a fresh `Env`; calling /// it twice would shadow the same entries with identical types. pub fn install(env: &mut crate::Env) { - // Iter 22-floats.3: arithmetic and comparison ops are polymorphic. + // arithmetic and comparison ops are polymorphic. // Same shape as `==` below — the {Int, Float}-restriction is // enforced at codegen, not at typecheck. `%` stays Int-only // (no fmod yet — `%` semantics for Float require an explicit @@ -134,7 +134,7 @@ pub fn install(env: &mut crate::Env) { }, ); - // Iter 16d: `__unreachable__` is the polymorphic bottom value. + // `__unreachable__` is the polymorphic bottom value. // Type: `forall a. a`. Used by the desugar pass as the chain // terminator of an exhaustive match, and available to user code as // a primitive panic point. Codegen lowers it to LLVM `unreachable`. @@ -149,7 +149,7 @@ pub fn install(env: &mut crate::Env) { }, ); - // Iter 22-floats.3: Float-conversion and inspection builtins. + // Float-conversion and inspection builtins. // Codegen lowering lands in iter 4; iter 3 only registers types. // `neg` is polymorphic (`forall a. (a) -> a`) for the same reason // the widened `+` is — Int and Float negation share one symbol; @@ -254,7 +254,7 @@ pub fn install(env: &mut crate::Env) { }, ); - // Iter 22-floats.3: Float bit-pattern constants. Bare values, not + // Float bit-pattern constants. Bare values, not // fns — reference site is `(var nan)`. Codegen emits // `double 0x7FF8000000000000` (NaN), `double 0x7FF0000000000000` // (+Inf), `double 0xFFF0000000000000` (-Inf) at the use site in @@ -382,7 +382,7 @@ mod tests { } } - /// Iter 22-floats.3: regression — `(+ 1 2)` still resolves to `Int` + /// regression — `(+ 1 2)` still resolves to `Int` /// after the widening from `(Int, Int) -> Int` to /// `forall a. (a, a) -> a`. Protects the no-regression invariant /// for every existing Int-using fixture: the polymorphic `+` @@ -394,7 +394,7 @@ mod tests { assert_eq!(ty, Type::int(), "(+ 1 2) must still type as Int"); } - /// Iter 22-floats.3: new acceptance — `(+ 1.5 2.5)` types as `Float`. + /// new acceptance — `(+ 1.5 2.5)` types as `Float`. /// Pre-widening this would have failed with `TypeMismatch` because /// `+` was monomorphic `(Int, Int) -> Int`. The widening to /// `forall a. (a, a) -> a` makes Float arithmetic a typecheck-clean @@ -408,7 +408,7 @@ mod tests { assert_eq!(ty, Type::float(), "(+ 1.5 2.5) must type as Float"); } - /// Iter 22-floats.3: regression — `(< 1 2)` still resolves to `Bool` + /// regression — `(< 1 2)` still resolves to `Bool` /// after the widening to `forall a. (a, a) -> Bool`. Mirrors the /// `+` regression check for the comparison-op path. #[test] @@ -417,7 +417,7 @@ mod tests { assert_eq!(ty, Type::bool_(), "(< 1 2) must still type as Bool"); } - /// Iter 22-floats.3: new acceptance — `(< 1.5 2.5)` types as `Bool`. + /// new acceptance — `(< 1.5 2.5)` types as `Bool`. /// Pre-widening this would have failed with `TypeMismatch`. The /// widening to `forall a. (a, a) -> Bool` lets Float ordering /// typecheck cleanly; codegen lowers to `fcmp olt double` in iter 4. @@ -429,7 +429,7 @@ mod tests { assert_eq!(ty, Type::bool_(), "(< 1.5 2.5) must type as Bool"); } - /// Iter 22-floats.3: `neg` is polymorphic — `forall a. (a) -> a`. + /// `neg` is polymorphic — `forall a. (a) -> a`. /// Both `(neg 5) : Int` and `(neg 1.5) : Float` typecheck. The /// polymorphic shape is the same as the widened arithmetic ops, so /// Int and Float negation share one symbol; codegen dispatches on @@ -443,7 +443,7 @@ mod tests { assert_eq!(ty_float, Type::float(), "(neg 1.5) must type as Float"); } - /// Iter 22-floats.3: `int_to_float : (Int) -> Float`. The + /// `int_to_float : (Int) -> Float`. The /// monomorphic conversion builtin — codegen lowers via `sitofp` in /// iter 4. Typecheck only validates the signature here. #[test] @@ -452,7 +452,7 @@ mod tests { assert_eq!(ty, Type::float(), "(int_to_float 5) must type as Float"); } - /// Iter 22-floats.3: `float_to_int_truncate : (Float) -> Int`. + /// `float_to_int_truncate : (Float) -> Int`. /// Saturating truncation toward zero per spec A4 — typecheck only /// validates the signature; semantics is iter 4's codegen lowering /// via `@llvm.fptosi.sat.i64.f64`. @@ -463,7 +463,7 @@ mod tests { assert_eq!(ty, Type::int(), "(float_to_int_truncate 1.5) must type as Int"); } - /// Iter 22-floats.3: `float_to_str : (Float) -> Str`. Codegen lowers + /// `float_to_str : (Float) -> Str`. Codegen lowers /// via runtime C glue in iter 4; typecheck only validates the signature. #[test] fn install_float_to_str_signature() { @@ -472,7 +472,7 @@ mod tests { assert_eq!(ty, Type::str_(), "(float_to_str 1.5) must type as Str"); } - /// Iter hs.4: `int_to_str : (Int) -> Str`. Codegen lowers via the + /// `int_to_str : (Int) -> Str`. Codegen lowers via the /// runtime C glue `ailang_int_to_str` from `runtime/str.c`. #[test] fn install_int_to_str_signature() { @@ -480,7 +480,7 @@ mod tests { assert_eq!(ty, Type::str_(), "(int_to_str 42) must type as Str"); } - /// Iter 24.1: `bool_to_str : (Bool) -> Str`. Codegen lowers via the + /// `bool_to_str : (Bool) -> Str`. Codegen lowers via the /// runtime C glue `ailang_bool_to_str` from `runtime/str.c`. #[test] fn install_bool_to_str_signature() { @@ -491,7 +491,7 @@ mod tests { assert_eq!(ty, Type::str_(), "(bool_to_str true) must type as Str"); } - /// Iter 24.1: `str_clone : (Str borrow) -> Str`. Codegen lowers via the + /// `str_clone : (Str borrow) -> Str`. Codegen lowers via the /// runtime C glue `ailang_str_clone` from `runtime/str.c`. #[test] fn install_str_clone_signature() { @@ -504,7 +504,7 @@ mod tests { #[test] fn install_str_concat_signature() { - // Iter str-concat: `str_concat : (Str borrow, Str borrow) -> Str + // `str_concat : (Str borrow, Str borrow) -> Str // own`. Codegen lowers via the runtime C glue `ailang_str_concat` // from `runtime/str.c`. let mut env = Env::default(); @@ -539,7 +539,7 @@ mod tests { } } - /// Iter 22-floats.3: `is_nan : (Float) -> Bool`. Codegen lowers to + /// `is_nan : (Float) -> Bool`. Codegen lowers to /// `fcmp uno double %x, %x` in iter 4 — typecheck only validates /// the signature here. #[test] @@ -549,7 +549,7 @@ mod tests { assert_eq!(ty, Type::bool_(), "(is_nan 1.5) must type as Bool"); } - /// Iter 22-floats.3: `nan`, `inf`, `neg_inf` are bare-value constants + /// `nan`, `inf`, `neg_inf` are bare-value constants /// of type `Float`. They are NOT functions — reference site is /// `(var nan)`, not `(app nan)`. Parallel to `__unreachable__` which /// is `forall a. a`, but here the type is the concrete `Float` @@ -565,7 +565,7 @@ mod tests { assert_eq!(ty_neg_inf, Type::float(), "neg_inf must type as Float"); } - /// Iter 22-floats.3: pattern-matching on Float literals is hard- + /// pattern-matching on Float literals is hard- /// rejected at typecheck per spec line 723-735 recommendation (a). /// IEEE-`==` semantics make Float patterns semantically dubious /// (NaN never matches; equality is bit-exact not approximate). diff --git a/crates/ailang-check/src/diagnostic.rs b/crates/ailang-check/src/diagnostic.rs index f5111ba..4f87beb 100644 --- a/crates/ailang-check/src/diagnostic.rs +++ b/crates/ailang-check/src/diagnostic.rs @@ -109,9 +109,8 @@ use serde::Serialize; /// /// Serializes as a lowercase string (`"error"` / `"warning"`) so that /// `ail check --json` consumers can branch on it without parsing prose. -/// Iter 19a is the first iter to emit [`Severity::Warning`] (via the -/// linearity-pass `over-strict-mode` lint); pre-19a the variant was -/// reserved. +/// Currently the only emitter of [`Severity::Warning`] is the +/// linearity-pass `over-strict-mode` lint. #[derive(Serialize, Debug, Clone, Copy, PartialEq, Eq)] #[serde(rename_all = "lowercase")] pub enum Severity { @@ -145,7 +144,7 @@ pub struct Diagnostic { pub def: Option, /// Free structured context. Empty = `{}`. pub ctx: serde_json::Value, - /// Iter 18c.2: machine-applicable fix suggestions, each a snippet of + /// machine-applicable fix suggestions, each a snippet of /// form-A AILang the author can paste back at the offending site. /// Empty = no rewrite suggested. The list is always emitted (so JSON /// consumers see a stable shape; they can branch on `.is_empty()`). @@ -156,7 +155,7 @@ pub struct Diagnostic { /// One machine-applicable rewrite suggestion attached to a [`Diagnostic`]. /// -/// Iter 18c.2: emitted by the linearity check to point the author (or an +/// emitted by the linearity check to point the author (or an /// LLM consumer of `ail check --json`) at the spelled fix. `replacement` /// is form-A AILang text — parseable by `ailang_surface::parse_term`. #[derive(Serialize, Debug, Clone)] @@ -188,7 +187,7 @@ impl Diagnostic { } } - /// Iter 19a: builds a [`Severity::Warning`] diagnostic. Same shape + /// builds a [`Severity::Warning`] diagnostic. Same shape /// as [`Diagnostic::error`] otherwise. Used by lint-style passes /// that report observations rather than hard typecheck failures /// (currently the only emitter is the linearity pass's @@ -204,7 +203,7 @@ impl Diagnostic { } } - /// Iter 18c.2: append a [`SuggestedRewrite`]. Builder-style. Used by + /// append a [`SuggestedRewrite`]. Builder-style. Used by /// the linearity check to attach the form-A fix it computed. Other /// diagnostics leave the field empty. pub fn with_suggested_rewrite( @@ -265,7 +264,7 @@ mod tests { assert!(s.contains("\"actual\":\"Bool\""), "{s}"); } - /// Iter 18c.2: a diagnostic carrying a `SuggestedRewrite` serialises + /// a diagnostic carrying a `SuggestedRewrite` serialises /// the rewrite under `suggested_rewrites` with the description and /// the form-A replacement. #[test] diff --git a/crates/ailang-check/src/lib.rs b/crates/ailang-check/src/lib.rs index 2698735..f38d7ef 100644 --- a/crates/ailang-check/src/lib.rs +++ b/crates/ailang-check/src/lib.rs @@ -164,7 +164,7 @@ pub(crate) fn substitute_rigids(t: &Type, mapping: &BTreeMap) -> T } } -/// Iter 23.4: substitute named rigid vars throughout a `Term`, +/// substitute named rigid vars throughout a `Term`, /// recursing into every sub-Term and applying [`substitute_rigids`] /// to every embedded `Type` (in `Term::Lam.param_tys`, /// `Term::Lam.ret_ty`). All other Term variants carry no inline @@ -473,7 +473,7 @@ pub enum CheckError { /// A pattern referenced a ctor that does not exist in the /// scrutinee's resolved [`TypeDef`]. The Pattern::Ctor lookup - /// is type-driven (post-ct.2): it walks `td.ctors` for the + /// is type-driven (post-canonical-type-form): it walks `td.ctors` for the /// `expected` Type::Con's TypeDef and reports this code when /// no ctor of that name is present. Code: `unknown-ctor-in-pattern`. #[error("unknown constructor `{0}` in pattern")] @@ -554,13 +554,13 @@ pub enum CheckError { #[error("invalid def name `{name}`: contains `.` (reserved for qualified refs)")] InvalidDefName { name: String }, - /// Iter 14e: a `Term::App { tail: true, .. }` or + /// a `Term::App { tail: true, .. }` or /// `Term::Do { tail: true, .. }` was found in a non-tail position. - /// Code: `tail-call-not-in-tail-position`. See Decision 8. + /// Code: `tail-call-not-in-tail-position`. See `design/contracts/tail-calls.md`. #[error("call marked `tail` is not in tail position")] TailCallNotInTailPosition, - /// Iter 18d.1: a `Term::ReuseAs { body, .. }` was found whose `body` + /// a `Term::ReuseAs { body, .. }` was found whose `body` /// is not an allocating Term variant (i.e. not `Term::Ctor` and not /// `Term::Lam`). `(reuse-as ...)` only makes sense when the body /// produces a fresh allocation that can take over the source's @@ -572,7 +572,7 @@ pub enum CheckError { #[error("reuse-as body must be an allocating term (Term::Ctor or Term::Lam), got {got}")] ReuseAsNonAllocatingBody { got: String, body_form_a: String }, - /// Iter 22b.2 (Task 9): a polymorphic `FnDef` calls a class method + /// a polymorphic `FnDef` calls a class method /// (e.g. `show x` where `x: a`), producing a residual class /// constraint `(class, a)` at the call site, but the fn's declared /// `Forall.constraints` does not list that constraint (and the @@ -594,7 +594,7 @@ pub enum CheckError { at_type: String, }, - /// Iter 22b.2 (Task 10): a `FnDef` calls a class method at a + /// a `FnDef` calls a class method at a /// fully-concrete type (e.g. `show 42` resolves the residual class /// param to `Int`), but the workspace registry has no /// `instance class at_type` entry. This is the dual of @@ -613,14 +613,14 @@ pub enum CheckError { method: String, class: String, at_type: String, - /// mq.2: additive candidate-class list. Empty on residuals from - /// the single-class dispatch path (pre-mq.2 shape preserved). + /// additive candidate-class list. Empty on residuals from + /// the single-class dispatch path (pre-canonical-class-form shape preserved). /// Non-empty when the diagnostic originated from the /// multi-candidate refinement path with zero registry survivors. candidate_classes: Vec, }, - /// mq.2: a monomorphic call site ` x` with multiple + /// a monomorphic call site ` x` with multiple /// candidate classes (each declaring `` and each having an /// instance for `x`'s concrete type) cannot be resolved unambiguously. /// The LLM-author writes an explicit qualifier @@ -638,7 +638,7 @@ pub enum CheckError { candidate_classes: Vec, }, - /// mq.2: an explicit class qualifier in `Term::Var.name` names a + /// an explicit class qualifier in `Term::Var.name` names a /// qualified class that is not in the workspace registry of /// candidate classes for the method. Code: `unknown-class`. `ctx`: /// `{"name": ""}`. @@ -685,7 +685,7 @@ pub enum CheckError { #[error("`recur` must be in tail position of its enclosing `loop` body — it is a back-jump, not a value-producing sub-expression")] RecurNotInTailPosition, - /// Iter 22b.3: an internal invariant in the typechecker / mono pass + /// an internal invariant in the typechecker / mono pass /// was violated — surfaced as an error so callers can propagate /// rather than abort, but in well-formed inputs (typecheck has /// succeeded) it must never fire. Code: `internal`. @@ -846,7 +846,7 @@ impl CheckError { if let Some(name) = self.def() { d = d.with_def(name); } - // Iter 18d.1: typecheck-side suggested_rewrites for the + // typecheck-side suggested_rewrites for the // reuse-as-non-allocating-body diagnostic. The replacement is // simply the body without the `(reuse-as ...)` wrapper — the // hint is meaningless here, so drop it. @@ -856,7 +856,7 @@ impl CheckError { body_form_a.clone(), ); } - // Iter 23.5: Float-aware addendum on `NoInstance`. When the + // Float-aware addendum on `NoInstance`. When the // unresolved class is `Eq` or `Ord` AND the type is `Float`, // append a cross-reference to design/contracts/float-semantics.md so // the LLM author learns the partial-Float story immediately @@ -866,7 +866,7 @@ impl CheckError { // (`ne` / `lt` / `le` / `gt` / `ge` / direct `eq` / `compare`) // are the natural surface where the LLM hits this. if let CheckError::NoInstance { class, at_type, .. } = self.inner() { - // mq.1: `class` carries the qualified workspace-key shape + // `class` carries the qualified workspace-key shape // (`prelude.Eq` / `prelude.Ord`), not the bare name. if (class == "prelude.Eq" || class == "prelude.Ord") && at_type == "Float" { d.message = format!( @@ -875,7 +875,7 @@ impl CheckError { d.message ); } else if class == "prelude.Show" { - // Iter 24.3: Show-aware addendum on `NoInstance`. Fires + // Show-aware addendum on `NoInstance`. Fires // for any type lacking a Show instance — most commonly // a function type (`print f` where f is bare-fn-typed) // or a user type the LLM-author forgot to give an @@ -888,7 +888,7 @@ impl CheckError { the prelude; see design/contracts/typeclasses.md. \ User types declare their own \ `instance prelude.Show ` in the type's defining \ - module per Decision 11 coherence.", + module per the typeclass design coherence.", d.message ); } @@ -917,7 +917,7 @@ impl CheckError { /// workspace will inevitably produce `unknown-module` errors on qualified /// references — which is correct. pub fn check_module(m: &Module) -> Vec { - // Iter 16a: flatten nested constructor patterns before any check + // flatten nested constructor patterns before any check // logic touches the AST. The rewrite is pure and runs in memory; // canonical-JSON hashes (computed by `ailang_core::load_module` / // `def_hash`) are unaffected because they use the on-disk form. @@ -928,7 +928,7 @@ pub fn check_module(m: &Module) -> Vec { entry: m.name.clone(), modules, root_dir: std::path::PathBuf::from("."), - // Iter 22b.1: single-module check_module entry point does not + // single-module check_module entry point does not // build a registry. The registry is workspace-load-time // metadata; check_module is invoked from in-memory paths // (tests, single-file CLI) where no workspace DFS happened. @@ -977,7 +977,7 @@ pub fn check_workspace(ws: &Workspace) -> Vec { return pre_desugar_errors; } } - // Iter 16a: desugar every module of the workspace before any check + // desugar every module of the workspace before any check // logic runs. The rewrite is pure and per-module; we rebuild a // workspace shell around the desugared modules (paths and entry // are unchanged). @@ -989,7 +989,7 @@ pub fn check_workspace(ws: &Workspace) -> Vec { .map(|(k, m)| (k.clone(), ailang_core::desugar::desugar_module(m))) .collect(), root_dir: ws.root_dir.clone(), - // Iter 22b.1: pass the registry through unchanged. The desugar + // pass the registry through unchanged. The desugar // pass does not touch class/instance defs (see desugar.rs: // 22b.1 passthrough), so the registry built at load time // remains valid against the desugared modules. @@ -1021,7 +1021,7 @@ pub fn check_workspace(ws: &Workspace) -> Vec { let mut diagnostics: Vec = Vec::new(); for name in order { let m = &ws.modules[name]; - // mq.3: synth-time warnings collected per module then merged + // synth-time warnings collected per module then merged // into the per-module diagnostic stream alongside typecheck // errors. The synth warnings flow through `check_in_workspace` // even when `had_typecheck_errors` is true; surface them so a @@ -1039,7 +1039,7 @@ pub fn check_workspace(ws: &Workspace) -> Vec { module_diags.push(e.to_diagnostic()); } module_diags.extend(synth_warnings); - // Iter 18c.2: linearity check runs only on modules that + // linearity check runs only on modules that // typechecked clean. Running it on a body that already has a // type error would wade into a partly-defined IR (e.g. // unresolved Var lookups, mismatched ctor arities) and produce @@ -1047,7 +1047,7 @@ pub fn check_workspace(ws: &Workspace) -> Vec { // all-explicit-mode fn are exactly the surface the check is // designed to inspect. if !had_typecheck_errors { - // Iter 23.5: linearity check needs visibility into + // linearity check needs visibility into // polymorphic free fns + class methods from implicitly- // imported modules (currently just the auto-injected // prelude). Without this, a Borrow-mode user fn that @@ -1069,7 +1069,7 @@ pub fn check_workspace(ws: &Workspace) -> Vec { }) .collect(); module_diags.extend(linearity::check_module_with_visible(m, &visible_extra)); - // Iter 18d.2: reuse-as shape compatibility check. Runs on + // reuse-as shape compatibility check. Runs on // the same activation gate as linearity (all-explicit-mode // fns only). The check resolves each `(reuse-as // )` site against the path-ctor of `` and @@ -1079,7 +1079,7 @@ pub fn check_workspace(ws: &Workspace) -> Vec { // JSON consumer. module_diags.extend(reuse_shape::check_module(m)); } - // Iter 19b: apply per-fn `suppress` filter. Runs after every + // apply per-fn `suppress` filter. Runs after every // diagnostic source has appended so any code the author // listed can actually be matched. Drops matching diagnostics // and pushes `empty-suppress-reason` (Error) for malformed @@ -1117,7 +1117,7 @@ pub fn check(m: &Module) -> Result { // unreachable Float-pattern arm in `type_check_pattern`. See // `pre_desugar_validation` for the full rationale. pre_desugar_validation::reject_float_patterns_in_module(m)?; - // Iter 16a: desugar nested ctor patterns before constructing the + // desugar nested ctor patterns before constructing the // trivial workspace. See `check_module` for the rationale. The // returned `CheckedModule.symbols` content hashes are derived from // the *original* on-disk module so they keep the canonical-bytes @@ -1132,7 +1132,7 @@ pub fn check(m: &Module) -> Result { entry: m.name.clone(), modules, root_dir: std::path::PathBuf::from("."), - // Iter 22b.1: the legacy single-module `check` entry point + // the legacy single-module `check` entry point // builds an empty registry. See `check_module` for the same // pattern; once 22b.2 typecheck arms read the registry, both // paths must populate it from the in-memory module. @@ -1141,7 +1141,7 @@ pub fn check(m: &Module) -> Result { let module_globals = build_module_globals(&ws)?; // `check` keeps single-error semantics for callers (snapshot tests, // legacy code). Multi-diagnose is exposed via `check_module` / - // `check_workspace`. mq.3: synth warnings are collected here but + // `check_workspace`. Synth warnings are collected here but // discarded — `check`'s legacy contract returns only the first // error, and warnings on this path have no surface to flow to. let mut synth_warnings: Vec = Vec::new(); @@ -1161,7 +1161,7 @@ pub fn check(m: &Module) -> Result { name: def.name().to_string(), args: vec![], }, - // Iter 22b.1: class/instance defs are not yet checked. + // class/instance defs are not yet checked. // The symbols map is keyed by definition name; class/ // instance contribute their class name, but with no // concrete pre-monomorphisation type we represent them @@ -1179,7 +1179,7 @@ pub fn check(m: &Module) -> Result { Ok(CheckedModule { symbols }) } -/// Iter 16b.3: typecheck `m` and return both the [`CheckedModule`] +/// typecheck `m` and return both the [`CheckedModule`] /// (for tooling that wants the original on-disk symbol identities) /// AND the lifted module ready for codegen — i.e. the desugared /// module with every surviving `Term::LetRec` replaced by a @@ -1204,7 +1204,7 @@ pub fn check_and_lift(m: &Module) -> Result<(CheckedModule, Module)> { Ok((cm, lifted)) } -/// Iter 15a: builds the ADT type-def table per module. Sibling of +/// builds the ADT type-def table per module. Sibling of /// [`build_module_globals`]: gives the body checker O(1) lookup of any /// type declared anywhere in the workspace, keyed by module name. /// Read by qualified `Type::Con.name` resolution @@ -1230,7 +1230,7 @@ pub(crate) fn build_module_types( out } -/// Iter 22b.2: per-module top-level symbol table. Holds the classic +/// per-module top-level symbol table. Holds the classic /// fn/const/type-name-to-type mapping in [`Self::fns`] and class-method /// names in [`Self::class_methods`]. The two channels are kept separate /// because class-method resolution at a call site needs more context @@ -1245,7 +1245,7 @@ pub struct ModuleGlobals { /// shape — every call site that only needs HM lookup goes through /// here. pub fns: IndexMap, - /// mq.3: re-keyed by `(qualified-class-name, method-name)` tuple — + /// re-keyed by `(qualified-class-name, method-name)` tuple — /// post-retirement of `MethodNameCollision`, two classes can declare /// same-named methods within the same workspace. The tuple key /// disambiguates without losing the per-module ordering guarantee @@ -1258,15 +1258,15 @@ pub struct ModuleGlobals { impl ModuleGlobals { /// True if class `class` declares a method named `name` in this - /// module. mq.3: tuple-keyed lookup post-`MethodNameCollision` + /// module. Tuple-keyed lookup after `MethodNameCollision` /// retirement. pub fn has_class_method(&self, class: &str, name: &str) -> bool { self.class_methods.contains_key(&(class.to_string(), name.to_string())) } /// The class that declared the method `name` in class `class`, or - /// `None` if no such entry exists. (Pre-mq.3 callers passed only the - /// method name; post-mq.3 they must disambiguate by class.) + /// `None` if no such entry exists. (Pre-canonical-class-form callers passed only the + /// method name; post-canonical-class-form they must disambiguate by class.) pub fn class_method_class(&self, class: &str, name: &str) -> Option<&str> { self.class_methods .get(&(class.to_string(), name.to_string())) @@ -1279,9 +1279,9 @@ impl ModuleGlobals { self.class_methods.get(&(class.to_string(), name.to_string())) } - /// mq.3: enumerate all `(class, method) -> entry` pairs whose + /// enumerate all `(class, method) -> entry` pairs whose /// method name matches `name`. Returns zero-or-more entries — - /// pre-mq.3 always at most one, post-mq.3 a workspace may have + /// pre-canonical-class-form always at most one, post-canonical-class-form a workspace may have /// multiple classes declaring the same method. pub fn class_method_candidates(&self, name: &str) -> Vec<(&String, &ClassMethodEntry)> { self.class_methods @@ -1291,7 +1291,7 @@ impl ModuleGlobals { } } -/// Iter 22b.2: per-class-method metadata stored in +/// per-class-method metadata stored in /// [`ModuleGlobals::class_methods`]. Carries everything the 22b.2 /// typecheck arms (`missing-constraint`, `no-instance`) need to resolve /// a `Term::Var { name }` that refers to a class method into a residual @@ -1300,7 +1300,7 @@ impl ModuleGlobals { pub struct ClassMethodEntry { /// The class that declared the method (e.g. `Show` for `show`). /// - /// mq.1: carries the qualified form `.`; + /// carries the qualified form `.`; /// flows downstream into `ResidualConstraint.class` and /// `MonoTarget::ClassMethod.class` unchanged. Match the registry /// key shape so discharge / mono lookups land on the right entry. @@ -1323,7 +1323,7 @@ pub struct ClassMethodEntry { /// without checking bodies. Duplicates and dot-in-def names are reported /// here as errors immediately — they would taint all further diagnostics. /// -/// Iter 22b.2 (Task 8): also collects class-method names from `Def::Class` +/// also collects class-method names from `Def::Class` /// into [`ModuleGlobals::class_methods`], so that pass-2 lookup of a /// class method via `Term::Var { name }` resolves to a known global /// rather than firing `unbound-var`. The class-method channel is kept @@ -1338,7 +1338,7 @@ pub fn build_module_globals( let mut globals = ModuleGlobals::default(); for def in &m.defs { let def_name = def.name(); - // mq.1: for `Def::Instance`, `def.name()` returns + // for `Def::Instance`, `def.name()` returns // `inst.class` which carries the canonical-form value // (qualified for cross-module). The dot-in-def-name check // is meant for `Fn`/`Const`/`Type` whose names are author- @@ -1352,7 +1352,7 @@ pub fn build_module_globals( }), )); } - // Iter 22b.2: pre-22b.2 the dup check ran across the single + // pre-22b.2 the dup check ran across the single // `IndexMap`, but class / instance defs early- // `continue`d before the insert, so they never participated // in dup detection (and `instance Foo` re-using the class @@ -1396,13 +1396,13 @@ pub fn build_module_globals( ); } Def::Class(cd) => { - // Iter 22b.2 (Task 8): register every method + // register every method // declared by the class as a class-method global, // with enough metadata for the typecheck arms in // Tasks 9 / 10 to build a residual constraint at // the call site. // - // mq.1: store the qualified class name + // store the qualified class name // (`.`) so the residual // emitted at the call site matches the workspace // registry's qualified key. @@ -1480,16 +1480,16 @@ pub fn build_check_env(ws: &Workspace) -> Env { .collect(); for g in mg.values() { for ((qualified_class, method_name), e) in &g.class_methods { - // mq.3: env.class_methods is tuple-keyed by + // env.class_methods is tuple-keyed by // `(qualified-class, method-name)`. Multiple classes may // share a method name post-`MethodNameCollision`-retirement. env.class_methods.insert( (qualified_class.clone(), method_name.clone()), e.clone(), ); - // mq.2: build the inverse method → candidate-class set in + // build the inverse method → candidate-class set in // the same loop. `e.class_name` already carries the - // qualified form (mq.1 invariant), so the set is keyed on + // qualified form (canonical-class-form invariant), so the set is keyed on // workspace-flat qualified class names directly. env.method_to_candidate_classes .entry(method_name.clone()) @@ -1510,7 +1510,7 @@ pub fn build_check_env(ws: &Workspace) -> Env { let key = imp.alias.clone().unwrap_or_else(|| imp.module.clone()); import_map.insert(key, imp.module.clone()); } - // Iter 23.1: the prelude module is implicitly imported into + // the prelude module is implicitly imported into // every non-prelude module. A user-declared explicit // `import { module: "prelude" }` would already place // "prelude" in the map above (idempotent insert via entry). @@ -1525,10 +1525,10 @@ pub fn build_check_env(ws: &Workspace) -> Env { // env.class_superclasses — walk every module's Def::Class. // - // mq.1: key + value both carry the qualified class name. The map + // key + value both carry the qualified class name. The map // is queried by `expand_declared_constraints` with `c.class` // (a `Constraint.class` value, which is canonical-form on the - // residual side post-mq.1), so both halves of the map must match + // residual side post-canonical-class-form), so both halves of the map must match // the same workspace-key shape. A bare `SuperclassRef.class` // (same-module superclass) is lifted via `qualify_class_ref` — // mirrored inline below to avoid pulling in the workspace helper. @@ -1572,7 +1572,7 @@ fn check_in_workspace( m: &Module, ws: &Workspace, module_globals: &BTreeMap, - // mq.3: synth-time warnings collected here so the caller can + // synth-time warnings collected here so the caller can // surface them into the workspace-wide diagnostic stream. Today // only `class-method-shadowed-by-fn` flows through this channel // (emitted from `synth` via `check_fn`'s warnings accumulator). @@ -1592,7 +1592,7 @@ fn check_in_workspace( // workspace-flat union. // Cross-module `Pattern::Ctor` resolution no longer touches // this overlay — that path is type-driven via `expected` and - // consults `env.module_types` directly (ct.2.2). + // consults `env.module_types` directly (the canonical-type lookup refactor). env.types.clear(); env.ctor_index.clear(); for def in &m.defs { @@ -1636,7 +1636,7 @@ fn check_in_workspace( let key = imp.alias.clone().unwrap_or_else(|| imp.module.clone()); import_map.insert(key, imp.module.clone()); } - // Iter 23.1: the prelude module is implicitly imported into + // the prelude module is implicitly imported into // every non-prelude module's body-check env. The wiring here // populates `env.imports` (the active per-module table the // body-check pass reads); the sibling block in `build_check_env` @@ -1661,7 +1661,7 @@ fn check_in_workspace( } } } - // mq.3: stamp the synth warnings with the module's name as `def` + // stamp the synth warnings with the module's name as `def` // so the JSON consumer can correlate them with the surrounding // module. The warning's `def` field carries the call-site def name // already (set inside `check_fn`); the workspace-wide caller @@ -1702,7 +1702,7 @@ fn check_def( /// bugfix-instance-body-unbound-var (2026-05-13): route each /// `InstanceMethod` body through `check_fn` by wrapping it in a /// synthetic `FnDef`. The InstanceMethod body is conventionally a -/// `Term::Lam` (the post-mq.3 shape; see iter mq.3 journal +/// `Term::Lam` (the post-canonical-class-form shape; see iter method-dispatch-refactor journal /// "instance-method body shape ... existing-convention is `Term::Lam` /// with paramTypes/retType"); we extract its `param_tys` / `ret_ty` /// / `params` / `body` into an `FnDef` shape and reuse the existing @@ -1724,7 +1724,7 @@ fn check_def( /// diagnostic strings. /// /// Non-Lam method bodies are not part of the current schema (all -/// fixtures in the corpus and the convention reaffirmed in iter mq.3 +/// fixtures in the corpus and the convention reaffirmed when method dispatch became type-driven /// use `Term::Lam`); they are silently skipped here. If a future /// schema admits non-Lam shapes, this arm must be extended to walk /// them — silent skip is the conservative choice today because @@ -1781,7 +1781,7 @@ fn check_instance( } fn check_type_def(td: &TypeDef, env: &Env) -> Result<()> { - // Iter 13a: a parameterised ADT (`vars` non-empty) installs its + // a parameterised ADT (`vars` non-empty) installs its // type parameters as rigid vars while checking the ctor field // types, so `List a = ... | Cons(a, List a)` resolves both // occurrences of `a` and the recursive use of `List a` correctly. @@ -1809,7 +1809,7 @@ fn check_type_well_formed(t: &Type, env: &Env) -> Result<()> { } return Ok(()); } - // Iter 15a: a qualified type name `module.Type` resolves + // a qualified type name `module.Type` resolves // through the import map and the per-module type table. The // bare-name path is unchanged. let td_opt: Option<&TypeDef> = if name.matches('.').count() == 1 { @@ -1905,7 +1905,7 @@ fn check_fn(f: &FnDef, env: &Env, out_warnings: &mut Vec) -> Result< env.rigid_vars.insert(v.clone()); } - // Iter 13a: validate the declared parameter and return types + // validate the declared parameter and return types // against the type environment. Catches misuses like // `Box` (arity mismatch on a parameterised ADT) before // they leak into the body and produce confusing downstream errors. @@ -1974,19 +1974,19 @@ fn check_fn(f: &FnDef, env: &Env, out_warnings: &mut Vec) -> Result< } } - // mq.3: build the post-superclass-expansion declared-constraint + // build the post-superclass-expansion declared-constraint // set ONCE here and stash on env, so synth's Var-arm class-method // branch can hand it to `resolve_method_dispatch`'s constraint- // driven filter. The post-synth missing-constraint check below // re-reads the same expanded list from env to keep one source of // truth. // - // mq.1 follow-on: declared constraints carry the canonical-form + // Canonical-class-form follow-on: declared constraints carry the canonical-form // `class` value (bare for same-module, qualified for cross-module). // Lift to the workspace-key shape (always qualified) before // expanding — residuals always carry qualified `class` because // they come from `ClassMethodEntry.class_name` which is qualified - // post-mq.1. + // post-canonical-class-form let declared_constraints: Vec = match &f.ty { Type::Forall { constraints, .. } => constraints .iter() @@ -2019,12 +2019,12 @@ fn check_fn(f: &FnDef, env: &Env, out_warnings: &mut Vec) -> Result< let mut loop_stack: Vec> = Vec::new(); let body_ty = synth(&f.body, &env, &mut locals, &mut loop_stack, &mut effects, &f.name, &mut subst, &mut counter, &mut residuals, &mut free_fn_calls, &mut warnings)?; unify(&ret_ty, &body_ty, &mut subst)?; - // mq.3: surface synth-time warnings into the caller-supplied + // surface synth-time warnings into the caller-supplied // accumulator. The warnings already carry `def: Some(f.name)` // set inside synth's emission site. out_warnings.extend(warnings); - // Iter 14e: tail-position verification (Decision 8). Runs after + // tail-position verification (the explicit-tail-call contract). Runs after // the main type-check so that a tail-call marker on a malformed // call doesn't drown out the underlying type error. verify_tail_positions(&f.body, true)?; @@ -2042,20 +2042,20 @@ fn check_fn(f: &FnDef, env: &Env, out_warnings: &mut Vec) -> Result< } } - // Iter 22b.2 (Task 9): missing-constraint check. The expanded + // missing-constraint check. The expanded // declared-constraint set (declared + one-step superclass per - // Decision 11) was computed pre-synth and stashed on - // `env.active_declared_constraints` (mq.3). Var-shaped residuals + // the typeclass design) was computed pre-synth and stashed on + // `env.active_declared_constraints`. Var-shaped residuals // not covered by the expanded set fire `MissingConstraint`. Concrete // residuals are deferred to Task 10's `no-instance` arm. let expanded = &env.active_declared_constraints; for r in &residuals { let r_ty = subst.apply(&r.type_); - // mq.2: multi-candidate residuals refine before the existing + // multi-candidate residuals refine before the existing // single-class discharge path. `MethodNameCollision` keeps the // candidate set singleton in real workspaces today; this branch - // is exercised exclusively by unit tests until mq.3. + // is exercised exclusively by unit tests until the type-driven dispatch refactor. if r.candidates.is_some() { // Normalize the type before hashing — same contract as the // single-class path below. @@ -2134,7 +2134,7 @@ fn check_fn(f: &FnDef, env: &Env, out_warnings: &mut Vec) -> Result< }); } } else if is_fully_concrete(&r_ty) { - // Iter 22b.2 (Task 10): fully-concrete residual — the class + // fully-concrete residual — the class // method was called at a concrete type, so the obligation // can only be discharged by an existing workspace instance. // Look up `(class, type_hash(r_ty))` in the registry; absence @@ -2163,12 +2163,12 @@ fn check_fn(f: &FnDef, env: &Env, out_warnings: &mut Vec) -> Result< // `Type::Fn`, `Type::Forall`) are out of scope for the 22b.2 // diagnostics: metavars mean the body never connected the // class param to anything observable, and `Fn`/`Forall` cannot - // appear as instance heads (Decision 11). + // appear as instance heads (the typeclass design). } Ok(()) } -/// Iter 22b.2 (Task 10): true iff the type tree contains no `Type::Var` +/// true iff the type tree contains no `Type::Var` /// at any depth. A fully-concrete type is the only shape eligible for /// the no-instance lookup — anything carrying a metavar / rigid-var /// component is either covered by the missing-constraint check or @@ -2184,7 +2184,7 @@ pub(crate) fn is_fully_concrete(t: &Type) -> bool { } } -/// Iter 22b.2 (Task 9): per-call-site residual class constraint +/// per-call-site residual class constraint /// recorded by `synth` when a `Term::Var` resolves to a class method. /// Carries the method name (for diagnostic rendering), the class, and /// the type the class is applied to at the call site. The type is @@ -2209,18 +2209,18 @@ pub struct ResidualConstraint { /// diagnostic to point the author at which call site triggered /// the residual. pub method: String, - /// mq.2: candidate-class set for the multi-candidate dispatch - /// path. `None` ⇒ single-class semantics (pre-mq.2 behaviour); + /// candidate-class set for the multi-candidate dispatch + /// path. `None` ⇒ single-class semantics (pre-canonical-class-form behaviour); /// the [`Self::class`] field is the resolved class. `Some(set)` /// ⇒ multi-candidate residual; discharge / mono refinement filters /// the set at fn-body-end. With `MethodNameCollision` still active - /// (pre-mq.3), real workspaces only ever construct `None`-candidate + /// (pre-canonical-class-form), real workspaces only ever construct `None`-candidate /// residuals; the `Some`-path is exercised exclusively by unit - /// tests until mq.3 retires the workaround. + /// tests until the type-driven dispatch refactor retires the workaround. pub candidates: Option>, } -/// mq.2: outcome of the dispatch-resolution helper +/// outcome of the dispatch-resolution helper /// [`resolve_method_dispatch`]. The synth Var-arm consumer translates /// this enum into either a singleton [`ResidualConstraint`] /// (`Resolved`) or a multi-candidate residual (`Multi`) for discharge- @@ -2247,7 +2247,7 @@ pub enum MethodDispatchOutcome { }, } -/// mq.2: resolve a method-call site per the spec's 5-step rule. Pure +/// resolve a method-call site per the spec's 5-step rule. Pure /// function — no Env mutation, no residual emission. /// /// `qualifier_prefix` is the dot-stripped class prefix (e.g. @@ -2284,8 +2284,8 @@ pub fn resolve_method_dispatch( return MethodDispatchOutcome::UnknownClass(q.to_string()); } - // Step 4: bare-method form, singleton candidate set — pre-mq.3 - // path. Returns immediately so the post-mq.3 multi-candidate + // Step 4: bare-method form, singleton candidate set — pre-canonical-class-form + // path. Returns immediately so the post-canonical-class-form multi-candidate // logic below stays gated on a non-singleton candidate set. if candidates.len() == 1 { return MethodDispatchOutcome::Resolved( @@ -2348,7 +2348,7 @@ pub fn resolve_method_dispatch( } } -/// mq.2: outcome of multi-candidate residual refinement at discharge +/// outcome of multi-candidate residual refinement at discharge /// time. Consumed by `check_fn`'s residual loop and by mono's /// residual-to-target mapping. #[derive(Debug, Clone, PartialEq, Eq)] @@ -2382,11 +2382,11 @@ pub enum RefineOutcome { }, } -/// mq.2: refine a multi-candidate residual at discharge time per the +/// refine a multi-candidate residual at discharge time per the /// spec's 5-step rule (constraint-discharge refinement subsection). /// Caller MUST pass a residual whose `candidates` is `Some(...)`; /// single-candidate residuals (`candidates: None`) bypass this helper -/// and discharge directly against the registry as in pre-mq.2. +/// and discharge directly against the registry as in pre-canonical-class-form /// /// Refinement strategy: /// - Concrete `type_`: filter the candidate set against the registry @@ -2458,7 +2458,7 @@ pub fn refine_multi_candidate_residual( } } -/// Iter 23.4: per-call-site polymorphic-free-fn observation recorded by +/// per-call-site polymorphic-free-fn observation recorded by /// `synth` when a `Term::Var` resolves to a `Type::Forall`-quantified /// top-level def (NOT a class method — those are pushed to /// [`ResidualConstraint`] instead). Carries the bare name, the owning @@ -2489,7 +2489,7 @@ pub struct FreeFnCall { pub metas: Vec, } -/// mq.1: lift a canonical-form class-ref value to the qualified +/// lift a canonical-form class-ref value to the qualified /// workspace-key shape. Bare ⇒ prepend `caller_module` (the bare /// form names the caller-module-local class under the canonical-form /// rule); qualified ⇒ as-is. Sibling of `workspace::qualify_class_ref` @@ -2503,7 +2503,7 @@ pub(crate) fn qualify_class_ref_in_check(class_ref: &str, caller_module: &str) - } } -/// mq.2: split a `Term::Var.name` into `(method_name, +/// split a `Term::Var.name` into `(method_name, /// optional_qualifier_prefix)` at the last dot. /// /// - `"show"` → `("show", None)` @@ -2515,7 +2515,7 @@ pub(crate) fn qualify_class_ref_in_check(class_ref: &str, caller_module: &str) - /// qualifiers via [`qualifier_is_class_shape`]: PascalCase /// single-segment prefixes (e.g. `"Show"`) and multi-segment /// prefixes (e.g. `"prelude.Show"`) are class qualifiers per -/// mq.1's canonical-form rule; lowercase single-segment prefixes +/// the canonical-class-form rule; lowercase single-segment prefixes /// (e.g. `"std_list"`) are module names and the caller falls /// through to the cross-module-fn path. pub(crate) fn parse_method_qualifier(name: &str) -> (&str, Option) { @@ -2552,12 +2552,12 @@ pub(crate) fn qualifier_is_class_shape(qualifier_opt: &Option) -> bool { } } -/// mq.2 dispatch entry predicate: the synth Var-arm runs this to +/// Type-driven dispatch entry predicate: the synth Var-arm runs this to /// decide whether `name` should be routed through the class-method /// dispatch helper. Matches a bare method name (e.g. `"show"`) or a /// class-shaped qualifier (`"Show.show"`, `"prelude.Show.show"`). /// A 1-dot lowercase name (`"std_list.length"`) is structurally a -/// cross-module-fn call per mq.1's canonical-form rule and falls +/// cross-module-fn call per the canonical-class-form rule's canonical-form rule and falls /// through to the qualified-fn arm. fn is_class_method_dispatch(name: &str, env: &Env) -> bool { let (method_name, qualifier_opt) = parse_method_qualifier(name); @@ -2598,10 +2598,10 @@ pub(crate) fn any_candidate_class_has_instance( }) } -/// Iter 22b.2 (Task 9): expand a list of declared class constraints -/// with their one-step superclass closure (Decision 11). For every +/// expand a list of declared class constraints +/// with their one-step superclass closure (the typeclass design). For every /// declared `(C, t)`, append `(S, t)` if class `C` has a superclass -/// `S`. Only one step — Decision 11 forbids deeper chains. The +/// `S`. Only one step — The typeclass design forbids deeper chains. The /// expanded list is what [`check_fn`] uses to decide whether a /// residual is satisfied. fn expand_declared_constraints( @@ -2620,7 +2620,7 @@ fn expand_declared_constraints( out } -/// Iter 22b.2 (Task 9): equality of constraint types for the +/// equality of constraint types for the /// missing-constraint match. The residual type has been /// `subst.apply`-d, so two rigid vars match iff their names are equal; /// a `Type::Con` matches another `Type::Con` iff their head names and @@ -2639,9 +2639,10 @@ fn constraint_type_matches(declared: &Type, residual: &Type) -> bool { } } -/// Iter 14e: verifies that every `Term::App { tail: true, .. }` and +/// verifies that every `Term::App { tail: true, .. }` and /// `Term::Do { tail: true, .. }` actually sits in tail position, per -/// Decision 8. +/// the explicit-tail-call contract +/// (`design/contracts/tail-calls.md`). /// /// `is_tail` is the tail-context flag for the term currently being /// visited. The propagation rules (from design/contracts/tail-calls.md): @@ -2713,7 +2714,7 @@ pub fn verify_tail_positions(t: &Term, is_tail: bool) -> Result<()> { verify_tail_positions(body, true) } Term::LetRec { body, in_term, .. } => { - // Iter 16b.3: `Term::LetRec` may now survive the desugar + // `Term::LetRec` may now survive the desugar // pass (when it captures `Term::Let`-bound names whose // types are only known after typecheck). The body is the // body of a fn-typed binding; the recursive name is what @@ -2724,12 +2725,12 @@ pub fn verify_tail_positions(t: &Term, is_tail: bool) -> Result<()> { verify_tail_positions(in_term, is_tail) } Term::Clone { value } => { - // Iter 18c.1: clone is identity. Tail-position propagates + // clone is identity. Tail-position propagates // through unchanged — `(clone tail-call)` is a tail call. verify_tail_positions(value, is_tail) } Term::ReuseAs { source, body } => { - // Iter 18d.1: reuse-as is identity at codegen. The `source` + // reuse-as is identity at codegen. The `source` // is a side-effect-free Var in shipping fixtures, so it is // not a tail call; the `body` is the value of the whole // expression and inherits the enclosing tail position. @@ -2867,7 +2868,7 @@ fn check_const(c: &ConstDef, env: &Env, out_warnings: &mut Vec) -> R let mut effects = BTreeSet::new(); let mut subst = Subst::default(); let mut counter: u32 = 0; - // Iter 22b.2 (Task 9): const bodies don't carry a `Forall.constraints` + // const bodies don't carry a `Forall.constraints` // (rejected upstream), so any residual would have to be concrete — // Task 10's no-instance arm will pick those up. We thread an // accumulator only to keep `synth`'s signature uniform. @@ -2911,7 +2912,7 @@ pub(crate) fn synth( counter: &mut u32, residuals: &mut Vec, free_fn_calls: &mut Vec, - // mq.3: out-parameter for synth-time structured warnings. Symmetric + // out-parameter for synth-time structured warnings. Symmetric // to `residuals` / `free_fn_calls`. Currently only the // `class-method-shadowed-by-fn` warning is emitted here; future // synth-time warnings (if any) would flow through the same channel. @@ -2932,13 +2933,13 @@ pub(crate) fn synth( // the use site (textbook ML rule); rigid vars and concrete // types pass through unchanged. // - // Iter 22b.2 (Task 9): the class-method channel sits between + // the class-method channel sits between // local globals and qualified cross-module ref. When a name // resolves through `env.class_methods`, we substitute the // class param with a fresh metavar and record a residual // class constraint — `check_fn` later compares the residual // set against the declared `Forall.constraints`. - // Iter 23.4: alongside the bare type, capture + // alongside the bare type, capture // (owner_module, unqualified_name_in_owner_module) // for any non-local resolution that lands on a // `Type::Forall` AND is a real workspace-declared @@ -2957,7 +2958,7 @@ pub(crate) fn synth( // observation; the mono pass `subst.apply`s the metas // post-synth to recover concrete type-args at each // polymorphic free-fn call site. - // mq.3: a helper closure that emits the + // a helper closure that emits the // `class-method-shadowed-by-fn` warning when a fn-precedence // branch resolves a name that ALSO has class-method // candidates in the workspace. The fn resolution proceeds; @@ -3038,12 +3039,12 @@ pub(crate) fn synth( .and_then(|mg| mg.get(name).map(|ty| (mod_name.clone(), ty.clone()))) }) { - // mq.3: implicit-import free-fn precedence runs BEFORE + // implicit-import free-fn precedence runs BEFORE // the class-method branch (fn wins per spec // §"Class-fn collisions"). When the name has class- // method candidates too, emit the shadow warning. // - // Iter 23.4-prep: bare-name fall-through to free fns of + // bare-name fall-through to free fns of // implicitly-imported modules. Today only the prelude is // implicitly imported (see `build_check_env` and // `check_in_workspace`'s prelude-injection), so the @@ -3066,7 +3067,7 @@ pub(crate) fn synth( // unqualified name in the owner module is the same `name`. (qualified, Some((owner_module, name.clone()))) } else if is_class_method_dispatch(name, env) { - // mq.2: type-driven dispatch entry. The Var arm runs + // type-driven dispatch entry. The Var arm runs // before App-arm unification, so the arg type is not // available here. The dispatch helper resolves on // (a) explicit class qualifier, (b) singleton candidate @@ -3074,10 +3075,10 @@ pub(crate) fn synth( // filter (rigid-var case); otherwise it emits `Multi` // for discharge-time refinement. // - // With `MethodNameCollision` still active (pre-mq.3), + // With `MethodNameCollision` still active (pre-canonical-class-form), // real workspaces never reach the `Multi` branch — the // candidate set is always singleton. The branch is - // installed here for mq.3 to exercise without further + // installed here for the type-driven dispatch refactor to exercise without further // dispatch-side edits. let (method_name, qualifier_opt) = parse_method_qualifier(name); let candidates = env @@ -3124,7 +3125,7 @@ pub(crate) fn synth( } }; - // mq.3: class_methods is tuple-keyed by + // class_methods is tuple-keyed by // `(qualified-class, method)`. The dispatcher above // resolved a tentative class (single-survivor or first // BTreeSet element for the multi-candidate path) — use @@ -3179,7 +3180,7 @@ pub(crate) fn synth( name: suffix.to_string(), } })?; - // Iter 15a: qualify any bare type-cons referring to a + // qualify any bare type-cons referring to a // type defined in the owning module so that signatures // pulled across the boundary unify against // qualified-form ctors and types in the consumer @@ -3196,7 +3197,7 @@ pub(crate) fn synth( } else { return Err(CheckError::UnknownIdent(name.clone())); }; - // Iter 23.4: if raw is `Type::Forall` AND we know the + // if raw is `Type::Forall` AND we know the // owner module (i.e. the resolution went through a non- // local branch), record a FreeFnCall observation BEFORE // instantiating. The metas are the fresh metavars we @@ -3210,7 +3211,7 @@ pub(crate) fn synth( (&raw, &free_fn_owner) { let (metas, inst) = instantiate(vars, body, counter); - // Iter 24.3: push a residual for each declared constraint + // push a residual for each declared constraint // of the poly free fn with the rigid var substituted by // its fresh metavar. The discharge loop at the enclosing // `check_fn`'s post-synth phase resolves the residual @@ -3357,13 +3358,13 @@ pub(crate) fn synth( Ok(sig.ret) } Term::Ctor { type_name, ctor, args } => { - // ct.2 Task 3: Term::Ctor lookup is direct post-ct.1. + // Canonical-type-lookup: Term::Ctor lookup is direct post-canonical-type-form // Canonical type_name: bare = local TypeDef, qualified = // explicit cross-module. The imports-fallback (iter // 23.1.3) is gone — bare cross-module refs are rejected - // upstream by the workspace validator (ct.1). + // upstream by the workspace validator (via the canonical-form validator). // - // Iter 15b: when the type is cross-module, `cdef.fields` is + // when the type is cross-module, `cdef.fields` is // written in the owning module's local namespace. A recursive // self-reference like `Cons a (List a)` carries `Con // { name: "List" }` even though, from the consumer's view, @@ -3413,7 +3414,7 @@ pub(crate) fn synth( got: args.len(), }); } - // Iter 15b: qualify local type-cons in the field types when + // qualify local type-cons in the field types when // the ctor's owning type is cross-module. No-op when the // type is local (owning_module is None). let qualified_fields: Vec = match &owning_module { @@ -3430,7 +3431,7 @@ pub(crate) fn synth( } None => cdef.fields.clone(), }; - // Iter 13a: parameterised ADT — instantiate the type's vars + // parameterised ADT — instantiate the type's vars // with fresh metavars, substitute them through every ctor // field type, and let the field types' metavars be solved by // unifying against the actual arg types. The result type @@ -3504,7 +3505,7 @@ pub(crate) fn synth( } if !has_open_arm { - // Iter 15a: a qualified scrutinee type (`module.Type`, + // a qualified scrutinee type (`module.Type`, // produced by a cross-module ctor) resolves through // `env.module_types`; bare names use `env.types` as // before. @@ -3552,7 +3553,7 @@ pub(crate) fn synth( synth(rhs, env, locals, loop_stack, effects, in_def, subst, counter, residuals, free_fn_calls, warnings) } Term::Lam { params, param_tys, ret_ty, effects: lam_effects, body } => { - // Iter loop-recur.tidy: reject lambda-captures-of-loop-binder. + // reject lambda-captures-of-loop-binder. // Walk the lambda body for free Vars (names not bound by // the lambda's own params) that match any loop binder on // the enclosing `loop_stack`. Loop binders are @@ -3616,7 +3617,7 @@ pub(crate) fn synth( }) } Term::LetRec { name, ty, params, body, in_term } => { - // Iter 16b.3: a `Term::LetRec` reaches `synth` only when + // a `Term::LetRec` reaches `synth` only when // the desugar pass deferred it (some capture is // `Term::Let`-bound and its type is only knowable here). // We synthesize it as if it were a recursive fn-typed @@ -3715,14 +3716,14 @@ pub(crate) fn synth( in_ty } Term::Clone { value } => { - // Iter 18c.1: `(clone X)` typechecks identically to `X`. + // `(clone X)` typechecks identically to `X`. // No constraint generated, no environment change. The // wrapper records author intent for the future RC inc/dec // emission pass (18c.3); typing is pure passthrough. synth(value, env, locals, loop_stack, effects, in_def, subst, counter, residuals, free_fn_calls, warnings) } Term::ReuseAs { source, body } => { - // Iter 18d.1: `(reuse-as SRC NEW-CTOR)` requires `body` to + // `(reuse-as SRC NEW-CTOR)` requires `body` to // be an allocating term — `Term::Ctor` or `Term::Lam`. Any // other shape is a structural error: the wrapper has // nothing to rewrite. The `source` term has no body-shape @@ -3856,7 +3857,7 @@ fn maybe_instantiate(t: Type, counter: &mut u32) -> Type { } } -/// Iter 15a: rewrites bare `Type::Con` references that resolve against +/// rewrites bare `Type::Con` references that resolve against /// `local_types` into qualified `module.Type` form. Used when pulling a /// fn type across module boundaries: a `Maybe a` declared inside /// `std_maybe` becomes `std_maybe.Maybe a` when seen from a consumer @@ -3940,7 +3941,7 @@ fn type_check_pattern( Ok(vec![]) } Pattern::Ctor { ctor, fields } => { - // Iter 16a: nested **Ctor** sub-patterns are removed by + // nested **Ctor** sub-patterns are removed by // `ailang_core::desugar::desugar_module` before this checker // runs, so we only have to defend against the still-rejected // **Lit** sub-pattern case. The `nested-ctor-pattern-not-allowed` @@ -3961,7 +3962,7 @@ fn type_check_pattern( } } } - // ct.2 Task 2: Pattern::Ctor lookup is type-driven post-ct.1. + // Canonical-type-pattern lookup: Pattern::Ctor lookup is type-driven post-canonical-type-form // The scrutinee's Type::Con name is canonical (bare = local // TypeDef, qualified = explicit cross-module). Derive the // TypeDef from `expected` and find the ctor by name within @@ -4013,12 +4014,12 @@ fn type_check_pattern( got: fields.len(), }); } - // Iter 13a: substitute the scrutinee's concrete type-args + // substitute the scrutinee's concrete type-args // into each cdef field type (e.g. `Cons(a, List a)` checked // against a `List Int` scrutinee yields field types // `Int, List Int`). // - // Iter 15b: when the resolved ctor lives in an imported + // when the resolved ctor lives in an imported // module, qualify any bare local type-cons in the field // types first — symmetric to the term-ctor fix. let qualified_fields: Vec = match &resolved_owning_module { @@ -4117,7 +4118,7 @@ pub struct Env { /// [`Self::imports`] with the current module's map at the call /// site. pub module_imports: BTreeMap>, - /// Iter 15a: ADT type definitions per module of the workspace. Used + /// ADT type definitions per module of the workspace. Used /// to resolve qualified type references (`module.Type` in /// `Type::Con.name`) and qualified `Term::Ctor.type_name`, and to /// fall back when a bare `Pattern::Ctor.ctor` cannot be resolved @@ -4132,7 +4133,7 @@ pub struct Env { /// these names are legal as `Type::Var { name }` and unify only /// with themselves. pub rigid_vars: BTreeSet, - /// mq.3: workspace-flat aggregate of every module's + /// workspace-flat aggregate of every module's /// [`ModuleGlobals::class_methods`], re-keyed to /// `(qualified-class-name, method-name)` so post-retirement of /// `MethodNameCollision` two classes can share a method name. @@ -4141,15 +4142,15 @@ pub struct Env { /// name. Mono's presence checks consult /// [`Self::method_to_candidate_classes`] (method-keyed natively). pub class_methods: BTreeMap<(String, String), ClassMethodEntry>, - /// mq.2: workspace-flat inverse of [`Self::class_methods`] — for + /// workspace-flat inverse of [`Self::class_methods`] — for /// each method name, the set of qualified class names that declare - /// it. Pre-mq.3, the `MethodNameCollision` invariant guarantees - /// each set is singleton; mq.3 lifts the invariant and any + /// it. Pre-canonical-class-form, the `MethodNameCollision` invariant guarantees + /// each set is singleton; the type-driven dispatch refactor lifts the invariant and any /// cardinality above one becomes legal. Synth's `Term::Var` arm /// consults this index for type-driven dispatch (spec /// §Architecture's 5-step rule). pub method_to_candidate_classes: BTreeMap>, - /// mq.3: post-superclass-expansion declared constraints of the + /// post-superclass-expansion declared constraints of the /// active fn body being checked. Populated by `check_fn` before /// invoking `synth` on the body; empty at workspace entry. /// Consumed by `synth`'s Var-arm class-method branch when calling @@ -4158,15 +4159,16 @@ pub struct Env { /// fallback (e.g. inside a polymorphic fn body where the arg /// type is still a rigid type variable). pub active_declared_constraints: Vec, - /// Iter 22b.2 (Task 9): one-step superclass expansion table. + /// one-step superclass expansion table. /// Maps each class name to its superclass class name. Absence from /// the map means the class has no superclass — populated only for /// classes that declare one. Used by `check_fn` to expand - /// `Forall.constraints` for the missing-constraint check - /// (Decision 11: max one step). The schema permits at most one + /// `Forall.constraints` for the missing-constraint check (the + /// typeclass design caps superclass expansion at one step). The + /// schema permits at most one /// superclass per class. pub class_superclasses: BTreeMap, - /// Iter 22b.2 (Task 10): workspace-wide instance registry, threaded + /// workspace-wide instance registry, threaded /// through from [`Workspace::registry`]. Read by `check_fn` in the /// concrete-residual path: when a class-method call at a fully- /// concrete type produces a residual `(class, type_)` whose @@ -4230,7 +4232,7 @@ mod tests { subst.apply(&ty) } - /// Iter loop-recur.tidy: `CheckError::LoopBinderCapturedByLambda` + /// `CheckError::LoopBinderCapturedByLambda` /// carries the stable kebab code `loop-binder-captured-by-lambda` /// and a non-panicking `ctx()` arm carrying the captured name — /// symmetric to `mut_var_captured_by_lambda_diagnostic_has_kebab_code`. @@ -4285,7 +4287,7 @@ mod tests { let _ty = synth_standalone(&term); } - /// Iter loop-recur.tidy: a lambda nested inside a `Term::Loop` + /// a lambda nested inside a `Term::Loop` /// whose body references a loop binder declared by the enclosing /// loop is rejected with `LoopBinderCapturedByLambda`. The /// captured-name field reports the offending binder. @@ -4598,7 +4600,7 @@ mod tests { assert!(format!("{err}").contains("type mismatch")); } - /// Iter 12a: a top-level def annotated `forall a. (a) -> a` is + /// a top-level def annotated `forall a. (a) -> a` is /// admitted; its body checks against a rigid type var. The /// var name (`a`) is in scope as a rigid throughout the body. #[test] @@ -4627,7 +4629,7 @@ mod tests { check(&m).expect("polymorphic id should typecheck"); } - /// Iter 12a: `id` instantiates fresh metavars at each use site. + /// `id` instantiates fresh metavars at each use site. /// Calling `id(42)` produces an `Int`; calling `id(true)` would /// produce a `Bool`. Two distinct uses must not bleed into each /// other (each gets its own metavar). @@ -4692,7 +4694,7 @@ mod tests { check(&m).expect("two distinct id instantiations should typecheck"); } - /// Iter 12a: a `Forall` callee at App must instantiate consistently. + /// a `Forall` callee at App must instantiate consistently. /// `id(42) :: Bool` is rejected because the metavar gets pinned to /// `Int` by the arg, which conflicts with the declared `Bool` ret. #[test] @@ -4741,7 +4743,7 @@ mod tests { assert!(msg.contains("type mismatch"), "got: {msg}"); } - /// Iter 12a: two-var polymorphism. `apply : forall a b. ((a -> b), + /// two-var polymorphism. `apply : forall a b. ((a -> b), /// a) -> b`. Body applies the function. We instantiate at two /// different use sites with different (a, b) pairs. #[test] @@ -4823,7 +4825,7 @@ mod tests { check(&m).expect("apply instantiation should typecheck"); } - /// Iter 13a: parameterised ADT — `type Box[a] = MkBox(a)` is well- + /// parameterised ADT — `type Box[a] = MkBox(a)` is well- /// formed and `MkBox(42)` typechecks at result type `Box`. #[test] fn parameterised_adt_ctor_at_int() { @@ -4866,7 +4868,7 @@ mod tests { check(&m).expect("Box ctor should typecheck"); } - /// Iter 13a: ctor field type substitution and match-arm bindings. + /// ctor field type substitution and match-arm bindings. /// `unbox :: forall a. (Box) -> a` extracts the wrapped value. #[test] fn parameterised_adt_polymorphic_unbox() { @@ -4962,7 +4964,7 @@ mod tests { check(&m).expect("polymorphic Box should typecheck at both instantiations"); } - /// Iter 13a: parameterised-ADT arity is enforced. `Box` (1 var) + /// parameterised-ADT arity is enforced. `Box` (1 var) /// used as `Box` must error. #[test] fn parameterised_adt_arity_is_enforced() { @@ -5002,7 +5004,7 @@ mod tests { assert!(msg.contains("expects 1 type arg"), "got: {msg}"); } - /// Iter 10: `seq` requires lhs to be Unit. A non-Unit lhs is a + /// `seq` requires lhs to be Unit. A non-Unit lhs is a /// type error — the value of lhs gets discarded so a useful (non- /// Unit) value would silently vanish. #[test] @@ -5036,7 +5038,7 @@ mod tests { assert!(msg.contains("type mismatch"), "got: {msg}"); } - /// Iter 14e: a `Term::App { tail: true, .. }` in non-tail position + /// a `Term::App { tail: true, .. }` in non-tail position /// must surface as `tail-call-not-in-tail-position`. Construction: /// the recursive call sits as an argument to a Cons ctor (the /// classic constructor-blocked recursion from the 14d survey). @@ -5125,7 +5127,7 @@ mod tests { } } - /// Iter 15a, path 1: a qualified `Type::Con` (`lib.Box`) resolves + /// a qualified `Type::Con` (`lib.Box`) resolves /// through `module_types` rather than the local-module `types`. #[test] fn cross_module_qualified_type_in_param_resolves() { @@ -5154,7 +5156,7 @@ mod tests { assert!(diags.is_empty(), "expected green; got {diags:?}"); } - /// Iter 15a, path 2: a qualified `Term::Ctor.type_name` + /// a qualified `Term::Ctor.type_name` /// (`lib.Box`) resolves and constructs `lib.Box`. #[test] fn cross_module_qualified_term_ctor_resolves() { @@ -5187,10 +5189,10 @@ mod tests { assert!(diags.is_empty(), "expected green; got {diags:?}"); } - /// Iter 15a (post-ct.2): a bare `Pattern::Ctor` (e.g. `MkBox x`) + /// a bare `Pattern::Ctor` (e.g. `MkBox x`) /// against a scrutinee of type `lib.Box` resolves through /// the type-driven lookup keyed on `expected.name`. Previously - /// this exercised an imports-fallback; post-ct.2 the lookup is + /// this exercised an imports-fallback; post-canonical-type-form the lookup is /// anchored to the scrutinee's qualified TypeDef directly. #[test] fn cross_module_pat_ctor_typedriven_resolves() { @@ -5228,7 +5230,7 @@ mod tests { assert!(diags.is_empty(), "expected green; got {diags:?}"); } - /// Iter 15b: a recursive cross-module ADT (`std_list.List a` with a + /// a recursive cross-module ADT (`std_list.List a` with a /// `Cons a (List a)` ctor) round-trips through both `Term::Ctor` synth /// and pattern-ctor binding without unqualified-field-name unification /// failures. The bug fixed in 15b: `cdef.fields` on the imported side @@ -5332,20 +5334,20 @@ mod tests { assert!(diags.is_empty(), "expected green; got {diags:?}"); } - /// ct.2 Task 1: a class method whose declared return type is a + /// Canonical-type qualification: a class method whose declared return type is a /// bare local Type::Con in the defining module (e.g. /// `compare : a -> a -> Ordering` declared inside `prelude`) must /// be presented to a consumer module's typecheck context with the /// return type qualified (`prelude.Ordering`). Without this, the /// consumer's `Pattern::Ctor LT` against the scrutinee no longer - /// resolves once the post-ct.1 canonical form removes the + /// resolves once the post-canonical-type-form canonical form removes the /// imports-fallback that previously papered over the mismatch. #[test] fn ct2_class_method_cross_module_qualifies_return_type() { // Defining module declares `Ordering` locally and a class // `Ord` with a method `compare : a -> a -> Ordering`. The // `Ordering` Type::Con is bare-local in the class method's ty, - // which is canonical post-ct.1 (bare = local to defining module). + // which is canonical post-canonical-type-form (bare = local to defining module). let prelude = Module { schema: SCHEMA.into(), name: "p".into(), @@ -5489,14 +5491,14 @@ mod tests { ); } - /// ct.2 Task 1: `qualify_local_types`'s `Type::Forall` arm must + /// Canonical-type qualification: `qualify_local_types`'s `Type::Forall` arm must /// recurse into `constraints[].type_`. A class method whose /// declared type is `Forall{Ord a}. Fn[..]` carries the /// constraint type `Type::Var { name: "a" }` — fine — but a /// hypothetical `Forall{Show p.Foo}. Fn[..]` would carry a /// bare-local `Foo` if the defining module is `p`, and the /// consumer must see `p.Foo` after the cross-module qualifier - /// runs. Symmetric to the ct.1.5a-followup fix on + /// runs. Symmetric to the canonical-form-normalisation follow-up fix on /// `normalize_type_for_registry`. #[test] fn ct2_qualify_local_types_recurses_into_forall_constraints() { @@ -5539,7 +5541,7 @@ mod tests { } } - /// Iter 14e: a `Term::App { tail: true, .. }` that genuinely sits + /// a `Term::App { tail: true, .. }` that genuinely sits /// in tail position (as the rhs of a `Seq` that is the body of a /// `Match` arm that is the body of the fn) must pass. #[test] @@ -5618,7 +5620,7 @@ mod tests { check(&m).expect("tail-call in tail position should typecheck"); } - /// Iter 16b.3: a `Term::LetRec` that captures a `Term::Let`-bound + /// a `Term::LetRec` that captures a `Term::Let`-bound /// name (whose type is known only after typecheck) reaches `synth` /// because the desugar pass leaves it in place. The new typing /// rule for `Term::LetRec` accepts it: extends locals with `name` @@ -5701,7 +5703,7 @@ mod tests { check(&m).expect("LetRec with Let-binding capture should typecheck"); } - /// Iter 16b.3: a LetRec whose body returns the wrong type (Bool + /// a LetRec whose body returns the wrong type (Bool /// instead of the declared Int) is caught by the new typing rule. /// Property protected: the new arm in `synth` for `Term::LetRec` /// runs `unify(&ret_ty, &body_ty, subst)` just like `Term::Lam` @@ -5790,7 +5792,7 @@ mod tests { ); } - /// Iter 16b.3: `lift_letrecs` on a module containing a deferred + /// `lift_letrecs` on a module containing a deferred /// LetRec produces a module whose defs include a synthetic /// `$lr_N` FnDef and whose original fn body has rewritten /// call sites. @@ -5901,7 +5903,7 @@ mod tests { } } - /// Iter 16b.4: a LetRec whose body captures a `Pattern::Ctor`-Var + /// a LetRec whose body captures a `Pattern::Ctor`-Var /// match-arm binding is lifted by `lift_letrecs` to a synthetic /// top-level fn whose signature has the binding's type appended. /// Property protected: `type_check_pattern_for_lift` substitutes @@ -6024,7 +6026,7 @@ mod tests { assert_eq!(synth.params, vec!["z".to_string(), "x".to_string()]); } - /// Iter 16b.6: post-typecheck `lift_letrecs` lifts a deferred + /// post-typecheck `lift_letrecs` lifts a deferred /// LetRec inside a polymorphic enclosing fn into a `Forall`-typed /// synthetic top-level fn, mirroring the enclosing fn's type /// vars. Property protected: @@ -6191,7 +6193,7 @@ mod tests { ); } - /// Iter 16e: helper that wraps `body` in a fn whose return type is + /// helper that wraps `body` in a fn whose return type is /// `Bool`, runs `check`, and returns the diagnostics. Used by the /// polymorphic-`==` typecheck tests below. fn check_eq_body_returns_bool(body: Term) -> Vec { @@ -6223,7 +6225,7 @@ mod tests { } } - /// Iter 16e: `==` accepts Int args (regression — the pre-16e + /// `==` accepts Int args (regression — the pre-16e /// behaviour stays identical). #[test] fn eq_typechecks_at_int() { @@ -6234,7 +6236,7 @@ mod tests { assert!(check_eq_body_returns_bool(body).is_empty()); } - /// Iter 16e: `==` accepts Bool args. Pre-16e this was a typecheck + /// `==` accepts Bool args. Pre-16e this was a typecheck /// error because `==` was declared `(Int, Int) -> Bool`. #[test] fn eq_typechecks_at_bool() { @@ -6245,7 +6247,7 @@ mod tests { assert!(check_eq_body_returns_bool(body).is_empty()); } - /// Iter 16e: `==` accepts Str args. Same story as Bool. + /// `==` accepts Str args. Same story as Bool. #[test] fn eq_typechecks_at_str() { let body = eq_app( @@ -6255,7 +6257,7 @@ mod tests { assert!(check_eq_body_returns_bool(body).is_empty()); } - /// Iter 16e: `==` accepts Unit args. + /// `==` accepts Unit args. #[test] fn eq_typechecks_at_unit() { let body = eq_app( @@ -6265,7 +6267,7 @@ mod tests { assert!(check_eq_body_returns_bool(body).is_empty()); } - /// Iter 16e: `==`'s type still demands the two sides to agree — + /// `==`'s type still demands the two sides to agree — /// `(== 1 true)` must fail to unify the second arg's `Bool` /// against the metavar already pinned to `Int` by the first. #[test] @@ -6281,7 +6283,7 @@ mod tests { ); } - /// Iter 18d.1: a `(reuse-as xs 42)` where the body is a literal + /// a `(reuse-as xs 42)` where the body is a literal /// (not a `Term::Ctor` and not `Term::Lam`) must emit /// `reuse-as-non-allocating-body` with `ctx.got = "lit"`. The /// suggested rewrite drops the wrapper; the replacement parses @@ -6394,7 +6396,7 @@ mod tests { ); } - /// ct.2 Task 2: a `Pattern::Ctor` against a scrutinee of type + /// Canonical-type-pattern lookup: a `Pattern::Ctor` against a scrutinee of type /// `Type::Con { name: "p.T", .. }` looks up the TypeDef in /// `env.module_types["p"]["T"]` and finds the ctor by name within /// it — no `env.ctor_index` consult, no imports-walk. This pins the @@ -6458,9 +6460,9 @@ mod tests { assert!(diags.is_empty(), "expected green; got {diags:?}"); } - /// ct.2 Task 2: when the scrutinee carries a Type::Con name that + /// Canonical-type-pattern lookup: when the scrutinee carries a Type::Con name that /// doesn't resolve to any TypeDef (workspace-wide), the pattern - /// lookup must fail closed. The post-ct.1 validator catches this + /// lookup must fail closed. The post-canonical-type-form validator catches this /// shape earlier for canonical inputs; this test pins the residual /// runtime guard against constructed (non-loaded) AST. #[test] @@ -6510,12 +6512,12 @@ mod tests { ); } - /// ct.2 Task 3: a bare `Term::Ctor.type_name` that does not resolve + /// Canonical-type-lookup: a bare `Term::Ctor.type_name` that does not resolve /// to a local TypeDef must error with `UnknownType` (or /// `UnknownCtor`, depending on which check fires first). The /// imports-fallback that previously synthesised a qualified result /// from an arbitrary imported module is gone. The - /// post-ct.1 validator catches this shape at load time for + /// post-canonical-type-form validator catches this shape at load time for /// canonical inputs; this test pins the residual runtime guard /// against constructed (non-loaded) AST. #[test] @@ -6579,9 +6581,9 @@ mod tests { ); } - /// ct.2 Task 3: a qualified `Term::Ctor.type_name` + /// Canonical-type-lookup: a qualified `Term::Ctor.type_name` /// (`p.Ordering` / `LT`) continues to resolve cleanly through the - /// qualified branch. This is the canonical form post-ct.1 and the + /// qualified branch. This is the canonical form post-canonical-type-form and the /// hot path for every Term::Ctor in a migrated fixture. #[test] fn ct2_term_ctor_qualified_cross_module_resolves() { @@ -6638,7 +6640,7 @@ mod tests { assert!(diags.is_empty(), "expected green; got {diags:?}"); } - /// Iter 23.4-prep Task 2: bare-name resolution falls through to free + /// bare-name resolution falls through to free /// fns of implicitly-imported modules. The consumer calls bare `dbl`, /// declared in `prelude` (the singleton implicit import today). The /// pre-fix behaviour is `unbound-var` because the `Term::Var` lookup @@ -6701,7 +6703,7 @@ mod tests { ); } - /// mq.2.1: `AmbiguousMethodResolution` is the new check-time + /// `AmbiguousMethodResolution` is the new check-time /// diagnostic for multi-candidate residuals that survive type-driven /// filtering at a monomorphic call site. Verifies the variant is /// constructible, its `code()` returns the structured-diagnostic key, @@ -6723,7 +6725,7 @@ mod tests { assert!(rendered.contains("userlib.Show"), "Display must name candidate, got: {rendered}"); } - /// mq.2.1: `UnknownClass` is the new check-time diagnostic for an + /// `UnknownClass` is the new check-time diagnostic for an /// explicit class qualifier in `Term::Var.name` that names a /// qualified class not in the workspace. #[test] @@ -6736,9 +6738,9 @@ mod tests { assert!(rendered.contains("unknownlib.Show"), "Display must echo the qualified name, got: {rendered}"); } - /// mq.2.2: `NoInstance` carries an additive `candidate_classes` + /// `NoInstance` carries an additive `candidate_classes` /// list. When non-empty, the rendered ctx() carries the candidate - /// list; when empty, the ctx() shape is unchanged from pre-mq.2 + /// list; when empty, the ctx() shape is unchanged from pre-canonical-class-form /// form (backwards compatible for single-class call sites). #[test] fn mq2_no_instance_with_candidate_classes_renders() { @@ -6760,7 +6762,7 @@ mod tests { assert_eq!(ctx["candidate_classes"][1], "userlib.Show"); } - /// mq.2.2: empty `candidate_classes` keeps the existing single-class + /// empty `candidate_classes` keeps the existing single-class /// ctx() shape — no `candidate_classes` key in the JSON. #[test] fn mq2_no_instance_empty_candidates_back_compat() { @@ -6778,9 +6780,9 @@ mod tests { "empty candidate_classes must not render in ctx; got: {ctx}"); } - /// mq.2.3: `ResidualConstraint` carries an optional candidate-class + /// `ResidualConstraint` carries an optional candidate-class /// set for the multi-candidate dispatch path. `None` preserves the - /// pre-mq.2 single-class semantics (the `class` field is the + /// pre-canonical-class-form single-class semantics (the `class` field is the /// resolved class). `Some(...)` carries the candidate set; the /// `class` field's content is a tentative value until discharge /// resolves it. @@ -6806,7 +6808,7 @@ mod tests { assert_eq!(multi.candidates, Some(multi_set)); } - /// mq.2.5: smoke test on `parse_method_qualifier` — the helper + /// smoke test on `parse_method_qualifier` — the helper /// underpinning the rewritten synth Var-arm. Bare names pass /// through with `None` qualifier; dotted names split at the last /// dot so the prefix is the (possibly-multi-dot) class qualifier. @@ -6823,7 +6825,7 @@ mod tests { ); } - /// mq.2.7: a multi-candidate residual with a concrete `type_` + /// a multi-candidate residual with a concrete `type_` /// resolved post-unification to `Int` is filtered against the /// registry. Single survivor → Resolved. #[test] @@ -6851,7 +6853,7 @@ mod tests { assert_eq!(outcome, RefineOutcome::Resolved("prelude.Show".to_string())); } - /// mq.2.7: zero registry survivors → NoInstance with the full + /// zero registry survivors → NoInstance with the full /// candidate-class set echoed back to the author. #[test] fn mq2_discharge_multi_candidate_concrete_type_zero_survivors_no_instance() { @@ -6874,7 +6876,7 @@ mod tests { } } - /// mq.2.7: multi survivors → AmbiguousMethodResolution. + /// multi survivors → AmbiguousMethodResolution. #[test] fn mq2_discharge_multi_candidate_concrete_type_multi_survivors_ambiguous() { let mut candidates = BTreeSet::new(); @@ -6901,10 +6903,10 @@ mod tests { } } - /// mq.2.4: `Env` carries a workspace-flat + /// `Env` carries a workspace-flat /// `method_to_candidate_classes: BTreeMap>` /// inverse map of `class_methods`. Today's `MethodNameCollision` - /// invariant guarantees each set is singleton; post-mq.3 the + /// invariant guarantees each set is singleton; post-canonical-class-form the /// cardinality > 1 case becomes legal. #[test] fn mq2_env_method_to_candidate_classes_built() { @@ -6946,7 +6948,7 @@ mod tests { assert_eq!(candidates.len(), 1, "MethodNameCollision invariant: singleton"); } - /// mq.3.1: `Env` carries an `active_declared_constraints` field + /// `Env` carries an `active_declared_constraints` field /// holding the active fn's POST-superclass-expansion declared /// constraints. Empty at workspace entry; populated by `check_fn` /// before invoking `synth` on the fn body. Consumed by `synth`'s @@ -6959,7 +6961,7 @@ mod tests { "active_declared_constraints empty at default-construction"); } - /// mq.3.3: when synth's Var-arm resolves a name to a free fn + /// when synth's Var-arm resolves a name to a free fn /// (locals / caller-module-fn / implicit-import fn) AND the same /// method name has class-method candidates in the workspace, a /// structured warning `class-method-shadowed-by-fn` is emitted @@ -7084,8 +7086,8 @@ mod tests { ); } - /// mq.3.2: `Env.class_methods` is keyed by `(QualifiedClassName, - /// MethodName)` tuple — post-mq.3 the workspace can carry multiple + /// `Env.class_methods` is keyed by `(QualifiedClassName, + /// MethodName)` tuple — post-canonical-class-form the workspace can carry multiple /// classes sharing a method name, so the key must disambiguate. /// O(1) lookup post-dispatch via `(resolved_class, method)`. #[test] diff --git a/crates/ailang-check/src/lift.rs b/crates/ailang-check/src/lift.rs index 99d294a..07daa2e 100644 --- a/crates/ailang-check/src/lift.rs +++ b/crates/ailang-check/src/lift.rs @@ -35,7 +35,7 @@ use std::collections::{BTreeMap, BTreeSet}; use crate::{builtins, synth, CheckError, Env, Result, Subst}; -/// Iter 16b.3: post-typecheck pass that eliminates every surviving +/// post-typecheck pass that eliminates every surviving /// `Term::LetRec` from `m` by lifting it to a synthetic top-level /// `Def::Fn`. Returns a new module that goes to codegen. /// @@ -99,7 +99,7 @@ pub fn lift_letrecs(m: &Module) -> Result { name: def.name().to_string(), args: vec![], }, - // Iter 22b.1: skip class/instance defs in the global-type + // skip class/instance defs in the global-type // collection. Class methods become globally typed names // only after 22b.3 monomorphisation; until then there is // no concrete `Type` to register here. @@ -159,7 +159,7 @@ pub fn lift_letrecs(m: &Module) -> Result { } } } - // Iter 13a: install rigid vars from a Forall enclosing + // install rigid vars from a Forall enclosing // fn so check_type_well_formed inside synth doesn't // reject them. Save and restore so the lifter env is // clean across defs. @@ -171,7 +171,7 @@ pub fn lift_letrecs(m: &Module) -> Result { } } } - // Iter 16b.6: stash the enclosing fn's Forall.vars (or + // stash the enclosing fn's Forall.vars (or // empty for a monomorphic enclosing fn) for the LetRec // arm. Save and restore so it never leaks across defs. let saved_forall_vars = @@ -191,7 +191,7 @@ pub fn lift_letrecs(m: &Module) -> Result { c.value = lifter.lift_in_term(&c.value, &mut locals, &c.name)?; } Def::Type(_) => {} - // Iter 22b.1: class/instance defs do not enter the lift + // class/instance defs do not enter the lift // pass yet. 22b.2 will lift method-body lambdas in instance // methods; for 22b.1 this is a passthrough. Def::Class(_) | Def::Instance(_) => {} @@ -206,7 +206,7 @@ pub fn lift_letrecs(m: &Module) -> Result { /// `ailang-core::desugar` but resolves capture types via `synth` /// instead of relying on a `ScopeEntry` map. /// -/// Iter 16b.6 field: +/// Forall-tracking field: /// - `current_def_forall_vars` carries the enclosing fn's /// `Type::Forall.vars` while lifting its body. Empty for monomorphic /// enclosing fns. Read by the LetRec arm to wrap the synthetic @@ -218,10 +218,10 @@ struct Lifter<'a> { counter: u64, module_names: &'a mut BTreeSet, lifted: Vec, - /// Iter 16b.6: enclosing fn's Forall.vars (or empty if mono). + /// enclosing fn's Forall.vars (or empty if mono). /// Reset on entry to each `Def::Fn`. current_def_forall_vars: Vec, - /// Iter 16b.7: names of LetRecs whose bodies are currently being + /// names of LetRecs whose bodies are currently being /// lifted (i.e. enclosing `Term::LetRec` names visible at this /// point in the walk). Mirrors the desugar pass's /// `ScopeEntry::EnclosingLetRec` marker. An inner LetRec capturing @@ -367,11 +367,11 @@ impl<'a> Lifter<'a> { rhs: Box::new(self.lift_in_term(rhs, locals, in_def)?), }), Term::Clone { value } => Ok(Term::Clone { - // Iter 18c.1: structural recursion through the wrapper. + // structural recursion through the wrapper. value: Box::new(self.lift_in_term(value, locals, in_def)?), }), Term::ReuseAs { source, body } => Ok(Term::ReuseAs { - // Iter 18d.1: structural recursion through both children. + // structural recursion through both children. source: Box::new(self.lift_in_term(source, locals, in_def)?), body: Box::new(self.lift_in_term(body, locals, in_def)?), }), @@ -395,7 +395,7 @@ impl<'a> Lifter<'a> { .collect::>>()?, }), Term::LetRec { name, ty, params, body, in_term } => { - // Iter 16b.3: post-order traversal — lift any inner + // post-order traversal — lift any inner // LetRecs first. Within the body's scope, `name` and // `params` are visible. // @@ -422,7 +422,7 @@ impl<'a> Lifter<'a> { let prev = locals.insert(n.clone(), t.clone()); body_pushed.push((n.clone(), prev)); } - // Iter 16b.7: mark `name` as an enclosing LetRec while + // mark `name` as an enclosing LetRec while // lifting its body, so any inner LetRec that captures // `name` panics with the closure-of-self message rather // than silently lifting with `name` as a fn-typed extra @@ -470,7 +470,7 @@ impl<'a> Lifter<'a> { // is built below after we know `lifted_name` and `extras`. let in_has_value_use = find_non_callee_use(&lifted_in, name).is_some(); - // Iter 16b.6: reject name-as-value in `in_term` when the + // reject name-as-value in `in_term` when the // enclosing fn is polymorphic — the eta-Lam wrap would // need to be `Forall`-quantified, but `Term::Lam` has no // such slot. Closure conversion with polymorphism is the @@ -501,7 +501,7 @@ impl<'a> Lifter<'a> { .cloned() .collect(); - // Iter 16b.7: if any capture refers to an enclosing + // if any capture refers to an enclosing // LetRec's NAME (rather than its params or a regular // local), reject. Lifting `name$lr_N` with `outer` as a // fn-typed extra param would mis-compile: the outer @@ -538,7 +538,7 @@ impl<'a> Lifter<'a> { // Build the lifted FnDef. // - // Iter 16b.6: when the enclosing fn is polymorphic, wrap + // when the enclosing fn is polymorphic, wrap // the augmented Fn in a `Type::Forall` mirroring the // enclosing fn's type vars (`current_def_forall_vars`). // The capture types may mention any of those vars; the @@ -673,7 +673,7 @@ impl<'a> Lifter<'a> { } } - /// Iter 16b.3: produce a fresh `$lr_N` name not present in + /// produce a fresh `$lr_N` name not present in /// `module_names`. Bumps `counter` and `module_names` so a later /// lift cannot collide. fn fresh_lifted(&mut self, hint: &str) -> String { @@ -704,17 +704,17 @@ impl<'a> Lifter<'a> { let mut subst = Subst::default(); let mut counter: u32 = 0; let mut effects: BTreeSet = BTreeSet::new(); - // Iter 22b.2 (Task 9): the lift pass does its own type + // the lift pass does its own type // synthesis to figure out free-var types for letrec captures. // Class-method residuals collected here are discarded — the // missing-constraint check has already run for the enclosing // fn during `check_fn`. Threading the accumulator only keeps // `synth`'s signature uniform. let mut residuals: Vec = Vec::new(); - // Iter 23.4: free-fn-call observations are similarly discarded + // free-fn-call observations are similarly discarded // here — the mono pass will re-synth bodies post-lift. let mut free_fn_calls: Vec = Vec::new(); - // mq.3: lift's synth re-entry is post-typecheck — warnings + // lift's synth re-entry is post-typecheck — warnings // (e.g. class-method-shadowed-by-fn) have already been // surfaced upstream by `check_workspace`. Discard here. let mut warnings_discarded: Vec = Vec::new(); @@ -727,7 +727,7 @@ impl<'a> Lifter<'a> { } } -/// Iter 16b.3: returns true iff any `Def::Fn` body or `Def::Const` +/// returns true iff any `Def::Fn` body or `Def::Const` /// value in `m` reaches a `Term::LetRec`. Used as a fast-path skip: /// modules without any deferred LetRec need no traversal at all. fn contains_any_letrec(m: &Module) -> bool { @@ -749,9 +749,9 @@ fn contains_any_letrec(m: &Module) -> bool { Term::Lam { body, .. } => term_has_letrec(body), Term::Seq { lhs, rhs } => term_has_letrec(lhs) || term_has_letrec(rhs), Term::LetRec { .. } => true, - // Iter 18c.1: identity passthrough for the letrec-detection scan. + // identity passthrough for the letrec-detection scan. Term::Clone { value } => term_has_letrec(value), - // Iter 18d.1: structural recursion through both children. + // structural recursion through both children. Term::ReuseAs { source, body } => term_has_letrec(source) || term_has_letrec(body), Term::Loop { binders, body } => { binders.iter().any(|b| term_has_letrec(&b.init)) || term_has_letrec(body) @@ -769,7 +769,7 @@ fn contains_any_letrec(m: &Module) -> bool { false } -/// Iter 16b.3: scan `defs` for the highest existing `*$lr_N` suffix +/// scan `defs` for the highest existing `*$lr_N` suffix /// and return that N. Used to seed `Lifter.counter` past any /// 16b.2-lifted defs that already live in the desugared module. fn highest_lr_suffix(defs: &[Def]) -> Option { @@ -786,7 +786,7 @@ fn highest_lr_suffix(defs: &[Def]) -> Option { max } -/// Iter 16b.3: minimal pattern → bindings inference for the lift +/// minimal pattern → bindings inference for the lift /// pass. Called only for patterns the typechecker has already /// accepted, so we propagate just enough to resolve capture types /// (we don't re-run unification here — the typechecker did that diff --git a/crates/ailang-check/src/linearity.rs b/crates/ailang-check/src/linearity.rs index b3ce076..0eaeca4 100644 --- a/crates/ailang-check/src/linearity.rs +++ b/crates/ailang-check/src/linearity.rs @@ -145,7 +145,7 @@ use ailang_core::ast::{Arm, Ctor, Def, FnDef, Module, ParamMode, Pattern, Term, use ailang_surface::{term_to_form_a, type_to_form_a}; use std::collections::HashMap; -/// Iter 19a.1: a `Type::Con` whose name is `Int`/`Bool`/`Str`/`Unit` +/// a `Type::Con` whose name is `Int`/`Bool`/`Str`/`Unit` /// is a primitive value type — no RC, no heap. Reading a primitive /// pattern-binder bumps `consume_count` in the uniqueness table but /// does not force the scrutinee param to be `Own`. The lint filters @@ -191,7 +191,7 @@ struct BinderState { borrow_count: u32, } -/// Iter 18c.2: top-level entry. Walks every fn in `m` and emits +/// top-level entry. Walks every fn in `m` and emits /// linearity diagnostics for the all-explicit-mode fns. Other fns are /// skipped without traversal. /// @@ -207,7 +207,7 @@ pub(crate) fn check_module(m: &Module) -> Vec { check_module_with_visible(m, &[]) } -/// Iter 23.5: workspace-aware entry. `visible_extra` is a slice of +/// workspace-aware entry. `visible_extra` is a slice of /// modules whose top-level fns and class methods should be visible /// to `callee_arg_modes` (so a Borrow-mode user fn that forwards its /// borrow params into an imported polymorphic free fn — e.g. the @@ -219,13 +219,13 @@ pub(crate) fn check_module(m: &Module) -> Vec { /// — they are checked when their own module is processed. pub(crate) fn check_module_with_visible(m: &Module, visible_extra: &[&Module]) -> Vec { let mut globals: HashMap = HashMap::new(); - // Iter 19a.1: ctor name → field types, used by the + // ctor name → field types, used by the // `over-strict-mode` lint to filter out primitive-typed // pattern-binders (Int / Bool / Str / Unit) — their consume // does not force ownership of the scrutinee. let mut ctors: HashMap> = HashMap::new(); - // Iter 24.1: register builtin signatures so `callee_arg_modes` + // register builtin signatures so `callee_arg_modes` // resolves them when a user fn forwards a Borrow-mode param // into a builtin like `str_clone : (Str borrow) -> Str`. The // previously-registered class-method types (below, in the @@ -238,7 +238,7 @@ pub(crate) fn check_module_with_visible(m: &Module, visible_extra: &[&Module]) - globals.insert(name.clone(), strip_forall(ty).clone()); } - // Iter 23.5: register fns + class methods from visible-extra + // register fns + class methods from visible-extra // modules first, so a name-clash inside `m` overwrites the // imported entry (matching the synth-side scope rule that // local-defs shadow implicit imports). @@ -277,7 +277,7 @@ pub(crate) fn check_module_with_visible(m: &Module, visible_extra: &[&Module]) - ctors.insert(name.clone(), fields.clone()); } } - // Iter 23.4-prep: class methods register their types into + // class methods register their types into // the globals map so `callee_arg_modes` can see their // `param_modes`. Without this, a borrow-mode fn that // forwards its borrow params into a class-method call @@ -293,7 +293,7 @@ pub(crate) fn check_module_with_visible(m: &Module, visible_extra: &[&Module]) - } } - // Iter 19a: the over-strict-mode lint reads `consume_count` from + // the over-strict-mode lint reads `consume_count` from // the uniqueness side table. Build it once for the whole module; // reusing the existing pass keeps the "what counts as a consume" // definition in a single place. @@ -371,7 +371,7 @@ fn check_fn( checker.walk(&f.body, Position::Consume); - // Iter 19a / 19a.1: `over-strict-mode` lint. After the linearity + // `over-strict-mode` lint. After the linearity // walk has completed (and any errors have been recorded), inspect // each `Own`-annotated param: if `consume_count(p) == 0` AND no // pattern-binder of any `(match p ...)` arm in the body has @@ -565,7 +565,7 @@ impl<'a> Checker<'a> { self.walk(value, Position::Borrow); } Term::ReuseAs { source, body } => { - // Iter 18d.1: linearity for `(reuse-as SRC BODY)`: + // linearity for `(reuse-as SRC BODY)`: // - `SRC` must be a bare `Var { name }` of an // in-scope binder (else `reuse-as-source-not-bare-var`). // - The binder must currently have `consumed == false` @@ -779,7 +779,7 @@ fn make_use_after_consume(def: &str, binder: &str) -> Diagnostic { ) } -/// Iter 18d.1: build a `reuse-as-source-not-bare-var` diagnostic. +/// build a `reuse-as-source-not-bare-var` diagnostic. /// Fires when a `Term::ReuseAs.source` is anything other than a bare /// `Term::Var { name }` referring to an in-scope binder. The /// suggested rewrite drops the meaningless wrapper and keeps the @@ -817,7 +817,7 @@ fn make_reuse_as_source_not_bare_var(def: &str, source: &Term, body: &Term) -> D ) } -/// Iter 18d.1: `use-after-consume` raised at a `(reuse-as )` +/// `use-after-consume` raised at a `(reuse-as )` /// site whose `` binder has already been consumed elsewhere. Same /// code as the regular use-after-consume; the suggested rewrite is to /// drop the (now unsatisfiable) reuse hint and keep the body — the @@ -838,7 +838,7 @@ fn make_use_after_consume_at_reuse_as(def: &str, binder: &str, body: &Term) -> D ) } -/// Iter 19a.1: precise sub-binder check for the over-strict-mode +/// precise sub-binder check for the over-strict-mode /// lint. Returns `true` iff some `(match p ...)` somewhere in `t`, /// where `p` is the param `pname`, has an arm whose pattern-binders /// include at least one binder with `consume_count > 0` in the @@ -959,7 +959,7 @@ fn any_sub_binder_consumed_for( } } -/// Iter 19a.1: returns `true` if `pat` introduces any **heap-typed** +/// returns `true` if `pat` introduces any **heap-typed** /// binder whose recorded `consume_count` is `> 0`. Primitive-typed /// pattern-binders (`Int` / `Bool` / `Str` / `Unit`) never count /// because reading a primitive does not force the scrutinee to be @@ -982,7 +982,7 @@ fn pattern_has_consumed_heap_binder( pattern_has_consumed_heap_binder_at(pat, None, uniq, def_name, ctors) } -/// Iter 19a.1: helper carrying the optional declared type of `pat`. +/// helper carrying the optional declared type of `pat`. /// At the top of an arm pattern, `declared_ty` is `None` (we don't /// track the scrutinee's type in the lint); the type becomes known /// when we descend into a `Pattern::Ctor`'s fields, where the @@ -1136,7 +1136,7 @@ fn term_mentions_any_binder(t: &Term, binders: &[String]) -> bool { } } -/// Iter 19a: a scrutinee "is" `pname` if it's a bare `Var { pname }` +/// a scrutinee "is" `pname` if it's a bare `Var { pname }` /// or `Clone(Var { pname })`. Anything else (a fresh App result, a /// let-binder, …) does not put `pname` directly in scrutinee /// position. @@ -1148,7 +1148,7 @@ fn scrutinee_matches_param(scrutinee: &Term, pname: &str) -> bool { } } -/// Iter 19a: build the `over-strict-mode` warning. `inner` is the +/// build the `over-strict-mode` warning. `inner` is the /// fn-type with the original mode; we synthesise a relaxed copy with /// `param_modes[i] = Borrow` and render it through /// [`type_to_form_a`] to produce the suggested rewrite. @@ -1173,7 +1173,7 @@ fn make_over_strict_mode(def: &str, binder: &str, inner: &Type, i: usize) -> Dia ) } -/// Iter 19a: clone of `inner` (assumed `Type::Fn`) with +/// clone of `inner` (assumed `Type::Fn`) with /// `param_modes[i]` rewritten to `ParamMode::Borrow`. Other slots /// are preserved bit-for-bit. Falls back to returning `inner` /// unchanged if `inner` is not a `Type::Fn` (defensive — caller @@ -1310,7 +1310,7 @@ mod tests { assert!(check_module(&m).is_empty()); } - /// Iter 18d.1: a `(reuse-as 42 (Cons ...))` whose source is a literal + /// a `(reuse-as 42 (Cons ...))` whose source is a literal /// (not a bare `Var`) is flagged with `reuse-as-source-not-bare-var`. /// The suggested rewrite drops the wrapper and keeps the body. #[test] @@ -1347,7 +1347,7 @@ mod tests { .unwrap_or_else(|e| panic!("suggested rewrite must parse: {rep} ({e})")); } - /// Iter 18d.1: a `(reuse-as p0 ...)` where `p0` was already + /// a `(reuse-as p0 ...)` where `p0` was already /// consumed by an earlier sibling fires `use-after-consume` at the /// reuse-as site (not `reuse-as-source-not-bare-var`). #[test] @@ -1383,7 +1383,7 @@ mod tests { use crate::diagnostic::Severity; - /// Iter 19a, positive: a fn with `(own T)` whose body merely + /// a fn with `(own T)` whose body merely /// borrows the param via a `Borrow`-mode call → fires /// `over-strict-mode` (Warning) with binder=p0, current=own, /// suggested=borrow, and a non-empty rewrite. @@ -1453,7 +1453,7 @@ mod tests { ); } - /// Iter 19a, negative: a fn with `(own T)` whose body consumes + /// a fn with `(own T)` whose body consumes /// the param directly does not fire `over-strict-mode`. #[test] fn over_strict_mode_silent_when_body_consumes_param() { @@ -1475,7 +1475,7 @@ mod tests { ); } - /// Iter 19a, negative: a fn with `(borrow T)` and a borrowing + /// a fn with `(borrow T)` and a borrowing /// body never fires `over-strict-mode` (the lint is gated on /// `Own`). #[test] @@ -1498,7 +1498,7 @@ mod tests { ); } - /// Iter 19a.1, positive: a fn with `(own T)` whose body is + /// a fn with `(own T)` whose body is /// `(match p0 ...)` and never consumes `p0` directly nor any of /// `p0`'s sub-binders DOES fire `over-strict-mode` under the /// precise sub-binder analysis. This is the test that 19a left @@ -1532,7 +1532,7 @@ mod tests { assert_eq!(osm[0].def.as_deref(), Some("f")); } - /// Iter 19a.1: helper to build an `IntList = Nil | Cons(Int, + /// helper to build an `IntList = Nil | Cons(Int, /// IntList)` ADT def. Used by the head_or_zero-shape tests /// (positive + negative variants below) so the lint can look up /// each ctor field's type and filter out primitives. @@ -1555,7 +1555,7 @@ mod tests { }) } - /// Iter 19a.1: helper to build a fn whose single param is + /// helper to build a fn whose single param is /// `(own IntList)` and whose body is `body`. Distinct from /// `fn_with_modes` (which uses a generic `List` type for params) /// so the test exercises a real ctor-field-types lookup. @@ -1577,7 +1577,7 @@ mod tests { }) } - /// Iter 19a.1, negative: a fn with `(own IntList)` whose body + /// a fn with `(own IntList)` whose body /// is `match xs { Cons(h, t) => t }` consumes the *heap-typed* /// pattern-binder `t`. The lint must NOT fire — the /// sub-consume forces ownership. @@ -1707,7 +1707,7 @@ mod tests { ); } - /// Iter 19a.1, positive: a fn with `(own IntList)` whose body + /// a fn with `(own IntList)` whose body /// is `match xs { Nil => 0, Cons(h, t) => h }` returns the /// **primitive-typed** `h` (`Int`) and never moves the /// heap-typed `t`. The lint MUST fire — `(borrow IntList)` @@ -1765,7 +1765,7 @@ mod tests { ); } - /// Iter 23.4-prep Task 1: a borrow-mode fn that forwards its borrow + /// a borrow-mode fn that forwards its borrow /// params into a class-method call must not fire /// `consume-while-borrowed` false positives. The pre-fix behaviour /// is two diagnostics (one per param) because diff --git a/crates/ailang-check/src/mono.rs b/crates/ailang-check/src/mono.rs index 0703825..89aea7b 100644 --- a/crates/ailang-check/src/mono.rs +++ b/crates/ailang-check/src/mono.rs @@ -61,7 +61,7 @@ use crate::Result; use indexmap::IndexMap; use std::collections::{BTreeMap, BTreeSet}; -/// Iter 22b.3: workspace-wide monomorphisation pass entry. See the +/// workspace-wide monomorphisation pass entry. See the /// module-level doc for the architecture and contract. /// /// Pre-condition: `ws` has been typechecked (`check_workspace(ws)` @@ -113,7 +113,7 @@ pub fn monomorphise_workspace(ws: &Workspace) -> Result { // target's `defining_module`. Mark the key as synthesised // so the next round won't re-collect. // - // Iter 22b.3 uses ws_owned.registry as the source of + // The mono pass uses ws_owned.registry as the source of // truth for ClassDef + InstanceDef lookups. Both come // from the un-modified workspace registry — synthesis // never mutates the registry. @@ -179,7 +179,7 @@ pub fn monomorphise_workspace(ws: &Workspace) -> Result { // collection-phase walk (cursor alignment depends on it). let mut env_mod = env.clone(); apply_per_module_types_overlay(&mut env_mod, &ws_owned, mname); - // Iter 23.4: precompute the per-module poly-free-fn name + // precompute the per-module poly-free-fn name // set used as the rewrite walker's second predicate. let poly_free_fns = poly_free_fn_names_for_module(&ws_owned, &env_mod, mname); // 2026-05-14 bugfix: per-poly-free-fn-name constraint count, @@ -255,7 +255,7 @@ pub fn monomorphise_workspace(ws: &Workspace) -> Result { Ok(ws_owned) } -/// Iter 22b.3: walk every fn / const body in the workspace, +/// walk every fn / const body in the workspace, /// returning the union of [`collect_mono_targets`] outputs. Const /// bodies are wrapped in a synthetic zero-arg `FnDef` for the /// collection call — the residual gathering only depends on body @@ -290,7 +290,7 @@ fn collect_targets_workspace_wide( Ok(out) } -/// Iter 22b.3.6: wrap a [`ConstDef`] in a synthetic zero-arg +/// wrap a [`ConstDef`] in a synthetic zero-arg /// [`AstFnDef`] for residual-collection / rewrite purposes. Const /// bodies have no parameter list, so the residual gathering only /// depends on body shape — the wrapping is a structural adapter, @@ -309,7 +309,7 @@ fn const_as_pseudo_fn(c: &ConstDef) -> AstFnDef { } } -/// Iter 23.4: compute the set of names (bare + dot-qualified) that +/// compute the set of names (bare + dot-qualified) that /// `synth`'s Var arm would resolve to a polymorphic free fn when /// called from module `mname`. The rewrite walker and the slot /// collector both consult this set to decide whether a `Term::Var` @@ -409,12 +409,12 @@ fn poly_free_fn_constraint_counts_for_module( out } -/// Iter 22b.3: workspace-wide `class-name -> ClassDef` index. +/// workspace-wide `class-name -> ClassDef` index. /// Used by the fixpoint to look up the matching class definition /// when synthesising a fn. /// -/// mq.1: keys carry the qualified class name -/// (`.`) to match the post-mq.1 residual / +/// keys carry the qualified class name +/// (`.`) to match the post-canonical-class-form residual / /// `MonoTarget::ClassMethod.class` field and the registry's /// qualified entries key. fn build_class_index(ws: &Workspace) -> BTreeMap { @@ -430,7 +430,7 @@ fn build_class_index(ws: &Workspace) -> BTreeMap { idx } -/// Iter 22b.3 / iter 23.4: returns `true` iff any module in `ws` +/// returns `true` iff any module in `ws` /// declares at least one specialisable target — a [`Def::Class`] /// or [`Def::Instance`] (class-method residuals) OR a /// `Type::Forall`-quantified [`Def::Fn`] (free-fn case). Cheap @@ -451,7 +451,7 @@ fn workspace_has_specialisable_targets(ws: &Workspace) -> bool { }) } -/// Iter 22b.3 / iter 23.4: deterministic mono-symbol name for a +/// deterministic mono-symbol name for a /// `(base-name, types...)` tuple. The N-ary form supports both /// single-type-var class methods (today's six primitive Eq/Ord /// symbols pass a one-element slice and produce identical output @@ -480,7 +480,7 @@ pub fn mono_symbol(base: &str, ty: &Type) -> String { mono_symbol_n(base, std::slice::from_ref(ty)) } -/// Iter 23.4: N-ary variant of [`mono_symbol`]. The single-type +/// N-ary variant of [`mono_symbol`]. The single-type /// public-API stays as [`mono_symbol`] (used by class-method /// emission) for surface compatibility; N-ary callers (free-fn /// emission with multi-arg type instantiations) use this form @@ -519,7 +519,7 @@ fn primitive_surface_name(ty: &Type) -> Option<&'static str> { } } -/// Iter 22b.3 / iter 23.4: a specialisation request. One synthesised +/// a specialisation request. One synthesised /// monomorphic `Def::Fn` will be produced per unique /// [`mono_target_key`]. Two source-body kinds share one fixpoint: /// @@ -551,7 +551,7 @@ pub enum MonoTarget { }, } -/// Iter 22b.3 / iter 23.4: dedup key for a [`MonoTarget`]. Class-method +/// dedup key for a [`MonoTarget`]. Class-method /// targets key on `("class", ".", )`; free-fn /// targets key on `("free", "", )`. The two /// kinds are guaranteed-disjoint by the first component, so a @@ -614,7 +614,7 @@ fn apply_per_module_types_overlay(env: &mut crate::Env, ws: &Workspace, module_n } } -/// Iter 22b.3: re-run [`crate::synth`] on `f`'s body to recover +/// re-run [`crate::synth`] on `f`'s body to recover /// the per-fn residual class constraints. Filter to fully- /// concrete residuals (the only ones eligible for monomorphisation), /// look up each `(class, type-hash)` in the registry to recover @@ -632,7 +632,7 @@ fn apply_per_module_types_overlay(env: &mut crate::Env, ws: &Workspace, module_n /// target-collection site, or `Type::unit()`-default at the residual- /// ordering site). /// -/// Iter 24.tidy: extracted from two byte-identical call sites at +/// extracted from two byte-identical call sites at /// `collect_mono_targets` and `collect_residuals_ordered` per /// audit-24's [medium-2] drift item. The byte-identity invariant /// (Phase 2 synthesis name must match Phase 3 rewrite cursor's @@ -678,25 +678,23 @@ pub fn collect_mono_targets( env.rigid_vars.insert(v.clone()); } env.current_module = module_name.to_string(); - // Iter 22c (sibling of commit 5c5180f): seed `env.globals` from - // the current module's fns so `synth`'s `Term::Var` lookup - // (lib.rs:1678) resolves bare same-module references — most - // importantly self-recursive top-level fns. Mirrors - // `check_in_workspace` (lib.rs:1135-1139). Top-level fn names - // are only per-module-unique (workspace-wide collisions are - // legal), so seeding must be scoped to the current module — + // Seed `env.globals` from the current module's fns so `synth`'s + // `Term::Var` lookup (lib.rs:1678) resolves bare same-module + // references — most importantly self-recursive top-level fns. + // Mirrors `check_in_workspace` (lib.rs:1135-1139). Top-level fn + // names are only per-module-unique (workspace-wide collisions + // are legal), so seeding must be scoped to the current module — // unlike the workspace-wide flat `env.types` / `env.ctor_index` - // tables that 5c5180f populated in `build_workspace_env`. + // tables that `build_workspace_env` populates. if let Some(g) = env.module_globals.get(module_name).cloned() { for (n, t) in g { env.globals.insert(n, t); } } - // Iter 22c (sibling of commit 13b36cc): seed `env.imports` from - // the current module's import list so `synth`'s qualified-var - // path (lib.rs:1697) resolves `Mod.fn` references. Mirrors - // `check_in_workspace` (lib.rs:1147-1152). Per-module because - // aliases collide across modules. + // Seed `env.imports` from the current module's import list so + // `synth`'s qualified-var path (lib.rs:1697) resolves `Mod.fn` + // references. Mirrors `check_in_workspace` (lib.rs:1147-1152). + // Per-module because aliases collide across modules. if let Some(im) = env.module_imports.get(module_name).cloned() { env.imports = im; } @@ -710,7 +708,7 @@ pub fn collect_mono_targets( let mut counter: u32 = 0; let mut residuals: Vec = Vec::new(); let mut free_fn_calls: Vec = Vec::new(); - // mq.3: mono's residual-collection synth re-entry collects-and- + // mono's residual-collection synth re-entry collects-and- // discards warnings — typecheck has already run and surfaced any // shadow warnings; mono is a post-typecheck pass. let mut warnings_discarded: Vec = Vec::new(); @@ -756,8 +754,8 @@ pub fn collect_mono_targets( defining_module: entry.defining_module.clone(), }); } - // Iter 23.4 Task 4: free-fn arm. Each `FreeFnCall` observation - // recorded by `synth`'s Var arm carries the bare name, the + // Free-fn arm: each `FreeFnCall` observation recorded by + // `synth`'s Var arm carries the bare name, the // owning module (resolved through synth's lookup ladder), the // forall vars (declaration order), and the per-var fresh // metavars. Post-`synth`, `subst.apply` each meta to recover @@ -772,7 +770,7 @@ pub fn collect_mono_targets( // would have rejected; the Unit default is sound and // deterministic. for fc in free_fn_calls { - // Iter 23.5: if any resolved type-arg is a rigid Type::Var + // if any resolved type-arg is a rigid Type::Var // (i.e. a forall var of the *enclosing* polymorphic fn, // not a free metavar), skip this target. The enclosing fn // will be monomorphised in its own right, and that mono @@ -825,7 +823,7 @@ pub fn collect_mono_targets( Ok(out) } -/// Iter 23.5: true iff `t` contains a non-metavar `Type::Var` anywhere. +/// true iff `t` contains a non-metavar `Type::Var` anywhere. /// Used by free-fn target collection to distinguish rigid forall vars /// (skip — wait for the enclosing fn's mono pass) from unbound metavars /// (Unit-default — no later round will pin them). The naming @@ -843,7 +841,7 @@ fn contains_rigid_var(t: &Type) -> bool { } } -/// Iter 22b.3: produce a `FnDef` for a single (target, class, +/// produce a `FnDef` for a single (target, class, /// instance) triple. The synthesised fn: /// /// 1. has name [`mono_symbol`]`(target.method, target.type_)`, @@ -949,7 +947,7 @@ pub fn synthesise_mono_fn( }) } -/// Iter 23.4: synthesise a monomorphic `FnDef` for a polymorphic +/// synthesise a monomorphic `FnDef` for a polymorphic /// free-fn target. The source body comes directly from the /// polymorphic `Def::Fn`'s `body` (NOT from `Registry::entries`); /// rigid-var substitution applies the `target.type_args` to the @@ -1049,7 +1047,7 @@ pub fn synthesise_mono_fn_for_free_fn( }) } -/// Iter 22b.3 / iter 23.4: rewrite every polymorphic call site in +/// rewrite every polymorphic call site in /// `body` to the corresponding mono symbol, using `ordered_targets` /// — the per-call-site resolved targets collected by a parallel /// synth-replay run on the same body via [`collect_residuals_ordered`]. @@ -1096,7 +1094,7 @@ fn rewrite_mono_calls( // residual OR a poly-free-fn observation. Locally // shadowed names match neither — synth pushes nothing. // - // mq.3: the class-method side is method-keyed natively via + // the class-method side is method-keyed natively via // `method_to_candidate_classes`. Class disambiguation // (which class declared the resolved method) lives in the // residual slot the cursor consumes, not here. @@ -1248,7 +1246,7 @@ fn rewrite_mono_calls( } } -/// Iter 22b.3.6: collect the variable binders introduced by a +/// collect the variable binders introduced by a /// match pattern. Used by the walker's `Term::Match` arm to extend /// `locals` for each arm body. Mirrors `Pattern`'s shape: /// - [`Pattern::Wild`] / [`Pattern::Lit`] bind nothing, @@ -1271,7 +1269,7 @@ fn pattern_binders(pat: &Pattern) -> Vec { out } -/// mq.2: resolve a residual's class for mono target construction. +/// resolve a residual's class for mono target construction. /// Returns `Some(class)` for a discharge-ready residual (single-class /// or refined-multi-candidate); `None` if the multi-candidate residual /// cannot be refined — in which case typecheck-side already raised a @@ -1292,7 +1290,7 @@ pub fn resolve_residual_class_for_mono( } } -/// Iter 22b.3: traversal-ordered residual collection — used by +/// traversal-ordered residual collection — used by /// the rewrite walker to align cursor positions. Unlike /// [`collect_mono_targets`], this includes non-concrete residuals /// as `None`, preserving one-entry-per-callsite alignment. @@ -1317,7 +1315,7 @@ pub(crate) fn collect_residuals_ordered( env.rigid_vars.insert(v.clone()); } env.current_module = module_name.to_string(); - // Iter 22c: same `env.globals` seeding as `collect_mono_targets` + // same `env.globals` seeding as `collect_mono_targets` // — see the comment there for rationale. Both fns re-run `synth` // and must agree with the main check path's per-module env shape. if let Some(g) = env.module_globals.get(module_name).cloned() { @@ -1325,7 +1323,7 @@ pub(crate) fn collect_residuals_ordered( env.globals.insert(n, t); } } - // Iter 22c: same `env.imports` seeding as `collect_mono_targets` + // same `env.imports` seeding as `collect_mono_targets` // — see the comment there for rationale. if let Some(im) = env.module_imports.get(module_name).cloned() { env.imports = im; @@ -1340,7 +1338,7 @@ pub(crate) fn collect_residuals_ordered( let mut counter: u32 = 0; let mut residuals: Vec = Vec::new(); let mut free_fn_calls: Vec = Vec::new(); - // mq.3: mono's residual-collection synth re-entry collects-and- + // mono's residual-collection synth re-entry collects-and- // discards warnings — typecheck has already run and surfaced any // shadow warnings; mono is a post-typecheck pass. let mut warnings_discarded: Vec = Vec::new(); @@ -1362,8 +1360,8 @@ pub(crate) fn collect_residuals_ordered( &mut warnings_discarded, )?; - // Iter 23.4 Task 6: build two per-channel slot lists — one for - // class-method residuals, one for poly-free-fn observations — + // Build two per-channel slot lists — one for class-method + // residuals, one for poly-free-fn observations — // each in synth's push order. Then walk the AST in synth's // traversal order, classifying each `Term::Var` site as // class-method, poly-free-fn, or neither (and consuming from @@ -1380,7 +1378,7 @@ pub(crate) fn collect_residuals_ordered( let r_ty_norm = env .workspace_registry .normalize_type_for_lookup(module_name, &r_ty); - // mq.2: resolve the residual's class via the multi- + // resolve the residual's class via the multi- // candidate refinement helper. Single-class residuals // (`candidates: None`) flow through `r.class.clone()` // unchanged; multi-candidate residuals filter against @@ -1463,13 +1461,13 @@ pub(crate) fn collect_residuals_ordered( Ok(out) } -/// Iter 23.4 helper for `collect_residuals_ordered`: walk a body +/// walk a body /// in synth's traversal order, interleaving class-method and /// poly-free-fn slots in source-AST order. The walker MUST stay /// in lockstep with `rewrite_mono_calls` — same predicates, same /// shadowing handling. /// -/// mq.3: class-method presence is method-keyed natively via +/// class-method presence is method-keyed natively via /// `method_to_candidate_classes` (post-`MethodNameCollision`-retirement, /// `class_methods` is tuple-keyed by `(class, method)` and not /// directly probable by method name alone). @@ -1625,7 +1623,7 @@ mod tests { use super::*; use ailang_core::ast::Type; - /// mq.2.8: mono's residual-class resolver refines multi-candidate + /// mono's residual-class resolver refines multi-candidate /// residuals via the same logic as discharge. A multi-candidate /// residual with a concrete `type_` and a single registry-survivor /// resolves to that class. @@ -1650,7 +1648,7 @@ mod tests { assert_eq!(resolved, Some("prelude.Show".to_string())); } - /// mq.2.8: single-class residual (candidates: None) flows through + /// single-class residual (candidates: None) flows through /// unchanged. #[test] fn mq2_mono_single_class_residual_unchanged() { @@ -1665,7 +1663,7 @@ mod tests { assert_eq!(resolved, Some("prelude.Eq".to_string())); } - /// mq.2.8: multi-candidate residual that cannot refine (zero + /// multi-candidate residual that cannot refine (zero /// registry survivors) returns None. #[test] fn mq2_mono_multi_candidate_no_survivors_returns_none() { diff --git a/crates/ailang-check/src/reuse_shape.rs b/crates/ailang-check/src/reuse_shape.rs index 24e0eaa..9ad34d2 100644 --- a/crates/ailang-check/src/reuse_shape.rs +++ b/crates/ailang-check/src/reuse_shape.rs @@ -71,7 +71,7 @@ use ailang_core::ast::{Arm, Def, FnDef, Module, ParamMode, Pattern, Term, Type, use ailang_surface::term_to_form_a; use std::collections::HashMap; -/// Iter 18d.2: top-level entry. Walks every fn in `m` and emits +/// top-level entry. Walks every fn in `m` and emits /// `reuse-as-shape-mismatch` diagnostics for the all-explicit-mode /// fns. Other fns are skipped. /// diff --git a/crates/ailang-check/src/suppress_filter.rs b/crates/ailang-check/src/suppress_filter.rs index 878c80c..893d51e 100644 --- a/crates/ailang-check/src/suppress_filter.rs +++ b/crates/ailang-check/src/suppress_filter.rs @@ -131,7 +131,7 @@ mod tests { } } - /// Iter 19b: a suppress entry with a matching `(def, code)` pair + /// a suppress entry with a matching `(def, code)` pair /// drops the diagnostic from the accumulated list. #[test] fn matching_suppress_drops_the_diagnostic() { @@ -148,7 +148,7 @@ mod tests { assert!(diags.is_empty(), "diagnostic should have been dropped: {diags:?}"); } - /// Iter 19b: an empty `because` produces an `empty-suppress-reason` + /// an empty `because` produces an `empty-suppress-reason` /// Error AND does NOT drop the original diagnostic. #[test] fn empty_because_errors_and_does_not_suppress() { @@ -179,7 +179,7 @@ mod tests { ); } - /// Iter 19b: whitespace-only `because` is treated the same as + /// whitespace-only `because` is treated the same as /// empty — the trimmed text is what matters. #[test] fn whitespace_only_because_errors() { @@ -205,7 +205,7 @@ mod tests { ); } - /// Iter 19b: a suppress entry whose `code` does not match any + /// a suppress entry whose `code` does not match any /// diagnostic in the list is silently a no-op (the diagnostic /// registry is open-set; a typo costs "warning still fires", not /// a meta-error). @@ -226,7 +226,7 @@ mod tests { assert_eq!(diags[0].code, "over-strict-mode"); } - /// Iter 19b: a diagnostic on a *different* def is not affected by + /// a diagnostic on a *different* def is not affected by /// a suppression on this fn. #[test] fn suppression_only_drops_diagnostics_on_same_def() { @@ -246,7 +246,7 @@ mod tests { assert_eq!(diags[0].def.as_deref(), Some("g")); } - /// Iter 19b: a diagnostic with `def == None` is never dropped by + /// a diagnostic with `def == None` is never dropped by /// any suppression (no def-name to match against). #[test] fn def_none_diagnostic_is_never_dropped() { diff --git a/crates/ailang-check/src/uniqueness.rs b/crates/ailang-check/src/uniqueness.rs index d1ea326..17a31a9 100644 --- a/crates/ailang-check/src/uniqueness.rs +++ b/crates/ailang-check/src/uniqueness.rs @@ -108,7 +108,7 @@ pub type UniquenessTable = BTreeMap<(String, String), UniquenessInfo>; /// typecheck first. pub fn infer_module(m: &Module) -> UniquenessTable { let mut globals: BTreeMap = BTreeMap::new(); - // Iter 24.1: register builtin signatures so the App-arg walker + // register builtin signatures so the App-arg walker // sees their `param_modes` and walks `Borrow`-mode args as // `Position::Borrow` (not `Consume`). Without this, a builtin // like `str_clone : (Str borrow) -> Str` invoked on a heap-Str @@ -131,7 +131,7 @@ pub fn infer_module(m: &Module) -> UniquenessTable { globals.insert(c.name.clone(), strip_forall(&c.ty).clone()); } Def::Type(_) => {} - // Iter 22b.1: class/instance defs are skipped here. Class + // class/instance defs are skipped here. Class // method signatures contribute global names once // monomorphisation (22b.3) materialises them; the // pre-monomorphisation form has no concrete `Type` to @@ -327,7 +327,7 @@ impl<'a> Walker<'a> { self.walk(value, Position::Borrow); } Term::ReuseAs { source, body } => { - // Iter 18d.1: `(reuse-as SRC BODY)` is a Consume of + // `(reuse-as SRC BODY)` is a Consume of // `SRC` (the binder's slot is being taken over) and // a normal walk of `BODY`. So a bare-Var SRC bumps // its `consume_count` by one. Linearity is the diff --git a/crates/ailang-check/tests/env_construction_pin.rs b/crates/ailang-check/tests/env_construction_pin.rs index f79ec4d..4fcf269 100644 --- a/crates/ailang-check/tests/env_construction_pin.rs +++ b/crates/ailang-check/tests/env_construction_pin.rs @@ -98,7 +98,7 @@ fn build_env_inline_pre_refactor(ws: &ailang_core::workspace::Workspace) -> Env // env.class_superclasses — walk every module's Def::Class. // - // mq.1: both key and value carry the qualified workspace-key shape; + // both key and value carry the qualified workspace-key shape; // bare `SuperclassRef.class` is lifted using the parent class's // defining module. for (mod_name, m) in &ws.modules { diff --git a/crates/ailang-check/tests/method_collision_pin.rs b/crates/ailang-check/tests/method_collision_pin.rs index c98a411..da0c1d5 100644 --- a/crates/ailang-check/tests/method_collision_pin.rs +++ b/crates/ailang-check/tests/method_collision_pin.rs @@ -24,12 +24,12 @@ fn examples_dir() -> std::path::PathBuf { .join("examples") } -/// mq.3.5 (class-class repurpose): the fixture +/// Class-class repurpose: the fixture /// `examples/test_22b2_method_name_collision_class_class.ail.json` /// declares two classes `A` and `B` in the same module, each declaring -/// the method `foo`. Pre-mq.3 this fired +/// the method `foo`. Pre-canonical-class-form this fired /// `WorkspaceLoadError::MethodNameCollision { kind: "class-class" }`. -/// Post-mq.3 the workspace loads cleanly and +/// Post-canonical-class-form the workspace loads cleanly and /// `env.method_to_candidate_classes` carries both qualified classes as /// candidates for the method name `foo`. #[test] @@ -37,7 +37,7 @@ fn mq3_class_class_collision_loads_clean_and_populates_candidates() { let entry = examples_dir() .join("test_22b2_method_name_collision_class_class.ail"); let ws = load_workspace(&entry) - .expect("post-mq.3: workspace loads without collision diagnostic"); + .expect("post-canonical-class-form: workspace loads without collision diagnostic"); let env = build_check_env(&ws); let candidates = env @@ -59,18 +59,18 @@ fn mq3_class_class_collision_loads_clean_and_populates_candidates() { ); } -/// mq.3.5 (class-fn repurpose): the fixture +/// Class-fn repurpose: the fixture /// `examples/test_22b2_method_name_collision_class_fn.ail.json` -/// declares `class Greet { greet }` and `fn greet`. Pre-mq.3 this +/// declares `class Greet { greet }` and `fn greet`. Pre-canonical-class-form this /// fired `WorkspaceLoadError::MethodNameCollision { kind: "class-fn" }`. -/// Post-mq.3 the workspace loads cleanly — the method-vs-fn +/// Post-canonical-class-form the workspace loads cleanly — the method-vs-fn /// coexistence is now legal at load time; the call-site emits a -/// `class-method-shadowed-by-fn` warning (covered by mq.3.6 E2E, +/// `class-method-shadowed-by-fn` warning (covered by the multi-class E2E, /// not this load-level pin). #[test] fn mq3_class_fn_collision_loads_clean() { let entry = examples_dir() .join("test_22b2_method_name_collision_class_fn.ail"); load_workspace(&entry) - .expect("post-mq.3: workspace loads — class-vs-fn name overlap is no longer a load error"); + .expect("post-canonical-class-form: workspace loads — class-vs-fn name overlap is no longer a load error"); } diff --git a/crates/ailang-check/tests/no_per_type_print_ops.rs b/crates/ailang-check/tests/no_per_type_print_ops.rs index 371bd65..6137f76 100644 --- a/crates/ailang-check/tests/no_per_type_print_ops.rs +++ b/crates/ailang-check/tests/no_per_type_print_ops.rs @@ -27,7 +27,7 @@ fn per_type_print_effect_ops_are_retired() { for retired in ["io/print_int", "io/print_bool", "io/print_float"] { assert!( !env.effect_ops.contains_key(retired), - "{retired} must not appear in builtin registry post-rpe.1; \ + "{retired} must not appear in builtin registry post-per-type-print-retirement; \ use polymorphic `print` (Show-based) instead" ); } diff --git a/crates/ailang-check/tests/workspace.rs b/crates/ailang-check/tests/workspace.rs index cf0ca41..1b6002b 100644 --- a/crates/ailang-check/tests/workspace.rs +++ b/crates/ailang-check/tests/workspace.rs @@ -8,7 +8,7 @@ use ailang_check::{check_workspace, Severity}; use ailang_surface::load_workspace; use std::path::Path; -/// Iter 18c.2: assert that the linearity check's `suggested_rewrites` +/// assert that the linearity check's `suggested_rewrites` /// payload is non-empty AND each `replacement` parses as a form-A AILang /// term. This is the contract the check promises to consumers of /// `ail check --json`: a machine can take the replacement string and @@ -123,7 +123,7 @@ fn invalid_def_name_with_dot_is_reported() { ); } -/// Iter 6: body-check is multi-diagnose. A module with two independent +/// body-check is multi-diagnose. A module with two independent /// errors in different defs must produce two diagnostics — the first /// error must not short-circuit the second. #[test] @@ -202,7 +202,7 @@ fn body_errors_accumulate_across_defs() { assert!(defs.contains(&"bad_b"), "missing bad_b; got {defs:?}"); } -/// Iter 18c.2: a fn with all-explicit modes that consumes its +/// a fn with all-explicit modes that consumes its /// `(own (con List))` parameter twice should trigger /// `use-after-consume` on the second occurrence and ship a /// non-empty, well-formed `suggested_rewrites`. @@ -320,7 +320,7 @@ fn use_after_consume_on_own_param_is_reported() { assert_suggested_rewrites_well_formed(d); } -/// Iter 18c.2: a fn whose body passes `xs` to a `Borrow` arg and then, +/// a fn whose body passes `xs` to a `Borrow` arg and then, /// in a SIBLING arg slot of the same call, consumes it via an `Own` /// arg, should trigger `consume-while-borrowed`. /// @@ -424,7 +424,7 @@ fn consume_while_borrowed_in_sibling_arg_is_reported() { assert_suggested_rewrites_well_formed(d); } -/// Iter 18d.1: happy-path `(reuse-as xs (term-ctor List Cons ...))` +/// happy-path `(reuse-as xs (term-ctor List Cons ...))` /// inside an all-explicit-mode `map_inc`-style fn must produce NO /// diagnostics — neither `reuse-as-non-allocating-body` (body is a /// Ctor) nor `reuse-as-source-not-bare-var` (source is `xs`) nor @@ -548,7 +548,7 @@ fn reuse_as_happy_path_in_map_inc_is_linearity_clean() { ); } -/// Iter 18d.2: a deliberately mismatched reuse-as — `(reuse-as xs +/// a deliberately mismatched reuse-as — `(reuse-as xs /// (Nil))` inside the Cons arm of a match on `xs` — must surface as /// a `reuse-as-shape-mismatch` diagnostic. The source's path-ctor /// (Cons, 2 fields) and the body ctor (Nil, 0 fields) have different @@ -675,7 +675,7 @@ fn reuse_as_shape_mismatch_is_reported_on_cons_to_nil() { assert_suggested_rewrites_well_formed(d); } -/// Iter 18c.2: positive control. The ON-DISK `borrow_own_demo` fixture +/// positive control. The ON-DISK `borrow_own_demo` fixture /// (the only currently-shipping all-explicit-mode program) must remain /// linearity-clean. If this regresses, the check has become incorrect: /// `borrow_own_demo`'s `list_length` (borrow) and `sum_list` (own) diff --git a/crates/ailang-codegen/src/drop.rs b/crates/ailang-codegen/src/drop.rs index 83da2b7..d419b54 100644 --- a/crates/ailang-codegen/src/drop.rs +++ b/crates/ailang-codegen/src/drop.rs @@ -32,7 +32,7 @@ use super::synth::llvm_type; use super::{AllocStrategy, Emitter, FnSig, Result}; impl<'a> Emitter<'a> { - /// Iter 18c.4: emit a `void @drop__(ptr %p)` + /// emit a `void @drop__(ptr %p)` /// function that decrements the refcount of every pointer-typed /// field of every ctor, then frees the outer box. /// @@ -160,7 +160,7 @@ impl<'a> Emitter<'a> { self.body.push_str(&out); } - /// Iter 18e: emit `drop__` for a `(drop-iterative)` type. + /// emit `drop__` for a `(drop-iterative)` type. /// Replaces the recursive cascade in [`Self::emit_drop_fn_for_type`] /// with an iterative-with-explicit-worklist body so cells of /// arbitrary chain depth can free without consuming proportional @@ -328,7 +328,7 @@ impl<'a> Emitter<'a> { self.body.push_str(&out); } - /// Iter 18e helper: is `fty` the same ADT as `td_name` in the + /// is `fty` the same ADT as `td_name` in the /// current module? Used by the iterative-drop body to decide /// "push to worklist" (same type) vs. "call its drop fn directly" /// (different type). @@ -365,7 +365,7 @@ impl<'a> Emitter<'a> { } } - /// Iter 18c.4: pick the drop-fn symbol to call for a single + /// pick the drop-fn symbol to call for a single /// pointer-typed field. Routes ADT fields to their own /// `drop__` symbol so the recursion cascades through /// recursive types (List, Tree). Falls back to `ailang_rc_dec` @@ -430,7 +430,7 @@ impl<'a> Emitter<'a> { } } - /// Iter 18c.3: predicate the `Term::Let` lowering uses to decide + /// predicate the `Term::Let` lowering uses to decide /// whether a let-binder owns a fresh RC-heap allocation that /// codegen should `dec` at scope close. /// @@ -443,7 +443,7 @@ impl<'a> Emitter<'a> { /// non-escaping ones become stack `alloca`s and must NOT be /// `dec`'d (they are freed by LLVM at fn return). /// - /// Iter 18g.2 widens this to include `Term::App` whose callee's + /// The widened input set includes `Term::App` whose callee's /// fn-type carries `ret_mode == Own`. The mode contract states that /// the callee hands the returned cell's ownership to the caller's /// frame; the let-scope close is the right place for the caller's @@ -464,7 +464,7 @@ impl<'a> Emitter<'a> { !self.non_escape.contains(&term_ptr) } Term::App { callee, .. } => { - // Iter 18g.2: a call whose callee carries + // a call whose callee carries // `ret_mode == Own` hands a fresh heap allocation to // the caller's frame. Trackable. `Borrow` and // `Implicit` ret-modes do not carry that signal — @@ -479,7 +479,7 @@ impl<'a> Emitter<'a> { } } - /// Iter 18g.2: lookup helper for a callee's `ret_mode`. Returns + /// lookup helper for a callee's `ret_mode`. Returns /// `Some(mode)` when the callee resolves to a fn-typed term; /// `None` for shapes whose type is not a `Type::Fn` (typechecker /// would already have rejected an App on a non-fn, but the helper @@ -494,7 +494,7 @@ impl<'a> Emitter<'a> { } } - /// Iter 18c.4: pick the drop-fn symbol to call at the close of a + /// pick the drop-fn symbol to call at the close of a /// trackable `Term::Let` scope. For a `Term::Ctor` binder the /// symbol is `drop__` — derived from the ctor's /// `type_name` (which already encodes the owning module via the @@ -524,7 +524,7 @@ impl<'a> Emitter<'a> { .get(val_ssa) .cloned() .unwrap_or_else(|| "ailang_rc_dec".to_string()), - // Iter 18g.2: an Own-returning call hands a freshly heap- + // an Own-returning call hands a freshly heap- // allocated cell whose static type is the callee's // `ret`. Resolve the per-type drop fn from that ret-type // so the cascade walks the cell's pointer-typed children @@ -559,7 +559,7 @@ impl<'a> Emitter<'a> { } } - /// Iter 18g.tidy.fu2: tag-conditional partial-drop helper. + /// tag-conditional partial-drop helper. /// Emits `void @partial_drop__(ptr %p, i64 %mask)` — /// structurally parallel to [`Self::emit_drop_fn_for_type`] but /// gates each ptr-field dec on a bit of `%mask`. If bit j of the @@ -699,7 +699,7 @@ impl<'a> Emitter<'a> { self.body.push_str(&out); } - /// Iter 18g.tidy.fu2: resolve the `partial_drop__` + /// resolve the `partial_drop__` /// symbol for a type, parallel to the existing per-type drop /// dispatch in `field_drop_call`. Returns `None` for non-ADT /// types (Str, fn-typed, type-vars) — callers fall back to @@ -728,7 +728,7 @@ impl<'a> Emitter<'a> { } } - /// Iter 18g.tidy.fu2: build the i64 moved-slots bitmask for a + /// build the i64 moved-slots bitmask for a /// `partial_drop__` call. Bit j is set iff slot j is in /// `moved`. We reject slot indices ≥ 64 with `None` — no /// language-level ADT has that many fields, but the cap is @@ -747,7 +747,7 @@ impl<'a> Emitter<'a> { Some(mask) } - /// Iter 18d.3: emit a partial-drop sequence inline at a let-close + /// emit a partial-drop sequence inline at a let-close /// site whose binder has moved-out pattern slots. Replaces the /// uniform `drop__(ptr)` call: load each pointer-typed /// field whose slot index is NOT in `moved`, dispatch through @@ -757,11 +757,11 @@ impl<'a> Emitter<'a> { /// transferred to a pattern-bound binder that owns the dec /// for them. /// - /// Iter 18g.2 widened the input set to `Term::App` (Own- - /// returning call). The static per-field emission below is keyed + /// The widened input set includes `Term::App` (Own-returning + /// call). The static per-field emission below is keyed /// against a `Term::Ctor`'s known ctor; a `Term::App` binder /// whose body pattern-matches it has a *dynamic* runtime tag. - /// Iter 18g.tidy.fu2 closes that path: instead of falling back + /// instead of falling back /// to shallow `ailang_rc_dec` (which leaked the unmoved fields), /// we route through the tag-conditional helper /// [`Self::emit_partial_drop_fn_for_type`], which dispatches on @@ -775,7 +775,7 @@ impl<'a> Emitter<'a> { let (type_name, ctor_name) = match value { Term::Ctor { type_name, ctor, .. } => (type_name.as_str(), ctor.as_str()), _ => { - // Iter 18g.tidy.fu2: dynamic-tag partial-drop via the + // dynamic-tag partial-drop via the // per-type helper. `value` is `Term::App` (Own- // returning) — the binder's static type is the App's // ret type, recovered through `synth_arg_type` / @@ -836,7 +836,7 @@ impl<'a> Emitter<'a> { Ok(()) } - /// Iter 18c.4: build the IR text for a closure env's drop fn. + /// build the IR text for a closure env's drop fn. /// Layout: 8 bytes per capture, in declaration order. /// For each pointer-typed capture, emit a load + drop call; /// finally `ailang_rc_dec` the env block. @@ -878,7 +878,7 @@ impl<'a> Emitter<'a> { out } - /// Iter 18c.4: build the IR text for a closure pair's drop fn. + /// build the IR text for a closure pair's drop fn. /// Layout: { ptr thunk, ptr env } — env at offset 8. /// Loads env, calls the env drop, then decs the pair box. pub(crate) fn build_pair_drop_fn( diff --git a/crates/ailang-codegen/src/escape.rs b/crates/ailang-codegen/src/escape.rs index 6b094c1..ed1647a 100644 --- a/crates/ailang-codegen/src/escape.rs +++ b/crates/ailang-codegen/src/escape.rs @@ -174,18 +174,18 @@ fn walk(t: &Term, out: &mut NonEscapeSet) { walk(body, out); } Term::LetRec { body, in_term, .. } => { - // Iter 16b.x: LetRec is normally eliminated before codegen. + // LetRec is normally eliminated before codegen. // Still walk for completeness. walk(body, out); walk(in_term, out); } Term::Clone { value } => { - // Iter 18c.1: clone is identity at IR level — only walk + // clone is identity at IR level — only walk // sub-terms looking for Let-allocation candidates. walk(value, out); } Term::ReuseAs { source, body } => { - // Iter 18d.1: identity at IR level (codegen lowers `body`, + // identity at IR level (codegen lowers `body`, // ignores `source`). Walk both children for completeness. walk(source, out); walk(body, out); @@ -361,13 +361,13 @@ fn escapes(t: &Term, tainted: &BTreeSet, in_tail: bool) -> bool { true } Term::Clone { value } => { - // Iter 18c.1: clone is identity. The wrapper's escape + // clone is identity. The wrapper's escape // verdict is exactly that of its inner term, threaded // through with the same `in_tail` context. escapes(value, tainted, in_tail) } Term::ReuseAs { source, body } => { - // Iter 18d.1: identity at IR level. The whole expression's + // identity at IR level. The whole expression's // value is `body`, so its escape verdict is the body's // (threaded through `in_tail`). The `source` is evaluated // but its value is discarded; it cannot escape via the @@ -490,11 +490,11 @@ fn collect_free_vars(t: &Term, bound: &mut BTreeSet, out: &mut BTreeSet< collect_free_vars(in_term, bound, out); } Term::Clone { value } => { - // Iter 18c.1: clone is identity for free-var collection. + // clone is identity for free-var collection. collect_free_vars(value, bound, out); } Term::ReuseAs { source, body } => { - // Iter 18d.1: free vars are the union of source and body. + // free vars are the union of source and body. collect_free_vars(source, bound, out); collect_free_vars(body, bound, out); } diff --git a/crates/ailang-codegen/src/lambda.rs b/crates/ailang-codegen/src/lambda.rs index 20a23f7..65af0a9 100644 --- a/crates/ailang-codegen/src/lambda.rs +++ b/crates/ailang-codegen/src/lambda.rs @@ -31,7 +31,7 @@ use super::synth::{fn_sig_from_type, llvm_type}; use super::{AllocStrategy, CodegenError, Emitter, FnSig, Result}; impl<'a> Emitter<'a> { - /// Iter 8b: lower a `Term::Lam`. Walks the body to find captures + /// lower a `Term::Lam`. Walks the body to find captures /// (free vars relative to the enclosing scope, minus builtins and /// top-level fns), allocates a heap env for the captures, lifts the /// body to a top-level thunk fn `@ail___lam`, and @@ -122,17 +122,17 @@ impl<'a> Emitter<'a> { let saved_terminated = self.block_terminated; self.block_terminated = false; let saved_sigs = std::mem::take(&mut self.ssa_fn_sigs); - // Iter 17a: a lambda thunk is its own fn frame for escape + // a lambda thunk is its own fn frame for escape // analysis. Save the outer fn's non-escape set and recompute // for the lambda body. Restored at the end alongside the rest // of the saved emitter state. let saved_non_escape = std::mem::take(&mut self.non_escape); self.non_escape = escape::analyze_fn_body(lam_body); - // Iter 18d.3: a lambda thunk is its own fn frame for move + // a lambda thunk is its own fn frame for move // tracking — the outer fn's binders are not in scope inside // the thunk body (only its captures + params). Save and reset. let saved_moved = std::mem::take(&mut self.moved_slots); - // Iter mut.3: a lambda thunk is its own fn frame for the + // a lambda thunk is its own fn frame for the // mut-var bookkeeping introduced in iter mut.3 — the outer // fn's loop binders are not in scope inside the thunk (a // lambda body cannot reference enclosing loop binders; the @@ -150,7 +150,7 @@ impl<'a> Emitter<'a> { let saved_loop_frames = std::mem::take(&mut self.loop_frames); let saved_pending_allocas = std::mem::take(&mut self.pending_entry_allocas); let saved_entry_marker = self.entry_block_end_marker.take(); - // Iter 18d.4 fix: a lambda thunk is its own fn frame for + // A lambda thunk is its own fn frame for // param-mode lookup. The outer fn's params are not in scope // inside the thunk; the thunk's own params are pushed below // and (currently) carry no mode annotation, so they default @@ -269,7 +269,7 @@ impl<'a> Emitter<'a> { // 3. Emit allocation + capture filling + closure-pair packing // in the OUTER body. Captures use 8 bytes each; closure-pair // is 16 bytes. - // Iter 17a: env and closure-pair share the Lam term's escape + // env and closure-pair share the Lam term's escape // status — they have parallel lifetimes. If the closure pair // does not escape (only used as a callee in the outer body), // both can be `alloca`'d. The escape-analysis lookup runs @@ -334,7 +334,7 @@ impl<'a> Emitter<'a> { }; self.ssa_fn_sigs.insert(clos.clone(), fs); - // Iter 18c.4: emit per-pair drop fns for heap-allocated + // emit per-pair drop fns for heap-allocated // closures under `--alloc=rc`. `lam_local` closures live on // the stack and need no drop fn — LLVM `alloca` reclaims the // pair on fn return. For heap closures we emit two symbols: @@ -365,7 +365,7 @@ impl<'a> Emitter<'a> { Ok((clos, "ptr".into())) } - /// Iter 8b: walk a Term collecting free-variable names that should + /// walk a Term collecting free-variable names that should /// be captured by an enclosing lambda. Builtins and top-level fns /// are excluded — they're globally accessible. Qualified names /// (`prefix.def`) are excluded too. @@ -461,16 +461,16 @@ impl<'a> Emitter<'a> { Self::collect_captures(rhs, bound, captures, captures_set, builtins, top_level); } Term::LetRec { .. } => { - // Iter 16b.1: eliminated by desugar before codegen. + // eliminated by desugar before codegen. unreachable!("Term::LetRec eliminated by desugar") } Term::Clone { value } => { - // Iter 18c.1: clone is identity for capture analysis — + // clone is identity for capture analysis — // free vars of `(clone X)` are exactly the free vars of `X`. Self::collect_captures(value, bound, captures, captures_set, builtins, top_level); } Term::ReuseAs { source, body } => { - // Iter 18d.1: free vars are the union of source and body. + // free vars are the union of source and body. Self::collect_captures(source, bound, captures, captures_set, builtins, top_level); Self::collect_captures(body, bound, captures, captures_set, builtins, top_level); } diff --git a/crates/ailang-codegen/src/lib.rs b/crates/ailang-codegen/src/lib.rs index 7c68b9b..6369802 100644 --- a/crates/ailang-codegen/src/lib.rs +++ b/crates/ailang-codegen/src/lib.rs @@ -143,16 +143,16 @@ type Result = std::result::Result; /// Bench iter: which heap-allocation runtime the emitted IR targets. /// -/// `Gc` is the default (Boehm conservative GC, Decision 9 / Iter 14f). +/// `Gc` is the default (Boehm conservative GC). /// `Bump` swaps every `@GC_malloc` for `@bump_malloc`, which is supplied /// by `runtime/bump.c` — a no-free, statically-sized arena allocator /// used purely to quantify the GC's overhead via an A/B comparison. -/// `Rc` (Iter 18b, Decision 10) routes allocation through +/// `Rc` (the RC memory model) routes allocation through /// `@ailang_rc_alloc` from `runtime/rc.c`, which prefixes every payload -/// with an 8-byte refcount header. Iter 18b stops at allocator routing — -/// codegen does not yet emit `inc`/`dec` calls, so programs leak -/// every allocation under `Rc`. The actual instrumentation arrives in -/// Iter 18c once uniqueness inference is wired up. +/// with an 8-byte refcount header. The initial allocator-routing +/// step did not yet emit `inc`/`dec` calls, so programs leak every +/// allocation under `Rc`. The actual instrumentation arrives once +/// uniqueness inference is wired up. /// The IR is otherwise byte-identical between the three strategies /// modulo the allocator symbol name. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] @@ -214,7 +214,7 @@ impl AllocStrategy { /// modules (cross-module calls, transitive imports) — `emit_ir` is the /// short-cut for the single-file demo case. pub fn emit_ir(m: &Module) -> Result { - // Iter 16a: nested ctor patterns are desugared inside + // nested ctor patterns are desugared inside // `lower_workspace`, so single-module callers go through the // same code path with no extra work here. let mut modules = BTreeMap::new(); @@ -223,7 +223,7 @@ pub fn emit_ir(m: &Module) -> Result { entry: m.name.clone(), modules, root_dir: std::path::PathBuf::from("."), - // Iter 22b.1: emit_ir is the single-module shortcut. The + // emit_ir is the single-module shortcut. The // empty registry is fine for 22b.1 because codegen does not // yet read it; once 22b.3 monomorphisation runs, the queue // is built from class-method-call sites in already-typechecked @@ -295,7 +295,7 @@ pub fn lower_workspace_staticlib(ws: &Workspace) -> Result { } fn lower_workspace_inner(ws: &Workspace, alloc: AllocStrategy, target: Target) -> Result { - // Iter 16a: desugar every module before any lowering work runs. + // desugar every module before any lowering work runs. // The pass is idempotent and structurally identical to what // `ailang-check` runs at its public entries, so the codegen // sees the same flat-pattern AST as the typechecker. @@ -307,7 +307,7 @@ fn lower_workspace_inner(ws: &Workspace, alloc: AllocStrategy, target: Target) - .map(|(k, m)| (k.clone(), ailang_core::desugar::desugar_module(m))) .collect(), root_dir: ws.root_dir.clone(), - // Iter 22b.1: pass the registry through unchanged. The desugar + // pass the registry through unchanged. The desugar // pass does not touch class/instance defs (see desugar.rs: // 22b.1 passthrough), so the registry built at load time // remains valid against the desugared modules. @@ -318,7 +318,7 @@ fn lower_workspace_inner(ws: &Workspace, alloc: AllocStrategy, target: Target) - let mut body = String::new(); let mut all_strings: BTreeMap> = BTreeMap::new(); // ^ per module: list of (global-name, content). Order = insertion order. - // Iter hs.1: parallel aggregation for language `Str`-literal globals + // parallel aggregation for language `Str`-literal globals // (packed-struct shape). Same map shape; emitted by a parallel loop // alongside the existing one. let mut all_str_literals: BTreeMap> = BTreeMap::new(); @@ -335,12 +335,12 @@ fn lower_workspace_inner(ws: &Workspace, alloc: AllocStrategy, target: Target) - // `synth_arg_type` for ctor-arg type inference). let mut module_user_fns: BTreeMap> = BTreeMap::new(); let mut module_def_ail_types: BTreeMap> = BTreeMap::new(); - // Iter 15a: cross-module ctor table. Maps module name → ctor name → + // cross-module ctor table. Maps module name → ctor name → // CtorRef (with `type_name` *unqualified*, since the ctor is defined // in that module). Cross-module ctor lookups resolve through this // table instead of the per-Emitter `ctor_index`. let mut module_ctor_index: BTreeMap> = BTreeMap::new(); - // Iter 15b: per-module const table. Used to resolve `Term::Var` + // per-module const table. Used to resolve `Term::Var` // references to const defs (literal or non-literal) at lowering // time. Literal consts emit a global and are loaded; non-literal // consts (e.g. ctor expressions) are inlined at every reference @@ -384,7 +384,7 @@ fn lower_workspace_inner(ws: &Workspace, alloc: AllocStrategy, target: Target) - } } } - // Iter 15b: collect const defs for this module so non-literal + // collect const defs for this module so non-literal // consts can be inlined at `Term::Var` reference sites. let mut consts: BTreeMap = BTreeMap::new(); for def in &m.defs { @@ -409,13 +409,13 @@ fn lower_workspace_inner(ws: &Workspace, alloc: AllocStrategy, target: Target) - let key = imp.alias.clone().unwrap_or_else(|| imp.module.clone()); import_map.insert(key, imp.module.clone()); } - // Iter 23.2: mirror the typechecker's implicit-prelude import + // mirror the typechecker's implicit-prelude import // injection (crates/ailang-check/src/lib.rs:1198 and :1300). // User modules that call prelude-resident fns synthesised by - // Iter 22b.3 monomorphisation (e.g. `prelude.eq__Int` from a - // user calling `eq x y` on Ints) need the codegen-side + // monomorphisation (e.g. `prelude.eq__Int` from a user calling + // `eq x y` on Ints) need the codegen-side // import_map to resolve the `prelude.` prefix at the call - // site (`lower_call`'s prefix lookup). Post-ct.3 ctor lookup + // site (`lower_call`'s prefix lookup). Post-canonical-type-form ctor lookup // is canonical (bare = local, qualified routes via import_map); // this entry serves fn-name resolution only. if m.name != "prelude" { @@ -448,7 +448,7 @@ fn lower_workspace_inner(ws: &Workspace, alloc: AllocStrategy, target: Target) - // uses a monotonic counter, so alphabetic is enough). entries.sort_by(|a, b| a.0.cmp(&b.0)); all_strings.insert(mname.clone(), entries); - // Iter hs.1: parallel collection of `Str`-literal globals. + // parallel collection of `Str`-literal globals. let mut lit_entries: Vec<(String, String)> = Vec::new(); for (content, (name, _)) in &emitter.str_literals { lit_entries.push((name.clone(), content.clone())); @@ -498,7 +498,7 @@ fn lower_workspace_inner(ws: &Workspace, alloc: AllocStrategy, target: Target) - emitted_global = true; } } - // Iter hs.2: packed-struct globals for language `Str` literals. + // packed-struct globals for language `Str` literals. // First `i64` is the byte length (excluding the trailing NUL); the // `[N+1 x i8]` carries the bytes followed by the terminating NUL. // The IR-`Str` pointer that flows through the rest of codegen lands @@ -529,14 +529,14 @@ fn lower_workspace_inner(ws: &Workspace, alloc: AllocStrategy, target: Target) - // pipeline; `Bump` declares `@bump_malloc` instead, supplied by // `runtime/bump.c` and linked in lieu of `-lgc`. out.push_str(&format!("declare ptr @{}(i64)\n", alloc.fn_name())); - // Iter 18c.3: under `--alloc=rc`, also declare the inc/dec ABI from + // under `--alloc=rc`, also declare the inc/dec ABI from // `runtime/rc.c` so codegen can emit refcount calls at every // `Term::Clone` site and at end-of-scope of trackable RC binders. // `Gc` and `Bump` keep their pre-18c IR shape — nothing to declare. if matches!(alloc, AllocStrategy::Rc) { out.push_str("declare void @ailang_rc_inc(ptr)\n"); out.push_str("declare void @ailang_rc_dec(ptr)\n"); - // Iter 18e: drop-worklist ABI for `(drop-iterative)` types. + // drop-worklist ABI for `(drop-iterative)` types. // Declared unconditionally under `--alloc=rc` (the linker // drops symbols if no emitted fn references them); symmetric // with inc/dec above. See `runtime/rc.c` for the strategy. @@ -545,11 +545,11 @@ fn lower_workspace_inner(ws: &Workspace, alloc: AllocStrategy, target: Target) - out.push_str("declare ptr @ailang_drop_worklist_pop(ptr)\n"); out.push_str("declare void @ailang_drop_worklist_free(ptr)\n"); } - // Iter 16e: `==` on `Str` lowers to `@strcmp` followed by + // `==` on `Str` lowers to `@strcmp` followed by // `icmp eq i32 0`. NUL-terminated strings make this a one-liner; // libc supplies `strcmp` so no extra link flag is needed. out.push_str("declare i32 @strcmp(ptr, ptr)\n"); - // Iter 23.2: `runtime/str.c::ail_str_eq` backs the prelude's + // `runtime/str.c::ail_str_eq` backs the prelude's // `eq__Str` mono symbol (see `try_emit_primitive_instance_body`). // Declared unconditionally — the codegen intercept emits a call // to it whenever the monomorphiser synthesises `eq__Str` in any @@ -557,7 +557,7 @@ fn lower_workspace_inner(ws: &Workspace, alloc: AllocStrategy, target: Target) - // by `ail build` (see `crates/ail/src/main.rs::build_to`). The // `zeroext i1` return matches clang's lowering of C `_Bool`. out.push_str("declare zeroext i1 @ail_str_eq(ptr, ptr)\n"); - // Iter 23.3: `runtime/str.c::ail_str_compare` backs the prelude's + // `runtime/str.c::ail_str_compare` backs the prelude's // `compare__Str` mono symbol (see `try_emit_primitive_instance_body` // `compare__Str` arm below). Declared unconditionally on the same // rationale as `@ail_str_eq` — the .o is supplied by the @@ -566,7 +566,7 @@ fn lower_workspace_inner(ws: &Workspace, alloc: AllocStrategy, target: Target) - // to {-1, 0, +1} so the branch ladder in the intercept can compare // against constant 0 directly. out.push_str("declare i32 @ail_str_compare(ptr, ptr)\n"); - // Iter hs.4: heap-Str formatter externs from `runtime/str.c`. + // heap-Str formatter externs from `runtime/str.c`. // Both return a heap-allocated Str pointer (rc_header at offset // -8; consumer ABI shared with static-Str — len at offset 0, // bytes at offset 8). Declared unconditionally on the same @@ -575,7 +575,7 @@ fn lower_workspace_inner(ws: &Workspace, alloc: AllocStrategy, target: Target) - // clang -O2 dead-strips the declarations when no caller exists. out.push_str("declare ptr @ailang_int_to_str(i64)\n"); out.push_str("declare ptr @ailang_float_to_str(double)\n"); - // Iter 24.1: heap-Str primitive externs for the `show` instances + // heap-Str primitive externs for the `show` instances // — `ailang_bool_to_str` (Show Bool) and `ailang_str_clone` // (Show Str). Same dual-realisation ABI as int_to_str / // float_to_str: rc_header at offset -8 on the heap-Str output; @@ -708,7 +708,7 @@ struct Emitter<'a> { body: String, /// String constants: content -> (global name (without `@`), llvm type length incl. \0) strings: BTreeMap, - /// Iter hs.1 (amended hs.2): language `Str` literals interned as + /// language `Str` literals interned as /// packed-struct globals (`<{ i64, [N+1 x i8] }>`) carrying an /// explicit `len` field and the bytes + trailing NUL. Parallel to /// `strings` (which still serves runtime-internal format strings @@ -738,14 +738,14 @@ struct Emitter<'a> { /// decision-tree optimization). #[allow(dead_code)] types: BTreeMap>, - /// Iter 15a: cross-module ctor index, keyed by module name. Used by + /// cross-module ctor index, keyed by module name. Used by /// `lookup_ctor_by_type` (for `Term::Ctor.type_name`) and /// `lookup_ctor_in_pattern` (for `Pattern::Ctor.ctor`). Built once /// per workspace and shared by every Emitter. Replaces the per- /// emitter `ctor_index` of pre-15a — that table only knew the /// current module's ctors and broke on cross-module references. module_ctor_index: &'a BTreeMap>, - /// Iter 15b: per-module const defs, used to resolve `Term::Var` + /// per-module const defs, used to resolve `Term::Var` /// references (bare or qualified) to a const's body. Literal /// consts emit a global and are loaded via `@ail__`; /// non-literal consts are inlined at every reference site (sound @@ -754,28 +754,28 @@ struct Emitter<'a> { /// Current basic block label. Set by `start_block` and is /// the single source of truth for `phi` operands. current_block: String, - /// Iter 14e: true while the current block already ends in a + /// true while the current block already ends in a /// terminator (currently only `ret` after a `musttail call`). /// Callers in the term lowering walk consult this to skip /// fall-through `br` emission and to omit the value from a /// surrounding match-arm phi. Reset by [`Self::start_block`]. block_terminated: bool, - /// Iter 7: SSA value (or `@global`) -> its FnSig, for first-class + /// SSA value (or `@global`) -> its FnSig, for first-class /// function values. Populated whenever we lower a `Term::Var` to a /// top-level fn pointer or when a fn-typed parameter is bound at /// function entry. Used by `Term::App` when the callee is not a /// statically-known top-level name. ssa_fn_sigs: BTreeMap, - /// Iter 8b: name of the currently-emitted def (for lambda thunk + /// name of the currently-emitted def (for lambda thunk /// naming `_lam`). current_def: String, - /// Iter 8b: per-def counter for lambda thunks. Reset in emit_fn. + /// per-def counter for lambda thunks. Reset in emit_fn. lam_counter: u32, - /// Iter 8b: thunk fn IR text for lambdas encountered during + /// thunk fn IR text for lambdas encountered during /// lowering. Flushed at the end of emit_fn (LLVM IR allows fns in /// any order). deferred_thunks: Vec, - /// Iter 17a: per-fn escape-analysis result. Set of pointer-as-usize + /// per-fn escape-analysis result. Set of pointer-as-usize /// addresses of `Term::Ctor` and `Term::Lam` nodes that the /// analysis proved do not escape the fn frame they are allocated /// in. Such allocations lower to `alloca` instead of `@GC_malloc`. @@ -786,14 +786,14 @@ struct Emitter<'a> { /// Decided at the top-level entry point (`lower_workspace_inner`) /// and propagated to every site that emits a `call ptr @(...)`. alloc: AllocStrategy, - /// Iter 18c.3: per-binder uniqueness side-table for the current + /// per-binder uniqueness side-table for the current /// module, keyed by `(def_name, binder_name)`. Built once per /// emitter and consulted by `Term::Let` lowering to decide whether /// to emit `call void @ailang_rc_dec(ptr %v)` at scope close. The /// table is module-scoped because the inference is whole-fn local; /// no cross-module entries appear. uniqueness: UniquenessTable, - /// Iter 18c.4: per-closure-pair drop-function symbol. Keyed by the + /// per-closure-pair drop-function symbol. Keyed by the /// closure-pair SSA value (e.g. `%v17`) the most-recent /// `lower_lambda` call returned. Consulted by the `Term::Let` /// lowering to emit `call void @(ptr %v17)` instead of the @@ -802,7 +802,7 @@ struct Emitter<'a> { /// `--alloc=gc`/`--alloc=bump` has no drop fn and is freed by /// the collector / arena. closure_drops: BTreeMap, - /// Iter 18d.3: per-fn-body move tracking. Keyed by binder name, maps + /// per-fn-body move tracking. Keyed by binder name, maps /// to the set of positional ctor-field indices that have been /// "moved out" via a pattern destructure. A field is moved when a /// `(case (Ctor h t) )` arm binds a non-wildcard, pointer- @@ -829,7 +829,7 @@ struct Emitter<'a> { /// particular binder are removed when that binder leaves scope /// (on `Term::Let` body close, on match-arm body close). moved_slots: BTreeMap>, - /// Iter 18d.4 fix: per-fn-body parameter modes, keyed by parameter + /// per-fn-body parameter modes, keyed by parameter /// name. Set once at the top of `emit_fn` (and at lambda thunk /// entry) from the fn type's `param_modes`. Consulted by /// `lower_match`'s arm-close pattern-binder dec emission (Iter A) to @@ -871,7 +871,7 @@ struct Emitter<'a> { /// the loop-binder allocas mem2reg-eligible regardless of how /// deeply nested the originating `Term::Loop` is. pending_entry_allocas: String, - /// Iter mut.3: byte position in `self.body` immediately after + /// byte position in `self.body` immediately after /// the `entry:\n` label, captured during `start_block("entry")` /// at fn-body emission. Used to splice `pending_entry_allocas` /// into the entry block once body lowering completes. @@ -899,7 +899,7 @@ struct CtorRef { /// `Type::Var` references for parameterised ADTs (Iter 13b); these /// are substituted at every ctor / match-arm use site. ail_fields: Vec, - /// Iter 13b: type parameters of the owning TypeDef, in declaration + /// type parameters of the owning TypeDef, in declaration /// order. Empty for monomorphic ADTs (`type IntList = ...`); non- /// empty for parameterised ADTs (`type Box[a] = MkBox(a)` → /// `["a"]`). Used as the var-set for `unify_for_subst` when deriving @@ -936,7 +936,7 @@ impl<'a> Emitter<'a> { if let Def::Type(td) = def { let mut infos = Vec::new(); for c in td.ctors.iter() { - // Iter 13b: precomputed LLVM field types are only + // precomputed LLVM field types are only // meaningful for monomorphic ADTs. For parameterised // ADTs the field types reference free `Type::Var`s // and must be derived per use site after @@ -959,7 +959,7 @@ impl<'a> Emitter<'a> { } } - // Iter 18c.3: build the per-module uniqueness side-table once + // build the per-module uniqueness side-table once // per emitter. The inference is pure (no I/O, no global state), // so doing it here is cheap and keeps codegen's input self- // contained. @@ -1003,7 +1003,7 @@ impl<'a> Emitter<'a> { pub(crate) fn start_block(&mut self, label: &str) { self.body.push_str(label); self.body.push_str(":\n"); - // Iter mut.3: capture the entry-block splice point for + // capture the entry-block splice point for // `pending_entry_allocas`. The marker points to the byte // position immediately after the `entry:\n` label — the // splice (in `emit_fn`'s tail) inserts the hoisted allocas @@ -1039,7 +1039,7 @@ impl<'a> Emitter<'a> { // logical type. Heap boxes are allocated ad hoc via // GC_malloc (Boehm conservative collector, Iter 14f). } - // Iter 22b.1: class/instance defs do not emit IR yet. + // class/instance defs do not emit IR yet. // 22b.3 monomorphisation will rewrite class-method // calls into calls against synthesised monomorphic // FnDefs; once that pass runs, class/instance bodies @@ -1055,7 +1055,7 @@ impl<'a> Emitter<'a> { // sees only monomorphic defs; the drain loop and // `emit_specialised_fn` have been removed. - // Iter 18c.4: per-ADT drop functions. Emitted only under + // per-ADT drop functions. Emitted only under // `--alloc=rc`. One `void @drop__(ptr)` per // `Def::Type` in the current module — the call site for // recursive ADTs (`drop__List` calling itself on the tail) @@ -1067,7 +1067,7 @@ impl<'a> Emitter<'a> { for def in &self.module.defs { if let Def::Type(td) = def { if td.drop_iterative { - // Iter 18e: opt-in iterative-drop body. The + // opt-in iterative-drop body. The // recursive cascade overflows the C stack on // long chains (a million-cell list ≈ 8MB // stack); the iterative variant uses an @@ -1076,7 +1076,7 @@ impl<'a> Emitter<'a> { } else { self.emit_drop_fn_for_type(td); } - // Iter 18g.tidy.fu2: tag-conditional partial-drop + // tag-conditional partial-drop // helper alongside the recursive/iterative drop fn. // Used at carve-out sites where a binder's runtime // tag is dynamic and `moved_slots` is a strict @@ -1092,13 +1092,13 @@ impl<'a> Emitter<'a> { Ok(()) } - /// Iter 12b: emit one specialised version of a polymorphic def. + /// emit one specialised version of a polymorphic def. /// Substitutes rigid vars in both the type and the body, then /// calls `emit_fn` against a synthetic FnDef whose `name` already /// contains the descriptor — the existing mangling concatenates /// `ail__` and produces the desired symbol. fn emit_const(&mut self, c: &ConstDef) -> Result<()> { - // Iter 15b: non-literal const values (e.g. ctor expressions) are + // non-literal const values (e.g. ctor expressions) are // not emitted as globals. They are inlined at every `Term::Var` // reference site — sound because `check_const` rejects effectful // bodies, so re-evaluating the body at each use is observably @@ -1121,7 +1121,7 @@ impl<'a> Emitter<'a> { ), Literal::Unit => ("i8".to_string(), "0".to_string()), Literal::Str { value } => { - // Iter hs.2: emit a packed-struct global and return a + // emit a packed-struct global and return a // constexpr-GEP pointer landing on the `len`-field (now // the first field of the packed struct, since the // hs.1-era sentinel rc-header slot was removed). Every @@ -1155,7 +1155,7 @@ impl<'a> Emitter<'a> { } fn emit_fn(&mut self, f: &FnDef) -> Result<()> { - // Iter 18d.4: also lift `param_modes` out of the fn type. The + // also lift `param_modes` out of the fn type. The // fn-return Own-param dec emission below consults it to decide // which params get a drop call before `ret`. `Implicit` // entries (legacy / unannotated) and `Borrow` entries are @@ -1184,14 +1184,14 @@ impl<'a> Emitter<'a> { // Per-fn body: the sidetable starts empty. Top-level fn references // get registered on demand by `lower_term(Term::Var)`. self.ssa_fn_sigs.clear(); - // Iter 8b: lambda thunks live in the same module body but get + // lambda thunks live in the same module body but get // collected during lowering and appended after the parent fn. self.current_def = f.name.clone(); self.lam_counter = 0; - // Iter 18d.3: move tracking is per-fn-body. + // move tracking is per-fn-body. self.moved_slots.clear(); - // Iter 18d.4 fix: param-mode lookup is per-fn-body. Built from - // the fn type's `param_modes` (already destructured above) and + // Param-mode lookup is per-fn-body. Built from the fn type's + // `param_modes` (already destructured above) and // consulted by `lower_match`'s Iter A gate to skip arm-close // pattern-binder dec when the scrutinee is a non-Own param. self.current_param_modes.clear(); @@ -1206,7 +1206,7 @@ impl<'a> Emitter<'a> { let mode = param_modes.get(i).copied().unwrap_or(ParamMode::Implicit); self.current_param_modes.insert(pname.clone(), mode); } - // Iter 17a: run escape analysis over the fn body. The result + // run escape analysis over the fn body. The result // is queried at every `Term::Ctor` / `Term::Lam` lowering site // to decide between `alloca` (non-escaping) and `@GC_malloc` // (escaping). The analysis is purely additive — a stale or @@ -1239,7 +1239,7 @@ impl<'a> Emitter<'a> { pty.clone(), pty_ail.clone(), )); - // Iter 7: if this param is a function value, record its sig + // if this param is a function value, record its sig // so that `f(args)` inside the body can emit an indirect call. if let Some(fs) = fn_sig_from_type(pty_ail) { self.ssa_fn_sigs.insert(pssa, fs); @@ -1249,7 +1249,7 @@ impl<'a> Emitter<'a> { self.body.push_str(&sig); self.start_block("entry"); - // Iter 23.2: primitive-instance body intercept. Returns true if + // primitive-instance body intercept. Returns true if // the body was emitted in full (including the closing `}\n\n`). // When that fires we skip the normal body-lowering block below // but still fall through to deferred-thunk flush + closure-pair @@ -1271,7 +1271,7 @@ impl<'a> Emitter<'a> { f.name ))); } - // Iter 18d.4: fn-return Own-param dec. Symmetric to + // fn-return Own-param dec. Symmetric to // 18c.3/18c.4's `Term::Let`-scope-close drop and 18d.4's // arm-close pattern-binder dec, fired at the lexical // close of a fn body. For each parameter with @@ -1347,7 +1347,7 @@ impl<'a> Emitter<'a> { " call void @{drop_call}(ptr {p_ssa})\n" )); } else { - // Iter 18g.tidy.fu2: dynamic-tag partial-drop + // dynamic-tag partial-drop // via the per-type helper. The param's runtime // tag is dynamic but its static type is known // (`pty_ail`), so we route through @@ -1377,14 +1377,14 @@ impl<'a> Emitter<'a> { self.body .push_str(&format!(" ret {val_ty} {val}\n}}\n\n")); } else { - // Iter 14e: a `tail-app`/`tail-do` at the body root already + // a `tail-app`/`tail-do` at the body root already // emitted its own `ret` (after `musttail call`). Just close // the function body — no fall-through ret. self.body.push_str("}\n\n"); } } // close `if !body_was_intercepted` - // Iter mut.3: splice any deferred entry-block allocas at the + // splice any deferred entry-block allocas at the // captured marker (immediately after the `entry:\n` label). // If no mut-vars were emitted for this fn, // `pending_entry_allocas` is empty and this is a no-op. The @@ -1405,14 +1405,14 @@ impl<'a> Emitter<'a> { self.body.insert_str(marker, &allocas); } - // Iter 8b: flush lambda thunks collected while lowering this fn's + // flush lambda thunks collected while lowering this fn's // body. They go after the closing `}` of the parent fn, before // the adapter, so the parent fn is contiguous. for t in self.deferred_thunks.drain(..) { self.body.push_str(&t); } - // Iter 8a: emit closure-pair scaffold (adapter + static closure) + // emit closure-pair scaffold (adapter + static closure) // for this fn. The adapter takes an extra `ptr %_env` (ignored, // null sentinel for top-level fns) and forwards to the real fn. // The static closure pair `{ adapter_ptr, null }` is the value @@ -1422,7 +1422,7 @@ impl<'a> Emitter<'a> { Ok(()) } - /// Iter 8a: closure-pair scaffold for a top-level fn. Always emitted + /// closure-pair scaffold for a top-level fn. Always emitted /// (one wrapper per fn), so cross-module references just use the /// `__clos` symbol without coordination. fn emit_adapter_and_static_closure( @@ -1473,7 +1473,7 @@ impl<'a> Emitter<'a> { "i1".into(), ), Literal::Str { value } => { - // Iter hs.2: language `Str` literals materialise as + // language `Str` literals materialise as // a constexpr-GEP into the packed-struct global, // landing on the `len`-field (now the first field, // since the hs.1-era sentinel rc-header slot was @@ -1505,7 +1505,7 @@ impl<'a> Emitter<'a> { "neg_inf" => return Ok(("0xFFF0000000000000".into(), "double".into())), _ => {} } - // Iter 16d: `__unreachable__` is a polymorphic bottom + // `__unreachable__` is a polymorphic bottom // value (`forall a. a`). At codegen we emit LLVM // `unreachable` as the block terminator and return a // dummy SSA value. The surrounding `if`/`match`/`seq` @@ -1540,14 +1540,14 @@ impl<'a> Emitter<'a> { { return Ok((ssa.clone(), ty.clone())); } - // Iter 7: bare reference to a top-level fn yields a fn-pointer + // bare reference to a top-level fn yields a fn-pointer // value of type `ptr`. Cross-module via `prefix.def`, current // module via plain `def`. Sidetable carries the sig. if let Some((global, sig)) = self.resolve_top_level_fn(name) { self.ssa_fn_sigs.entry(global.clone()).or_insert(sig); return Ok((global, "ptr".into())); } - // Iter 15b: const lookup. Both bare (`xs`) and qualified + // const lookup. Both bare (`xs`) and qualified // (`prefix.xs`) forms resolve through `module_consts`. // Literal-bodied consts get a load from the global; non- // literal bodies (e.g. ctor expressions) are inlined. @@ -1575,7 +1575,7 @@ impl<'a> Emitter<'a> { Err(CodegenError::UnknownVar(name.clone())) } Term::Let { name, value, body } => { - // Iter 18c.3: decide whether this let-binder is + // decide whether this let-binder is // trackable for `dec` emission BEFORE lowering. The // value term must lower through `ailang_rc_alloc` — // that means `Term::Ctor` / `Term::Lam` whose escape @@ -1589,7 +1589,7 @@ impl<'a> Emitter<'a> { let (val_ssa, val_ty) = self.lower_term(value)?; self.locals .push((name.clone(), val_ssa.clone(), val_ty.clone(), val_ail)); - // Iter 18g.tidy.fu: let-alias-aware mode propagation. + // let-alias-aware mode propagation. // If `value` is a `Term::Var` referencing a name in // `current_param_modes`, the let-binder inherits that // mode for the duration of the body. Without this, @@ -1626,7 +1626,7 @@ impl<'a> Emitter<'a> { } } self.locals.pop(); - // Iter 18d.3: lift the binder's move set out of the + // lift the binder's move set out of the // side table. The binder is leaving scope here; we // remove the entry whether or not we end up using it // for the dec emission below. `take` returns a @@ -1635,7 +1635,7 @@ impl<'a> Emitter<'a> { let moves_for_binder: BTreeSet = self.moved_slots.remove(name).unwrap_or_default(); - // Iter 18c.3: emit a drop call at scope close iff: + // emit a drop call at scope close iff: // - we're tracking this binder (heap RC alloc above), // - the value type is `ptr` (not a primitive), // - uniqueness inference recorded `consume_count == 0` @@ -1647,7 +1647,7 @@ impl<'a> Emitter<'a> { // - the current block is still open (a tail-call / // `unreachable` already exited; nothing to emit). // - // Iter 18c.4: the drop call is no longer a raw + // the drop call is no longer a raw // `ailang_rc_dec`. For a `Term::Ctor` binder the call // routes through `@drop__` so any // boxed children of the cell cascade through their @@ -1667,7 +1667,7 @@ impl<'a> Emitter<'a> { Err(_) => true, // Don't emit on error path either. }; if consume_count == 0 && !body_returns_binder { - // Iter 18d.3: when the binder has moved-out + // when the binder has moved-out // pattern slots, the uniform `drop__` // would re-dec values that have already been // transferred to other binders. Inline a @@ -1715,7 +1715,7 @@ impl<'a> Emitter<'a> { self.start_block(&then_lbl); let (then_v, then_ty) = self.lower_term(then)?; - // Iter 14e: a tail-call in this branch already terminated + // a tail-call in this branch already terminated // its block; skip its branch to join and exclude from phi. let then_terminated = self.block_terminated; let then_block_end = self.current_block.clone(); @@ -1736,7 +1736,7 @@ impl<'a> Emitter<'a> { self.body.push_str(&format!(" br label %{join_lbl}\n")); } - // Iter 14e: if both branches terminated, the whole `if` is + // if both branches terminated, the whole `if` is // terminated and no join is reachable. Mark and bail. if then_terminated && else_terminated { self.block_terminated = true; @@ -1763,7 +1763,7 @@ impl<'a> Emitter<'a> { ev = else_v, elbl = else_block_end, )); - // Iter 7: if both branches yield the same fn-pointer sig, + // if both branches yield the same fn-pointer sig, // forward it to the phi SSA so subsequent indirect calls // can resolve. if then_ty == "ptr" { @@ -1810,7 +1810,7 @@ impl<'a> Emitter<'a> { } Term::Do { op, args, tail } => self.lower_effect_op(op, args, *tail), Term::Ctor { type_name, ctor, args } => { - // Iter 17a: pass the term pointer so `lower_ctor` can + // pass the term pointer so `lower_ctor` can // consult the escape-analysis result for this exact // allocation site. let term_ptr = (t as *const Term) as usize; @@ -1818,16 +1818,16 @@ impl<'a> Emitter<'a> { } Term::Match { scrutinee, arms } => self.lower_match(scrutinee, arms), Term::Lam { params, param_tys, ret_ty, effects: _, body } => { - // Iter 17a: same as `Ctor` — pass the term pointer for + // same as `Ctor` — pass the term pointer for // escape-analysis lookup. A non-escaping closure pair // (and its env) lower to `alloca`. let term_ptr = (t as *const Term) as usize; self.lower_lambda(params, param_tys, ret_ty, body, term_ptr) } Term::Seq { lhs, rhs } => { - // Iter 10: lower lhs for its effects, discard the SSA; + // lower lhs for its effects, discard the SSA; // lower rhs and return its value as the whole expression. - // Iter 14e: lhs may not legally be a `tail` call (the + // lhs may not legally be a `tail` call (the // typechecker rejects that), so `block_terminated` is // false after it. rhs is in the same tail context as the // surrounding seq, so a `tail-app` there will set @@ -1837,13 +1837,13 @@ impl<'a> Emitter<'a> { self.lower_term(rhs) } Term::LetRec { .. } => { - // Iter 16b.1: `Term::LetRec` is eliminated by the + // `Term::LetRec` is eliminated by the // desugar pass before codegen runs, so reaching it // here is a bug. unreachable!("Term::LetRec eliminated by desugar") } Term::Clone { value } => { - // Iter 18c.3: lower the inner value, then emit + // lower the inner value, then emit // `call void @ailang_rc_inc(ptr %v)` under `--alloc=rc`. // Inc is skipped for non-`ptr` values (primitives like // `i64` carry no refcount) and for `@`-prefixed SSAs @@ -1865,7 +1865,7 @@ impl<'a> Emitter<'a> { Ok((val_ssa, val_ty)) } Term::ReuseAs { source, body } => { - // Iter 18d.2: under --alloc=rc, lower as a runtime- + // under --alloc=rc, lower as a runtime- // refcount-1 dispatch — if the source's box is // unique we overwrite it in place (skipping the // alloc-and-cascade-dec round-trip); otherwise we @@ -2009,13 +2009,13 @@ impl<'a> Emitter<'a> { } } - /// Resolves a `Term::Ctor.type_name` (canonical post-ct.1: bare + /// Resolves a `Term::Ctor.type_name` (canonical form: bare /// = local TypeDef, qualified `.` = explicit /// cross-module) to the codegen-side `CtorRef`. Qualified names /// route through `import_map`; bare names hit the current /// module's `module_ctor_index` directly. No imports-walk - /// fallback — the typechecker (post-ct.2) and the workspace - /// validator (post-ct.1) have already pinned canonical form. + /// fallback — the typechecker (post-canonical-type-form) and the workspace + /// validator (post-canonical-type-form) have already pinned canonical form. pub(crate) fn lookup_ctor_by_type( &self, type_name: &str, @@ -2071,7 +2071,7 @@ impl<'a> Emitter<'a> { } } - /// Iter 15a: collects the set of type names declared in `owner_module`. + /// collects the set of type names declared in `owner_module`. /// Used to mirror the typechecker's `qualify_local_types` rewrite /// when reading a polymorphic fn's signature pulled across the /// import boundary. @@ -2086,7 +2086,7 @@ impl<'a> Emitter<'a> { .unwrap_or_default() } - /// Iter 15a: resolves a ctor in pattern position. The current + /// resolves a ctor in pattern position. The current /// `module_name`'s ctor table is consulted first; on miss, the /// imported modules are scanned (the typechecker has already /// vetted unambiguity, so the first hit wins — local always @@ -2128,7 +2128,7 @@ impl<'a> Emitter<'a> { } fn lower_app(&mut self, name: &str, args: &[Term], tail: bool) -> Result<(String, String)> { - // Iter 16e: `==` is polymorphic (`forall a. (a, a) -> Bool`). + // `==` is polymorphic (`forall a. (a, a) -> Bool`). // Dispatch on the resolved AIL arg type — the LLVM `ptr` shape // aliases multiple AIL types (Str vs ADT vs Fn), so we cannot // dispatch on the LLVM type alone. ADT/Fn equality is rejected @@ -2245,7 +2245,7 @@ impl<'a> Emitter<'a> { return Ok((dst, "i1".into())); } if name == "int_to_str" { - // Iter hs.4: lowers to the runtime C glue + // lowers to the runtime C glue // `ailang_int_to_str(i64) -> ptr` defined in // `runtime/str.c`. Returned pointer is a heap-Str (see // the `float_to_str` arm below for the dual-realisation @@ -2261,7 +2261,7 @@ impl<'a> Emitter<'a> { return Ok((dst, "ptr".to_string())); } if name == "float_to_str" { - // Iter hs.4: lowers to the runtime C glue + // lowers to the runtime C glue // `ailang_float_to_str(double) -> ptr` defined in // `runtime/str.c`. The returned pointer is a heap-Str // (rc_header at offset -8; consumer ABI shared with @@ -2280,7 +2280,7 @@ impl<'a> Emitter<'a> { return Ok((dst, "ptr".to_string())); } if name == "bool_to_str" { - // Iter 24.1: lowers to the runtime C glue + // lowers to the runtime C glue // `ailang_bool_to_str(i1) -> ptr` defined in // `runtime/str.c`. Returned pointer is a heap-Str // (rc_header at offset -8; consumer ABI shared with @@ -2296,7 +2296,7 @@ impl<'a> Emitter<'a> { return Ok((dst, "ptr".to_string())); } if name == "str_clone" { - // Iter 24.1: lowers to the runtime C glue + // lowers to the runtime C glue // `ailang_str_clone(ptr) -> ptr` defined in // `runtime/str.c`. Reads `len` from offset 0 of the // source Str payload and allocates a fresh heap-Str @@ -2314,7 +2314,7 @@ impl<'a> Emitter<'a> { return Ok((dst, "ptr".to_string())); } if name == "str_concat" { - // Iter str-concat: lowers to the runtime C glue + // lowers to the runtime C glue // `ailang_str_concat(ptr, ptr) -> ptr` defined in // `runtime/str.c`. Reads `len` from offset 0 of each // source Str payload and allocates a fresh heap-Str @@ -2338,7 +2338,7 @@ impl<'a> Emitter<'a> { // Logic identical to the typechecker (see `synth` for `Term::Var`). if name.matches('.').count() == 1 { let (prefix, suffix) = name.split_once('.').expect("checked"); - // Iter 24.3: fall back to direct module-name lookup when + // fall back to direct module-name lookup when // the prefix isn't in the current module's import_map. A // post-mono synthesised body may carry cross-module // references to modules its source template didn't import @@ -2422,7 +2422,7 @@ impl<'a> Emitter<'a> { .collect::>() .join(", "); let dst = self.fresh_ssa(); - // Iter 14e: emit `musttail call ... ret` for `tail: true`. The + // emit `musttail call ... ret` for `tail: true`. The // call SSA flows directly into the `ret`, satisfying LLVM's // "must immediately ret" rule. Same calling convention and // signature as the surrounding fn (the typechecker enforces @@ -2442,7 +2442,7 @@ impl<'a> Emitter<'a> { Ok((dst, sig.ret.clone())) } - /// Iter 8a: indirect call through a closure-pair pointer. The + /// indirect call through a closure-pair pointer. The /// callee SSA points at `{ ptr thunk, ptr env }`; we GEP+load both /// halves and call `thunk(env, args...)`. The user-visible `sig` /// describes only the user-level params/ret — the env_ptr is @@ -2501,7 +2501,7 @@ impl<'a> Emitter<'a> { param_tys.push_str(pt); } let dst = self.fresh_ssa(); - // Iter 14e: indirect tail calls. Same `musttail`/`ret` shape as + // indirect tail calls. Same `musttail`/`ret` shape as // emit_call. The thunk's signature uniformly inserts an // `env_ptr` first arg, but a `musttail call` to a thunk whose // signature exactly matches the parent fn's prototype + @@ -2524,7 +2524,7 @@ impl<'a> Emitter<'a> { } - /// Iter 7: is `name` a callee that can be resolved at compile time + /// is `name` a callee that can be resolved at compile time /// (no fn-pointer needed)? True for builtin operators, the /// current-module top-level fns (mono or poly), and qualified /// `prefix.def`. Locals shadow this — the caller checks for @@ -2563,7 +2563,7 @@ impl<'a> Emitter<'a> { .is_some_and(|m| m.contains_key(name)) } - /// Iter 8a: resolve `name` to a top-level fn-value, i.e. the address + /// resolve `name` to a top-level fn-value, i.e. the address /// of its static closure pair `@ail___clos`, plus the user- /// visible FnSig (params/ret WITHOUT the env_ptr — that's added at /// the call site by the closure ABI). Returns None if the name does @@ -2572,7 +2572,7 @@ impl<'a> Emitter<'a> { fn resolve_top_level_fn(&self, name: &str) -> Option<(String, FnSig)> { if name.matches('.').count() == 1 { let (prefix, suffix) = name.split_once('.')?; - // Iter 24.3: dotted form resolves through the current + // dotted form resolves through the current // module's import_map first (the standard cross-module // reference path). If the prefix isn't in import_map, // fall back to a direct module-name lookup against @@ -2602,7 +2602,7 @@ impl<'a> Emitter<'a> { )) } - /// Iter 15b: resolve a `Term::Var` reference to a const def. Returns + /// resolve a `Term::Var` reference to a const def. Returns /// `(owning_module, ConstDef)` on hit. Both bare current-module /// references and qualified `prefix.name` cross-module references /// resolve through the same path; the prefix routes through the @@ -2623,7 +2623,7 @@ impl<'a> Emitter<'a> { } fn lower_effect_op(&mut self, op: &str, args: &[Term], tail: bool) -> Result<(String, String)> { - // Iter 14e: `musttail` requires identical caller/callee + // `musttail` requires identical caller/callee // prototypes (same return type, same param types). The MVP's // runtime print helpers (`printf`, `puts`) return `i32`, but the // AILang fn enclosing a `tail-do io/print_*` returns `Unit` @@ -2648,7 +2648,7 @@ impl<'a> Emitter<'a> { "io/print_str needs ptr".into(), )); } - // Iter hs.1: `Str` values now flow as a pointer to the + // `Str` values now flow as a pointer to the // `len`-field of the packed-struct slab; @puts needs // the bytes pointer 8 bytes further on. let bytes = self.fresh_ssa(); @@ -2669,7 +2669,7 @@ impl<'a> Emitter<'a> { } } - /// Iter 23.2: hand-rolled body for monomorphiser-synthesised + /// hand-rolled body for monomorphiser-synthesised /// primitive instance methods whose natural lambda-lowering would /// not produce the spec-mandated IR shape. Returns `Ok(true)` if /// the body was emitted (including the closing `}` and a final @@ -2713,7 +2713,7 @@ impl<'a> Emitter<'a> { let n = self.locals.len(); let a_ssa = self.locals[n - 2].1.clone(); let b_ssa = self.locals[n - 1].1.clone(); - // Iter hs.1: IR-Str pointers now land on the + // IR-Str pointers now land on the // `len`-field of the packed-struct slab; @ail_str_eq's // strcmp-based body needs the bytes pointer 8 bytes // further on. @@ -2776,7 +2776,7 @@ impl<'a> Emitter<'a> { let n = self.locals.len(); let a_ssa = self.locals[n - 2].1.clone(); let b_ssa = self.locals[n - 1].1.clone(); - // Iter hs.1: IR-Str pointers now land on the + // IR-Str pointers now land on the // `len`-field of the packed-struct slab; @ail_str_compare's // strcmp-based body needs the bytes pointer 8 bytes // further on. @@ -2802,7 +2802,7 @@ impl<'a> Emitter<'a> { } } - /// Iter 23.3: emit one arm of the `compare__T` branch ladder. + /// emit one arm of the `compare__T` branch ladder. /// Starts a fresh basic block labelled `label`, constructs the /// matching `Ordering` ctor (LT / EQ / GT) via `lower_ctor`, and /// emits a `ret ptr `. Used three times per `compare__T` @@ -2827,7 +2827,7 @@ impl<'a> Emitter<'a> { Ok(()) } - /// Iter 23.3: emit the labelled three-way branch ladder shared + /// emit the labelled three-way branch ladder shared /// across all three `compare__T` intercept arms. Given two /// instruction RHS strings (the LT-test and the EQ-test, e.g. /// `"icmp slt i64 %a, %b"`), assigns each to a fresh SSA, wires @@ -2873,7 +2873,7 @@ impl<'a> Emitter<'a> { Ok(()) } - /// Iter 16e: lower a `==` call after the two operands have been + /// lower a `==` call after the two operands have been /// emitted. Dispatches on the resolved AIL type of the arg side /// (both sides have the same type after typecheck). The `_a_ll` /// hint is the LLVM type the lowering produced for `a`; we use @@ -2914,7 +2914,7 @@ impl<'a> Emitter<'a> { Ok((dst, "i1".into())) } "Str" => { - // Iter hs.1: IR-Str pointers now land on the + // IR-Str pointers now land on the // `len`-field of the packed-struct slab; @strcmp // needs the bytes pointer 8 bytes further on. // Parallel to the `eq__Str` and `compare__Str` @@ -2976,8 +2976,8 @@ impl<'a> Emitter<'a> { self.counter } - /// Iter hs.1 (amended hs.2): parallel to the legacy - /// `intern_string` (retired in iter rpe.1 alongside the per-type + /// parallel to the legacy + /// `intern_string` (retired in the per-type-print-op retirement alongside the per-type /// print effect-ops it served), but for /// language `Str` literals emitted as packed-struct globals /// (len + bytes + NUL). Shares the same monotonic `str_counter` @@ -2995,7 +2995,7 @@ impl<'a> Emitter<'a> { name } - /// Iter 12b: lightweight AILang-type computation for an expression + /// lightweight AILang-type computation for an expression /// in the current scope. Mirrors what the typechecker already /// derived; we replay it here only because the typechecker doesn't /// hand its annotations down. @@ -3047,7 +3047,7 @@ impl<'a> Emitter<'a> { } if name.matches('.').count() == 1 { let (prefix, suffix) = name.split_once('.').expect("checked"); - // Iter 24.3: try the current module's import_map + // try the current module's import_map // first (standard cross-module reference), then // fall back to a direct module-name lookup for // post-mono synthesised cross-module references @@ -3070,7 +3070,7 @@ impl<'a> Emitter<'a> { .get(target) .and_then(|m| m.get(suffix)) { - // Iter 15a: qualify any bare type-cons that + // qualify any bare type-cons that // refer to types declared in `target` so the // returned signature lines up with the // qualified ctors / type names produced @@ -3092,7 +3092,7 @@ impl<'a> Emitter<'a> { { return Ok(ty.clone()); } - // Iter 15b: const refs participate in arg-type + // const refs participate in arg-type // synthesis. Bare or qualified, both forms route // through `resolve_const` and yield the const's // declared type. Const types are already qualified @@ -3158,15 +3158,15 @@ impl<'a> Emitter<'a> { )) }), Term::Ctor { type_name, ctor, args } => { - // Iter 13b: derive concrete type-args of a parameterised + // derive concrete type-args of a parameterised // ADT instance from the recursively-synthesised arg // types. For monomorphic ADTs (`type_vars.is_empty()`) // we keep the pre-13b shape `Type::Con { args: vec![] }` // — matching what the typechecker produces. - // Iter 15a: a qualified `type_name` resolves through the + // a qualified `type_name` resolves through the // cross-module ctor index. The result `Type::Con.name` // stays qualified to match what the typechecker emits. - // Iter 15b: when the ctor is cross-module, `cref.ail_fields` + // when the ctor is cross-module, `cref.ail_fields` // is written in the owning module's local namespace, so a // recursive self-reference like `Cons a (List a)` carries // a bare `Con("List", _)` even though every other place @@ -3242,15 +3242,15 @@ impl<'a> Emitter<'a> { } Term::Seq { rhs, .. } => self.synth_with_extras(rhs, extras), Term::LetRec { .. } => { - // Iter 16b.1: eliminated by desugar before codegen. + // eliminated by desugar before codegen. unreachable!("Term::LetRec eliminated by desugar") } Term::Clone { value } => { - // Iter 18c.1: clone is identity — same type as inner. + // clone is identity — same type as inner. self.synth_with_extras(value, extras) } Term::ReuseAs { body, .. } => { - // Iter 18d.1: identity — the result type is the body's + // identity — the result type is the body's // type. The source is dropped at codegen. self.synth_with_extras(body, extras) } @@ -3331,7 +3331,7 @@ mod tests { ); } - /// Iter 16e: codegen rejects `==` on ADT-typed args with a clear + /// codegen rejects `==` on ADT-typed args with a clear /// error. The typechecker accepts the call (the rigid var of /// `forall a. (a, a) -> Bool` unifies with the ADT type), so the /// rejection has to happen here. The diagnostic must mention the @@ -3394,7 +3394,7 @@ mod tests { ); } - /// Iter 16e: same negative-path guard for function-typed args. + /// same negative-path guard for function-typed args. /// `==` on `Fn` is rejected with a "not supported for function /// types" message. #[test] @@ -3476,7 +3476,7 @@ mod tests { } } - /// Iter 18c.4: under `--alloc=rc`, codegen emits one + /// under `--alloc=rc`, codegen emits one /// `define void @drop__` per `Def::Type`. For a recursive /// ADT — a `IntList` with `Cons(Int, IntList)` — the `Cons` arm /// of the drop fn loads the tail field and calls the ADT's own @@ -3845,7 +3845,7 @@ mod tests { assert!(ir.contains("0xFFF0000000000000"), "-inf bit pattern missing: {ir}"); } - /// Iter 23.2: codegen intercepts a fn named `eq__Str` (the + /// codegen intercepts a fn named `eq__Str` (the /// monomorphiser's synthesised symbol for `Eq Str.eq`) and emits /// a two-instruction body that calls @ail_str_eq, regardless of /// what the lambda body in the source would lower to. Pinned with @@ -3904,7 +3904,7 @@ mod tests { ); } - /// Iter 23.2.2-fixup: every top-level fn — including primitive- + /// every top-level fn — including primitive- /// instance-bodied ones like `eq__Str` — must flow through /// `emit_adapter_and_static_closure` so that referencing the fn /// as a `Term::Var` value (dictionary entry, by-value pass to @@ -3971,7 +3971,7 @@ mod tests { // `eq_primitives_smoke.ail` Form-A fixture; the original `.ail.json` // fixture is deleted in T8). - /// Iter 23.3: `runtime/str.c::ail_str_compare` backs the prelude's + /// `runtime/str.c::ail_str_compare` backs the prelude's /// `compare__Str` mono symbol (see `try_emit_primitive_instance_body`). /// The declaration is unconditional in the IR header alongside /// `@ail_str_eq`; this test pins the header line so a regression @@ -4006,7 +4006,7 @@ mod tests { ); } - /// Iter 23.3: codegen intercepts a fn named `compare__Int` and + /// codegen intercepts a fn named `compare__Int` and /// emits a three-way branch ladder: `icmp slt i64 a, b` → /// LT-block; else `icmp eq i64 a, b` → EQ-block; else GT-block. /// Each block constructs the matching Ordering ctor via the @@ -4026,7 +4026,7 @@ mod tests { ); } - /// Iter 23.3: same shape for `compare__Bool`. The LT-test uses + /// same shape for `compare__Bool`. The LT-test uses /// `icmp ult i1` (unsigned: false=0 ult true=1 gives the natural /// Bool ordering false < true); the EQ-test uses `icmp eq i1`. #[test] @@ -4043,7 +4043,7 @@ mod tests { ); } - /// Iter 23.3: `compare__Str` calls `@ail_str_compare` to get the + /// `compare__Str` calls `@ail_str_compare` to get the /// normalised {-1, 0, +1}, then branches: slt 0 → LT, eq 0 → EQ, /// else GT. #[test] @@ -4120,7 +4120,7 @@ mod tests { } } - /// Iter hs.2: language `Str` literals emit as packed-struct globals + /// language `Str` literals emit as packed-struct globals /// carrying an explicit `len` field followed by the bytes + trailing /// NUL. (The hs.1-era sentinel rc-header slot was removed per the /// amended spec; static-Str pointers are kept out of `ailang_rc_dec` @@ -4164,7 +4164,7 @@ mod tests { ); } - /// Iter hs.2: a `Literal::Str` at a callsite materialises a constexpr + /// a `Literal::Str` at a callsite materialises a constexpr /// `getelementptr` landing on the `len`-field of the packed-struct /// global (now the *first* field, since the hs.1-era sentinel /// rc-header slot was removed per the amended spec). This pins the @@ -4202,7 +4202,7 @@ mod tests { ); } - /// Iter hs.1: after the layout migration, the `io/print_str` + /// after the layout migration, the `io/print_str` /// path must `getelementptr i8` +8 onto the IR-Str pointer before /// passing it to `@puts`, so `@puts` receives the bytes pointer /// (skipping the `len` field) and produces correct output. @@ -4243,7 +4243,7 @@ mod tests { ); } - /// Iter hs.1: `eq__Str`'s body must `getelementptr i8` +8 on + /// `eq__Str`'s body must `getelementptr i8` +8 on /// both operand pointers before calling `@ail_str_eq`, since the /// IR-Str pointer now lands on the `len`-field, not the bytes. #[test] @@ -4302,7 +4302,7 @@ mod tests { ); } - /// Iter hs.1: `compare__Str`'s body must `getelementptr i8` +8 + /// `compare__Str`'s body must `getelementptr i8` +8 /// on both operand pointers before calling `@ail_str_compare`, /// symmetric to the `eq__Str` change. #[test] @@ -4328,7 +4328,7 @@ mod tests { ); } - /// Iter hs.1: the `==` operator on `Str` lowers via an inline + /// the `==` operator on `Str` lowers via an inline /// `@strcmp` call (separate from the `eq__Str` instance-method /// intercept used by the dictionary path). Both operand pointers /// must be `+8` GEP'd to land on the bytes pointer before @@ -4398,7 +4398,7 @@ mod tests { ); } - /// Iter hs.4: a `Term::App` calling `int_to_str` lowers to + /// a `Term::App` calling `int_to_str` lowers to /// `call ptr @ailang_int_to_str(i64 %a)`. Pins the new builtin's /// lowering shape against the runtime C glue introduced in iter /// hs.3. @@ -4439,7 +4439,7 @@ mod tests { ); } - /// Iter hs.4: `float_to_str` no longer raises CodegenError::Internal + /// `float_to_str` no longer raises CodegenError::Internal /// — it lowers to `call ptr @ailang_float_to_str(double %a)`, /// symmetric to the new `int_to_str` arm. #[test] @@ -4479,7 +4479,7 @@ mod tests { ); } - /// Iter 24.1: a `Term::App` calling `bool_to_str` lowers to + /// a `Term::App` calling `bool_to_str` lowers to /// `call ptr @ailang_bool_to_str(i1 %a)`. Pins the new builtin's /// lowering shape against the runtime C glue introduced in this /// iter (mirror of hs.4's `int_to_str_lowers_to_ailang_int_to_str_call`). @@ -4520,7 +4520,7 @@ mod tests { ); } - /// Iter 24.1: a `Term::App` calling `str_clone` lowers to + /// a `Term::App` calling `str_clone` lowers to /// `call ptr @ailang_str_clone(ptr %a)`. Pins the new builtin's /// lowering shape against the runtime C glue introduced in this /// iter (mirror of hs.4's `int_to_str_lowers_to_ailang_int_to_str_call`). @@ -4561,7 +4561,7 @@ mod tests { ); } - /// Iter str-concat: a `Term::App` calling `str_concat` lowers to + /// a `Term::App` calling `str_concat` lowers to /// `call ptr @ailang_str_concat(ptr %a, ptr %b)`. Pins the new /// builtin's extern declaration AND the lower_app arm together /// (mirror of the str_clone IR pin above). diff --git a/crates/ailang-codegen/src/match_lower.rs b/crates/ailang-codegen/src/match_lower.rs index a2a4fa8..5f3b6e5 100644 --- a/crates/ailang-codegen/src/match_lower.rs +++ b/crates/ailang-codegen/src/match_lower.rs @@ -33,7 +33,7 @@ impl<'a> Emitter<'a> { /// i1 and i8 fields also occupy a full 8-byte slot — the typed /// load/store instructions write/read only the required size. /// - /// Iter 17a: `term_ptr` is the pointer-as-usize of the lowered + /// `term_ptr` is the pointer-as-usize of the lowered /// `Term::Ctor` node. If escape analysis flagged this site as /// non-escaping (i.e., the value cannot live past the current fn /// frame), allocation lowers to LLVM `alloca` instead of @@ -52,13 +52,13 @@ impl<'a> Emitter<'a> { "ctor `{type_name}/{ctor_name}` arity" ))); } - // Iter 13b: for parameterised ADTs the precomputed `cref.fields` + // for parameterised ADTs the precomputed `cref.fields` // is meaningless because field types reference rigid type vars. // Derive a per-use-site substitution from the arg types and // re-lower each field type. Monomorphic ADTs hit the fast path // (no var-set, substitution is empty, ail_fields lower exactly // like cref.fields). - // Iter 15b: for cross-module ctors, qualify any local type-cons + // for cross-module ctors, qualify any local type-cons // in `cref.ail_fields` (symmetric to the term-ctor synth fix). let qualified_ail_fields: Vec = if type_name.matches('.').count() == 1 { let (prefix, _) = type_name.split_once('.').expect("checked"); @@ -108,7 +108,7 @@ impl<'a> Emitter<'a> { // design/contracts/frozen-value-layout.md. size = 8 + n*8, tag@0, fields@8+i*8. let size_bytes = 8 + (compiled.len() * 8) as i64; let p = self.fresh_ssa(); - // Iter 17a: pick allocator based on escape analysis. `alloca` + // pick allocator based on escape analysis. `alloca` // for non-escaping (stack-allocated, freed on fn return); // `@GC_malloc` for everything else. if self.non_escape.contains(&term_ptr) { @@ -139,7 +139,7 @@ impl<'a> Emitter<'a> { Ok((p, "ptr".into())) } - /// Iter 18d.2: lower `Term::ReuseAs { source, body = Term::Ctor }` + /// lower `Term::ReuseAs { source, body = Term::Ctor }` /// under `--alloc=rc` as a runtime refcount-1 dispatch. /// /// IR shape (Lean 4 / Roc lineage): @@ -192,7 +192,7 @@ impl<'a> Emitter<'a> { "reuse-as source type must be ptr, got {src_ty}" ))); } - // Iter 18d.3: the source binder name keys into `moved_slots` + // the source binder name keys into `moved_slots` // so the reuse arm can skip dec'ing fields that earlier // pattern-extracted out of the source. Linearity already // ensures `source` is `Term::Var` here; the var-resolution @@ -323,7 +323,7 @@ impl<'a> Emitter<'a> { // 5. Reuse arm: overwrite tag and field slots in place. // - // Iter 18d.3: per-field dec is now safe — moved-out slots + // per-field dec is now safe — moved-out slots // are skipped via the codegen `moved_slots` side table; // non-moved slots are dec'd via `field_drop_call`'s null- // guarded drop fns BEFORE the new field values overwrite @@ -344,7 +344,7 @@ impl<'a> Emitter<'a> { // pointer-typed fields are dec'd via `field_drop_call`, // closing the 18d.2 leak. self.start_block(&reuse_lbl); - // Iter 18d.3: dec non-moved pointer-typed slots before the + // dec non-moved pointer-typed slots before the // overwrite. The dec fns are null-guarded, so an empty slot // is a no-op. Clone the move set out of the side table so // the loop below can mutably borrow `self` for SSA allocation. @@ -449,7 +449,7 @@ impl<'a> Emitter<'a> { "match on non-ADT scrutinee (got {s_ty}); MVP supports only ADTs" ))); } - // Iter 18d.3: move tracking key — the bare-Var binder name of + // move tracking key — the bare-Var binder name of // the scrutinee, if it has one. A complex expression scrutinee // (e.g. the result of `(map_inc xs)` matched directly) has no // binder we can key off; moves through such matches are @@ -486,7 +486,7 @@ impl<'a> Emitter<'a> { open_var = Some(name.clone()); } Pattern::Ctor { ctor, fields } => { - // Iter 15a: lookup falls back to imported modules when + // lookup falls back to imported modules when // a bare ctor name doesn't resolve locally. let cref = self.lookup_ctor_in_pattern(ctor)?; let bindings: Vec> = fields @@ -530,7 +530,7 @@ impl<'a> Emitter<'a> { for (i, (cref, arm, bindings)) in ctor_arms.iter().enumerate() { self.start_block(&arm_labels[i]); - // Iter 13b: derive substitution for parameterised ADTs from + // derive substitution for parameterised ADTs from // the scrutinee's concrete type-args. Map `cref.type_vars[i]` // → `s_ail.args[i]`. For monomorphic ADTs the mapping is // empty and substitution is a no-op. The substituted AILang @@ -559,7 +559,7 @@ impl<'a> Emitter<'a> { } }; // Load fields and bind as locals. - // Iter 15b: when the scrutinee's ADT lives in another module, + // when the scrutinee's ADT lives in another module, // `cref.ail_fields[idx]` carries the field type written in // the owner's local namespace. Qualify it before substituting // — symmetric to the term-ctor and pat-ctor fixes in @@ -571,13 +571,13 @@ impl<'a> Emitter<'a> { _ => None, }; let mut pushed = 0usize; - // Iter 18d.3: collect the binder names this arm pushes so + // collect the binder names this arm pushes so // we can drop their `moved_slots` entries when the arm // body closes (these binders are themselves freshly named // here — their move tracking starts empty and ends at arm // body close). // - // Iter 18d.4: the metadata is widened to (name, SSA, + // the metadata is widened to (name, SSA, // llvm-type, AILang-type) so that the arm-close drop // emission can route the call through the binder's // per-type drop symbol without re-walking `self.locals`. @@ -608,7 +608,7 @@ impl<'a> Emitter<'a> { self.body.push_str(&format!( " {v} = load {fty}, ptr {addr}, align 8\n" )); - // Iter 18d.3: a non-wildcard, pointer-typed slot + // a non-wildcard, pointer-typed slot // bound here is treated as moved out of the // scrutinee binder. The source slot is NOT // mutated; codegen records the move statically so @@ -632,9 +632,9 @@ impl<'a> Emitter<'a> { pushed += 1; } } - // Iter 18d.4 fix + 18g.1: scrutinee ownership signal, - // hoisted here so both the pre-tail-call shallow-dec - // (Iter 18g.1, below) and the arm-close pattern-binder + // Scrutinee ownership signal, hoisted here so both the + // pre-tail-call shallow-dec (below) and the arm-close + // pattern-binder // dec (Iter A, further below) consult the same gate. // // If the scrutinee resolves to a fn-param whose mode is @@ -657,7 +657,7 @@ impl<'a> Emitter<'a> { }, None => true, }; - // Iter 18g.1: pre-tail-call shallow-dec for moved-from + // pre-tail-call shallow-dec for moved-from // scrutinee outer cell. Iter A (arm-close pattern-binder // dec, below) cannot fire when `arm.body` is a tail call: // `lower_term` emits `musttail call ... ret` and sets @@ -739,7 +739,7 @@ impl<'a> Emitter<'a> { } } let (val, vty) = self.lower_term(&arm.body)?; - // Iter 18d.4: arm-close pattern-binder dec. Symmetric to + // arm-close pattern-binder dec. Symmetric to // 18c.3/18c.4's `Term::Let`-scope-close drop emission, but // fired at the lexical close of a match arm. For each // pattern-bound binder this arm pushed, emit a drop call @@ -763,11 +763,11 @@ impl<'a> Emitter<'a> { // through the tail slot. Now the arm itself dec's `t`, // freeing the 4-cell tail. // - // Iter 18d.4 fix (regression - // `alloc_rc_pattern_bind_in_implicit_fn_does_not_dec_borrowed_children`): - // Iter A is symmetric to Iter B (Own-param dec at fn - // return) and must share Iter B's mode gate. If the - // scrutinee resolves to a fn-param whose mode is + // Regression + // `alloc_rc_pattern_bind_in_implicit_fn_does_not_dec_borrowed_children`: + // arm-close pattern-binder dec is symmetric to Own-param + // dec at fn return and must share the same mode gate. If + // the scrutinee resolves to a fn-param whose mode is // `Borrow` or `Implicit`, the caller still holds a // reference to the heap value the pattern-binders were // loaded from. Dec'ing those binders fragments the @@ -823,7 +823,7 @@ impl<'a> Emitter<'a> { " call void @{drop_call}(ptr {b_ssa})\n" )); } else { - // Iter 18g.tidy.fu2: pattern-binder with + // pattern-binder with // statically-recorded moved slots routes // through the tag-conditional helper // `partial_drop__(b, mask)`. The @@ -852,13 +852,13 @@ impl<'a> Emitter<'a> { for _ in 0..pushed { self.locals.pop(); } - // Iter 18d.3: arm-bound binders go out of scope at arm + // arm-bound binders go out of scope at arm // body close. Drop their move-tracking entries (any moves // recorded against `h`/`t` belonged to this arm only). for (bname, _, _, _) in &arm_pushed_binders { self.moved_slots.remove(bname); } - // Iter 14e: if the arm body lowered to a `musttail call` + + // if the arm body lowered to a `musttail call` + // `ret`, the block is already terminated. Skip the // fall-through `br` and exclude this arm from the join phi. if !self.block_terminated { @@ -912,7 +912,7 @@ impl<'a> Emitter<'a> { } // join - // Iter 14e: if every arm tail-called and terminated its own + // if every arm tail-called and terminated its own // block, no predecessor branches into the join. Mark the whole // match as block-terminated and emit no join body — the // surrounding context (top-level fn body, seq rhs, etc.) checks diff --git a/crates/ailang-codegen/src/subst.rs b/crates/ailang-codegen/src/subst.rs index 8757158..57e0a14 100644 --- a/crates/ailang-codegen/src/subst.rs +++ b/crates/ailang-codegen/src/subst.rs @@ -15,7 +15,7 @@ use std::collections::{BTreeMap, BTreeSet}; use super::{CodegenError, Result}; -/// Iter 12b: derive a name → concrete-type substitution from the +/// derive a name → concrete-type substitution from the /// declared params of a `Forall` body and the actual arg types at a /// call site. Walks both sides in parallel; whenever a `Type::Var` /// (rigid name) appears on the params side, binds it to the @@ -45,7 +45,7 @@ pub(crate) fn derive_substitution( // through return-type unification, but at the call site we only // see args; if needed, callers can extend this with expected-ret // info. - // Iter 15a: a forall var that the args couldn't pin (e.g. + // a forall var that the args couldn't pin (e.g. // `is_none(Nothing) : forall a. (Maybe a) -> Bool` — `a` is // genuinely unobservable from the args alone) defaults to `Unit`. // The specialised body must not actually read an `a`-typed value, @@ -71,7 +71,7 @@ pub(crate) fn unify_for_subst( vars: &BTreeSet<&str>, subst: &mut BTreeMap, ) -> Result<()> { - // Iter 14a fix, extended in 15g-aux: a `$u`-prefixed var is a + // A `$u`-prefixed var is a // synth-only wildcard produced by `synth_arg_type` for nullary // ctors of a parameterised ADT (e.g. `Nil : List<$u>`). It // carries no real constraint — accept without binding so a @@ -104,7 +104,7 @@ pub(crate) fn unify_for_subst( match (param, arg) { (Type::Var { name }, _) if vars.contains(name.as_str()) => { if let Some(prev) = subst.get(name).cloned() { - // Iter 15b: the previously-bound type may be more + // the previously-bound type may be more // concrete than `arg` (e.g. `prev = List` from a // sibling binding, `arg = List<$u>` from a synth- // wildcard nullary ctor). Use recursive unification @@ -149,7 +149,7 @@ pub(crate) fn unify_for_subst( } } -/// Iter 15a: rewrites bare `Type::Con` references that resolve against +/// rewrites bare `Type::Con` references that resolve against /// `owner_local_types` into qualified `module.Type` form. Mirrors /// `ailang_check::qualify_local_types`. Used when the codegen pulls a /// polymorphic fn signature across the import boundary; without this @@ -204,7 +204,7 @@ pub(crate) fn qualify_local_types_codegen( } } -/// Iter 12b: substitute rigid type vars in `t` according to `subst`. +/// substitute rigid type vars in `t` according to `subst`. /// Used to specialise the type of a polymorphic def for a given /// instantiation. pub(crate) fn apply_subst_to_type(t: &Type, subst: &BTreeMap) -> Type { diff --git a/crates/ailang-codegen/src/synth.rs b/crates/ailang-codegen/src/synth.rs index 9185c16..33d326a 100644 --- a/crates/ailang-codegen/src/synth.rs +++ b/crates/ailang-codegen/src/synth.rs @@ -28,7 +28,7 @@ pub(crate) fn llvm_type(t: &Type) -> Result { // at the LLVM level. The actual signature travels via the // emitter's `ssa_fn_sigs` sidetable. Type::Fn { .. } => Ok("ptr".into()), - // Iter 13b: an unresolved rigid `Type::Var` reaching codegen is + // an unresolved rigid `Type::Var` reaching codegen is // a substitution bug. Earlier this silently lowered as `ptr` // (via the ADT fallback) and produced garbage IR; failing loudly // here surfaces the bug in the test suite. @@ -56,11 +56,11 @@ pub(crate) fn fn_sig_from_type(t: &Type) -> Option { None } -/// Iter 12b: AILang type of a builtin operator. Used by +/// AILang type of a builtin operator. Used by /// `synth_arg_type` for arg-type inference at polymorphic call sites. /// Mirrors what the typechecker installs in its env via `builtins`. pub(crate) fn builtin_ail_type(name: &str) -> Option { - // Iter 22-floats.3: same widening as `crates/ailang-check/src/ + // same widening as `crates/ailang-check/src/ // builtins.rs` — `+`/`-`/`*`/`/` and `!=`/`<`/`<=`/`>`/`>=` are // polymorphic. `%` stays monomorphic-Int. Codegen lowering for // these ops still goes through `builtin_binop` (Int-only) in @@ -104,7 +104,7 @@ pub(crate) fn builtin_ail_type(name: &str) -> Option { "+" | "-" | "*" | "/" => poly_a_a_to_a(), "%" => int_int_int(), "!=" | "<" | "<=" | ">" | ">=" => poly_a_a_to_bool(), - // Iter 16e: `==` is polymorphic — `forall a. (a, a) -> Bool`. + // `==` is polymorphic — `forall a. (a, a) -> Bool`. // The mono pipeline asks `synth_arg_type` for the actual arg // types at the call site; `lower_app` then dispatches to the // right LLVM instruction (icmp eq i64 / i1, @strcmp, or @@ -130,14 +130,14 @@ pub(crate) fn builtin_ail_type(name: &str) -> Option { param_modes: vec![], ret_mode: ParamMode::Implicit, }, - // Iter 16d: `__unreachable__` is the polymorphic bottom value + // `__unreachable__` is the polymorphic bottom value // (`forall a. a`). Mirrors the typechecker's `builtins::install`. "__unreachable__" => Type::Forall { vars: vec!["a".into()], constraints: vec![], body: Box::new(Type::Var { name: "a".into() }), }, - // Iter 22-floats.3: Float-conversion and inspection builtins. + // Float-conversion and inspection builtins. // Same lockstep with `crates/ailang-check/src/builtins.rs`. "neg" => Type::Forall { vars: vec!["a".into()], @@ -199,7 +199,7 @@ pub(crate) fn builtin_ail_type(name: &str) -> Option { param_modes: vec![], ret_mode: ParamMode::Implicit, }, - // Iter 22-floats.3: Float bit-pattern constants. Bare values, + // Float bit-pattern constants. Bare values, // not fns — referenced via `Term::Var`. Mirrors the // typechecker's `builtins::install`. Codegen emits LLVM hex- // float literals at the use site in iter 4. @@ -208,7 +208,7 @@ pub(crate) fn builtin_ail_type(name: &str) -> Option { }) } -/// Iter 12b: AILang return type of a built-in effect op. The op's +/// AILang return type of a built-in effect op. The op's /// param signature is irrelevant here since we only consume the ret. pub(crate) fn builtin_effect_op_ret(op: &str) -> Option { Some(match op { diff --git a/crates/ailang-codegen/tests/eq_primitives_pin.rs b/crates/ailang-codegen/tests/eq_primitives_pin.rs index 1bbf423..0ad01fa 100644 --- a/crates/ailang-codegen/tests/eq_primitives_pin.rs +++ b/crates/ailang-codegen/tests/eq_primitives_pin.rs @@ -30,7 +30,7 @@ fn lower_eq_primitives_smoke() -> String { lower_workspace(&ws).expect("lower") } -/// Iter 23.2: integration with the auto-loaded prelude. Compiling a +/// integration with the auto-loaded prelude. Compiling a /// workspace that calls `eq` on an Int pair must produce a /// mono-synthesised `@ail_prelude_eq__Int` whose body lowers to /// `icmp eq i64`. Full pipeline: end-of-typecheck → monomorphise → @@ -48,7 +48,7 @@ fn eq_int_call_produces_icmp_i64_mono_fn() { ); } -/// Iter 23.2: same shape as `eq_int_call_produces_icmp_i64_mono_fn` +/// same shape as `eq_int_call_produces_icmp_i64_mono_fn` /// for Bool. Asserts the mono-synthesised `@ail_prelude_eq__Bool` /// lowers to `icmp eq i1`. #[test] @@ -64,7 +64,7 @@ fn eq_bool_call_produces_icmp_i1_mono_fn() { ); } -/// Iter 23.2: Str's mono-symbol body is hand-rolled by the +/// Str's mono-symbol body is hand-rolled by the /// `try_emit_primitive_instance_body` intercept. Asserts the intercept /// fires for the prelude-loaded `eq__Str` synthesised against a real /// user fixture that calls `eq` on Str. diff --git a/crates/ailang-core/src/ast.rs b/crates/ailang-core/src/ast.rs index 9a1ad57..6e7a148 100644 --- a/crates/ailang-core/src/ast.rs +++ b/crates/ailang-core/src/ast.rs @@ -204,7 +204,7 @@ pub struct FnDef { /// Optional source-level documentation string. #[serde(default, skip_serializing_if = "Option::is_none")] pub doc: Option, - /// Iter embedding-abi-m1: when `Some(sym)`, this fn is the + /// when `Some(sym)`, this fn is the /// embedding boundary. Codegen's `Target::StaticLib` mode /// additionally emits an externally-visible C entrypoint /// `@` (signature frozen as of M3 — diff --git a/crates/ailang-core/src/desugar.rs b/crates/ailang-core/src/desugar.rs index 31e48c4..246035c 100644 --- a/crates/ailang-core/src/desugar.rs +++ b/crates/ailang-core/src/desugar.rs @@ -109,7 +109,7 @@ use crate::ast::*; use std::collections::{BTreeMap, BTreeSet}; -/// Iter 16b.2: per-name scope information used by the LetRec lifter. +/// per-name scope information used by the LetRec lifter. /// `KnownType(t)` is set for fn-params and Lam-params, where the /// type is statically declared. Other binder kinds use the /// placeholder variants and the LetRec lifter rejects captures of @@ -169,7 +169,7 @@ pub fn desugar_module(m: &Module) -> Module { Def::Type(td) => { used.insert(td.name.clone()); } - // Iter 22b.1: class/instance defs are passthrough for the + // class/instance defs are passthrough for the // desugar pass — their bodies (default methods, instance // method bodies) will be desugared once 22b.2 wires the // class/instance arms into typecheck. For 22b.1 the names @@ -183,7 +183,7 @@ pub fn desugar_module(m: &Module) -> Module { } } } - // Iter 16b.1: pre-collect every top-level def name. The LetRec + // pre-collect every top-level def name. The LetRec // lifter consults this set to (a) know whether a free var of a // LetRec body resolves to a top-level def (no capture) and // (b) keep its fresh-name generator from colliding with an @@ -203,8 +203,8 @@ pub fn desugar_module(m: &Module) -> Module { for def in &mut out.defs { match def { Def::Fn(f) => { - // Iter 16b.2 / 16b.6: build initial scope from fn-params - // with their declared types (peeled out of `f.ty`). + // Build initial scope from fn-params with their + // declared types (peeled out of `f.ty`). // // 16b.6: a `Type::Forall { vars, body: Fn(ptys, ...) }` // enclosing fn is now supported. Its param types may @@ -245,7 +245,7 @@ pub fn desugar_module(m: &Module) -> Module { } } } - // Iter 16b.6: stash the enclosing fn's Forall.vars (or + // stash the enclosing fn's Forall.vars (or // empty for a mono enclosing fn) for the LetRec arm. let saved = std::mem::take(&mut d.current_def_forall_vars); d.current_def_forall_vars = match &f.ty { @@ -260,14 +260,14 @@ pub fn desugar_module(m: &Module) -> Module { c.value = d.desugar_term(&c.value, &scope); } Def::Type(_) => {} - // Iter 22b.1: class/instance defs are not desugared yet. + // class/instance defs are not desugared yet. // Their bodies (default methods, instance method bodies) // will be processed once 22b.2 lands the class/instance // arms in typecheck and 22b.3 in codegen. Def::Class(_) | Def::Instance(_) => {} } } - // Iter 16b.1: append every lifted fn to the desugared module. + // append every lifted fn to the desugared module. // Order: original defs first, then lifts in the order they were // produced by the bottom-up walk. Typecheck/codegen are order- // insensitive at the def list level (they index by name), so this @@ -338,11 +338,11 @@ fn collect_used_in_term(t: &Term, used: &mut BTreeSet) { collect_used_in_term(in_term, used); } Term::Clone { value } => { - // Iter 18c.1: identity for the used-name walk. + // identity for the used-name walk. collect_used_in_term(value, used); } Term::ReuseAs { source, body } => { - // Iter 18d.1: structural recursion through both children. + // structural recursion through both children. collect_used_in_term(source, used); collect_used_in_term(body, used); } @@ -383,7 +383,7 @@ fn collect_used_in_pattern(p: &Pattern, used: &mut BTreeSet) { /// contains every source-level identifier (var-bind or var-reference) /// in the module so that [`fresh`](Self::fresh) cannot shadow one. /// -/// Iter 16b.1 fields: +/// LetRec-lift fields: /// - `lifted` accumulates synthetic top-level fns produced by /// [`Term::LetRec`] desugaring. Appended to `Module.defs` once the /// per-def walk finishes. @@ -393,7 +393,7 @@ fn collect_used_in_pattern(p: &Pattern, used: &mut BTreeSet) { /// generator [`fresh_lifted`](Self::fresh_lifted) consults it so /// later lifts cannot collide with earlier ones. /// -/// Iter 16b.6 field: +/// Forall-tracking field: /// - `current_def_forall_vars` carries the enclosing fn's /// `Type::Forall.vars` while desugaring its body. Empty for /// monomorphic enclosing fns. Read by the LetRec lifter to wrap @@ -406,7 +406,7 @@ struct Desugarer { used: BTreeSet, lifted: Vec, module_top_names: BTreeSet, - /// Iter 16b.6: the enclosing fn's Forall.vars (or empty if mono). + /// the enclosing fn's Forall.vars (or empty if mono). /// Reset on entry to each `Def::Fn`. current_def_forall_vars: Vec, } @@ -427,7 +427,7 @@ impl Desugarer { } } - /// Iter 16b.1: returns a name of the form `$lr_N` not in + /// returns a name of the form `$lr_N` not in /// `used` and not in `module_top_names`. Bumps both sets so a /// later lift cannot collide. The `hint` is the source-level /// LetRec name; it makes lifted bindings traceable through the @@ -541,12 +541,12 @@ impl Desugarer { rhs: Box::new(self.desugar_term(rhs, scope)), }, Term::Clone { value } => Term::Clone { - // Iter 18c.1: pure structural recursion through the + // pure structural recursion through the // wrapper. Same pattern as `Term::Let`'s value branch. value: Box::new(self.desugar_term(value, scope)), }, Term::ReuseAs { source, body } => Term::ReuseAs { - // Iter 18d.1: pure structural recursion through both + // pure structural recursion through both // children. Same pattern as `Term::Clone`. source: Box::new(self.desugar_term(source, scope)), body: Box::new(self.desugar_term(body, scope)), @@ -582,18 +582,18 @@ impl Desugarer { .collect(), }, Term::LetRec { name, ty, params, body, in_term } => { - // Iter 16b.1: lift to a synthetic top-level fn (no-capture). - // Iter 16b.2: extend the lift to the path-1 safe subset — + // lift to a synthetic top-level fn (no-capture). + // extend the lift to the path-1 safe subset — // captures of fn-params / Lam-params (whose types are // known statically). - // Iter 16b.3: when ANY capture is `LetBound` the desugar + // when ANY capture is `LetBound` the desugar // pass cannot resolve its type (the let-value's type is // only known after typecheck). For that case we LEAVE // the LetRec in place, with body and in_term recursively // desugared. A post-typecheck pass (`lift_letrecs` in // `ailang-check`) walks every surviving LetRec and lifts // it using the typechecker's resolved types. - // Iter 16b.4: extends the defer path to `MatchArm` + // extends the defer path to `MatchArm` // captures. The post-typecheck pass already walks // `Term::Match` arms and (since 16b.3) calls // `type_check_pattern_for_lift` to extend locals with @@ -672,7 +672,7 @@ impl Desugarer { } let in_has_value_use = find_non_callee_use(&desugared_in, name).is_some(); - // Iter 16b.6: reject name-as-value in `in_term` when the + // reject name-as-value in `in_term` when the // enclosing fn is polymorphic. The 16b.5 wrap synthesises // a `Term::Lam` whose AST has no `Forall` slot, so // wrapping a polymorphic lifted fn into a monomorphic Lam @@ -773,7 +773,7 @@ impl Desugarer { // Build augmented type: original Fn with capture types // appended to `params`. // - // Iter 16b.6: when the ENCLOSING fn is polymorphic + // when the ENCLOSING fn is polymorphic // (`current_def_forall_vars` non-empty), wrap the // augmented Fn in a `Type::Forall` mirroring the // enclosing fn's type vars. The capture types may @@ -933,7 +933,7 @@ impl Desugarer { } let s = self.fresh(); let s_var = Term::Var { name: s.clone() }; - // Iter 16d: `default` is unreachable for valid programs (the + // `default` is unreachable for valid programs (the // typechecker requires either a catch-all arm or exhaustive // ctor coverage). Use the polymorphic bottom builtin // `__unreachable__` (`forall a. a`) so the terminator unifies @@ -975,7 +975,7 @@ impl Desugarer { body: Box::new(arm.body.clone()), }, Pattern::Lit { lit } => { - // Iter 16c: a top-level lit arm desugars to `if (== s_var lit) + // a top-level lit arm desugars to `if (== s_var lit) // then arm.body else fall_k`. Eliminates `Pattern::Lit` from // the desugared output entirely; codegen never sees it. let cmp = build_eq(s_var.clone(), lit); @@ -1035,7 +1035,7 @@ impl Desugarer { body: Box::new(body), }, Pattern::Lit { lit } => { - // Iter 16c: a lit sub-pattern desugars to `if (== fv lit) body + // a lit sub-pattern desugars to `if (== fv lit) body // else fall_k`. Same shape as the top-level case, but on the // field-bound fresh variable rather than the original // scrutinee. @@ -1070,7 +1070,7 @@ impl Desugarer { /// by [`Desugarer::desugar_match`] to skip the let-binding and chain /// construction for already-flat matches. /// -/// Iter 16c: [`Pattern::Lit`] is **not** flat — it always desugars to a +/// [`Pattern::Lit`] is **not** flat — it always desugars to a /// [`Term::If`] via [`build_eq`], so it must take the chain path even /// when no other arm needs flattening. Otherwise the early-return in /// [`Desugarer::desugar_match`] would leak a `Pattern::Lit` arm to @@ -1078,7 +1078,7 @@ impl Desugarer { fn is_flat(p: &Pattern) -> bool { match p { Pattern::Wild | Pattern::Var { .. } => true, - // Iter 16c: lit patterns are not flat; they desugar to If. + // lit patterns are not flat; they desugar to If. Pattern::Lit { .. } => false, Pattern::Ctor { fields, .. } => fields .iter() @@ -1086,7 +1086,7 @@ fn is_flat(p: &Pattern) -> bool { } } -/// Iter 16c: Build an equality-test term for a lit pattern. The shape is +/// Build an equality-test term for a lit pattern. The shape is /// `(app == scrutinee lit_term)` for Int/Bool/Str. Unit literals are /// degenerate — every Unit value is equal — so an emitted `true` literal /// stands in. @@ -1114,7 +1114,7 @@ fn build_eq(scrutinee: Term, lit: &Literal) -> Term { } } -/// Iter 16b.1: collect free variable names of `t`, with `bound` being +/// collect free variable names of `t`, with `bound` being /// the set of names lexically bound at this point. A /// [`Term::Var`] `{ name }` is "free" iff `name` is not in `bound`. /// Compound binders ([`Term::Let`], [`Term::Lam`], [`Term::Match`] @@ -1123,7 +1123,7 @@ fn build_eq(scrutinee: Term, lit: &Literal) -> Term { /// Used by the [`Term::LetRec`] desugar to decide whether a local /// recursive let would capture any name from the enclosing scope. /// -/// Iter 16b.3: made `pub` so the post-typecheck `lift_letrecs` pass +/// made `pub` so the post-typecheck `lift_letrecs` pass /// in `ailang-check` can reuse it (same free-var computation; the /// post-typecheck pass needs it to recompute captures of LetRec /// nodes that desugar deferred). @@ -1195,11 +1195,11 @@ pub fn free_vars_in_term(t: &Term, bound: &BTreeSet, out: &mut BTreeSet< free_vars_in_term(in_term, &b_in, out); } Term::Clone { value } => { - // Iter 18c.1: `(clone X)` has the same free vars as `X`. + // `(clone X)` has the same free vars as `X`. free_vars_in_term(value, bound, out); } Term::ReuseAs { source, body } => { - // Iter 18d.1: free vars are the union of source and body. + // free vars are the union of source and body. free_vars_in_term(source, bound, out); free_vars_in_term(body, bound, out); } @@ -1222,11 +1222,11 @@ pub fn free_vars_in_term(t: &Term, bound: &BTreeSet, out: &mut BTreeSet< } } -/// Iter 16b.1: collect every name a pattern binds into `out`. Mirrors +/// collect every name a pattern binds into `out`. Mirrors /// `Pattern::pattern_bound_names` in `ailang-codegen`; duplicated here /// because `ailang-core` cannot depend on the codegen crate. /// -/// Iter 16b.3: made `pub` so callers (e.g. `ailang-check::lift_letrecs`) +/// made `pub` so callers (e.g. `ailang-check::lift_letrecs`) /// can reproduce the same shadowing semantics during their own /// scope-aware walks. pub fn pattern_binds(p: &Pattern, out: &mut BTreeSet) { @@ -1243,7 +1243,7 @@ pub fn pattern_binds(p: &Pattern, out: &mut BTreeSet) { } } -/// Iter 16b.1: rewrite every free [`Term::Var`] `{ name == from }` +/// rewrite every free [`Term::Var`] `{ name == from }` /// reference in `t` to [`Term::Var`] `{ name = to }`. Respects /// shadowing: if a binder rebinds `from`, occurrences inside that /// binder's scope stop being free and are left alone. @@ -1252,7 +1252,7 @@ pub fn pattern_binds(p: &Pattern, out: &mut BTreeSet) { /// fn — every recursive self-call site in the lifted body and every /// reference in the in-term needs to point at the new global name. /// -/// Iter 16b.3: made `pub` for the same reason as +/// made `pub` for the same reason as /// [`subst_call_with_extras`]. pub fn subst_var(t: &Term, from: &str, to: &str) -> Term { match t { @@ -1355,11 +1355,11 @@ pub fn subst_var(t: &Term, from: &str, to: &str) -> Term { } } Term::Clone { value } => Term::Clone { - // Iter 18c.1: structural recursion through the wrapper. + // structural recursion through the wrapper. value: Box::new(subst_var(value, from, to)), }, Term::ReuseAs { source, body } => Term::ReuseAs { - // Iter 18d.1: structural recursion through both children. + // structural recursion through both children. source: Box::new(subst_var(source, from, to)), body: Box::new(subst_var(body, from, to)), }, @@ -1402,7 +1402,7 @@ pub fn subst_var(t: &Term, from: &str, to: &str) -> Term { } } -/// Iter 16b.2: peel a `Type::Forall { body: Type::Fn { ... } }` to +/// peel a `Type::Forall { body: Type::Fn { ... } }` to /// expose the inner `Type::Fn`. Returns `Some(&Type::Fn)` if the /// type is `Fn` or `Forall`; `None` otherwise. fn peel_forall_to_fn(t: &Type) -> Option<&Type> { @@ -1413,13 +1413,13 @@ fn peel_forall_to_fn(t: &Type) -> Option<&Type> { } } -/// Iter 16b.2: walk `t` and rewrite every `Term::App { callee = +/// walk `t` and rewrite every `Term::App { callee = /// Var{name}, args }` to `Term::App { callee = Var{lifted}, args = /// args ++ extras_as_vars }`. Recurses into all sub-terms. Does /// not touch `Term::Var { name }` in non-callee positions — that's /// flagged separately by [`find_non_callee_use`]. /// -/// Iter 16b.3: made `pub` so the post-typecheck `lift_letrecs` pass +/// made `pub` so the post-typecheck `lift_letrecs` pass /// in `ailang-check` can reuse it (same call-site rewrite shape; the /// only difference is that the capture types come from the /// typechecker's env rather than being statically known). @@ -1512,11 +1512,11 @@ pub fn subst_call_with_extras(t: &Term, name: &str, lifted: &str, extras: &[Stri in_term: Box::new(subst_call_with_extras(in_term, name, lifted, extras)), }, Term::Clone { value } => Term::Clone { - // Iter 18c.1: structural recursion through the wrapper. + // structural recursion through the wrapper. value: Box::new(subst_call_with_extras(value, name, lifted, extras)), }, Term::ReuseAs { source, body } => Term::ReuseAs { - // Iter 18d.1: structural recursion through both children. + // structural recursion through both children. source: Box::new(subst_call_with_extras(source, name, lifted, extras)), body: Box::new(subst_call_with_extras(body, name, lifted, extras)), }, @@ -1540,13 +1540,13 @@ pub fn subst_call_with_extras(t: &Term, name: &str, lifted: &str, extras: &[Stri } } -/// Iter 16b.2: returns `Some(t)` if `t` contains a `Term::Var { +/// returns `Some(t)` if `t` contains a `Term::Var { /// name }` reference in a position that is **not** the callee of a /// `Term::App`. Returns `None` if every reference is in callee /// position. Used by the LetRec lifter to enforce the "direct-call /// only" restriction in 16b.2. /// -/// Iter 16b.3: made `pub` for the same reason as +/// made `pub` for the same reason as /// [`subst_call_with_extras`]. pub fn find_non_callee_use(t: &Term, name: &str) -> Option { match t { @@ -1585,9 +1585,9 @@ pub fn find_non_callee_use(t: &Term, name: &str) -> Option { } Term::LetRec { body, in_term, .. } => find_non_callee_use(body, name) .or_else(|| find_non_callee_use(in_term, name)), - // Iter 18c.1: clone is identity for the non-callee scan. + // clone is identity for the non-callee scan. Term::Clone { value } => find_non_callee_use(value, name), - // Iter 18d.1: scan both source and body. + // scan both source and body. Term::ReuseAs { source, body } => { find_non_callee_use(source, name).or_else(|| find_non_callee_use(body, name)) } @@ -1676,7 +1676,7 @@ mod tests { } } - /// Iter 16a: a `match` containing `(Cons a (Cons b _))` desugars to + /// a `match` containing `(Cons a (Cons b _))` desugars to /// a tree with no nested ctor sub-patterns. Property: the rewriter's /// output never has a `Pattern::Ctor` whose field is itself a /// `Pattern::Ctor`. @@ -1804,7 +1804,7 @@ mod tests { assert!(matches!(body, Term::Match { .. })); } - /// Iter 16b.1: a [`Term::LetRec`] whose body has no captures from + /// a [`Term::LetRec`] whose body has no captures from /// the enclosing scope is lifted to a synthetic top-level fn. The /// resulting module's `defs` grows by one and the original LetRec /// is gone from the term tree. @@ -1886,7 +1886,7 @@ mod tests { } } - /// Iter 16b.2: a LetRec whose body captures a fn-param of the + /// a LetRec whose body captures a fn-param of the /// enclosing scope (here, `outer` is a parameter of `main`) is /// **lifted** with the capture appended to the lifted fn's /// signature. The original 16b.1 panic ("would capture") is @@ -2009,7 +2009,7 @@ mod tests { } } - /// Iter 16b.3: a LetRec whose body captures a `Term::Let`-bound + /// a LetRec whose body captures a `Term::Let`-bound /// name is no longer rejected at desugar time. Instead the /// desugar pass leaves the LetRec in place (with body and /// in_term recursively desugared) so the post-typecheck pass in @@ -2088,7 +2088,7 @@ mod tests { ); } - /// Iter 16b.4: a LetRec whose body captures a name bound by a + /// a LetRec whose body captures a name bound by a /// `Pattern::Ctor` Var sub-pattern (a `ScopeEntry::MatchArm` /// binding) is no longer rejected at desugar time. The desugar /// pass leaves the LetRec in place (with body and in_term @@ -2205,7 +2205,7 @@ mod tests { ); } - /// Iter 16b.5: name-as-value INSIDE the LetRec's own body is + /// name-as-value INSIDE the LetRec's own body is /// still rejected (would require an eta-Lam that calls the /// pre-lift name — chicken-and-egg with the call-site rewrite). /// Here `(let g f ...)` binds the LetRec name `f` to a let-bound @@ -2264,7 +2264,7 @@ mod tests { let _ = desugar_module(&m); } - /// Iter 16b.5: name-as-value in the in-clause is now SUPPORTED. + /// name-as-value in the in-clause is now SUPPORTED. /// The desugar pass lifts the LetRec to a synthetic top-level fn /// AND wraps the rewritten in-term in `(let f (lam ...) ...)` /// whose lam eta-expands the lifted fn. After desugar, the @@ -2369,7 +2369,7 @@ mod tests { } } - /// Iter 16b.6: a LetRec inside a polymorphic enclosing fn gets its + /// a LetRec inside a polymorphic enclosing fn gets its /// fn-param captures lifted with a `Forall(vars, Fn(...))` augmented /// signature, mirroring the enclosing fn's type vars. Without 16b.6, /// the desugar pass would have classified the fn-params as @@ -2510,7 +2510,7 @@ mod tests { assert_eq!(lifted.params, vec!["k".to_string(), "x".to_string()]); } - /// Iter 16b.6: a LetRec inside a polymorphic enclosing fn whose + /// a LetRec inside a polymorphic enclosing fn whose /// `in_term` uses the LetRec name as a value (not as a callee) is /// rejected. The 16b.5 eta-Lam wrap can't quantify `Forall.vars` /// because `Term::Lam` has no Forall slot — wrapping a polymorphic @@ -2569,7 +2569,7 @@ mod tests { let _ = desugar_module(&m); } - /// Iter 16b.7: a nested LetRec whose inner LetRec captures the + /// a nested LetRec whose inner LetRec captures the /// OUTER LetRec's NAME (not its params or any other local) is /// rejected at desugar time with the closure-of-self message. /// Capturing the outer's PARAMS is supported (covered by the @@ -2651,7 +2651,7 @@ mod tests { let _ = desugar_module(&m); } - /// Iter 16b.7 (positive companion): a nested LetRec whose inner + /// a nested LetRec whose inner /// LetRec captures the OUTER LetRec's PARAM (not its name) lifts /// cleanly through the existing 16b.2 fast path. The outer's /// params live in the inner's outer-scope as `KnownType`, so the @@ -2757,7 +2757,7 @@ mod tests { } } - /// Iter 16c: walks a term, returns true iff any [`Pattern::Lit`] is + /// walks a term, returns true iff any [`Pattern::Lit`] is /// reachable in any [`Term::Match`] arm. After 16c desugaring, the /// invariant is `false` for every desugared output — `Pattern::Lit` /// is gone before typecheck/codegen runs. @@ -2808,7 +2808,7 @@ mod tests { } } - /// Iter 16c: a top-level lit arm desugars to a `Term::If` whose + /// a top-level lit arm desugars to a `Term::If` whose /// condition is `(== s_var lit)`. The desugared body must contain /// no `Pattern::Lit` anywhere. #[test] @@ -2876,7 +2876,7 @@ mod tests { ); } - /// Iter 16c: a `(pat-ctor Cons (pat-lit 0) _)` sub-pattern desugars + /// a `(pat-ctor Cons (pat-lit 0) _)` sub-pattern desugars /// to a tree with no `Pattern::Lit` anywhere — the lit is rewritten /// to a `Term::If` against the field-bound fresh variable. #[test] @@ -2944,7 +2944,7 @@ mod tests { ); } - /// Iter 16c regression guard: `is_flat` must classify + /// `is_flat` must classify /// `Pattern::Lit` as **not** flat, so the chain machinery in /// [`Desugarer::desugar_match`] is always invoked for lit-arms /// (rather than the early-return that would leak the lit pattern @@ -2960,7 +2960,7 @@ mod tests { ); } - /// Iter 16d: the chain machinery's deepest fall-through is the + /// the chain machinery's deepest fall-through is the /// polymorphic `__unreachable__` builtin (`forall a. a`), not a /// `Unit` literal. A match with two lit arms and a wildcard /// catches all paths via the wild-arm body, so the deepest diff --git a/crates/ailang-core/src/pretty.rs b/crates/ailang-core/src/pretty.rs index 49cef5a..22fc3bc 100644 --- a/crates/ailang-core/src/pretty.rs +++ b/crates/ailang-core/src/pretty.rs @@ -63,7 +63,7 @@ pub fn manifest(m: &Module) -> String { }; ("type", body) } - // Iter 22b.1: one-line summary `class C a` / `instance C T`. + // one-line summary `class C a` / `instance C T`. // Full pretty rendering of method signatures and bodies // lands in 22b.4 alongside the prose projection arms. Def::Class(c) => ("class", format!("{} {}", c.name, c.param)), diff --git a/crates/ailang-core/src/workspace.rs b/crates/ailang-core/src/workspace.rs index 26d80d7..41d139a 100644 --- a/crates/ailang-core/src/workspace.rs +++ b/crates/ailang-core/src/workspace.rs @@ -40,7 +40,7 @@ use std::path::{Path, PathBuf}; /// by `Module.name`. `root_dir` is the directory the entry file lives /// in; all imports are resolved relative to it. /// -/// Iter 22b.1 (Decision 11): `registry` is the workspace-global +/// `registry` is the workspace-global /// instance registry, built at the end of [`load_workspace`] after the /// DFS over imports completes. It is empty for any workspace whose /// modules contain no [`crate::ast::Def::Instance`] defs. @@ -55,11 +55,11 @@ pub struct Workspace { /// Directory the entry file lives in; all imports are resolved /// relative to it. pub root_dir: PathBuf, - /// Iter 22b.1: workspace-global typeclass instance registry. + /// workspace-global typeclass instance registry. pub registry: Registry, } -/// Iter 22b.1: workspace-global instance registry (Decision 11). +/// workspace-global instance registry (the typeclass design). /// /// Built at the end of [`load_workspace`] after all modules are /// loaded. Keyed by `(class-name, canonical-type-hash)`; values are @@ -71,7 +71,7 @@ pub struct Workspace { /// 22b.1 enforces three coherence checks during build: /// /// 1. **Coherence (orphan-freedom).** Every `instance C T` lives in -/// the module of `C` or in the module of `T` (per Decision 11 +/// the module of `C` or in the module of `T` (per the typeclass design /// axis 3). Otherwise → [`WorkspaceLoadError::OrphanInstance`]. /// 2. **Uniqueness.** No two entries share a key. Otherwise → /// [`WorkspaceLoadError::DuplicateInstance`]. @@ -82,7 +82,7 @@ pub struct Workspace { pub struct Registry { /// Map from `(class-name, type-hash)` to the registry entry. pub entries: BTreeMap<(String, String), RegistryEntry>, - /// ct.1.5a: workspace-wide map from user-defined type-name to its + /// workspace-wide map from user-defined type-name to its /// defining module. Used by [`Self::normalize_type_for_lookup`] to /// rewrite a bare `Type::Con.name` to its always-qualified form /// before computing the registry key. Primitives are not present. @@ -104,7 +104,7 @@ pub struct Registry { } impl Registry { - /// ct.1.5a + ctt.2: produce the canonical form of `t` for + /// the canonical-form normalisation step: produce the canonical form of `t` for /// registry-key hashing. Bare-non-primitive `Type::Con` names /// get qualified to `.`; already-qualified /// names stay; bare names whose defining module is unknown stay @@ -201,10 +201,11 @@ pub enum WorkspaceLoadError { )] ModuleHashMismatch { name: String }, - /// Iter 22b.1: an [`crate::ast::Def::Instance`] was declared in a + /// an [`crate::ast::Def::Instance`] was declared in a /// module that is neither the class's defining module nor the /// instance type's defining module. Coherence violation per - /// Decision 11 axis 3 ("orphan-freedom"). The lookup is hard: + /// the orphan-freedom axis of the typeclass design. The lookup + /// is hard: /// AILang does not provide a `--allow-orphans` flag. #[error( "orphan instance: `instance {class} {type_repr}` declared in module `{defining_module}`, \ @@ -218,10 +219,10 @@ pub enum WorkspaceLoadError { type_module: String, }, - /// Iter 22b.1: two [`crate::ast::Def::Instance`]s share the same + /// two [`crate::ast::Def::Instance`]s share the same /// `(class, canonical-type-hash)` key. Coherence requires /// uniqueness; the registry has no way to disambiguate at - /// resolution time. Per Decision 11 there is no + /// resolution time. Per the typeclass design there is no /// `AmbiguousInstance` diagnostic — coherence makes the lookup /// unambiguous by construction, and a duplicate is a workspace /// configuration error, not a per-call-site one. @@ -235,11 +236,11 @@ pub enum WorkspaceLoadError { second_module: String, }, - /// Iter 22b.1: an [`crate::ast::Def::Instance`] does not specify + /// an [`crate::ast::Def::Instance`] does not specify /// a body for a required (non-default) method of its class. /// Default-bearing methods may be inherited; non-default /// (abstract-required) methods must be specified by every - /// instance. Per Decision 11 §"Defaults and superclasses". + /// instance. Per the typeclass design §"Defaults and superclasses". #[error( "instance `{class} {type_repr}` is missing a body for method `{method}` (no default)" )] @@ -249,8 +250,8 @@ pub enum WorkspaceLoadError { method: String, }, - /// Iter 22b.2: class-schema validation. A class's `superclass.type` - /// does not equal its own `param`. Decision 11 single-superclass + /// class-schema validation. A class's `superclass.type` + /// does not equal its own `param`. the typeclass design's single-superclass /// model requires the superclass to be applied to the same param /// (e.g. `class Ord a extends Eq a`, not `extends Eq b`). #[error( @@ -264,7 +265,7 @@ pub enum WorkspaceLoadError { got_type: String, }, - /// Iter 22b.2: class-schema validation. A class method's + /// class-schema validation. A class method's /// signature contains a constraint referencing a type variable /// that is neither bound by the method's `Forall.vars` nor equal /// to the class's `param`. @@ -278,7 +279,7 @@ pub enum WorkspaceLoadError { var: String, }, - /// Iter 22b.2: an instance specifies a body for a method name + /// an instance specifies a body for a method name /// that the corresponding class does not declare. Symmetric to /// `MissingMethod` but in the opposite direction. #[error( @@ -290,9 +291,9 @@ pub enum WorkspaceLoadError { method: String, }, - /// Iter 22b.2: an instance `C T` was declared, but `C`'s + /// an instance `C T` was declared, but `C`'s /// superclass `S` does not have an instance for the same type - /// `T`. Decision 11 single-superclass model requires `instance S + /// `T`. the typeclass design's single-superclass model requires `instance S /// T` to exist whenever `instance C T` exists. #[error( "instance `{class} {type_repr}` requires superclass instance `{superclass} {type_repr}`, but none was found" @@ -303,14 +304,14 @@ pub enum WorkspaceLoadError { type_repr: String, }, - /// Iter 23.1: a user module attempted to use the reserved + /// a user module attempted to use the reserved /// module name `prelude`. The prelude is auto-injected by /// the loader, so a user module of the same name would /// collide silently. Reject explicitly. #[error("module name {name:?} is reserved (auto-injected by the loader)")] ReservedModuleName { name: String }, - /// ct.1 (canonical-type-names): a `Type::Con` whose `name` is + /// the canonical-form rule for type references: a `Type::Con` whose `name` is /// neither a primitive nor a local TypeDef of the owning module /// was encountered. Under the canonical-form rule, bare = /// local; a bare cross-module ref is a schema violation. @@ -327,7 +328,7 @@ pub enum WorkspaceLoadError { candidates: Vec, }, - /// ct.1: a qualified `Type::Con` of the form `.` + /// a qualified `Type::Con` of the form `.` /// was encountered, but `` is not a known module in the /// workspace, or `` is known but declares no TypeDef /// named ``. @@ -340,11 +341,11 @@ pub enum WorkspaceLoadError { name: String, }, - /// mq.1 (canonical-class-names): a class-reference field + /// the canonical-form rule for class references: a class-reference field /// (`InstanceDef.class`, `Constraint.class`, or /// `SuperclassRef.class`) carries a bare name that does not resolve /// to a local class of the owning module. Under the canonical-form - /// rule extended in mq.1, bare = local-class-of-owning-module; a + /// rule extended for class references, bare = local-class-of-owning-module; a /// bare cross-module class reference is a schema violation. /// `candidates` lists the qualified forms found by scanning the /// owning module's imports for matching class declarations. @@ -359,7 +360,7 @@ pub enum WorkspaceLoadError { candidates: Vec, }, - /// mq.1 (canonical-class-names): a qualified class reference of + /// the canonical-form rule for class references: a qualified class reference of /// the form `.` was encountered, but `` is /// not a known module in the workspace, or `` is known /// but declares no class by that name. Sibling of @@ -373,14 +374,14 @@ pub enum WorkspaceLoadError { name: String, }, - /// ct.1: a class-reference field (`InstanceDef.class`, + /// a class-reference field (`InstanceDef.class`, /// `SuperclassRef.class`, `Constraint.class`, or /// `ClassDef.name`) contains a `.` — under this milestone class /// names are NOT module-qualified (see DESIGN spec §"Out of /// scope: Class names"). The schema rejects qualified forms so /// half-migrated files cannot silently load. /// - /// mq.1: narrowed to `ClassDef.name` only; the three other + /// narrowed to `ClassDef.name` only; the three other /// fields now follow the canonical-form rule and use /// `BareCrossModuleClassRef` / `BadCrossModuleClassRef`. #[error( @@ -497,10 +498,10 @@ pub fn build_workspace( // `ailang_surface::PRELUDE_AIL` + `ailang_surface::parse_prelude`); core // exposes the two-phase composition. -/// mq.1: take a class-ref field value (`InstanceDef.class`, +/// take a class-ref field value (`InstanceDef.class`, /// `SuperclassRef.class`, `Constraint.class`) and a defining context, /// and produce the qualified workspace key. Bare ⇒ prepend the -/// `caller_module` argument; qualified ⇒ as-is. Symmetric to ct.1's +/// `caller_module` argument; qualified ⇒ as-is. Symmetric to the canonical-form rule's /// `normalize_type_for_registry` for `Type::Con`. /// /// For an `InstanceDef.class` or `Constraint.class` field the caller @@ -514,7 +515,7 @@ fn qualify_class_ref(class_ref: &str, caller_module: &str) -> String { } } -/// Iter 22b.1: build the workspace-global typeclass instance registry. +/// build the workspace-global typeclass instance registry. /// /// Two passes: /// @@ -534,7 +535,7 @@ fn build_registry( // Pass 1: collect "where is X defined" maps, plus a class lookup // by name (needed for the method-completeness check). // - // mq.1: `class_def_module` and `class_by_name` are keyed by the + // `class_def_module` and `class_by_name` are keyed by the // qualified class name `.`. All consumers // (Pass-2 coherence lookup, method-completeness check, superclass // walk, etc.) query with `qualify_class_ref` applied to the field @@ -562,7 +563,7 @@ fn build_registry( } } - // mq.3: the `MethodNameCollision` pre-pass (variant + Origin enum + + // the `MethodNameCollision` pre-pass (variant + Origin enum + // per-def loop) was retired here. Bare-method resolution no longer // requires workspace-wide method-name uniqueness — synth's // `Term::Var` arm consults `Env.method_to_candidate_classes` @@ -588,7 +589,7 @@ fn build_registry( // user-defined module — instances on primitives // therefore must live in the class's module. // - // mq.1: lookup keys are qualified + // lookup keys are qualified // (`.`); the `inst.class` // field is the canonical-form value (bare or qualified) // which `qualify_class_ref` lifts to the workspace key. @@ -597,7 +598,7 @@ fn build_registry( .get(&inst_class_key) .cloned() .unwrap_or_else(|| "".into()); - // mq.1: type-leg lookup must accept the canonical form + // type-leg lookup must accept the canonical form // for the head name. Bare head ⇒ key by // (caller_module, head); qualified `.` ⇒ // key by (owner, bare) directly (the owner IS the @@ -625,14 +626,14 @@ fn build_registry( } // Uniqueness: a `(class, type-hash)` key must appear - // at most once across the whole workspace. ct.1.5a: the + // at most once across the whole workspace. The // type expression is normalised to its always-qualified // form before hashing so a bare-local declaration in // the type's defining module and a qualified-cross-module // declaration from elsewhere produce the same key (both // refer to the same type under the canonical-form rule). // - // mq.1: registry key is keyed by the qualified class + // registry key is keyed by the qualified class // form too, so a bare same-module instance and a // qualified cross-module instance on the same // (class, type) collide on this check. @@ -677,7 +678,7 @@ fn build_registry( // Symmetric to MissingMethod: an instance must // not specify a body for a method name the class - // never declared. Decision 11 forbids ad-hoc + // never declared. The typeclass design forbids ad-hoc // additions to a class's method set at the // instance site. let declared: BTreeSet<&str> = @@ -704,11 +705,11 @@ fn build_registry( } } - // Iter 22b.2: superclass-instance completeness. For every entry, + // superclass-instance completeness. For every entry, // walk the class's superclass chain and require an entry for each // step at the same type-hash. // - // mq.1: `class_name` (the entries key first half) is the qualified + // `class_name` (the entries key first half) is the qualified // class name; the superclass-ref field `sc.class` is canonical-form, // which `qualify_class_ref` lifts to the qualified key — using the // parent class's defining module as the caller context (the @@ -716,7 +717,7 @@ fn build_registry( for (key, entry) in entries.iter() { let (class_name, type_hash) = key; let type_repr = type_head_name(&entry.instance.type_); - // Iter 22b.2 leaves superclass-cycle detection to a future arm; + // Superclass-cycle detection is left to a future arm; // here we just terminate the walk. let mut visited: BTreeSet<&str> = BTreeSet::new(); let mut current = class_by_name.get(class_name.as_str()).copied(); @@ -738,7 +739,7 @@ fn build_registry( type_repr: type_repr.clone(), }); } - // mq.1: walk lookup uses the qualified key; the next + // walk lookup uses the qualified key; the next // step's owning-module context is the superclass's // defining module (read from `class_def_module`). current = class_by_name.get(sc_class_key.as_str()).copied(); @@ -758,7 +759,7 @@ fn build_registry( }) } -/// Iter 22b.2: class-schema validation. Runs before `build_registry`. +/// class-schema validation. Runs before `build_registry`. /// Two diagnostics fire from here: `invalid-superclass-param`, /// `constraint-references-unbound-type-var`. The `kind-mismatch` /// diagnostic was retired at ctt.3 — the malformed shape now @@ -805,7 +806,7 @@ fn validate_classdefs( Ok(()) } -/// ct.1: the five primitive type names that are always bare under +/// the five primitive type names that are always bare under /// the canonical-form rule. Kept in sync with `Type::int`, /// `Type::bool_`, `Type::str_`, `Type::unit`, `Type::float` in /// `crate::ast`. @@ -813,7 +814,7 @@ fn is_primitive_type_name(name: &str) -> bool { matches!(name, "Int" | "Bool" | "Str" | "Unit" | "Float") } -/// ct.1.5a: normalize a `Type` by qualifying every bare-non-primitive +/// normalize a `Type` by qualifying every bare-non-primitive /// `Type::Con.name` to its always-qualified form /// (`.`). Primitives stay bare; already-qualified /// names stay as-is; bare names whose defining module is unknown stay @@ -891,7 +892,7 @@ fn normalize_type_for_registry( } } -/// ct.1: enforce the canonical-form rule on every `Type::Con` and +/// enforce the canonical-form rule on every `Type::Con` and /// `Term::Ctor.type_name` reference in every loaded module. Runs /// after prelude injection and before class-schema validation, so a /// stale bare cross-module ref fires the canonical-form diagnostic @@ -922,7 +923,7 @@ pub(crate) fn validate_canonical_type_names( local_types.insert(mod_name.clone(), s); } - // mq.1: symmetric pre-pass over `Def::Class` names. The map is + // symmetric pre-pass over `Def::Class` names. The map is // module → bare-class-name set; used by `check_class_ref` to apply // the canonical-form rule to the three migrated class-ref fields // (`InstanceDef.class`, `Constraint.class`, `SuperclassRef.class`). @@ -961,7 +962,7 @@ pub(crate) fn validate_canonical_type_names( })?; check_class_name_fields(def, mod_name)?; - // mq.1: apply the canonical-form rule to the three + // apply the canonical-form rule to the three // migrated class-ref fields. match def { Def::Instance(id) => { @@ -994,7 +995,7 @@ pub(crate) fn validate_canonical_type_names( Ok(()) } -/// mq.1: apply the canonical-form rule to a class-reference field +/// apply the canonical-form rule to a class-reference field /// value (`InstanceDef.class`, `Constraint.class`, or /// `SuperclassRef.class`). Sibling of `check_type_con_name` for /// class refs. @@ -1071,7 +1072,7 @@ fn check_class_ref( }) } -/// ct.1: apply the canonical-form rule to one `Type::Con.name` +/// apply the canonical-form rule to one `Type::Con.name` /// (also reused for `Term::Ctor.type_name` in Task 2). fn check_type_con_name( name: &str, @@ -1140,7 +1141,7 @@ fn check_type_con_name( }) } -/// ct.1: walk every `Type` reachable from a single `Def`, calling +/// walk every `Type` reachable from a single `Def`, calling /// `f` on each. Recurses into `Type::Fn.params/ret`, `Type::Con.args`, /// `Type::Forall.constraints/body`, plus the obvious top-level fields /// of each `Def` variant AND every Type annotation embedded in a Term @@ -1188,7 +1189,7 @@ where } } -/// ct.1: walk a `Term` and call `f` on every Type annotation embedded +/// walk a `Term` and call `f` on every Type annotation embedded /// in it (Lam.param_tys, Lam.ret_ty, LetRec.ty). Recurses through /// every Term sub-position. Does NOT fire on `Term::Ctor.type_name` /// (that's a `String`, not a `Type`; handled by `walk_def_terms` in @@ -1263,7 +1264,7 @@ where } } -/// ct.1: recursive walk of a `Type`, calling `f` at every node. +/// recursive walk of a `Type`, calling `f` at every node. fn walk_type(t: &Type, f: &mut F) -> Result<(), WorkspaceLoadError> where F: FnMut(&Type) -> Result<(), WorkspaceLoadError>, @@ -1292,7 +1293,7 @@ where } } -/// ct.1: walk every `Term::Ctor.type_name` reachable from a single +/// walk every `Term::Ctor.type_name` reachable from a single /// `Def`, calling `f` on the type_name strings. Used to enforce the /// canonical-form rule on term-side type references. fn walk_def_terms(def: &Def, f: &mut F) -> Result<(), WorkspaceLoadError> @@ -1320,7 +1321,7 @@ where } } -/// ct.1: recursive walk of a `Term`, calling `f` on every +/// recursive walk of a `Term`, calling `f` on every /// `Term::Ctor.type_name`. Embedded `Type` annotations (Lam param / /// return types, LetRec types) ride the `walk_def_types` / /// `walk_term_embedded_types` path — but `Term::Ctor.type_name` is @@ -1397,7 +1398,7 @@ where } } -/// ct.1: Pattern::Ctor carries a `ctor` name (matches against a +/// Pattern::Ctor carries a `ctor` name (matches against a /// scrutinee's TypeDef) but NOT a type_name field — the type is /// inferred from the scrutinee. So Pattern walking only recurses; /// no canonical-form check fires here. `f` is kept in the signature @@ -1419,11 +1420,11 @@ where } } -/// ct.1: reject any `.` in `ClassDef.name`. The defining-site name is +/// reject any `.` in `ClassDef.name`. The defining-site name is /// bare by convention (symmetric to `TypeDef.name`); a qualified form /// indicates a malformed file. /// -/// mq.1: narrowed from the four class-reference fields to +/// narrowed from the four class-reference fields to /// `ClassDef.name` only. The three referencing fields /// (`InstanceDef.class`, `Constraint.class`, `SuperclassRef.class`) /// now follow the canonical-form rule and are validated by @@ -1445,14 +1446,15 @@ fn check_class_name_fields( Ok(()) } -/// Iter 22b.1: extract the head-constructor name of a type, for the +/// extract the head-constructor name of a type, for the /// "where is this type defined" lookup and for diagnostic-message /// rendering. /// /// For `Type::Con { name, .. }` (the only legal head shape for a /// non-orphan instance) returns `name`. Other variants (`Var`, -/// `Forall`, `Fn`) are not legal as instance heads — Decision 11 -/// requires a concrete type expression at the instance head — so we +/// `Forall`, `Fn`) are not legal as instance heads — the typeclass +/// design requires a concrete type expression at the instance head — +/// so we /// emit a stable fallback string. The fallback prevents diagnostic /// rendering from panicking on a malformed fixture; the "real" /// rejection of non-concrete instance heads will arrive as a @@ -1640,7 +1642,7 @@ mod tests { // ailang-surface dev-dep cycle disallows `surface::load_workspace` // calls from this in-mod test crate). - // Iter 22b.1: a workspace whose own modules contain no + // a workspace whose own modules contain no // `Def::Instance` defs produces no non-prelude registry entries. // This is the happy-path baseline for the registry-build pass — // every pre-22b workspace falls into this case. With the @@ -1660,7 +1662,7 @@ mod tests { .join("examples") } - // Iter 22b.1: a coherent instance (in the class's module) + // a coherent instance (in the class's module) // loads cleanly and produces one registry entry from the // fixture itself. With the auto-loaded prelude (iter 23.2) // the registry also contains the prelude's own entries; the @@ -1668,7 +1670,7 @@ mod tests { // `iter22b1_instance_in_class_module_loads_clean` relocated to // `crates/ailang-core/tests/workspace_pin.rs` in iter form-a.1 Task 5. - // Iter 22b.1: an instance declared in a module that is neither + // an instance declared in a module that is neither // the class's module nor the type's module fires `OrphanInstance`. // The fixture imports `test_22b1_orphan_third_classmod` (which // owns `class TShow`) and the entry module declares `instance @@ -1678,17 +1680,17 @@ mod tests { // `iter22b1_orphan_instance_fires_diagnostic` relocated to // `crates/ailang-core/tests/workspace_pin.rs` in iter form-a.1 Task 5. - // Iter 22b.1 / mq.1: two instances of the same `(class, type)` - // pair collide on the registry's uniqueness check. + // Two instances of the same `(class, type)` pair collide on the + // registry's uniqueness check. // - // Pre-mq.1 the test used a two-module fixture + // Pre-canonical-class-form the test used a two-module fixture // (`test_22b1_dup_a` declared the class + first instance, // `test_22b1_dup_b` declared the type + second instance) which - // relied on bare cross-module class refs. The mq.1 + // relied on bare cross-module class refs. The canonical-form rule // canonical-form rule makes that shape structurally // unrepresentable (any second instance in a module that owns // neither the class nor the type fires `OrphanInstance` first). - // Post-mq.1 the only way to land two instances on the same + // Post-canonical-class-form the only way to land two instances on the same // canonical key is to have them in the same module (the class's // or the type's module). The fixture now declares both // instances in `test_22b1_dup_same_module` — both class-leg @@ -1697,7 +1699,7 @@ mod tests { // `iter22b1_duplicate_instance_fires_diagnostic` relocated to // `crates/ailang-core/tests/workspace_pin.rs` in iter form-a.1 Task 5. - // Iter 22b.1: an instance that omits a required (non-default) + // an instance that omits a required (non-default) // method of its class fires `MissingMethod`. The fixture's // `class TEq` declares `teq` and `tne` as both non-default; the // instance only specifies `tne`, leaving `teq` missing. @@ -1713,13 +1715,13 @@ mod tests { // pd.2 (post-shim retirement; in-mod tests cannot reach // `ailang_surface::load_workspace` due to the dev-dep cycle). - // Iter 22b.2: an instance that specifies a body for a method + // an instance that specifies a body for a method // name the class never declared must fire // `OverridingNonExistentMethod`. Symmetric counterpart to // `MissingMethod`: the latter fires when the class declares a // non-default method that the instance omits; this one fires // when the instance provides a body the class did not ask for. - // Decision 11 forbids ad-hoc additions to a class's method set + // The typeclass design forbids ad-hoc additions to a class's method set // at the instance site. // (Class is `TEq` rather than `Eq` to avoid colliding with the // auto-loaded prelude's `class Eq`.) @@ -1730,20 +1732,20 @@ mod tests { // relocated to `crates/ailang-core/tests/workspace_pin.rs` in iter // pd.2. - // mq.3: the two `..._method_name_collision_fires` pin tests + // the two `..._method_name_collision_fires` pin tests // (class-class and class-fn variants) were retired here and // re-located as positive-load tests in // `crates/ailang-check/tests/method_collision_pin.rs`. Post- - // mq.3 those on-disk fixtures load cleanly and the equivalent + // Now those on-disk fixtures load cleanly and the equivalent // observations move to `env.method_to_candidate_classes` // (multi-entry set) plus the call-site warning. - // Iter 22b.2: an instance `C T` whose class `C` declares a + // an instance `C T` whose class `C` declares a // superclass `S` requires that `instance S T` also exist in the // workspace. The fixture declares `class TEq a`, `class TOrd a // extends TEq a`, and `instance TOrd Int` — but no `instance TEq // Int` — so registry build must fire - // `MissingSuperclassInstance`. Decision 11 single-superclass + // `MissingSuperclassInstance`. the typeclass design's single-superclass // model requires `instance S T` whenever `instance C T` exists. // (Classes are `TEq` / `TOrd` rather than `Eq` / `Ord` to avoid // colliding with the auto-loaded prelude's `class Eq`.) @@ -1812,7 +1814,7 @@ mod tests { })).unwrap() } - /// ct.1: a `Type::Con` whose `name` is a primitive (`Int` / `Bool` / + /// a `Type::Con` whose `name` is a primitive (`Int` / `Bool` / /// `Str` / `Unit` / `Float`) must be accepted bare. The five /// primitive names are the only legal bare-non-local Type::Con names /// under the canonical-form rule. @@ -1824,7 +1826,7 @@ mod tests { validate_canonical_type_names(&modules, &["prelude"]).expect("Float must be accepted"); } - /// ct.1: a bare Type::Con whose `name` matches a local TypeDef in + /// a bare Type::Con whose `name` matches a local TypeDef in /// the same module must be accepted (the canonical-form rule: bare = /// local). #[test] @@ -1834,7 +1836,7 @@ mod tests { .expect("local Foo must be accepted"); } - /// ct.1: a bare Type::Con whose `name` is neither a primitive nor a + /// a bare Type::Con whose `name` is neither a primitive nor a /// local TypeDef must fire `BareCrossModuleTypeRef`. With no imports, /// the candidates list is empty. #[test] @@ -1853,7 +1855,7 @@ mod tests { } } - /// ct.1: a bare Type::Con whose `name` resolves to one imported + /// a bare Type::Con whose `name` resolves to one imported /// module's TypeDef must fire `BareCrossModuleTypeRef` with that /// qualified form in `candidates` (so the diagnostic can suggest the /// fix). @@ -1873,7 +1875,7 @@ mod tests { } } - /// ct.1: a qualified Type::Con `.` where `` is a + /// a qualified Type::Con `.` where `` is a /// known module AND `` is one of its TypeDefs must be accepted. #[test] fn ct1_validator_accepts_qualified_xmod_ref() { @@ -1884,7 +1886,7 @@ mod tests { .expect("other.Ordering must be accepted"); } - /// ct.1: a qualified Type::Con `.` where `` is + /// a qualified Type::Con `.` where `` is /// NOT a known module must fire `BadCrossModuleTypeRef`. Symmetric /// case: `` known but no TypeDef `` in it. #[test] @@ -1901,7 +1903,7 @@ mod tests { } } - /// ct.1: a Type::Con embedded inside a `Term::Lam.param_tys` is a + /// a Type::Con embedded inside a `Term::Lam.param_tys` is a /// Type-position occurrence, just inside a Term tree. The validator /// must walk into Lam-internal types so an LLM author can't smuggle /// a bare cross-module ref past it by hiding it in a lambda @@ -1939,7 +1941,7 @@ mod tests { } } - /// ct.1: a `Term::Ctor` whose `type_name` is a bare cross-module ref + /// a `Term::Ctor` whose `type_name` is a bare cross-module ref /// must fire `BareCrossModuleTypeRef`. Symmetric to the Type::Con /// rule but the field lives on Term, not Type. #[test] @@ -1980,7 +1982,7 @@ mod tests { } } - /// ct.1: a qualified `ClassDef.name` (e.g. `other.Eq`) must fire + /// a qualified `ClassDef.name` (e.g. `other.Eq`) must fire /// `QualifiedClassName`. Class names stay bare in this milestone. #[test] fn ct1_validator_rejects_qualified_classdef_name() { @@ -2009,9 +2011,9 @@ mod tests { } } - /// mq.1: a qualified `InstanceDef.class` referencing a class - /// declared in another known module is the canonical form post-mq.1 - /// and is accepted by the validator. Inverted from the pre-mq.1 + /// a qualified `InstanceDef.class` referencing a class + /// declared in another known module is the canonical form post-canonical-class-form + /// and is accepted by the validator. Inverted from the pre-canonical-class-form /// `ct1_validator_rejects_qualified_instancedef_class` test: /// `InstanceDef.class` moved bare→canonical. #[test] @@ -2042,11 +2044,11 @@ mod tests { modules.insert("other".to_string(), other); modules.insert("m".to_string(), m); validate_canonical_type_names(&modules, &["prelude"]) - .expect("qualified InstanceDef.class is the canonical form post-mq.1"); + .expect("qualified InstanceDef.class is the canonical form post-canonical-class-form"); } - /// mq.1: a qualified `SuperclassRef.class` referencing a class in - /// another known module is the canonical form post-mq.1 and is + /// a qualified `SuperclassRef.class` referencing a class in + /// another known module is the canonical form post-canonical-class-form and is /// accepted. Inverted from /// `ct1_validator_rejects_qualified_superclassref_class`. #[test] @@ -2078,12 +2080,12 @@ mod tests { modules.insert("other".to_string(), other); modules.insert("m".to_string(), m); validate_canonical_type_names(&modules, &["prelude"]) - .expect("qualified SuperclassRef.class is the canonical form post-mq.1"); + .expect("qualified SuperclassRef.class is the canonical form post-canonical-class-form"); } - /// mq.1: a qualified `Constraint.class` (inside a `Type::Forall`) + /// a qualified `Constraint.class` (inside a `Type::Forall`) /// referencing a class in another known module is the canonical - /// form post-mq.1 and is accepted. Inverted from + /// form post-canonical-class-form and is accepted. Inverted from /// `ct1_validator_rejects_qualified_constraint_class`. #[test] fn ct1_validator_accepts_qualified_constraint_class() { @@ -2124,17 +2126,17 @@ mod tests { modules.insert("other".to_string(), other); modules.insert("m".to_string(), m); validate_canonical_type_names(&modules, &["prelude"]) - .expect("qualified Constraint.class is the canonical form post-mq.1"); + .expect("qualified Constraint.class is the canonical form post-canonical-class-form"); } - /// ct.1.5a: registry-side duplicate detection survives the + /// registry-side duplicate detection survives the /// asymmetric canonical-form representation. Two coherent instances /// on the same type-def, one declared bare-local (in the type's /// defining module) and one declared qualified-cross-module (in the /// class's defining module), must collide on the registry's /// `(class, type-hash)` key. /// - /// This is the regression that broke during the ct.1.5 migration + /// This is the regression that broke during the canonical-form migration /// dry-run: after migrating `test_22b1_dup_a.ail.json` to qualified /// `test_22b1_dup_b.MyInt`, the unmigrated `test_22b1_dup_b.ail.json` /// (which keeps bare `MyInt` because the type IS local there) @@ -2185,7 +2187,7 @@ mod tests { // Module `other`: declares `type MyInt` and the qualified- // cross-module instance `instance cls.TShow MyInt`. Imports // `cls` for the class ref. The `class` field carries the - // canonical form (qualified for cross-module per mq.1); the + // canonical form (qualified for cross-module per the canonical-class-form rule); the // `type` field stays bare-local. let other: Module = serde_json::from_value(serde_json::json!({ "schema": crate::SCHEMA, @@ -2231,7 +2233,7 @@ mod tests { } } - /// ct.1.5a follow-up: `normalize_type_for_registry` must recurse into + /// canonical-form normalisation follow-up: `normalize_type_for_registry` must recurse into /// `Type::Forall.constraints[].type_` just like it does into /// `Type::Forall.body`. A bare `Type::Con` nested inside a Forall /// constraint that lives in a different module must be rewritten to @@ -2291,14 +2293,14 @@ mod tests { } } - /// ct.1: a qualified `Constraint.class` nested inside a + /// a qualified `Constraint.class` nested inside a /// `ClassDef.methods[].ty.Forall.constraints` must fire /// `QualifiedClassName`. Distinct from the `Def::Fn` site: the /// `Def::Class` branch's per-method Forall walk needs independent /// coverage. /// - /// mq.1: inverted — qualified `Constraint.class` inside a - /// `ClassDef.methods` Forall is the canonical form post-mq.1 and is + /// inverted — qualified `Constraint.class` inside a + /// `ClassDef.methods` Forall is the canonical form post-canonical-class-form and is /// accepted by the validator. #[test] fn ct1_validator_accepts_qualified_constraint_class_in_classdef_method() { @@ -2343,37 +2345,37 @@ mod tests { modules.insert("other".to_string(), other); modules.insert("m".to_string(), m); validate_canonical_type_names(&modules, &["prelude"]) - .expect("qualified Constraint.class in ClassDef-method Forall is the canonical form post-mq.1"); + .expect("qualified Constraint.class in ClassDef-method Forall is the canonical form post-canonical-class-form"); } - /// ct.1: on-disk fixture for `BareCrossModuleTypeRef`. Bare + /// on-disk fixture for `BareCrossModuleTypeRef`. Bare /// `Ordering` Term::Ctor with no imports; the validator catches it /// after prelude injection (so the candidate list contains /// `prelude.Ordering`). // `ct1_fixture_bare_xmod_rejected` relocated to // `crates/ailang-core/tests/workspace_pin.rs` in iter pd.2. - /// ct.1: on-disk fixture for `BadCrossModuleTypeRef`. Qualified + /// on-disk fixture for `BadCrossModuleTypeRef`. Qualified /// `Mystery.Type` with `Mystery` not a known module. // `ct1_fixture_bad_qualifier` relocated to // `crates/ailang-core/tests/workspace_pin.rs` in iter pd.2. - /// mq.1: on-disk fixture for the post-mq.1 rejection path. The + /// on-disk fixture for the post-canonical-class-form rejection path. The /// fixture declares `instance prelude.Eq Int` outside the prelude /// and outside Int's defining module — under the canonical-form /// rule the qualified `prelude.Eq` ref is now schema-valid (post- - /// mq.1), and the downstream coherence check rejects the instance + /// class references in canonical form), and the downstream coherence check rejects the instance /// with `OrphanInstance` instead. /// - /// Pre-mq.1 this test asserted `QualifiedClassName` on the same + /// Pre-canonical-class-form this test asserted `QualifiedClassName` on the same /// fixture; the rename + reshape is the on-disk-fixture half of /// the four in-test inversions further up. // `ct1_fixture_qualified_class_orphan_post_mq1` relocated to // `crates/ailang-core/tests/workspace_pin.rs` in iter pd.2. - // mq.1: on-disk fixture pin — a workspace where the consumer's + // on-disk fixture pin — a workspace where the consumer's // `Constraint.class` references a class in an imported module via - // the qualified form loads cleanly. Symmetric to ct.1's positive + // the qualified form loads cleanly. Symmetric to the canonical-form rule's positive // cross-module type-ref fixture (`ct1_validator_accepts_qualified_xmod_ref` // in-test sibling). Guards the full load → validator → registry // path on a real on-disk pair. @@ -2427,7 +2429,7 @@ mod tests { format!(r#"{{"schema":"ailang/v0","name":"{name}","imports":[],"defs":[]}}"#) } - /// mq.1.1: `BareCrossModuleClassRef` is the sibling diagnostic for + /// `BareCrossModuleClassRef` is the sibling diagnostic for /// bare cross-module class references on `InstanceDef.class`, /// `Constraint.class`, and `SuperclassRef.class`. Verifies the /// variant is constructible and its Display surface names the @@ -2445,7 +2447,7 @@ mod tests { assert!(rendered.contains("prelude.Show"), "Display must list candidates, got: {rendered}"); } - /// mq.1.1b: `BadCrossModuleClassRef` is the sibling diagnostic for + /// `BadCrossModuleClassRef` is the sibling diagnostic for /// qualified class references whose owner module is unknown or /// declares no class by that name. Verifies the variant is /// constructible and its Display surface names the offending @@ -2462,7 +2464,7 @@ mod tests { "Display must mention class or module context, got: {rendered}"); } - /// mq.1.3: `InstanceDef.class` carrying a bare name that does NOT + /// `InstanceDef.class` carrying a bare name that does NOT /// resolve to a local class of the owning module must fire /// `BareCrossModuleClassRef`. The fixture imports a sibling module /// that declares the class under the bare name, so the candidate @@ -2505,7 +2507,7 @@ mod tests { } } - /// mq.1.3: `Constraint.class` on a `Def::Fn` carrying a bare name + /// `Constraint.class` on a `Def::Fn` carrying a bare name /// that does not resolve to a local class fires /// `BareCrossModuleClassRef`. #[test] @@ -2558,7 +2560,7 @@ mod tests { } } - /// mq.1.3: `SuperclassRef.class` on a `Def::Class` carrying a bare + /// `SuperclassRef.class` on a `Def::Class` carrying a bare /// name that does not resolve to a local class fires /// `BareCrossModuleClassRef`. #[test] @@ -2599,9 +2601,9 @@ mod tests { } } - /// mq.1.3: a qualified `InstanceDef.class` referencing a class in - /// an imported module is the canonical form post-mq.1 and is - /// accepted by `validate_canonical_type_names`. Symmetric to ct.1's + /// a qualified `InstanceDef.class` referencing a class in + /// an imported module is the canonical form post-canonical-class-form and is + /// accepted by `validate_canonical_type_names`. Symmetric to the canonical-form rule's /// "qualified Type::Con resolves" positive path. #[test] fn mq1_qualified_instancedef_class_accepted() { @@ -2665,7 +2667,7 @@ mod tests { fn module_with_bare_classref(name: &str, class_ref: &str) -> Module { // A module containing a single Def::Instance whose `class` field is // a bare class reference. The instance's `type` field is a local - // ADT defined in the same module to keep ct.1 happy. + // ADT defined in the same module to keep the canonical-form rule satisfied. serde_json::from_value(serde_json::json!({ "schema": crate::SCHEMA, "name": name, diff --git a/crates/ailang-core/tests/carve_out_inventory.rs b/crates/ailang-core/tests/carve_out_inventory.rs index 2e40118..1940dc3 100644 --- a/crates/ailang-core/tests/carve_out_inventory.rs +++ b/crates/ailang-core/tests/carve_out_inventory.rs @@ -31,7 +31,7 @@ const EXPECTED: &[&str] = &[ "test_ct1_bad_qualifier.ail.json", "test_ct1_bare_xmod_rejected.ail.json", "test_ct1_qualified_class_rejected.ail.json", - // Iter loop-recur.tidy — lambda-capture-of-loop-binder rejection + // Lambda-capture-of-loop-binder rejection "test_loop_binder_captured_by_lambda.ail.json", // loop-recur iter 2 — negative typecheck fixtures "test_recur_arity_mismatch.ail.json", diff --git a/crates/ailang-core/tests/ctt2_registry_rekey.rs b/crates/ailang-core/tests/ctt2_registry_rekey.rs index b5945c2..befc584 100644 --- a/crates/ailang-core/tests/ctt2_registry_rekey.rs +++ b/crates/ailang-core/tests/ctt2_registry_rekey.rs @@ -41,7 +41,7 @@ fn two_modules_with_same_bare_foo_both_register() { args: vec![], }); - // mq.1: registry-entries key is keyed by the qualified class name. + // registry-entries key is keyed by the qualified class name. let main_key = ("ctt2_collision_cls.MyC".to_string(), main_foo_hash); let lib_key = ("ctt2_collision_cls.MyC".to_string(), lib_foo_hash); diff --git a/crates/ailang-core/tests/docs_honesty_pin.rs b/crates/ailang-core/tests/docs_honesty_pin.rs index 454a731..e6ec9d5 100644 --- a/crates/ailang-core/tests/docs_honesty_pin.rs +++ b/crates/ailang-core/tests/docs_honesty_pin.rs @@ -81,9 +81,9 @@ fn design_md_has_no_doc_archaeology() { "design/: 'previously all diagnostics were Error' is project history → docs/journals/"); assert!(!d.contains("What did change in 2026-05-13 (this milestone) is"), "design/: 'what did change in (this milestone)' is post-mortem narrative"); - assert!(!d.contains("primitives were retired 2026-05-14 in iter rpe.1"), + assert!(!d.contains("primitives were retired 2026-05-14 in the per-type-print-op retirement"), "design/: 'X were retired in iter Y' is history → docs/journals/"); - assert!(!d.contains("were retired in iter rpe.1 (2026-05-14)"), + assert!(!d.contains("were retired in the per-type-print-op retirement (2026-05-14)"), "design/: the second per-type-print-op retirement-narrative sentence is history"); assert!(!d.contains("originally listed here as the eighth fixture, was retired"), "design/: 'originally listed … was retired by milestone X' is doc-archaeology"); diff --git a/crates/ailang-core/tests/effect_doc_honesty_pin.rs b/crates/ailang-core/tests/effect_doc_honesty_pin.rs index 17d251c..c92cdac 100644 --- a/crates/ailang-core/tests/effect_doc_honesty_pin.rs +++ b/crates/ailang-core/tests/effect_doc_honesty_pin.rs @@ -25,7 +25,7 @@ fn norm(s: &str) -> String { #[test] fn design_md_effect_prose_is_true() { - // Decision 3 effect prose -> design/models/effects.md; the + // Effect prose -> design/models/effects.md; the // "the built-in IO and Diverge ops" absent-pin's home // `## What is not (yet) supported` -> design/contracts/scope-boundaries.md. let d = norm(&[read("design/models/effects.md"), diff --git a/crates/ailang-core/tests/hash_pin.rs b/crates/ailang-core/tests/hash_pin.rs index 9e066d7..7aa5237 100644 --- a/crates/ailang-core/tests/hash_pin.rs +++ b/crates/ailang-core/tests/hash_pin.rs @@ -68,7 +68,7 @@ fn hash_changes_with_content() { assert_ne!(h1, h2); } -/// Iter 13a regression: adding `vars` to TypeDef and `args` to +/// adding `vars` to TypeDef and `args` to /// `Type::Con` must NOT change canonical-JSON hashes of any pre-13a /// definition. Recorded hashes were captured from on-disk modules /// before the schema extension; if this fires, a @@ -110,7 +110,7 @@ fn loop_recur_schema_extension_preserves_pre_loop_recur_hashes() { assert_eq!(def_hash(int_list_def), "b082192bd0c99202"); } -/// Iter 19b regression: adding `suppress` to FnDef must NOT change +/// adding `suppress` to FnDef must NOT change /// canonical-JSON hashes of any pre-19b fn whose `suppress` is empty. /// The `skip_serializing_if = "Vec::is_empty"` predicate on the field /// is what enforces this; if it is wrong, every existing fixture's @@ -146,7 +146,7 @@ fn iter19b_empty_suppress_preserves_pre_19b_hashes() { ); } -/// Iter 19b: an on-disk pre-19b fixture must still load cleanly +/// an on-disk pre-19b fixture must still load cleanly /// (no `suppress` field present in the JSON) and produce its /// canonical-byte hash unchanged. #[test] @@ -159,7 +159,7 @@ fn iter19b_schema_extension_preserves_pre_19b_hashes() { assert_eq!(def_hash(sum_def), "db33f57cb329935e"); } -/// Iter 22b.1 regression: adding `Def::Class` and `Def::Instance` must +/// adding `Def::Class` and `Def::Instance` must /// NOT change canonical-JSON hashes of any pre-22b def. The new variants /// are alternatives, not field additions — pre-22b fixtures simply do /// not produce a `Class` / `Instance` `Def`, and the existing arms are @@ -183,7 +183,7 @@ fn iter22b1_schema_extension_preserves_pre_22b_hashes() { assert_eq!(def_hash(int_list_def), "b082192bd0c99202"); } -/// Iter 22b.1: a ClassDef with no doc, no superclass, and an empty +/// a ClassDef with no doc, no superclass, and an empty /// methods list serialises to a stable canonical form. Two construction /// paths (default-elided optionals vs. JSON without those keys at all) /// hash bit-identically. @@ -207,7 +207,7 @@ fn iter22b1_classdef_empty_optionals_hash_stable() { ); } -/// Iter 22b.2: adding `constraints` to `Type::Forall` must NOT alter +/// adding `constraints` to `Type::Forall` must NOT alter /// canonical-JSON bytes of any pre-22b.2 polymorphic type. #[test] fn forall_without_constraints_hashes_bit_identical_to_pre_22b2() { @@ -228,9 +228,9 @@ fn forall_without_constraints_hashes_bit_identical_to_pre_22b2() { ); } -/// Iter ct.4 (canonical-type-names milestone close): pin the +/// pin the /// canonical-form hashes of the two cross-module fixtures migrated by -/// `ail migrate-canonical-types` in ct.1. These hashes are the +/// `ail migrate-canonical-types` in the canonical-form migration. These hashes are the /// post-migration state. #[test] fn ct4_migrated_fixtures_have_canonical_form_hashes() { @@ -239,13 +239,14 @@ fn ct4_migrated_fixtures_have_canonical_form_hashes() { let ord_mod = ailang_surface::load_module(&examples.join("ordering_match.ail")) .expect("examples/ordering_match.ail loads"); let main_def = ord_mod.defs.iter().find(|d| d.name() == "main").unwrap(); - // Iter rpe.1 updated the hash: `ordering_match.ail`'s body migrated - // from `(do io/print_int x)` to `(app print x)` as part of the - // corpus migration retiring the per-type print effect-ops. + // The hash reflects a corpus migration: `ordering_match.ail`'s + // body moved from `(do io/print_int x)` to `(app print x)` when + // the per-type print effect-ops were retired in favour of the + // polymorphic `print` helper. assert_eq!( def_hash(main_def), "b65a7f834703ffb4", - "ordering_match::main canonical hash must match captured post-rpe.1 value" + "ordering_match::main canonical hash must match captured post-per-type-print-retirement value" ); let dup_a_mod = ailang_surface::load_module(&examples.join("test_22b1_dup_a.ail")) @@ -267,7 +268,7 @@ fn ct4_migrated_fixtures_have_canonical_form_hashes() { ); } -/// Iter ct.4: re-assert that the canonical-form tightening did NOT +/// re-assert that the canonical-form tightening did NOT /// change hashes of intra-module-only fixtures. #[test] fn ct4_unmigrated_fixtures_remain_bit_identical() { diff --git a/crates/ailang-core/tests/spec_drift.rs b/crates/ailang-core/tests/spec_drift.rs index a771d6a..792763a 100644 --- a/crates/ailang-core/tests/spec_drift.rs +++ b/crates/ailang-core/tests/spec_drift.rs @@ -289,7 +289,7 @@ fn spec_mentions_every_def_kind() { Def::Fn(_) => "fn", Def::Const(_) => "const", Def::Type(_) => "type", - // Iter 22b.1: class/instance Def variants exist but are + // class/instance Def variants exist but are // not yet anchored in the prose-spec block. Once 22b.4 // adds prose projection for them, the FORM_A_SPEC text // gains `(class ` / `(instance ` anchors and this match @@ -305,7 +305,7 @@ fn spec_mentions_every_def_kind() { } } -/// The mode keywords (Decision 10) must appear so an LLM knows the +/// The mode keywords (the RC memory model) must appear so an LLM knows the /// Form-A wrapper syntax. #[test] fn spec_mentions_mode_keywords() { diff --git a/crates/ailang-core/tests/workspace_pin.rs b/crates/ailang-core/tests/workspace_pin.rs index bb65935..5d569a0 100644 --- a/crates/ailang-core/tests/workspace_pin.rs +++ b/crates/ailang-core/tests/workspace_pin.rs @@ -49,14 +49,14 @@ fn loads_example_workspace_happy_path() { assert_eq!(ws.entry, "ws_main"); assert!(ws.modules.contains_key("ws_main")); assert!(ws.modules.contains_key("ws_lib")); - // Iter 23.1: the loader auto-injects the `prelude` module, + // the loader auto-injects the `prelude` module, // so the count is the user's two modules plus prelude. assert_eq!(ws.modules.len(), 3); } #[test] fn loads_workspace_auto_injects_prelude() { - // Iter 23.1: the prelude module is implicitly part of every + // the prelude module is implicitly part of every // workspace, regardless of whether the user's modules import // it. Loading any well-formed workspace must result in // `ws.modules["prelude"]` being present with the Ordering @@ -78,7 +78,7 @@ fn loads_workspace_auto_injects_prelude() { "prelude must contain Ordering type def" ); - // Iter 23.2: prelude also ships the `Eq` class plus three + // prelude also ships the `Eq` class plus three // primitive instances (Eq Int, Eq Bool, Eq Str). assert!( prelude.defs.iter().any(|d| matches!( @@ -114,7 +114,7 @@ fn loads_workspace_auto_injects_prelude() { "prelude must contain `instance Eq Str`; saw Eq instances on: {eq_instance_types:?}" ); - // Iter 23.3: prelude also ships the `Ord` class plus three + // prelude also ships the `Ord` class plus three // primitive instances (Ord Int, Ord Bool, Ord Str). assert!( prelude.defs.iter().any(|d| matches!( @@ -179,7 +179,7 @@ fn iter22b1_instance_in_class_module_loads_clean() { .collect(); assert_eq!(fixture_entries.len(), 1); let (key, entry) = fixture_entries[0]; - // mq.1: registry key is keyed by the qualified class name. + // registry key is keyed by the qualified class name. // 24.2: class renamed `Show` → `TShow` workspace-wide. assert_eq!(&key.0, "test_22b1_orphan_class.TShow"); assert_eq!(entry.defining_module, "test_22b1_orphan_class"); @@ -188,7 +188,7 @@ fn iter22b1_instance_in_class_module_loads_clean() { assert_eq!(entry.instance.class, "TShow"); } -/// Iter 22b.1: an instance declared in a module that is neither +/// an instance declared in a module that is neither /// the class's module nor the type's module fires `OrphanInstance`. #[test] fn iter22b1_orphan_instance_fires_diagnostic() { @@ -209,8 +209,8 @@ fn iter22b1_orphan_instance_fires_diagnostic() { } } -/// Iter 22b.1 / mq.1: two instances of the same `(class, type)` pair -/// collide on the registry's uniqueness check. Post-mq.1 both +/// two instances of the same `(class, type)` pair +/// collide on the registry's uniqueness check. Post-canonical-class-form both /// instances must live in the class's or the type's module (see /// fixture). #[test] @@ -233,7 +233,7 @@ fn iter22b1_duplicate_instance_fires_diagnostic() { } } -/// Iter 22b.1: an instance that omits a required (non-default) method +/// an instance that omits a required (non-default) method /// of its class fires `MissingMethod`. #[test] fn iter22b1_missing_method_fires_diagnostic() { @@ -253,7 +253,7 @@ fn iter22b1_missing_method_fires_diagnostic() { } } -/// Iter 22b.2: an instance that specifies a body for a method name the +/// an instance that specifies a body for a method name the /// class never declared must fire `OverridingNonExistentMethod`. /// (`test_22b2_overriding_nonexistent` is NOT a §C4 carve-out — it /// stays as a `.ail`-loadable fixture.) @@ -274,7 +274,7 @@ fn instance_overriding_nonexistent_method_fires() { } } -/// Iter 22b.2: an instance `C T` whose class `C` declares a superclass +/// an instance `C T` whose class `C` declares a superclass /// `S` requires that `instance S T` also exist in the workspace. #[test] fn instance_without_superclass_instance_fires() { @@ -293,7 +293,7 @@ fn instance_without_superclass_instance_fires() { } } -/// mq.1: positive on-disk pair where `Constraint.class` references a +/// positive on-disk pair where `Constraint.class` references a /// class in an imported module via the qualified form loads cleanly. #[test] fn mq1_xmod_constraint_class_fixture_loads() { @@ -332,7 +332,7 @@ fn write_simple_module_json(dir: &Path, name: &str, imports: &[&str]) -> PathBuf path } -/// Iter 23.1 / pd.2 relocation: the loader auto-injects a module named +/// the loader auto-injects a module named /// "prelude", so a user module that also claims that name would collide /// silently. The collision is caught explicitly with `ReservedModuleName`. #[test] @@ -485,7 +485,7 @@ fn ct1_fixture_bad_qualifier() { /// pd.2 relocation: §C4 (a) carve-out fixture /// `test_ct1_qualified_class_rejected.ail.json` — declares /// `instance prelude.Eq Int` outside the prelude AND outside Int's -/// defining module; post-mq.1 the qualified class-ref is schema-valid +/// defining module; post-canonical-class-form the qualified class-ref is schema-valid /// and the downstream coherence check rejects with `OrphanInstance`. #[test] fn ct1_fixture_qualified_class_orphan_post_mq1() { diff --git a/crates/ailang-prose/src/lib.rs b/crates/ailang-prose/src/lib.rs index c4b14e6..6e23eee 100644 --- a/crates/ailang-prose/src/lib.rs +++ b/crates/ailang-prose/src/lib.rs @@ -89,7 +89,7 @@ fn write_def(out: &mut String, def: &Def, level: usize, owning_module: &str) { } } -/// Iter 19b: render the [`FnDef::suppress`] list as one +/// render the [`FnDef::suppress`] list as one /// `// @suppress : ` line per entry, indented to /// `level`. Multiple entries stack in declaration order; an empty /// list produces no output (and therefore no leading blank line). @@ -233,7 +233,7 @@ fn write_ctor(out: &mut String, c: &Ctor, owning_module: &str) { } fn write_fn_def(out: &mut String, fd: &FnDef, level: usize, owning_module: &str) { - // Iter 19b: render `suppress` entries as `// @suppress : ` + // render `suppress` entries as `// @suppress : ` // comment lines **above** the doc string. They are contract metadata // that the LLM-reader needs to see to understand why an annotation // that looks over-strict is correct here. One line per entry, in @@ -1783,7 +1783,7 @@ mod tests { assert!(out.contains("() -> Unit with IO {"), "got:\n{out}"); } - /// Iter 19b: a FnDef with a single `Suppress` entry renders the + /// a FnDef with a single `Suppress` entry renders the /// `// @suppress : ` line **above** the doc string, /// preserving both the suppression visibility and the doc content. /// The suppress line must appear before the `///`-prefixed doc @@ -1829,7 +1829,7 @@ mod tests { ); } - /// Iter 19b: multiple `Suppress` entries render as one + /// multiple `Suppress` entries render as one /// `// @suppress : ` line each, in declaration /// order, all above the doc string. #[test] @@ -1860,7 +1860,7 @@ mod tests { // Order: declaration order is preserved. } - /// Iter 19b: a FnDef with an empty `suppress` Vec emits no + /// a FnDef with an empty `suppress` Vec emits no /// `// @suppress` line at all (and therefore no extra leading /// blank line). Pre-19b prose snapshots stay byte-identical when /// re-rendered through the 19b code. @@ -1928,7 +1928,7 @@ mod tests { } // ====================================================================== - // Iter 20b: formatting polish + // formatting polish // ====================================================================== /// Convenience: two-arg App of a named callee. Used pervasively in diff --git a/crates/ailang-prose/tests/snapshot.rs b/crates/ailang-prose/tests/snapshot.rs index 2342caa..f451972 100644 --- a/crates/ailang-prose/tests/snapshot.rs +++ b/crates/ailang-prose/tests/snapshot.rs @@ -63,7 +63,7 @@ fn snapshot_rc_app_let_partial_drop_leak() { check_snapshot("rc_app_let_partial_drop_leak"); } -/// Iter 20b: this fixture exercises the infix renderer + paren elision. +/// this fixture exercises the infix renderer + paren elision. /// Pre-20b, the body had `if ==(n, 0) { ... }` and `tail sum_acc(-(n, /// 1), ICons(-(n, 1), acc))`; after 20b those collapse to `if n == 0` /// and `n - 1`. If this regresses, the renderer is broken. diff --git a/crates/ailang-surface/src/parse.rs b/crates/ailang-surface/src/parse.rs index 301cf79..3384eea 100644 --- a/crates/ailang-surface/src/parse.rs +++ b/crates/ailang-surface/src/parse.rs @@ -385,7 +385,7 @@ impl<'a> Parser<'a> { ctors.push(self.parse_ctor()?); } Some("drop-iterative") => { - // Iter 18e: `(drop-iterative)` opt-in annotation. + // `(drop-iterative)` opt-in annotation. // Takes no arguments — it is a flag. A second // `(drop-iterative)` clause is a parse error // (rejected here so that the JSON schema's @@ -510,7 +510,7 @@ impl<'a> Parser<'a> { let mut ty: Option = None; let mut params: Option> = None; let mut body: Option = None; - // Iter 19b: every `(suppress ...)` clause appends one entry. Order + // every `(suppress ...)` clause appends one entry. Order // is preserved (matches the on-disk JSON-AST order). let mut suppress: Vec = Vec::new(); loop { @@ -586,7 +586,7 @@ impl<'a> Parser<'a> { }) } - /// Iter 19b: parse one `(suppress (code "") (because ""))` + /// parse one `(suppress (code "") (because ""))` /// clause, returning a [`Suppress`] entry. Unknown sub-keywords /// inside the clause are rejected with [`ParseError::Production`]. /// The `because` text is allowed to be empty here (the typechecker @@ -639,7 +639,7 @@ impl<'a> Parser<'a> { Ok(Suppress { code, because }) } - /// Iter 19b: helper — consume one string-literal token. Used by + /// helper — consume one string-literal token. Used by /// [`Self::parse_suppress_attr`]. fn expect_string(&mut self, ctx: &'static str) -> Result { match self.peek().cloned() { @@ -1013,7 +1013,7 @@ impl<'a> Parser<'a> { "con" => self.parse_type_con(), "fn-type" => self.parse_fn_type(), "forall" => self.parse_forall_type(), - // Iter 18a: `borrow` / `own` are valid only as + // `borrow` / `own` are valid only as // wrappers around `fn-type` params or `ret`. At // top-level type position they are a parse error // with a clear message. @@ -1109,7 +1109,7 @@ impl<'a> Parser<'a> { }) } - /// Iter 18a: parse one fn-type slot — a type, optionally wrapped + /// parse one fn-type slot — a type, optionally wrapped /// in `(borrow T)` or `(own T)`. The mode is `Implicit` for a /// bare type, `Borrow` for `(borrow T)`, `Own` for `(own T)`. fn parse_param_with_mode(&mut self) -> Result<(Type, ParamMode), ParseError> { @@ -1304,7 +1304,7 @@ impl<'a> Parser<'a> { self.parse_app_body(false, "app-term") } - /// Iter 14e: `(tail-app callee arg+)` — same shape as `app` but + /// `(tail-app callee arg+)` — same shape as `app` but /// constructs `Term::App { tail: true, .. }`. The typechecker's /// tail-position pass verifies that the call really is in tail /// position; an unmarked call in tail position is also legal. @@ -1398,7 +1398,7 @@ impl<'a> Parser<'a> { self.parse_do_body(false, "do-term") } - /// Iter 14e: `(tail-do op arg*)` — same shape as `do` but + /// `(tail-do op arg*)` — same shape as `do` but /// constructs `Term::Do { tail: true, .. }`. fn parse_tail_do(&mut self) -> Result { self.expect_lparen("tail-do-term")?; @@ -1501,7 +1501,7 @@ impl<'a> Parser<'a> { }) } - /// Iter 16b.1: `(let-rec NAME (params PARAM*) (type T) (body TERM) + /// `(let-rec NAME (params PARAM*) (type T) (body TERM) /// (in TERM))` — local recursive fn-shaped binding. Eliminated by /// the desugar pass (lifted to a synthetic top-level fn) before /// typecheck. The body's recursive references to `NAME` are @@ -1529,7 +1529,7 @@ impl<'a> Parser<'a> { }) } - /// Iter 18c.1: `(clone TERM)` — explicit RC clone wrapper. In 18c.1 + /// `(clone TERM)` — explicit RC clone wrapper. In 18c.1 /// the wrapper is identity for typechecker and codegen; in 18c.3 /// the codegen will emit `call void @ailang_rc_inc` before yielding /// the inner value's SSA reg under `--alloc=rc`. @@ -1560,7 +1560,7 @@ impl<'a> Parser<'a> { }) } - /// Iter 18d.1: `(reuse-as SOURCE BODY)` — explicit reuse-as wrapper. + /// `(reuse-as SOURCE BODY)` — explicit reuse-as wrapper. /// In 18d.1 the wrapper is identity for codegen (lowers `body` and /// drops `source`); 18d.2 will lower this as in-place rewrite under /// `--alloc=rc`. The parser accepts any term in either slot — the @@ -1834,7 +1834,7 @@ mod tests { assert!(matches!(m.defs.len(), 1)); } - /// Iter 18a: `(borrow T)` and `(own T)` wrappers in fn-type + /// `(borrow T)` and `(own T)` wrappers in fn-type /// param/ret slots round-trip into [`ParamMode::Borrow`] / /// [`ParamMode::Own`] on `Type::Fn`. A bare type stays /// [`ParamMode::Implicit`] (and its mode is elided from the @@ -1870,7 +1870,7 @@ mod tests { } } - /// Iter 18a: `(borrow T)` outside an `fn-type` param/ret slot is + /// `(borrow T)` outside an `fn-type` param/ret slot is /// a parse error. Specifically, a `const` whose declared type is /// `(borrow ...)` must be rejected with a clear message — modes /// are *not* a top-level type production. @@ -1892,7 +1892,7 @@ mod tests { ); } - /// Iter 18c.1: `(clone X)` parses to `Term::Clone { value: Var "x" }`. + /// `(clone X)` parses to `Term::Clone { value: Var "x" }`. #[test] fn parses_clone_wraps_inner_term() { let m = parse( @@ -1918,7 +1918,7 @@ mod tests { } } - /// Iter 18c.1: `(clone)` with no inner term is rejected with a + /// `(clone)` with no inner term is rejected with a /// clear message. #[test] fn rejects_clone_without_argument() { @@ -1939,7 +1939,7 @@ mod tests { ); } - /// Iter 18d.1: `(reuse-as xs (term-ctor List Cons (...)))` parses to + /// `(reuse-as xs (term-ctor List Cons (...)))` parses to /// `Term::ReuseAs { source: Var "xs", body: Ctor "Cons" ... }`. #[test] fn parses_reuse_as_wraps_source_and_body() { @@ -1976,7 +1976,7 @@ mod tests { } } - /// Iter 18d.1: `(reuse-as)` with no args is rejected. + /// `(reuse-as)` with no args is rejected. #[test] fn rejects_reuse_as_with_no_arguments() { let err = parse( @@ -1996,7 +1996,7 @@ mod tests { ); } - /// Iter 18d.1: `(reuse-as x)` with a single arg is rejected. + /// `(reuse-as x)` with a single arg is rejected. #[test] fn rejects_reuse_as_with_one_argument() { let err = parse( @@ -2016,7 +2016,7 @@ mod tests { ); } - /// Iter 18d.1: round-trip via `parse_term` / `term_to_form_a` — + /// round-trip via `parse_term` / `term_to_form_a` — /// `(reuse-as xs (term-ctor List Cons 1 xs))` survives a print/parse /// cycle as the same `Term::ReuseAs`. #[test] @@ -2040,7 +2040,7 @@ mod tests { assert_eq!(term_to_form_a(&parsed), printed); } - /// Iter 18e: `(data T (drop-iterative))` parses with + /// `(data T (drop-iterative))` parses with /// `drop_iterative = true` AND round-trips through the printer /// back to the same canonical surface form. #[test] @@ -2073,7 +2073,7 @@ mod tests { } } - /// Iter 18e: `(data T)` with no `(drop-iterative)` clause parses + /// `(data T)` with no `(drop-iterative)` clause parses /// with `drop_iterative = false`. Default state must be the /// pre-18e shape so legacy fixtures' canonical bytes are stable. #[test] @@ -2096,7 +2096,7 @@ mod tests { } } - /// Iter 18e: `(drop-iterative ...arg...)` is rejected — the + /// `(drop-iterative ...arg...)` is rejected — the /// annotation is a flag with no payload. #[test] fn rejects_drop_iterative_with_arguments() { @@ -2116,7 +2116,7 @@ mod tests { ); } - /// Iter 16b.1: minimal `(let-rec ...)` round-trips through the + /// minimal `(let-rec ...)` round-trips through the /// parser into a `Term::LetRec` whose `name`, `params`, `body` and /// `in_term` line up with the source. #[test] @@ -2157,7 +2157,7 @@ mod tests { } } - /// Iter 19b: a `(fn ...)` carrying a single + /// a `(fn ...)` carrying a single /// `(suppress (code "...") (because "..."))` clause parses into /// [`FnDef::suppress`] with the corresponding [`Suppress`] entry. #[test] @@ -2183,7 +2183,7 @@ mod tests { } } - /// Iter 19b: multiple `(suppress ...)` clauses accumulate into + /// multiple `(suppress ...)` clauses accumulate into /// `FnDef::suppress` in declaration order. A second clause does /// NOT overwrite the first. #[test] @@ -2212,7 +2212,7 @@ mod tests { } } - /// Iter 19b: a `(fn ...)` without a `(suppress ...)` clause has + /// a `(fn ...)` without a `(suppress ...)` clause has /// `FnDef::suppress` empty (the default — round-trip identity /// with pre-19b fixtures). #[test] @@ -2235,7 +2235,7 @@ mod tests { } } - /// Iter 19b: a `(suppress ...)` with `(because "")` (empty + /// a `(suppress ...)` with `(because "")` (empty /// reason) still parses cleanly — the parser is intentionally /// permissive; the typechecker is what emits /// `empty-suppress-reason`. This keeps the surface symmetric @@ -2263,7 +2263,7 @@ mod tests { } } - /// Iter 19b: an unknown sub-attribute inside `(suppress ...)` + /// an unknown sub-attribute inside `(suppress ...)` /// produces a `ParseError::Production` naming the bad keyword and /// listing the legal ones (`code` / `because`). #[test] diff --git a/crates/ailang-surface/src/print.rs b/crates/ailang-surface/src/print.rs index 2bddf62..dda2066 100644 --- a/crates/ailang-surface/src/print.rs +++ b/crates/ailang-surface/src/print.rs @@ -44,7 +44,7 @@ pub fn term_to_form_a(t: &Term) -> String { out } -/// Iter 19a: render a [`Type`] as form-A. Used by diagnostics that want +/// render a [`Type`] as form-A. Used by diagnostics that want /// to suggest a relaxed signature (e.g. `over-strict-mode` shows the /// `(fn-type ...)` with `(borrow T)` in place of `(own T)`). Output is /// the same shape that appears as the `type` slot of a `(fn ...)` def. @@ -123,7 +123,7 @@ fn write_type_def(out: &mut String, td: &TypeDef, level: usize) { out.push('\n'); write_ctor(out, c, level + 1); } - // Iter 18e: `(drop-iterative)` annotation. Printed last (after + // `(drop-iterative)` annotation. Printed last (after // every ctor) so the canonical form lands consistent with the // design/contracts/data-model.md example. Omitted when `drop_iterative == false`. if td.drop_iterative { @@ -163,7 +163,7 @@ fn write_fn_def(out: &mut String, fd: &FnDef, level: usize) { write_string_lit(out, export); out.push(')'); } - // Iter 19b: emit one `(suppress ...)` clause per entry, after the + // emit one `(suppress ...)` clause per entry, after the // doc string and before the type. Round-trip stable: parser // re-reads each clause back into [`FnDef::suppress`]. for s in &fd.suppress { @@ -191,7 +191,7 @@ fn write_fn_def(out: &mut String, fd: &FnDef, level: usize) { out.push(')'); } -/// Iter 19b: print one `(suppress (code "") (because ""))` +/// print one `(suppress (code "") (because ""))` /// clause. Indentation matches the rest of the fn-def (one level /// deeper than the `(fn ...)` head). fn write_suppress(out: &mut String, s: &Suppress, level: usize) { @@ -323,7 +323,7 @@ fn write_instance_method(out: &mut String, m: &InstanceMethod, level: usize) { // ---- types ---------------------------------------------------------------- -/// Iter 18a: print one fn-type param/ret slot, wrapping with +/// print one fn-type param/ret slot, wrapping with /// `(borrow ...)` or `(own ...)` when the slot has an explicit /// mode. `Implicit` is printed bare so pre-18a fixtures round-trip /// unchanged. @@ -343,7 +343,7 @@ fn write_fn_type_slot(out: &mut String, t: &Type, mode: ParamMode) { } } -/// Iter 22b.4a.4.5: print one `(constraint )` pair, used +/// print one `(constraint )` pair, used /// inside the optional `(constraints …)` clause of a `(forall …)`. The /// clause itself is omitted entirely when `Type::Forall.constraints` is /// empty so pre-22b.2 forall fixtures stay bit-identical. @@ -422,7 +422,7 @@ fn write_term(out: &mut String, t: &Term, level: usize) { Term::Lit { lit } => write_lit(out, lit), Term::Var { name } => out.push_str(name), Term::App { callee, args, tail } => { - // Iter 14e: `tail-app` is the form for `App { tail: true }`; + // `tail-app` is the form for `App { tail: true }`; // otherwise the regular `app` head is used. Both productions // are positional analogues of each other — only the head // keyword differs. @@ -469,7 +469,7 @@ fn write_term(out: &mut String, t: &Term, level: usize) { out.push(')'); } Term::Do { op, args, tail } => { - // Iter 14e: `tail-do` mirrors `tail-app` for effect ops. + // `tail-do` mirrors `tail-app` for effect ops. out.push_str(if *tail { "(tail-do " } else { "(do " }); out.push_str(op); for a in args { @@ -537,7 +537,7 @@ fn write_term(out: &mut String, t: &Term, level: usize) { out.push(')'); } Term::Clone { value } => { - // Iter 18c.1: print as `(clone )`. The wrapper is + // print as `(clone )`. The wrapper is // identity at typecheck/codegen in 18c.1; only authored // intent is recorded for the future inc/dec emission pass. out.push_str("(clone "); @@ -545,7 +545,7 @@ fn write_term(out: &mut String, t: &Term, level: usize) { out.push(')'); } Term::ReuseAs { source, body } => { - // Iter 18d.1: print as `(reuse-as )`. The + // print as `(reuse-as )`. The // wrapper is identity at codegen in 18d.1 (the `body` is // lowered, the `source` is dropped); 18d.2 will lower this // as in-place rewrite under `--alloc=rc`. @@ -812,7 +812,7 @@ mod tests { round_trip(m, "minimal_instance"); } - /// Iter 22-floats.2 RED: `lex(print(L)) == L` round-trip property + /// `lex(print(L)) == L` round-trip property /// for Float literals. Six representative bit patterns: /// `1.5`, `0.0`, `-0.0`, `10.0`, `-0.375`, `1e10`. The round-trip /// uses the existing `round_trip(Module, &str)` helper, which