//! Pins the Float-aware `NoInstance` diagnostic added in milestone 23. //! //! Property protected: a polymorphic Eq/Ord helper invoked at Float //! fires `no-instance` (since Float has neither Eq nor Ord instance //! per design/contracts/0005-float-semantics.md) AND the diagnostic message //! cross-references the canonical float-semantics contract so the //! LLM author immediately learns the partial-Float story rather //! than seeing a bare "no instance" message. use ailang_check::check_workspace; use ailang_surface::load_workspace; use std::path::PathBuf; fn fixture(name: &str) -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("../../examples") .join(name) } #[test] fn eq_at_float_fires_float_aware_noinstance() { let ws = load_workspace(&fixture("eq_float_noinstance.ail")).expect("load"); let diags = check_workspace(&ws); assert!(!diags.is_empty(), "expected NoInstance diagnostic, got none"); let no_inst = diags .iter() .find(|d| d.code == "no-instance") .unwrap_or_else(|| panic!("expected diagnostic with code 'no-instance', got: {diags:#?}")); assert!( no_inst.message.contains("Float"), "expected Float-aware NoInstance diagnostic, got message: {:?}", no_inst.message ); // Cross-ref to the float-semantics contract — the LLM author should // be pointed at the canonical explanation of partial Float orderability. assert!( no_inst.message.contains("design/contracts/0005-float-semantics.md"), "expected NoInstance message to cross-reference the float-semantics contract, got: {:?}", no_inst.message ); // 2026-05-21 operator-routing-eq-ord: the addendum names the // explicit IEEE-aware alternative `float_eq` (and siblings) // alongside the contract cross-reference, so the LLM author // learns the named-fn surface from the diagnostic itself. assert!( no_inst.message.contains("float_eq"), "expected NoInstance message to name `float_eq` as the explicit IEEE-aware alternative, got: {:?}", no_inst.message ); } /// #25: the Float-aware `NoInstance` addendum branches on the called /// method. `compare` returns a three-way `Ordering`, but no `float_compare` /// ships — a deliberate non-ship per the operator-routing-eq-ord spec. The /// `eq`/`ne`/`lt`/`le`/`gt`/`ge` arm keeps the existing wording (the boolean /// `float_*` siblings are drop-in replacements); the `compare` arm must /// instead acknowledge the omission and point the author at building a /// three-way result from `float_lt` + `float_eq`, rather than listing the /// boolean siblings as if a `float_compare` existed. #[test] fn compare_at_float_names_deliberate_no_float_compare() { let ws = load_workspace(&fixture("compare_float_noinstance.ail")).expect("load"); let diags = check_workspace(&ws); let no_inst = diags .iter() .find(|d| d.code == "no-instance") .unwrap_or_else(|| panic!("expected diagnostic with code 'no-instance', got: {diags:#?}")); assert!( no_inst.message.contains("Float"), "expected Float-aware NoInstance diagnostic, got: {:?}", no_inst.message ); assert!( no_inst.message.contains("design/contracts/0005-float-semantics.md"), "expected the float-semantics contract cross-reference, got: {:?}", no_inst.message ); // The compare-specific branch: name the deliberate `float_compare` // non-ship and the `float_lt` + `float_eq` building blocks. assert!( no_inst.message.contains("float_compare"), "expected the compare addendum to name the deliberate `float_compare` non-ship, got: {:?}", no_inst.message ); assert!( no_inst.message.contains("float_lt") && no_inst.message.contains("float_eq"), "expected the compare addendum to name float_lt + float_eq as the three-way building blocks, got: {:?}", no_inst.message ); }