diff --git a/bench/orchestrator-stats/2026-05-13-iter-24.2.json b/bench/orchestrator-stats/2026-05-13-iter-24.2.json new file mode 100644 index 0000000..908d46f --- /dev/null +++ b/bench/orchestrator-stats/2026-05-13-iter-24.2.json @@ -0,0 +1,22 @@ +{ + "iter_id": "24.2", + "date": "2026-05-13", + "mode": "standard", + "outcome": "DONE", + "tasks_total": 8, + "tasks_completed": 8, + "reloops_per_task": { + "1": 0, + "2": 0, + "3": 0, + "4": 0, + "5": 0, + "6": 0, + "7": 0, + "8": 0 + }, + "review_loops_spec": 0, + "review_loops_quality": 0, + "blocked_reason": null, + "notes": "Task 2 expanded scope inline to recover from a plan undercount on 22b migration consumers (4 additional files: hash.rs ct4 pin, workspace.rs iter22b1 tests, prose snapshot, one mono_symbol_compound_type assertion). Plan defects fixed inline per mq.1 / mq.3 precedent — no formal re-loop, no Boss dispatch. Plan Task 4 fixture literal schema was wrong ({lit: 'int'} flat vs {lit: {kind: 'int', value: 42}} nested) — fixed inline on first round-trip. Plan Task 5 API name was placeholder (Env::from_workspace) — used actual build_check_env. All other tasks first-shot." +} diff --git a/crates/ail/tests/mono_hash_stability.rs b/crates/ail/tests/mono_hash_stability.rs index 4598332..30e9acb 100644 --- a/crates/ail/tests/mono_hash_stability.rs +++ b/crates/ail/tests/mono_hash_stability.rs @@ -16,6 +16,13 @@ fn fixture_path() -> PathBuf { d.join("examples").join("mono_hash_pin_smoke.ail.json") } +fn show_fixture_path() -> PathBuf { + let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + d.pop(); + d.pop(); + d.join("examples").join("show_mono_pin_smoke.ail.json") +} + #[test] fn primitive_eq_ord_mono_symbol_hashes_stay_bit_identical() { let ws = load_workspace(&fixture_path()).expect("workspace loads"); @@ -50,3 +57,38 @@ fn primitive_eq_ord_mono_symbol_hashes_stay_bit_identical() { assert_eq!(&h, pin, "{sym} body hash drifted"); } } + +#[test] +fn primitive_show_mono_symbol_hashes_stay_bit_identical() { + // 24.2 hash-stability pin for the four primitive `show__T` mono + // symbols synthesised from `prelude.Show {Int|Bool|Str|Float}` + // instance bodies. Loaded from a separate fixture + // (`show_mono_pin_smoke.ail.json`) so the Eq/Ord pin above stays + // structurally isolated. + let ws = load_workspace(&show_fixture_path()).expect("workspace loads"); + let diags = ailang_check::check_workspace(&ws); + assert!(diags.is_empty(), "typecheck diagnostics: {diags:?}"); + let post_mono = ailang_check::monomorphise_workspace(&ws).expect("mono green"); + + let prelude_mod = post_mono + .modules + .get("prelude") + .expect("prelude module present"); + + let pins: &[(&str, &str)] = &[ + ("show__Int", "891eaebe64b3180d"), + ("show__Bool", "1bdb621494e50607"), + ("show__Str", "3f1d57c90ddce081"), + ("show__Float", "5ab0bdd40b9f8b8f"), + ]; + + for (sym, pin) in pins { + let def = prelude_mod + .defs + .iter() + .find(|d| matches!(d, Def::Fn(f) if f.name == *sym)) + .unwrap_or_else(|| panic!("mono symbol {sym} not found in prelude module")); + let h = def_hash(def); + assert_eq!(&h, pin, "{sym} body hash drifted; captured: {h}"); + } +} diff --git a/crates/ail/tests/show_mono_synthesis.rs b/crates/ail/tests/show_mono_synthesis.rs new file mode 100644 index 0000000..a1bb513 --- /dev/null +++ b/crates/ail/tests/show_mono_synthesis.rs @@ -0,0 +1,42 @@ +//! Mono-synthesis existence pin for milestone 24.2's Show class. +//! +//! Asserts that the four primitive Show instances (Int/Bool/Str/Float) +//! cause the mono pass to synthesise `show__Int`, `show__Bool`, +//! `show__Str`, `show__Float` as `Def::Fn` entries in the prelude +//! post-mono module. + +use ailang_core::ast::Def; +use ailang_core::workspace::load_workspace; +use ailang_check::{check_workspace, monomorphise_workspace}; +use std::path::PathBuf; + +fn fixture_path() -> PathBuf { + let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + d.pop(); + d.pop(); + d.join("examples").join("show_mono_pin_smoke.ail.json") +} + +#[test] +fn primitive_show_mono_symbols_synthesise() { + let ws = load_workspace(&fixture_path()).expect("workspace loads"); + let diags = check_workspace(&ws); + assert!(diags.is_empty(), "typecheck diagnostics: {diags:#?}"); + let post_mono = monomorphise_workspace(&ws).expect("mono green"); + + let prelude_mod = post_mono + .modules + .get("prelude") + .expect("prelude module present"); + + for sym in &["show__Int", "show__Bool", "show__Str", "show__Float"] { + let found = prelude_mod + .defs + .iter() + .any(|d| matches!(d, Def::Fn(f) if f.name == *sym)); + assert!( + found, + "mono symbol {sym} not found in prelude module post-mono" + ); + } +} diff --git a/crates/ail/tests/typeclass_22b2.rs b/crates/ail/tests/typeclass_22b2.rs index 382e3e0..6e75650 100644 --- a/crates/ail/tests/typeclass_22b2.rs +++ b/crates/ail/tests/typeclass_22b2.rs @@ -19,7 +19,7 @@ fn examples_dir() -> PathBuf { /// Property protected: a class method declared in a `Def::Class` must be /// reachable as a name in the module's `ModuleGlobals` so that pass-2 -/// (`Term::Var { name: "show" }`) does not fire `unbound-var` for +/// (`Term::Var { name: "tshow" }`) does not fire `unbound-var` for /// well-formed class-method calls. The lookup must remember which class /// the method belongs to so Task 9 can build the residual constraint at /// the call site. @@ -37,15 +37,15 @@ fn class_method_is_in_module_globals() { // Pre-mq.3 callers passed the bare method name only; post-mq.3 they // must disambiguate by class. assert!( - mod_globals.has_class_method("test_22b2_class_method_lookup.Show", "show"), - "class method `show` (in class `Show`) must appear in module globals" + 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 // `.`. assert_eq!( - mod_globals.class_method_class("test_22b2_class_method_lookup.Show", "show"), - Some("test_22b2_class_method_lookup.Show"), - "class method `show` must remember its class is `Show` (qualified post-mq.1)" + 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)" ); } @@ -54,7 +54,7 @@ fn class_method_is_in_module_globals() { /// typecheck arms can find the matching `Registry` entries) and the /// declared method signature (so a residual class constraint can be /// built by instantiating the class param at the call site). The -/// fixture declares `class Show a { show: a -> Str }`, so the +/// fixture declares `class TShow a { show: a -> Str }`, so the /// recovered `method_ty` must be exactly `Type::Var "a" -> Type::Con /// "Str"` with empty effects. #[test] @@ -68,8 +68,8 @@ fn class_method_entry_carries_full_metadata() { .get("test_22b2_class_method_lookup") .expect("module globals present"); let cm_entry = mod_globals - .class_method("test_22b2_class_method_lookup.Show", "show") - .expect("class method `show` (in class `Show`) must be registered"); + .class_method("test_22b2_class_method_lookup.TShow", "tshow") + .expect("class method `tshow` (in class `TShow`) must be registered"); assert_eq!( cm_entry.defining_module, "test_22b2_class_method_lookup", @@ -90,7 +90,7 @@ fn class_method_entry_carries_full_metadata() { assert_eq!( params.len(), 1, - "method `show` must have exactly one parameter" + "method `tshow` must have exactly one parameter" ); match ¶ms[0] { Type::Var { name } => assert_eq!( @@ -122,7 +122,7 @@ fn class_method_entry_carries_full_metadata() { } assert!( effects.is_empty(), - "method `show` must have empty effects, got {:?}", + "method `tshow` must have empty effects, got {:?}", effects ); } @@ -134,7 +134,7 @@ fn class_method_entry_carries_full_metadata() { } /// Property protected: a polymorphic FnDef that calls a class method -/// (e.g. `show x` where `x: a`) but does not declare the matching class +/// (e.g. `tshow x` where `x: a`) but does not declare the matching class /// constraint in its `Forall.constraints` MUST receive a /// `missing-constraint` diagnostic. This is the principal soundness /// guarantee of class-method calls inside polymorphic functions: every @@ -160,7 +160,7 @@ fn fn_calling_class_method_without_declared_constraint_fires_missing_constraint( /// method typechecks clean — the residual constraint produced at the /// call site is satisfied by the in-scope declared constraint, and no /// diagnostic fires. This is the positive counterpart to the -/// missing-constraint check: declaring `Show a =>` makes `show x` +/// missing-constraint check: declaring `Show a =>` makes `tshow x` /// well-formed inside the body. #[test] fn fn_with_correct_constraint_typechecks_green() { @@ -225,7 +225,7 @@ fn fn_with_superclass_constraint_calling_subclass_method_fires_missing_constrain /// concrete type, the fn cannot push the obligation to a caller via a /// `Forall.constraints` annotation — it can only be discharged by an /// existing instance. Without this check, monomorphisation in 22b.3 would -/// have nothing to specialise to. Fixture: `main` calls `show 42` (so the +/// have nothing to specialise to. Fixture: `main` calls `tshow 42` (so the /// residual is `(Show, Int)`) without any `instance Show Int` declared in /// the workspace. #[test] @@ -250,7 +250,7 @@ fn fn_calling_class_method_at_type_without_instance_fires_no_instance() { /// otherwise sneak past the negative-only test: a registry that is empty /// at typecheck time, or keyed under the wrong shape, would silently fire /// `no-instance` even when the instance is present. Fixture: `main` calls -/// `show 42` with `instance Show Int` declared in the same workspace. +/// `tshow 42` with `instance Show Int` declared in the same workspace. #[test] fn fn_calling_class_method_at_type_with_instance_typechecks_green() { let entry = examples_dir().join("test_22b2_instance_present.ail.json"); @@ -268,13 +268,13 @@ fn fn_calling_class_method_at_type_with_instance_typechecks_green() { /// reachable from a fn in module B that imports A — i.e. the workspace- /// wide merge of `module_globals.class_methods` into `Env.class_methods` /// (per `check_in_workspace`) is what call-site resolution actually -/// consults. Without this merge, a `Term::Var { name: "show" }` in B would -/// fire `unbound-var`. The discriminator: when B's `main` calls `show 42` +/// consults. Without this merge, a `Term::Var { name: "tshow" }` in B would +/// fire `unbound-var`. The discriminator: when B's `main` calls `tshow 42` /// at a fully-concrete type and A has NO matching instance, the diagnostic /// must be `no-instance` (proving the class method resolved cross-module), /// NOT `unbound-var` (which would mean cross-module class-method lookup /// failed silently). Fixture: A = `test_22b2_xmod_classmod_noinst` declares -/// `class Show a`; B = `test_22b2_xmod_no_instance` calls `show 42`. +/// `class TShow a`; B = `test_22b2_xmod_no_instance` calls `tshow 42`. #[test] fn cross_module_class_method_resolves_and_fires_no_instance() { let entry = examples_dir().join("test_22b2_xmod_no_instance.ail.json"); @@ -289,7 +289,7 @@ fn cross_module_class_method_resolves_and_fires_no_instance() { ); assert!( !codes.contains(&"unbound-var"), - "must NOT fire unbound-var — class method `show` from module A is in scope in module B; got: {:?}", + "must NOT fire unbound-var — class method `tshow` from module A is in scope in module B; got: {:?}", codes ); } @@ -297,14 +297,14 @@ fn cross_module_class_method_resolves_and_fires_no_instance() { /// Property protected: a polymorphic fn in module B calling a class method /// declared in module A (without declaring the matching constraint) MUST /// fire `missing-constraint`. This pins the cross-module class-superclass -/// expansion path: B's `Forall.constraints` is empty, A's `class Show a` +/// expansion path: B's `Forall.constraints` is empty, A's `class TShow a` /// is in `Env.class_methods` via the workspace merge, and the residual -/// `(Show, a)` collected at the `show x` call site must reach `check_fn`'s +/// `(Show, a)` collected at the `tshow x` call site must reach `check_fn`'s /// missing-constraint arm. Without the cross-module merge, the call would /// produce `unbound-var`; with the merge but a broken superclass-table /// merge, the residual could be spuriously satisfied. Fixture: A = /// `test_22b2_xmod_classmod_noinst`; B = `test_22b2_xmod_missing_constraint` -/// declares `forall a. (a) -> Str` and calls `show x`. +/// declares `forall a. (a) -> Str` and calls `tshow x`. #[test] fn cross_module_polymorphic_fn_without_constraint_fires_missing_constraint() { let entry = examples_dir().join("test_22b2_xmod_missing_constraint.ail.json"); @@ -319,11 +319,11 @@ fn cross_module_polymorphic_fn_without_constraint_fires_missing_constraint() { } /// Property protected: cross-module instance lookup for the no-instance -/// arm. Module A declares `class Show a` AND `instance Show Int`; module -/// B imports A and calls `show 42`. The call must typecheck clean — the +/// arm. Module A declares `class TShow a` AND `instance Show Int`; module +/// B imports A and calls `tshow 42`. The call must typecheck clean — the /// workspace registry built at load time over all modules must include A's /// `instance Show Int`, and B's `check_fn` must find it under -/// `(class="Show", type_hash(Int))`. Without cross-module registry +/// `(class="TShow", type_hash(Int))`. Without cross-module registry /// population, B would spuriously fire `no-instance` at a type for which /// an instance demonstrably exists. Fixture: A = /// `test_22b2_xmod_classmod`; B = `test_22b2_xmod_instance_present`. @@ -342,7 +342,7 @@ fn cross_module_instance_satisfies_concrete_call() { /// Property protected: `check_workspace` aggregates diagnostics across /// distinct fns rather than stopping at the first failing fn. When two -/// independent polymorphic fns each call `show x` without declaring `Show +/// independent polymorphic fns each call `tshow x` without declaring `Show /// a`, the resulting diagnostic vec must contain TWO `missing-constraint` /// entries — one per fn — with the right `def` field on each. Without /// per-def aggregation in the `for def in &m.defs` loop of diff --git a/crates/ail/tests/typeclass_22b3.rs b/crates/ail/tests/typeclass_22b3.rs index 210a18e..14556dc 100644 --- a/crates/ail/tests/typeclass_22b3.rs +++ b/crates/ail/tests/typeclass_22b3.rs @@ -56,11 +56,11 @@ fn monomorphise_workspace_is_identity_on_class_free_workspace() { /// so no FnDefs are appended. /// /// The fixture (`test_22b3_no_call_sites.ail.json`) declares -/// `class Show` + `instance Show Int` with a Lam-shaped instance +/// `class TShow` + `instance Show Int` with a Lam-shaped instance /// body, but `main` is a literal — there is no `show ` call at any /// type. Updated in Task 5 from `test_22b2_instance_present.ail.json`, -/// which had a `show 42` call site that the real fixpoint now -/// (correctly) materialises into `show__Int`. +/// which had a `tshow 42` call site that the real fixpoint now +/// (correctly) materialises into `tshow__Int`. #[test] fn monomorphise_workspace_is_identity_when_no_concrete_call_sites() { let entry = examples_dir().join("test_22b3_no_call_sites.ail.json"); @@ -102,9 +102,9 @@ fn mono_symbol_primitive_types_use_surface_form() { let str_ty = Type::str_(); let unit_ty = Type::unit(); - assert_eq!(ailang_check::mono::mono_symbol("show", &int_ty), "show__Int"); + assert_eq!(ailang_check::mono::mono_symbol("tshow", &int_ty), "tshow__Int"); assert_eq!(ailang_check::mono::mono_symbol("eq", &bool_ty), "eq__Bool"); - assert_eq!(ailang_check::mono::mono_symbol("show", &str_ty), "show__Str"); + assert_eq!(ailang_check::mono::mono_symbol("tshow", &str_ty), "tshow__Str"); assert_eq!(ailang_check::mono::mono_symbol("foo", &unit_ty), "foo__Unit"); } @@ -112,9 +112,9 @@ fn mono_symbol_primitive_types_use_surface_form() { /// fully-concrete `(class, method, type)` triples observed at /// class-method call sites in a fn body, with the registry's /// `defining_module` attached to each. The fixture's `main` calls -/// `show 42` once with `Int`, and the workspace declares +/// `tshow 42` once with `Int`, and the workspace declares /// `instance Show Int` in the same module — the expected output is -/// a single MonoTarget naming `Show`, `show`, `Int`, and that +/// a single MonoTarget naming `TShow`, `tshow`, `Int`, and that /// module. #[test] fn collect_mono_targets_single_concrete_call_site() { @@ -149,8 +149,8 @@ fn collect_mono_targets_single_concrete_call_site() { match t { ailang_check::mono::MonoTarget::ClassMethod { class, method, type_, defining_module } => { // mq.1: MonoTarget.class carries the qualified workspace-key shape. - assert_eq!(class, "test_22b2_instance_present.Show"); - assert_eq!(method, "show"); + assert_eq!(class, "test_22b2_instance_present.TShow"); + assert_eq!(method, "tshow"); assert!( matches!(type_, ailang_core::ast::Type::Con { name, args } if name == "Int" && args.is_empty()), "type must be Int, got {:?}", @@ -171,11 +171,11 @@ fn mono_symbol_compound_type_uses_hash_suffix() { name: "Pair".into(), args: vec![Type::int(), Type::bool_()], }; - let name = ailang_check::mono::mono_symbol("show", &pair_ty); + let name = ailang_check::mono::mono_symbol("tshow", &pair_ty); - // Format: `show__<8-hex-chars>`. - assert!(name.starts_with("show__"), "name = {name}"); - let suffix = &name["show__".len()..]; + // Format: `tshow__<8-hex-chars>`. + assert!(name.starts_with("tshow__"), "name = {name}"); + let suffix = &name["tshow__".len()..]; assert_eq!(suffix.len(), 8, "compound suffix is 8 hex chars: {suffix}"); assert!( suffix.chars().all(|c| c.is_ascii_hexdigit()), @@ -183,7 +183,7 @@ fn mono_symbol_compound_type_uses_hash_suffix() { ); // Stability: a second call with the same type produces the same name. - let again = ailang_check::mono::mono_symbol("show", &pair_ty); + let again = ailang_check::mono::mono_symbol("tshow", &pair_ty); assert_eq!(name, again, "mono_symbol must be deterministic"); } @@ -370,9 +370,9 @@ fn synthesise_mono_fn_zero_arg_method_uses_body_verbatim() { ); } -/// Property: when two call sites of `show` apply at the same concrete +/// Property: when two call sites of `tshow` apply at the same concrete /// type (Int), the workspace fixpoint synthesises the matching FnDef -/// (`show__Int`) exactly once across the whole workspace. Verifies the +/// (`tshow__Int`) exactly once across the whole workspace. Verifies the /// dedup contract — the pass must not emit one FnDef per call site. #[test] fn fixpoint_dedupes_repeated_calls_at_same_type() { @@ -383,18 +383,18 @@ fn fixpoint_dedupes_repeated_calls_at_same_type() { let ws = ailang_check::monomorphise_workspace(&ws).expect("mono"); - // Count `show__Int` fns workspace-wide. + // Count `tshow__Int` fns workspace-wide. let mut count = 0usize; for m in ws.modules.values() { for d in &m.defs { if let ailang_core::ast::Def::Fn(f) = d { - if f.name == "show__Int" { + if f.name == "tshow__Int" { count += 1; } } } } - assert_eq!(count, 1, "exactly one show__Int across the workspace"); + assert_eq!(count, 1, "exactly one tshow__Int across the workspace"); } /// Property: when a synthesised instance body itself calls another class @@ -450,7 +450,7 @@ fn fixpoint_handles_chained_class_method_calls() { } /// Property: when a fn's body locally shadows a class-method name -/// (here `show` is rebound by a `let`-binding), synth resolves the +/// (here `tshow` is rebound by a `let`-binding), synth resolves the /// shadowed `Var` against the local and pushes NO residual for it. /// The rewrite walker must mirror that exact lookup-precedence rule /// — when a `Term::Var { name }` is in the local scope, the walker @@ -463,14 +463,14 @@ fn fixpoint_handles_chained_class_method_calls() { /// /// Body shape (`main`): /// let _t = show 5 in -- residual #0: show@Int -/// let show = "shadow" in -- shadow `show` +/// let show = "shadow" in -- shadow `tshow` /// let _u = show in -- shadowed Var (no residual) /// foo 7 -- residual #1: foo@Int /// /// `synth` pushes exactly two residuals (in order: show@Int, foo@Int). /// The shadow-aware walker must: -/// - rewrite the outer `show` callee → `show__Int`, -/// - leave the inner shadowed `show` Var literally as `show`, +/// - rewrite the outer `tshow` callee → `tshow__Int`, +/// - leave the inner shadowed `tshow` Var literally as `tshow`, /// - rewrite the `foo` callee → `foo__Int`. /// /// A shadow-blind walker would advance the cursor on the inner Var, @@ -483,7 +483,7 @@ fn rewrite_walker_skips_locally_shadowed_class_method() { let entry = examples_dir().join("test_22b3_shadow_class_method.ail.json"); let ws = ailang_core::load_workspace(&entry).expect("load"); let diags = ailang_check::check_workspace(&ws); - // mq.3: this fixture intentionally shadows the class method `show` + // mq.3: 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 @@ -519,14 +519,14 @@ fn rewrite_walker_skips_locally_shadowed_class_method() { other => panic!("outer value is not App: {:?}", other), }; assert_eq!( - outer_callee, "show__Int", - "the outer class-method call site must be rewritten to show__Int" + outer_callee, "tshow__Int", + "the outer class-method call site must be rewritten to tshow__Int" ); // Step inside `let show = "shadow" in `. let shadow_let_body = match outer_body { Term::Let { name, body, .. } => { - assert_eq!(name, "show", "expected shadowing let to bind `show`"); + assert_eq!(name, "tshow", "expected shadowing let to bind `tshow`"); body.as_ref() } other => panic!("expected shadowing Let, got {:?}", other), @@ -540,10 +540,10 @@ fn rewrite_walker_skips_locally_shadowed_class_method() { Term::Var { name } => name.as_str(), other => panic!("inner value is not Var: {:?}", other), }; - // The locally-shadowed `show` Var must NOT be rewritten. + // The locally-shadowed `tshow` Var must NOT be rewritten. assert_eq!( - shadowed_var_name, "show", - "locally-shadowed `show` Var must remain literal `show`, \ + shadowed_var_name, "tshow", + "locally-shadowed `tshow` Var must remain literal `tshow`, \ not be re-aligned to a later residual slot" ); // The real `foo 7` call site — the second residual — must be @@ -596,24 +596,24 @@ fn rewrite_uses_qualified_name_for_cross_module_call_site() { }; // class+instance live in test_22b2_xmod_classmod; caller in // test_22b2_xmod_instance_present → qualified call. - assert_eq!(callee_name, "test_22b2_xmod_classmod.show__Int"); + assert_eq!(callee_name, "test_22b2_xmod_classmod.tshow__Int"); // Placement: the synthesised def must live in the defining module, // not the caller's module. let classmod = ws.modules.get("test_22b2_xmod_classmod").unwrap(); let has_synth = classmod.defs.iter().any(|d| { - matches!(d, ailang_core::ast::Def::Fn(f) if f.name == "show__Int") + matches!(d, ailang_core::ast::Def::Fn(f) if f.name == "tshow__Int") }); assert!( has_synth, - "show__Int must live in the instance's defining module" + "tshow__Int must live in the instance's defining module" ); let caller_has_synth = main_module.defs.iter().any(|d| { - matches!(d, ailang_core::ast::Def::Fn(f) if f.name == "show__Int") + matches!(d, ailang_core::ast::Def::Fn(f) if f.name == "tshow__Int") }); assert!( !caller_has_synth, - "show__Int must NOT live in the caller's module" + "tshow__Int must NOT live in the caller's module" ); } @@ -670,8 +670,8 @@ fn synthetic_fixture_runs_and_prints_five() { } /// After the mono pass, the body of `main` in -/// test_22b2_instance_present must reference `show__Int` instead -/// of `show`. +/// test_22b2_instance_present must reference `tshow__Int` instead +/// of `tshow`. #[test] fn rewrite_replaces_class_method_var_with_mono_symbol_same_module() { let entry = examples_dir().join("test_22b2_instance_present.ail.json"); @@ -691,8 +691,8 @@ fn rewrite_replaces_class_method_var_with_mono_symbol_same_module() { }) .expect("main"); - // The body is `App { fn: Var "show", args: [Lit 42] }`. After - // rewrite, the callee Var must be `show__Int`. + // The body is `App { fn: Var "tshow", args: [Lit 42] }`. After + // rewrite, the callee Var must be `tshow__Int`. let callee_name: &str = match &main_fn.body { ailang_core::ast::Term::App { callee, .. } => match callee.as_ref() { ailang_core::ast::Term::Var { name } => name.as_str(), @@ -700,7 +700,7 @@ fn rewrite_replaces_class_method_var_with_mono_symbol_same_module() { }, other => panic!("body is not App: {:?}", other), }; - assert_eq!(callee_name, "show__Int"); + assert_eq!(callee_name, "tshow__Int"); } /// Helper: build & run a workspace via `ail run` and capture stdout diff --git a/crates/ailang-check/tests/show_dispatch_pin.rs b/crates/ailang-check/tests/show_dispatch_pin.rs new file mode 100644 index 0000000..b7e6527 --- /dev/null +++ b/crates/ailang-check/tests/show_dispatch_pin.rs @@ -0,0 +1,71 @@ +//! Singleton dispatch pin for milestone 24.2's Show class. +//! +//! Asserts that with `prelude.Show` shipping `show` and a user `TShow` +//! shipping `tshow`, the bare method-name dispatcher resolves each via +//! Step-2 singleton (each method has exactly one candidate class globally). + +use ailang_check::{build_check_env, check_workspace}; +use ailang_core::workspace::load_workspace; +use std::path::PathBuf; + +fn fixture_dir() -> PathBuf { + let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + d.pop(); + d.pop(); + d.join("examples") +} + +#[test] +fn bare_show_routes_to_prelude_show_via_singleton() { + // Workspace = auto-loaded prelude (containing class Show) plus a + // non-prelude entry that has zero `Show` declarations of its own. + // Asserts: env.method_to_candidate_classes["show"] == {"prelude.Show"} + // (a singleton), so Step-2 of resolve_method_dispatch resolves bare + // "show" without needing Steps 3-4. + let ws_path = fixture_dir().join("show_mono_pin_smoke.ail.json"); + let ws = load_workspace(&ws_path).expect("workspace loads"); + let diags = check_workspace(&ws); + assert!(diags.is_empty(), "typecheck diagnostics: {diags:#?}"); + + let env = build_check_env(&ws); + let candidates = env + .method_to_candidate_classes + .get("show") + .expect("method 'show' has candidates"); + assert_eq!( + candidates.len(), + 1, + "post-24.2-migration, 'show' has exactly one candidate class globally: got {candidates:?}" + ); + assert!( + candidates.iter().any(|c| c == "prelude.Show"), + "candidates do not include prelude.Show: {candidates:?}" + ); +} + +#[test] +fn bare_tshow_routes_to_user_tshow_via_singleton() { + // Workspace = auto-loaded prelude + the migrated 22b1_dup_classmod + // (which now declares class TShow with method tshow). + // Asserts: env.method_to_candidate_classes["tshow"] has exactly one + // candidate (the user TShow), workspace-globally. + let ws_path = fixture_dir().join("test_22b1_dup_classmod.ail.json"); + let ws = load_workspace(&ws_path).expect("workspace loads"); + let diags = check_workspace(&ws); + assert!(diags.is_empty(), "typecheck diagnostics: {diags:#?}"); + + let env = build_check_env(&ws); + let candidates = env + .method_to_candidate_classes + .get("tshow") + .expect("method 'tshow' has candidates"); + assert_eq!( + candidates.len(), + 1, + "post-22b-migration, 'tshow' is owned by exactly one class globally: got {candidates:?}" + ); + assert!( + candidates.iter().any(|c| c.ends_with(".TShow")), + "candidates do not include a *.TShow class: {candidates:?}" + ); +} diff --git a/crates/ailang-core/src/hash.rs b/crates/ailang-core/src/hash.rs index d8f1e3a..037865b 100644 --- a/crates/ailang-core/src/hash.rs +++ b/crates/ailang-core/src/hash.rs @@ -294,33 +294,35 @@ mod tests { .expect("examples/test_22b1_dup_a.ail.json present"); let dup_a_mod: crate::ast::Module = serde_json::from_slice(&dup_a_src).unwrap(); // mq.1: test_22b1_dup_a has one def post-migration — the instance - // of `test_22b1_dup_classmod.Show` for `test_22b1_dup_b.MyInt`. - // The `Show` class itself moved to `test_22b1_dup_classmod` to + // of `test_22b1_dup_classmod.TShow` for `test_22b1_dup_b.MyInt`. + // The `TShow` class itself moved to `test_22b1_dup_classmod` to // break the dup_a ↔ dup_b cycle that the pre-mq.1 bare-class // shape required for cross-module duplicate-instance coverage. + // 24.2: class renamed `Show` → `TShow` and method renamed + // `show` → `tshow` workspace-wide so prelude.Show can ship; the + // hash captures new bytes accordingly. assert_eq!(dup_a_mod.defs.len(), 1, "test_22b1_dup_a expected to have exactly 1 def (the instance)"); - // Hash captured post-mq.1 migration; the instance carries the - // canonical qualified `class` field. Computed by running this - // test once with a length-only assertion and recording the - // observed value. A future schema-touching change that shifts - // this hash without intent triggers this pin. + // Hash captured post-24.2 TShow/tshow rename. A future + // schema-touching change that shifts this hash without intent + // triggers this pin. assert_eq!( def_hash(&dup_a_mod.defs[0]), - "bdefb4ec75e046e4", - "test_22b1_dup_a instance hash drifted; expected post-mq.1 captured value" + "392c247f07de6517", + "test_22b1_dup_a instance hash drifted; expected post-24.2 captured value" ); - // Also pin the new test_22b1_dup_classmod fixture's `Show` class + // Also pin the new test_22b1_dup_classmod fixture's `TShow` class // hash: the class moved here from dup_a verbatim (same method - // signature), so the hash is independent of the migration. + // signature), so the hash is independent of the migration but + // does shift with the 24.2 Show→TShow / show→tshow rename. let dup_classmod_src = std::fs::read(examples.join("test_22b1_dup_classmod.ail.json")) .expect("examples/test_22b1_dup_classmod.ail.json present"); let dup_classmod_mod: crate::ast::Module = serde_json::from_slice(&dup_classmod_src).unwrap(); - assert_eq!(dup_classmod_mod.defs.len(), 1, "test_22b1_dup_classmod expected to have exactly 1 def (class Show)"); + assert_eq!(dup_classmod_mod.defs.len(), 1, "test_22b1_dup_classmod expected to have exactly 1 def (class TShow)"); assert_eq!( def_hash(&dup_classmod_mod.defs[0]), - "1c2573661ffd3da3", - "test_22b1_dup_classmod class Show canonical hash must equal the pre-mq.1 dup_a class Show hash (same bytes)" + "b8bca96c2d09ed93", + "test_22b1_dup_classmod class TShow canonical hash; post-24.2 captured value" ); } diff --git a/crates/ailang-core/src/workspace.rs b/crates/ailang-core/src/workspace.rs index 1de208b..a9f4f4b 100644 --- a/crates/ailang-core/src/workspace.rs +++ b/crates/ailang-core/src/workspace.rs @@ -1838,20 +1838,21 @@ mod tests { assert_eq!(fixture_entries.len(), 1); let (key, entry) = fixture_entries[0]; // mq.1: registry key is keyed by the qualified class name. - assert_eq!(&key.0, "test_22b1_orphan_class.Show"); + // 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"); // `instance.class` carries the canonical-form on-disk value // (bare for same-module per the canonical-form rule). - assert_eq!(entry.instance.class, "Show"); + assert_eq!(entry.instance.class, "TShow"); } /// Iter 22b.1: 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 Show`) and the entry module declares `instance - /// Show Int` itself — but the entry is not the class's module + /// owns `class TShow`) and the entry module declares `instance + /// TShow Int` itself — but the entry is not the class's module /// and `Int` is primitive, so neither leg of coherence is - /// satisfied. + /// satisfied. (24.2: class renamed `Show` → `TShow`.) #[test] fn iter22b1_orphan_instance_fires_diagnostic() { let entry = examples_dir().join("test_22b1_orphan_third.ail.json"); @@ -1865,7 +1866,7 @@ mod tests { } => { // mq.1: `class` carries the canonical form (qualified // for cross-module class refs). - assert_eq!(class, "test_22b1_orphan_third_classmod.Show"); + assert_eq!(class, "test_22b1_orphan_third_classmod.TShow"); assert_eq!(type_repr, "Int"); assert_eq!(defining_module, "test_22b1_orphan_third"); } @@ -1887,8 +1888,8 @@ mod tests { /// 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 - /// coherent, both collide on `(test_22b1_dup_same_module.Show, - /// type_hash(Int))`. + /// coherent, both collide on `(test_22b1_dup_same_module.TShow, + /// type_hash(Int))`. (24.2: class renamed `Show` → `TShow`.) #[test] fn iter22b1_duplicate_instance_fires_diagnostic() { let entry = examples_dir().join("test_22b1_dup_same_module.ail.json"); @@ -1902,7 +1903,7 @@ mod tests { } => { // `class` is the on-disk `inst.class` value; the // fixture is intra-module so it stays bare. - assert_eq!(class, "Show"); + assert_eq!(class, "TShow"); assert_eq!(type_repr, "Int"); // Both instances live in the same module post-mq.1, // by structural necessity — see test doc-comment. diff --git a/docs/DESIGN.md b/docs/DESIGN.md index b3207ec..6cc416e 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -1888,12 +1888,26 @@ end-to-end path is the milestone's typeclass acceptance gate Milestone 23 amends the above: the prelude now ships the `Ordering` ADT, the `Eq` and `Ord` classes, primitive `Eq Int/Bool/Str` and `Ord Int/Bool/Str` instances, and the five polymorphic free-fn helpers -`ne` / `lt` / `le` / `gt` / `ge`. `Show`, operator routing through -`Eq`/`Ord`, and `print`-rewire remain out of scope per their original -substantive reasons (heap-`Str` ABI, bench rebaseline). Float has -neither `Eq` nor `Ord` instance per §"Float semantics"; a polymorphic -helper invoked at Float fires `NoInstance` at typecheck with a -Float-aware diagnostic cross-referencing this section. +`ne` / `lt` / `le` / `gt` / `ge`. Operator routing through `Eq`/`Ord` +and `print`-rewire remain out of scope per their original substantive +reasons (bench rebaseline, milestone-24 dependency on the post-mq +dispatcher); `Show` itself ships in milestone 24. Float has neither +`Eq` nor `Ord` instance per §"Float semantics"; a polymorphic helper +invoked at Float fires `NoInstance` at typecheck with a Float-aware +diagnostic cross-referencing this section. + +Milestone 24 amends the above further: the prelude ships `class Show +a where show : (a borrow) -> Str` and primitive `Show Int`, +`Show Bool`, `Show Str`, `Show Float` instances. Float **is** included +in Show (unlike Eq/Ord) — IEEE-754 makes structural equality and total +ordering semantically dubious, but textual representation of a Float +is well-defined modulo the NaN-spelling caveat in §"Float semantics". +Each `Show ` instance body is a single-application lambda invoking +the corresponding runtime primitive (`int_to_str`, `bool_to_str`, +`str_clone`, `float_to_str`); no codegen intercept is required. The +polymorphic helper `print : forall a. Show a => a -> () !IO` ships in +the same milestone as the second iteration (24.3) and routes through +`show` and `io/print_str`. Primitive output goes through `io/print_int` / `io/print_bool` / `io/print_str` directly. diff --git a/docs/journals/2026-05-13-iter-24.2.md b/docs/journals/2026-05-13-iter-24.2.md new file mode 100644 index 0000000..c7faa74 --- /dev/null +++ b/docs/journals/2026-05-13-iter-24.2.md @@ -0,0 +1,189 @@ +# iter 24.2 — Show class + 4 primitive instances + 22b TShow/tshow migration + +**Date:** 2026-05-13 +**Started from:** 64cea0e +**Status:** DONE +**Tasks completed:** 8 of 8 + +## Summary + +Ships `class Show a where show : (a borrow) -> Str` in +`examples/prelude.ail.json` alongside primitive instances `Show Int`, +`Show Bool`, `Show Str`, `Show Float`. Each instance body is a +single-application lambda invoking the corresponding runtime primitive +(`int_to_str`, `bool_to_str`, `str_clone`, `float_to_str`) — first +prelude instance bodies to call runtime primitives directly +(prior Eq/Ord bodies used operator + codegen intercept). The 22b +typeclass test corpus migrates `class Show` / `show` → +`class TShow` / `tshow` preemptively (Tasks 1–2 before Task 3 lands +`prelude.Show`) so the workspace stays green throughout. Three new +tests pin the post-mono shape: mono-synthesis existence +(`show_mono_synthesis.rs`), dispatch singleton (`show_dispatch_pin.rs` +× 2 cases), and body-hash stability (`mono_hash_stability.rs::primitive_show_mono_symbol_hashes_stay_bit_identical`). +DESIGN.md §"Prelude (built-in) classes" gains a milestone-24 paragraph +naming Show + instances + the Float caveat. `print`-rewire stays +deferred to iter 24.3. + +## Per-task notes + +- iter 24.2.1: Renamed `"Show"`/`"show"` → `"TShow"`/`"tshow"` across 21 + `test_22b*_*.ail.json` fixture files; cross-module qualifier forms + (`".Show"` → `".TShow"`) handled by regex in 3 + fixtures (`test_22b1_dup_a`, `test_22b1_dup_b`, + `test_22b1_orphan_third`). False-positive scan empty; fixture + parse round-trip green. +- iter 24.2.2: Renamed literals in `crates/ail/tests/typeclass_22b{2,3}.rs` + (`"Show"` → `"TShow"`, `"show"` → `"tshow"`, `show__T` → `tshow__T`), + including comment-prose forms (`` `Show` `` → `` `TShow` ``, + `class Show` → `class TShow`, etc.) and one assertion-message string + ("exactly one show__Int across the workspace" → "tshow__Int..."). + Plan undercount surfaced: the rename touches more than the 2 named + test files — see Concerns below. +- iter 24.2.3: Inserted 5 new top-level defs into `examples/prelude.ail.json` + immediately before `fn ne` (line 223 pre-edit). The `instance Show Str` + body invokes `str_clone` as `Term::Var` — first prelude instance body + to do so. Round-trip green; `primitive_eq_ord_mono_symbol_hashes_stay_bit_identical` + stays green (Eq/Ord hashes unaffected by Show defs). +- iter 24.2.4: New fixture `examples/show_mono_pin_smoke.ail.json` + exercises `show 42`, `show true`, `show "x"`, `show 1.5` in a + let-chain ending in `io/print_str s1`. New test + `crates/ail/tests/show_mono_synthesis.rs::primitive_show_mono_symbols_synthesise` + asserts `show__Int`/`show__Bool`/`show__Str`/`show__Float` materialise + as `Def::Fn` entries in the `prelude` post-mono module. `str_clone` + reachability in instance-lambda position verified implicitly: + `show__Str` materialises, which means the lambda body's `Term::Var + "str_clone"` resolved against the checker's builtin table during + typecheck. +- iter 24.2.5: New pin test `crates/ailang-check/tests/show_dispatch_pin.rs` + asserts `env.method_to_candidate_classes["show"] == {"prelude.Show"}` + (singleton) and `env.method_to_candidate_classes["tshow"] == {"*.TShow"}` + (singleton). API spelled `build_check_env(&ws)` not `Env::from_workspace` + (plan's placeholder name). 2/2 cases pass first-shot. +- iter 24.2.6: Extended `crates/ail/tests/mono_hash_stability.rs` with + a second test function `primitive_show_mono_symbol_hashes_stay_bit_identical` + loading `show_mono_pin_smoke.ail.json` (kept separate from the + Eq/Ord fixture to avoid coupling). Hashes captured first-run: + `show__Int 891eaebe64b3180d`, `show__Bool 1bdb621494e50607`, + `show__Str 3f1d57c90ddce081`, `show__Float 5ab0bdd40b9f8b8f`. +- iter 24.2.7: DESIGN.md §"Prelude (built-in) classes" amended: + milestone-23 paragraph drops "Show" from the deferred list and notes + Show ships in milestone 24; new milestone-24 paragraph names class + signature, instance set (including Float), body shape (single-app + lambda invoking runtime primitive), and the iter-24.3 carry-forward + for `print`-rewire. +- iter 24.2.8: Workspace tests 552 passed / 0 failed + (was 548 pre-iter, +4 new: 1 `show_mono_synthesis` + 2 `show_dispatch_pin` + + 1 hash-stability). `bench/compile_check.py` exit 0 (24/24 stable). + `bench/cross_lang.py` exit 0 (25/25 stable). `bench/check.py` exit 1 + with 2 noise-class metrics (`bench_list_sum.bump_s` +11.71% — 4th + consecutive sighting per `audit-mq` / `mq.tidy` lineage; + `latency.implicit_at_rc.max_us` +145.55% — 7th consecutive sighting of + the documented noise envelope, metric-identity-migrating across runs). + Baseline pristine per the conservative-call convention. + +## Concerns + +- **Plan undercount on 22b migration scope.** The plan's "2 Rust test + files" carrier (typeclass_22b{2,3}.rs) missed 4 additional consumer + sites that hardcode the renamed literals: (a) + `crates/ailang-core/src/hash.rs` ct4 canonical-form hash pin (2 + entries); (b) `crates/ailang-core/src/workspace.rs` 3 in-crate iter22b1 + tests; (c) `crates/ailang-prose/tests/snapshot.rs` (1 snapshot file at + `examples/test_22b2_instance_present.prose.txt`); (d) one + `mono_symbol_compound_type_uses_hash_suffix` assertion in typeclass_22b3.rs + that called `mono_symbol("tshow", ...)` (post my batch rename) but + still asserted `name.starts_with("show__")`. All fixed inline per the + precedent of mq.1 / mq.3 "Plan defects fixed inline" — fixture parse + was the only "green" pre-iter signal the plan's Step 3/4 anchored on, + which masked the broader downstream consumer surface. Suggested + Boss-side reaction: future migration plans run an upfront + `grep -rln '""' crates/` to enumerate the full + consumer set before committing to a per-file enumeration. +- **`str_clone` reachability is implicit.** Pre-flight note 3 flagged + this as needing first-round-trip verification; verification is + implicit in `primitive_show_mono_symbols_synthesise` passing + (mono of `show 42 / true / "x" / 1.5` requires the four `show__T` + bodies to typecheck, including the `str_clone` call in `show__Str`). + No explicit isolated unit test for `str_clone`-in-lambda was added + because the integration pin is strictly stronger; consider an + isolated unit pin only if a future regression makes the integration + test slow to bisect. +- **Hash-stability pin uses a separate fixture, not the existing + `mono_hash_pin_smoke.ail.json`.** Plan Task 6 offered both options; + the separate-fixture path was chosen because `show_mono_pin_smoke.ail.json` + already existed (minted in Task 4) and reusing it kept the Eq/Ord + pin structurally isolated. Both fixtures live side-by-side; the + second test function in `mono_hash_stability.rs` mirrors the first + byte-for-byte modulo the fixture path and pin table. +- **`bench/check.py` exit 1 is the documented recurring noise envelope.** + `latency.implicit_at_rc.*` max-tail metric has been migrating + between runs since `audit-cma` (2026-05-12) — 7th consecutive + observation; `bench_list_sum.bump_s` is the 4th since `audit-mq`. + Plan Task 8 Step 4 explicitly anticipates "1-3 regressed metrics + ratify-without-attribution per conservative-call convention"; this + iter follows that. The baseline stays pristine. + +## Known debt + +- **iter 24.3 still pending.** Carries `print : forall a. Show a => a -> () !IO` + rewire to route through `show` + `io/print_str`. Spec at + `docs/specs/2026-05-13-24-show-print.md`. 24.2 is structurally + complete; 24.3 is operationally distinct. +- **mq1 cross-module fixtures unmigrated by design.** + `examples/mq1_xmod_constraint_class.ail.json` and `..._dep.ail.json` + reference `mq1_xmod_constraint_class_dep.Show` (cross-module, fully + qualified) and ship zero `show` call sites; no dispatch ambiguity + arises once `prelude.Show` lands. They stay as-is per the plan's + pre-flight ratification. +- **mq3 fixtures deliberately retain two-Show shapes.** + `examples/mq3_two_show_*` fixtures test the multi-class dispatch + scenarios (ambiguity + qualifier resolution) and stay unchanged in + 24.2 by design. + +## Files touched + +**Created (3):** +- `crates/ail/tests/show_mono_synthesis.rs` +- `crates/ailang-check/tests/show_dispatch_pin.rs` +- `examples/show_mono_pin_smoke.ail.json` + +**Modified (29):** + +Prelude + DESIGN: +- `examples/prelude.ail.json` (+5 defs) +- `docs/DESIGN.md` (§"Prelude (built-in) classes" amendment) + +22b fixture migration (21 files): +- `examples/test_22b1_dup_a.ail.json` +- `examples/test_22b1_dup_b.ail.json` +- `examples/test_22b1_dup_classmod.ail.json` +- `examples/test_22b1_dup_same_module.ail.json` +- `examples/test_22b1_orphan_class.ail.json` +- `examples/test_22b1_orphan_third.ail.json` +- `examples/test_22b1_orphan_third_classmod.ail.json` +- `examples/test_22b2_class_method_lookup.ail.json` +- `examples/test_22b2_constraint_declared.ail.json` +- `examples/test_22b2_instance_present.ail.json` +- `examples/test_22b2_missing_constraint.ail.json` +- `examples/test_22b2_no_instance.ail.json` +- `examples/test_22b2_two_fns_missing_constraint.ail.json` +- `examples/test_22b2_xmod_classmod.ail.json` +- `examples/test_22b2_xmod_classmod_noinst.ail.json` +- `examples/test_22b2_xmod_instance_present.ail.json` +- `examples/test_22b2_xmod_missing_constraint.ail.json` +- `examples/test_22b2_xmod_no_instance.ail.json` +- `examples/test_22b3_dup_call_sites.ail.json` +- `examples/test_22b3_no_call_sites.ail.json` +- `examples/test_22b3_shadow_class_method.ail.json` + +22b consumer-test migration (5 source files + 1 snapshot): +- `crates/ail/tests/typeclass_22b2.rs` +- `crates/ail/tests/typeclass_22b3.rs` +- `crates/ailang-core/src/hash.rs` (ct4 hash pin) +- `crates/ailang-core/src/workspace.rs` (3 iter22b1 tests) +- `crates/ail/tests/mono_hash_stability.rs` (new show test fn) +- `examples/test_22b2_instance_present.prose.txt` (snapshot accept) + +## Stats + +`bench/orchestrator-stats/2026-05-13-iter-24.2.json` diff --git a/examples/prelude.ail.json b/examples/prelude.ail.json index dae432e..f0d08cf 100644 --- a/examples/prelude.ail.json +++ b/examples/prelude.ail.json @@ -219,6 +219,108 @@ } ] }, + { + "kind": "class", + "name": "Show", + "param": "a", + "doc": "Producer of a human-readable Str representation. Ships in milestone 24 with primitive instances for Int/Bool/Str/Float; user types declare their own instance.", + "methods": [ + { + "name": "show", + "type": { + "k": "fn", + "params": [{ "k": "var", "name": "a" }], + "param_modes": ["borrow"], + "ret": { "k": "con", "name": "Str" }, + "effects": [] + } + } + ] + }, + { + "kind": "instance", + "class": "Show", + "type": { "k": "con", "name": "Int" }, + "methods": [ + { + "name": "show", + "body": { + "t": "lam", + "params": ["x"], + "paramTypes": [{ "k": "con", "name": "Int" }], + "retType": { "k": "con", "name": "Str" }, + "body": { + "t": "app", + "fn": { "t": "var", "name": "int_to_str" }, + "args": [{ "t": "var", "name": "x" }] + } + } + } + ] + }, + { + "kind": "instance", + "class": "Show", + "type": { "k": "con", "name": "Bool" }, + "methods": [ + { + "name": "show", + "body": { + "t": "lam", + "params": ["x"], + "paramTypes": [{ "k": "con", "name": "Bool" }], + "retType": { "k": "con", "name": "Str" }, + "body": { + "t": "app", + "fn": { "t": "var", "name": "bool_to_str" }, + "args": [{ "t": "var", "name": "x" }] + } + } + } + ] + }, + { + "kind": "instance", + "class": "Show", + "type": { "k": "con", "name": "Str" }, + "methods": [ + { + "name": "show", + "body": { + "t": "lam", + "params": ["x"], + "paramTypes": [{ "k": "con", "name": "Str" }], + "retType": { "k": "con", "name": "Str" }, + "body": { + "t": "app", + "fn": { "t": "var", "name": "str_clone" }, + "args": [{ "t": "var", "name": "x" }] + } + } + } + ] + }, + { + "kind": "instance", + "class": "Show", + "type": { "k": "con", "name": "Float" }, + "methods": [ + { + "name": "show", + "body": { + "t": "lam", + "params": ["x"], + "paramTypes": [{ "k": "con", "name": "Float" }], + "retType": { "k": "con", "name": "Str" }, + "body": { + "t": "app", + "fn": { "t": "var", "name": "float_to_str" }, + "args": [{ "t": "var", "name": "x" }] + } + } + } + ] + }, { "kind": "fn", "name": "ne", diff --git a/examples/show_mono_pin_smoke.ail.json b/examples/show_mono_pin_smoke.ail.json new file mode 100644 index 0000000..c7df4ed --- /dev/null +++ b/examples/show_mono_pin_smoke.ail.json @@ -0,0 +1,49 @@ +{ + "schema": "ailang/v0", + "name": "show_mono_pin_smoke", + "imports": [], + "defs": [ + { + "kind": "fn", + "name": "main", + "type": { + "k": "fn", + "params": [], + "ret": { "k": "con", "name": "Unit" }, + "effects": ["IO"] + }, + "params": [], + "body": { + "t": "let", + "name": "s1", + "value": { "t": "app", + "fn": { "t": "var", "name": "show" }, + "args": [{ "t": "lit", "lit": { "kind": "int", "value": 42 } }] }, + "body": { + "t": "let", + "name": "s2", + "value": { "t": "app", + "fn": { "t": "var", "name": "show" }, + "args": [{ "t": "lit", "lit": { "kind": "bool", "value": true } }] }, + "body": { + "t": "let", + "name": "s3", + "value": { "t": "app", + "fn": { "t": "var", "name": "show" }, + "args": [{ "t": "lit", "lit": { "kind": "str", "value": "x" } }] }, + "body": { + "t": "let", + "name": "s4", + "value": { "t": "app", + "fn": { "t": "var", "name": "show" }, + "args": [{ "t": "lit", "lit": { "kind": "float", "bits": "3ff8000000000000" } }] }, + "body": { "t": "do", + "op": "io/print_str", + "args": [{ "t": "var", "name": "s1" }] } + } + } + } + } + } + ] +} diff --git a/examples/test_22b1_dup_a.ail.json b/examples/test_22b1_dup_a.ail.json index c9e687e..dd6d870 100644 --- a/examples/test_22b1_dup_a.ail.json +++ b/examples/test_22b1_dup_a.ail.json @@ -12,14 +12,14 @@ "defs": [ { "kind": "instance", - "class": "test_22b1_dup_classmod.Show", + "class": "test_22b1_dup_classmod.TShow", "type": { "k": "con", "name": "test_22b1_dup_b.MyInt" }, "methods": [ { - "name": "show", + "name": "tshow", "body": { "t": "lit", "lit": { diff --git a/examples/test_22b1_dup_b.ail.json b/examples/test_22b1_dup_b.ail.json index bb9b02d..d5a8a83 100644 --- a/examples/test_22b1_dup_b.ail.json +++ b/examples/test_22b1_dup_b.ail.json @@ -17,11 +17,11 @@ }, { "kind": "instance", - "class": "test_22b1_dup_classmod.Show", + "class": "test_22b1_dup_classmod.TShow", "type": { "k": "con", "name": "MyInt" }, "methods": [ { - "name": "show", + "name": "tshow", "body": { "t": "lit", "lit": { "kind": "str", "value": "from-b" } } } ] diff --git a/examples/test_22b1_dup_classmod.ail.json b/examples/test_22b1_dup_classmod.ail.json index cd113a4..7d90ff1 100644 --- a/examples/test_22b1_dup_classmod.ail.json +++ b/examples/test_22b1_dup_classmod.ail.json @@ -5,11 +5,11 @@ "defs": [ { "kind": "class", - "name": "Show", + "name": "TShow", "param": "a", "methods": [ { - "name": "show", + "name": "tshow", "type": { "k": "fn", "params": [{ "k": "var", "name": "a" }], diff --git a/examples/test_22b1_dup_same_module.ail.json b/examples/test_22b1_dup_same_module.ail.json index a27d38d..65d1a83 100644 --- a/examples/test_22b1_dup_same_module.ail.json +++ b/examples/test_22b1_dup_same_module.ail.json @@ -5,11 +5,11 @@ "defs": [ { "kind": "class", - "name": "Show", + "name": "TShow", "param": "a", "methods": [ { - "name": "show", + "name": "tshow", "type": { "k": "fn", "params": [{ "k": "var", "name": "a" }], @@ -21,22 +21,22 @@ }, { "kind": "instance", - "class": "Show", + "class": "TShow", "type": { "k": "con", "name": "Int" }, "methods": [ { - "name": "show", + "name": "tshow", "body": { "t": "lit", "lit": { "kind": "str", "value": "first" } } } ] }, { "kind": "instance", - "class": "Show", + "class": "TShow", "type": { "k": "con", "name": "Int" }, "methods": [ { - "name": "show", + "name": "tshow", "body": { "t": "lit", "lit": { "kind": "str", "value": "second" } } } ] diff --git a/examples/test_22b1_orphan_class.ail.json b/examples/test_22b1_orphan_class.ail.json index 61b9430..894a6de 100644 --- a/examples/test_22b1_orphan_class.ail.json +++ b/examples/test_22b1_orphan_class.ail.json @@ -5,11 +5,11 @@ "defs": [ { "kind": "class", - "name": "Show", + "name": "TShow", "param": "a", "methods": [ { - "name": "show", + "name": "tshow", "type": { "k": "fn", "params": [{ "k": "var", "name": "a" }], @@ -21,11 +21,11 @@ }, { "kind": "instance", - "class": "Show", + "class": "TShow", "type": { "k": "con", "name": "Int" }, "methods": [ { - "name": "show", + "name": "tshow", "body": { "t": "lit", "lit": { "kind": "str", "value": "" } } } ] diff --git a/examples/test_22b1_orphan_third.ail.json b/examples/test_22b1_orphan_third.ail.json index fe9bab9..7c30c8f 100644 --- a/examples/test_22b1_orphan_third.ail.json +++ b/examples/test_22b1_orphan_third.ail.json @@ -7,11 +7,11 @@ "defs": [ { "kind": "instance", - "class": "test_22b1_orphan_third_classmod.Show", + "class": "test_22b1_orphan_third_classmod.TShow", "type": { "k": "con", "name": "Int" }, "methods": [ { - "name": "show", + "name": "tshow", "body": { "t": "lit", "lit": { "kind": "str", "value": "" } } } ] diff --git a/examples/test_22b1_orphan_third_classmod.ail.json b/examples/test_22b1_orphan_third_classmod.ail.json index 4fa00a2..67bf0bb 100644 --- a/examples/test_22b1_orphan_third_classmod.ail.json +++ b/examples/test_22b1_orphan_third_classmod.ail.json @@ -5,11 +5,11 @@ "defs": [ { "kind": "class", - "name": "Show", + "name": "TShow", "param": "a", "methods": [ { - "name": "show", + "name": "tshow", "type": { "k": "fn", "params": [{ "k": "var", "name": "a" }], diff --git a/examples/test_22b2_class_method_lookup.ail.json b/examples/test_22b2_class_method_lookup.ail.json index 543311f..e6e9015 100644 --- a/examples/test_22b2_class_method_lookup.ail.json +++ b/examples/test_22b2_class_method_lookup.ail.json @@ -5,11 +5,11 @@ "defs": [ { "kind": "class", - "name": "Show", + "name": "TShow", "param": "a", "methods": [ { - "name": "show", + "name": "tshow", "type": { "k": "fn", "params": [{ "k": "var", "name": "a" }], @@ -21,11 +21,11 @@ }, { "kind": "instance", - "class": "Show", + "class": "TShow", "type": { "k": "con", "name": "Int" }, "methods": [ { - "name": "show", + "name": "tshow", "body": { "t": "lit", "lit": { "kind": "str", "value": "n" } } } ] diff --git a/examples/test_22b2_constraint_declared.ail.json b/examples/test_22b2_constraint_declared.ail.json index 0d5ef8c..cf19dce 100644 --- a/examples/test_22b2_constraint_declared.ail.json +++ b/examples/test_22b2_constraint_declared.ail.json @@ -5,11 +5,11 @@ "defs": [ { "kind": "class", - "name": "Show", + "name": "TShow", "param": "a", "methods": [ { - "name": "show", + "name": "tshow", "type": { "k": "fn", "params": [{ "k": "var", "name": "a" }], @@ -26,7 +26,7 @@ "k": "forall", "vars": ["a"], "constraints": [ - { "class": "Show", "type": { "k": "var", "name": "a" } } + { "class": "TShow", "type": { "k": "var", "name": "a" } } ], "body": { "k": "fn", @@ -38,7 +38,7 @@ "params": ["x"], "body": { "t": "app", - "fn": { "t": "var", "name": "show" }, + "fn": { "t": "var", "name": "tshow" }, "args": [{ "t": "var", "name": "x" }] } } diff --git a/examples/test_22b2_instance_present.ail.json b/examples/test_22b2_instance_present.ail.json index 1b6634a..7004403 100644 --- a/examples/test_22b2_instance_present.ail.json +++ b/examples/test_22b2_instance_present.ail.json @@ -4,9 +4,9 @@ "imports": [], "defs": [ { - "kind": "class", "name": "Show", "param": "a", + "kind": "class", "name": "TShow", "param": "a", "methods": [{ - "name": "show", + "name": "tshow", "type": { "k": "fn", "params": [{ "k": "var", "name": "a" }], "ret": { "k": "con", "name": "Str" }, "effects": [] @@ -15,10 +15,10 @@ }, { "kind": "instance", - "class": "Show", + "class": "TShow", "type": { "k": "con", "name": "Int" }, "methods": [{ - "name": "show", + "name": "tshow", "body": { "t": "lam", "params": ["x"], @@ -38,7 +38,7 @@ "params": [], "body": { "t": "app", - "fn": { "t": "var", "name": "show" }, + "fn": { "t": "var", "name": "tshow" }, "args": [{ "t": "lit", "lit": { "kind": "int", "value": 42 } }] } } diff --git a/examples/test_22b2_instance_present.prose.txt b/examples/test_22b2_instance_present.prose.txt index d7c1459..806f65a 100644 --- a/examples/test_22b2_instance_present.prose.txt +++ b/examples/test_22b2_instance_present.prose.txt @@ -1,11 +1,11 @@ // module test_22b2_instance_present -class Show a { - fn show(x: a) -> Str +class TShow a { + fn tshow(x: a) -> Str } -instance Show Int { - fn show { +instance TShow Int { + fn tshow { |x: a| -> Str { "n" } @@ -13,5 +13,5 @@ instance Show Int { } fn main() -> Str { - show(42) + tshow(42) } diff --git a/examples/test_22b2_missing_constraint.ail.json b/examples/test_22b2_missing_constraint.ail.json index a5b4d12..be2fd5b 100644 --- a/examples/test_22b2_missing_constraint.ail.json +++ b/examples/test_22b2_missing_constraint.ail.json @@ -5,11 +5,11 @@ "defs": [ { "kind": "class", - "name": "Show", + "name": "TShow", "param": "a", "methods": [ { - "name": "show", + "name": "tshow", "type": { "k": "fn", "params": [{ "k": "var", "name": "a" }], @@ -35,7 +35,7 @@ "params": ["x"], "body": { "t": "app", - "fn": { "t": "var", "name": "show" }, + "fn": { "t": "var", "name": "tshow" }, "args": [{ "t": "var", "name": "x" }] } } diff --git a/examples/test_22b2_no_instance.ail.json b/examples/test_22b2_no_instance.ail.json index 2c5005e..5805590 100644 --- a/examples/test_22b2_no_instance.ail.json +++ b/examples/test_22b2_no_instance.ail.json @@ -5,11 +5,11 @@ "defs": [ { "kind": "class", - "name": "Show", + "name": "TShow", "param": "a", "methods": [ { - "name": "show", + "name": "tshow", "type": { "k": "fn", "params": [{ "k": "var", "name": "a" }], @@ -31,7 +31,7 @@ "params": [], "body": { "t": "app", - "fn": { "t": "var", "name": "show" }, + "fn": { "t": "var", "name": "tshow" }, "args": [{ "t": "lit", "lit": { "kind": "int", "value": 42 } }] } } diff --git a/examples/test_22b2_two_fns_missing_constraint.ail.json b/examples/test_22b2_two_fns_missing_constraint.ail.json index 5e915ae..e6c2ed1 100644 --- a/examples/test_22b2_two_fns_missing_constraint.ail.json +++ b/examples/test_22b2_two_fns_missing_constraint.ail.json @@ -5,11 +5,11 @@ "defs": [ { "kind": "class", - "name": "Show", + "name": "TShow", "param": "a", "methods": [ { - "name": "show", + "name": "tshow", "type": { "k": "fn", "params": [{ "k": "var", "name": "a" }], @@ -35,7 +35,7 @@ "params": ["x"], "body": { "t": "app", - "fn": { "t": "var", "name": "show" }, + "fn": { "t": "var", "name": "tshow" }, "args": [{ "t": "var", "name": "x" }] } }, @@ -55,7 +55,7 @@ "params": ["y"], "body": { "t": "app", - "fn": { "t": "var", "name": "show" }, + "fn": { "t": "var", "name": "tshow" }, "args": [{ "t": "var", "name": "y" }] } } diff --git a/examples/test_22b2_xmod_classmod.ail.json b/examples/test_22b2_xmod_classmod.ail.json index baa95ab..013023f 100644 --- a/examples/test_22b2_xmod_classmod.ail.json +++ b/examples/test_22b2_xmod_classmod.ail.json @@ -5,11 +5,11 @@ "defs": [ { "kind": "class", - "name": "Show", + "name": "TShow", "param": "a", "methods": [ { - "name": "show", + "name": "tshow", "type": { "k": "fn", "params": [{ "k": "var", "name": "a" }], @@ -21,11 +21,11 @@ }, { "kind": "instance", - "class": "Show", + "class": "TShow", "type": { "k": "con", "name": "Int" }, "methods": [ { - "name": "show", + "name": "tshow", "body": { "t": "lam", "params": ["x"], diff --git a/examples/test_22b2_xmod_classmod_noinst.ail.json b/examples/test_22b2_xmod_classmod_noinst.ail.json index d8590cd..d9cec8d 100644 --- a/examples/test_22b2_xmod_classmod_noinst.ail.json +++ b/examples/test_22b2_xmod_classmod_noinst.ail.json @@ -5,11 +5,11 @@ "defs": [ { "kind": "class", - "name": "Show", + "name": "TShow", "param": "a", "methods": [ { - "name": "show", + "name": "tshow", "type": { "k": "fn", "params": [{ "k": "var", "name": "a" }], diff --git a/examples/test_22b2_xmod_instance_present.ail.json b/examples/test_22b2_xmod_instance_present.ail.json index cbbc6c4..959a69e 100644 --- a/examples/test_22b2_xmod_instance_present.ail.json +++ b/examples/test_22b2_xmod_instance_present.ail.json @@ -15,7 +15,7 @@ "params": [], "body": { "t": "app", - "fn": { "t": "var", "name": "show" }, + "fn": { "t": "var", "name": "tshow" }, "args": [{ "t": "lit", "lit": { "kind": "int", "value": 42 } }] } } diff --git a/examples/test_22b2_xmod_missing_constraint.ail.json b/examples/test_22b2_xmod_missing_constraint.ail.json index 158b419..27da36c 100644 --- a/examples/test_22b2_xmod_missing_constraint.ail.json +++ b/examples/test_22b2_xmod_missing_constraint.ail.json @@ -19,7 +19,7 @@ "params": ["x"], "body": { "t": "app", - "fn": { "t": "var", "name": "show" }, + "fn": { "t": "var", "name": "tshow" }, "args": [{ "t": "var", "name": "x" }] } } diff --git a/examples/test_22b2_xmod_no_instance.ail.json b/examples/test_22b2_xmod_no_instance.ail.json index 458224e..9217612 100644 --- a/examples/test_22b2_xmod_no_instance.ail.json +++ b/examples/test_22b2_xmod_no_instance.ail.json @@ -15,7 +15,7 @@ "params": [], "body": { "t": "app", - "fn": { "t": "var", "name": "show" }, + "fn": { "t": "var", "name": "tshow" }, "args": [{ "t": "lit", "lit": { "kind": "int", "value": 42 } }] } } diff --git a/examples/test_22b3_dup_call_sites.ail.json b/examples/test_22b3_dup_call_sites.ail.json index 63a44444..67834f3 100644 --- a/examples/test_22b3_dup_call_sites.ail.json +++ b/examples/test_22b3_dup_call_sites.ail.json @@ -4,9 +4,9 @@ "imports": [], "defs": [ { - "kind": "class", "name": "Show", "param": "a", + "kind": "class", "name": "TShow", "param": "a", "methods": [{ - "name": "show", + "name": "tshow", "type": { "k": "fn", "params": [{ "k": "var", "name": "a" }], "ret": { "k": "con", "name": "Str" }, "effects": [] @@ -15,10 +15,10 @@ }, { "kind": "instance", - "class": "Show", + "class": "TShow", "type": { "k": "con", "name": "Int" }, "methods": [{ - "name": "show", + "name": "tshow", "body": { "t": "lam", "params": ["x"], @@ -41,12 +41,12 @@ "name": "_a", "value": { "t": "app", - "fn": { "t": "var", "name": "show" }, + "fn": { "t": "var", "name": "tshow" }, "args": [{ "t": "lit", "lit": { "kind": "int", "value": 5 } }] }, "body": { "t": "app", - "fn": { "t": "var", "name": "show" }, + "fn": { "t": "var", "name": "tshow" }, "args": [{ "t": "lit", "lit": { "kind": "int", "value": 7 } }] } } diff --git a/examples/test_22b3_no_call_sites.ail.json b/examples/test_22b3_no_call_sites.ail.json index b72d73f..c4d8c39 100644 --- a/examples/test_22b3_no_call_sites.ail.json +++ b/examples/test_22b3_no_call_sites.ail.json @@ -4,9 +4,9 @@ "imports": [], "defs": [ { - "kind": "class", "name": "Show", "param": "a", + "kind": "class", "name": "TShow", "param": "a", "methods": [{ - "name": "show", + "name": "tshow", "type": { "k": "fn", "params": [{ "k": "var", "name": "a" }], "ret": { "k": "con", "name": "Str" }, "effects": [] @@ -15,10 +15,10 @@ }, { "kind": "instance", - "class": "Show", + "class": "TShow", "type": { "k": "con", "name": "Int" }, "methods": [{ - "name": "show", + "name": "tshow", "body": { "t": "lam", "params": ["x"], diff --git a/examples/test_22b3_shadow_class_method.ail.json b/examples/test_22b3_shadow_class_method.ail.json index c6ffbeb..5378cda 100644 --- a/examples/test_22b3_shadow_class_method.ail.json +++ b/examples/test_22b3_shadow_class_method.ail.json @@ -4,9 +4,9 @@ "imports": [], "defs": [ { - "kind": "class", "name": "Show", "param": "a", + "kind": "class", "name": "TShow", "param": "a", "methods": [{ - "name": "show", + "name": "tshow", "type": { "k": "fn", "params": [{ "k": "var", "name": "a" }], "ret": { "k": "con", "name": "Str" }, "effects": [] @@ -25,10 +25,10 @@ }, { "kind": "instance", - "class": "Show", + "class": "TShow", "type": { "k": "con", "name": "Int" }, "methods": [{ - "name": "show", + "name": "tshow", "body": { "t": "lam", "params": ["x"], @@ -66,17 +66,17 @@ "name": "_t", "value": { "t": "app", - "fn": { "t": "var", "name": "show" }, + "fn": { "t": "var", "name": "tshow" }, "args": [{ "t": "lit", "lit": { "kind": "int", "value": 5 } }] }, "body": { "t": "let", - "name": "show", + "name": "tshow", "value": { "t": "lit", "lit": { "kind": "str", "value": "shadow" } }, "body": { "t": "let", "name": "_u", - "value": { "t": "var", "name": "show" }, + "value": { "t": "var", "name": "tshow" }, "body": { "t": "app", "fn": { "t": "var", "name": "foo" },