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