iter 24.2: class Show + 4 primitive instances + 22b TShow migration
Ships class Show a where show : (a borrow) -> Str in the prelude
plus primitive Show Int / Bool / Str / Float instances. 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. Float is included in Show (unlike
Eq/Ord) because textual representation is well-defined modulo the
NaN-spelling caveat at DESIGN.md §Float semantics.
The 22b typeclass test corpus is migrated preemptively: 21 fixture
files and 6 consumer files (typeclass_22b{2,3}.rs +
hash.rs ct4 pin + workspace.rs iter22b1 tests + the
instance_present.prose.txt snapshot) rename class Show / show
to class TShow / tshow, analogous to the existing TEq/TOrd
convention. Migration runs before the prelude additions so the
workspace stays green throughout.
Three new tests pin the post-mono shape: show_mono_synthesis.rs
(existence of show__Int/Bool/Str/Float as Def::Fn entries in the
prelude post-mono module), show_dispatch_pin.rs (bare show and
tshow both Step-2 singletons workspace-globally),
mono_hash_stability.rs::primitive_show_mono_symbol_hashes_stay_bit_identical
(body hashes pinned for the 4 new mono symbols).
DESIGN.md §Prelude (built-in) classes drops Show from the
deferred-features list and appends a milestone-24 paragraph
naming the class signature + the 4 instances + the body shape.
print rewire stays deferred to iter 24.3.
Tests: 552 passed (was 548 + 4 new). bench/compile_check +
bench/cross_lang exit 0. bench/check exits 1 on the recurring
noise envelope per the conservative-call lineage.
This commit is contained in:
@@ -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."
|
||||
}
|
||||
@@ -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}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
// `<defining_module>.<Class>`.
|
||||
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
|
||||
|
||||
@@ -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 <x>` 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 <body>`.
|
||||
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
|
||||
|
||||
@@ -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:?}"
|
||||
);
|
||||
}
|
||||
@@ -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"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
+20
-6
@@ -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 <T>` instance body is a single-application lambda invoking
|
||||
the corresponding runtime primitive (`int_to_str`, `bool_to_str`,
|
||||
`str_clone`, `float_to_str`); no codegen intercept is required. The
|
||||
polymorphic helper `print : forall a. Show a => a -> () !IO` 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.
|
||||
|
||||
@@ -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
|
||||
(`"<modname>.Show"` → `"<modname>.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 '"<rename target>"' 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`
|
||||
@@ -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",
|
||||
|
||||
@@ -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" }] }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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": {
|
||||
|
||||
@@ -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" } }
|
||||
}
|
||||
]
|
||||
|
||||
@@ -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" }],
|
||||
|
||||
@@ -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" } }
|
||||
}
|
||||
]
|
||||
|
||||
@@ -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": "<int>" } }
|
||||
}
|
||||
]
|
||||
|
||||
@@ -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": "<int>" } }
|
||||
}
|
||||
]
|
||||
|
||||
@@ -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" }],
|
||||
|
||||
@@ -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" } }
|
||||
}
|
||||
]
|
||||
|
||||
@@ -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" }]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 } }]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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" }]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 } }]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" }]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"],
|
||||
|
||||
@@ -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" }],
|
||||
|
||||
@@ -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 } }]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"params": ["x"],
|
||||
"body": {
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "show" },
|
||||
"fn": { "t": "var", "name": "tshow" },
|
||||
"args": [{ "t": "var", "name": "x" }]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 } }]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 } }]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"],
|
||||
|
||||
@@ -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" },
|
||||
|
||||
Reference in New Issue
Block a user