From 93fb3334f94b2f229b73975a2b46c67bb1f6b8ad Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Sun, 12 Jul 2026 16:01:44 +0530 Subject: [PATCH 1/5] test: Add cross-crate generic instantiation test --- .../generic_reuse/.cargo/config.toml | 1 + tests/multicrate/generic_reuse/Cargo.lock | 14 ++++++ tests/multicrate/generic_reuse/Cargo.toml | 9 ++++ .../generic_reuse/provider/.cargo/config.toml | 1 + .../generic_reuse/provider/Cargo.toml | 8 +++ .../generic_reuse/provider/src/lib.rs | 49 +++++++++++++++++++ tests/multicrate/generic_reuse/src/main.rs | 37 ++++++++++++++ 7 files changed, 119 insertions(+) create mode 120000 tests/multicrate/generic_reuse/.cargo/config.toml create mode 100644 tests/multicrate/generic_reuse/Cargo.lock create mode 100644 tests/multicrate/generic_reuse/Cargo.toml create mode 120000 tests/multicrate/generic_reuse/provider/.cargo/config.toml create mode 100644 tests/multicrate/generic_reuse/provider/Cargo.toml create mode 100644 tests/multicrate/generic_reuse/provider/src/lib.rs create mode 100644 tests/multicrate/generic_reuse/src/main.rs diff --git a/tests/multicrate/generic_reuse/.cargo/config.toml b/tests/multicrate/generic_reuse/.cargo/config.toml new file mode 120000 index 0000000..09e5e30 --- /dev/null +++ b/tests/multicrate/generic_reuse/.cargo/config.toml @@ -0,0 +1 @@ +../../../../config.toml \ No newline at end of file diff --git a/tests/multicrate/generic_reuse/Cargo.lock b/tests/multicrate/generic_reuse/Cargo.lock new file mode 100644 index 0000000..34a577a --- /dev/null +++ b/tests/multicrate/generic_reuse/Cargo.lock @@ -0,0 +1,14 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "generic_reuse" +version = "0.1.0" +dependencies = [ + "provider", +] + +[[package]] +name = "provider" +version = "0.1.0" diff --git a/tests/multicrate/generic_reuse/Cargo.toml b/tests/multicrate/generic_reuse/Cargo.toml new file mode 100644 index 0000000..996a212 --- /dev/null +++ b/tests/multicrate/generic_reuse/Cargo.toml @@ -0,0 +1,9 @@ +cargo-features = ["profile-rustflags"] + +[package] +name = "generic_reuse" +version = "0.1.0" +edition = "2024" + +[dependencies] +provider = { path = "provider" } diff --git a/tests/multicrate/generic_reuse/provider/.cargo/config.toml b/tests/multicrate/generic_reuse/provider/.cargo/config.toml new file mode 120000 index 0000000..09e5e30 --- /dev/null +++ b/tests/multicrate/generic_reuse/provider/.cargo/config.toml @@ -0,0 +1 @@ +../../../../config.toml \ No newline at end of file diff --git a/tests/multicrate/generic_reuse/provider/Cargo.toml b/tests/multicrate/generic_reuse/provider/Cargo.toml new file mode 100644 index 0000000..8cc4fee --- /dev/null +++ b/tests/multicrate/generic_reuse/provider/Cargo.toml @@ -0,0 +1,8 @@ +cargo-features = ["profile-rustflags"] + +[package] +name = "provider" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/tests/multicrate/generic_reuse/provider/src/lib.rs b/tests/multicrate/generic_reuse/provider/src/lib.rs new file mode 100644 index 0000000..de49a1e --- /dev/null +++ b/tests/multicrate/generic_reuse/provider/src/lib.rs @@ -0,0 +1,49 @@ +// Provider crate for the generic_reuse regression test. The downstream binary +// instantiates these generics with types the provider never used, and re-uses +// instantiations the provider only partially exercised. + +use core::ops::{Add, Mul}; + +pub struct Holder { + value: T, +} + +impl Holder { + pub fn new(value: T) -> Holder { + Holder { value } + } + + pub fn get(&self) -> T { + self.value + } + + pub fn replace(&mut self, value: T) -> T { + let old = self.value; + self.value = value; + old + } +} + +// The provider instantiates Holder but only ever calls `new` and `get`. +// The binary also calls `replace` on Holder, so both crates emit a +// Holder_i32 class with complementary method sets — the java-linker must +// merge them instead of dropping one copy. +pub fn provider_score() -> i32 { + let holder = Holder::new(41); + holder.get() + 1 +} + +// A generic function containing a capturing closure. The closure's JVM class +// is named from the monomorphized instance, so whichever crate instantiates +// scaled_sum must define the closure class itself; previously the class was +// only defined for local DefIds, producing NoClassDefFoundError at runtime. +pub fn scaled_sum + Mul>(a: T, b: T, k: T) -> T { + let scale = move |v: T| v * k; + scale(a) + scale(b) +} + +// The provider also instantiates scaled_sum::, so the same closure +// instance exists in both crates and the linker must deduplicate it. +pub fn provider_scaled() -> i32 { + scaled_sum(2, 3, 4) +} diff --git a/tests/multicrate/generic_reuse/src/main.rs b/tests/multicrate/generic_reuse/src/main.rs new file mode 100644 index 0000000..8394a77 --- /dev/null +++ b/tests/multicrate/generic_reuse/src/main.rs @@ -0,0 +1,37 @@ +// Regression test: cross-crate generic instantiation. +// +// Upstream generics instantiated by a downstream crate must be codegen'd in +// the instantiating crate (the defining crate never saw the instantiation), +// their closure classes must be defined locally, and classes emitted by +// several crates with complementary method sets must be merged by the +// java-linker rather than first-wins deduplicated. Failures here historically +// showed up as NoSuchMethodError / NoClassDefFoundError at runtime. + +use provider::{Holder, provider_scaled, provider_score, scaled_sum}; + +fn main() { + // Instantiation the provider exercised itself. + assert!(provider_score() == 42); + + // Same instantiation (Holder), but calling a method the provider + // never instantiated — forces the complementary-method-set merge. + let mut shared = Holder::new(1); + assert!(shared.replace(5) == 1); + assert!(shared.get() == 5); + + // Fresh instantiations the provider never saw. + let mut long_holder = Holder::new(10_i64); + assert!(long_holder.get() == 10); + assert!(long_holder.replace(-3) == 10); + assert!(long_holder.get() == -3); + + let float_holder = Holder::new(2.5_f64); + assert!(float_holder.get() == 2.5); + + // Generic-with-closure instantiated upstream (dedup against provider's + // copy) and downstream with a brand-new type (local closure class). + assert!(provider_scaled() == 20); + assert!(scaled_sum(2, 3, 4) == 20); + assert!(scaled_sum(1.5_f64, 2.5_f64, 2.0_f64) == 8.0); + assert!(scaled_sum(100_i64, 200_i64, 3_i64) == 900); +} From f492cb38f6ac1d086c47e71e14bbb3034d814098 Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Sun, 12 Jul 2026 16:02:18 +0530 Subject: [PATCH 2/5] test: Add unit receiver trait dispatch test --- .../unit_receiver_dispatch/.cargo/config.toml | 1 + .../binary/unit_receiver_dispatch/Cargo.lock | 7 ++ .../binary/unit_receiver_dispatch/Cargo.toml | 8 ++ .../binary/unit_receiver_dispatch/src/main.rs | 75 +++++++++++++++++++ 4 files changed, 91 insertions(+) create mode 120000 tests/binary/unit_receiver_dispatch/.cargo/config.toml create mode 100644 tests/binary/unit_receiver_dispatch/Cargo.lock create mode 100644 tests/binary/unit_receiver_dispatch/Cargo.toml create mode 100644 tests/binary/unit_receiver_dispatch/src/main.rs diff --git a/tests/binary/unit_receiver_dispatch/.cargo/config.toml b/tests/binary/unit_receiver_dispatch/.cargo/config.toml new file mode 120000 index 0000000..09e5e30 --- /dev/null +++ b/tests/binary/unit_receiver_dispatch/.cargo/config.toml @@ -0,0 +1 @@ +../../../../config.toml \ No newline at end of file diff --git a/tests/binary/unit_receiver_dispatch/Cargo.lock b/tests/binary/unit_receiver_dispatch/Cargo.lock new file mode 100644 index 0000000..9101a2c --- /dev/null +++ b/tests/binary/unit_receiver_dispatch/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "unit_receiver_dispatch" +version = "0.1.0" diff --git a/tests/binary/unit_receiver_dispatch/Cargo.toml b/tests/binary/unit_receiver_dispatch/Cargo.toml new file mode 100644 index 0000000..8e76956 --- /dev/null +++ b/tests/binary/unit_receiver_dispatch/Cargo.toml @@ -0,0 +1,8 @@ +cargo-features = ["profile-rustflags"] + +[package] +name = "unit_receiver_dispatch" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/tests/binary/unit_receiver_dispatch/src/main.rs b/tests/binary/unit_receiver_dispatch/src/main.rs new file mode 100644 index 0000000..7cad54b --- /dev/null +++ b/tests/binary/unit_receiver_dispatch/src/main.rs @@ -0,0 +1,75 @@ +// Regression test: trait methods dispatched on zero-sized receivers. +// +// A unit receiver has no runtime representation, so there is no object to +// invokevirtual on. ZST receivers must route through compiled static dispatch instead. + +trait Score { + fn score(&self, base: i32) -> i32; + fn tag(&self) -> i32; +} + +impl Score for () { + fn score(&self, base: i32) -> i32 { + base + 1 + } + + fn tag(&self) -> i32 { + 7 + } +} + +// Zero-sized struct receiver: same class of bug as `()`. +#[derive(Clone, Copy)] +struct Marker; + +impl Score for Marker { + fn score(&self, base: i32) -> i32 { + base * 2 + } + + fn tag(&self) -> i32 { + 11 + } +} + +// Forwarding impl mirroring core's `impl Debug for &T`, which is where the +// original failure surfaced: `<&() as Debug>::fmt` re-dispatching to +// `<() as Debug>::fmt` on the pointee. +impl Score for &T { + fn score(&self, base: i32) -> i32 { + (**self).score(base) + } + + fn tag(&self) -> i32 { + (**self).tag() + } +} + +// Generic dispatch so the receiver type is only known at monomorphization. +fn total(value: T, base: i32) -> i32 { + value.score(base) + value.tag() +} + +fn by_ref(value: &T, base: i32) -> i32 { + value.score(base) +} + +fn main() { + // Direct calls on the unit value. + let unit = (); + assert!(unit.score(10) == 11); + assert!(unit.tag() == 7); + + // Through generics (T = () and T = Marker). + assert!(total((), 100) == 108); + assert!(total(Marker, 100) == 211); + + // Through the &T forwarding impl (T = () — the original failure shape). + assert!(by_ref(&(), 5) == 6); + assert!(total(&(), 20) == 28); + assert!(by_ref(&Marker, 5) == 10); + + // Double indirection for good measure. + let unit_ref = &(); + assert!(by_ref(&unit_ref, 3) == 4); +} From 92d7ddd6d261cf20b416e8e2aed6be7b6360683a Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Sun, 12 Jul 2026 16:03:39 +0530 Subject: [PATCH 3/5] test: Add test for captureless clousre coercing to fn pointer --- .../fn_ptr_closure_shims/.cargo/config.toml | 1 + tests/binary/fn_ptr_closure_shims/Cargo.lock | 7 ++ tests/binary/fn_ptr_closure_shims/Cargo.toml | 8 ++ tests/binary/fn_ptr_closure_shims/src/main.rs | 77 +++++++++++++++++++ 4 files changed, 93 insertions(+) create mode 120000 tests/binary/fn_ptr_closure_shims/.cargo/config.toml create mode 100644 tests/binary/fn_ptr_closure_shims/Cargo.lock create mode 100644 tests/binary/fn_ptr_closure_shims/Cargo.toml create mode 100644 tests/binary/fn_ptr_closure_shims/src/main.rs diff --git a/tests/binary/fn_ptr_closure_shims/.cargo/config.toml b/tests/binary/fn_ptr_closure_shims/.cargo/config.toml new file mode 120000 index 0000000..09e5e30 --- /dev/null +++ b/tests/binary/fn_ptr_closure_shims/.cargo/config.toml @@ -0,0 +1 @@ +../../../../config.toml \ No newline at end of file diff --git a/tests/binary/fn_ptr_closure_shims/Cargo.lock b/tests/binary/fn_ptr_closure_shims/Cargo.lock new file mode 100644 index 0000000..2954947 --- /dev/null +++ b/tests/binary/fn_ptr_closure_shims/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "fn_ptr_closure_shims" +version = "0.1.0" diff --git a/tests/binary/fn_ptr_closure_shims/Cargo.toml b/tests/binary/fn_ptr_closure_shims/Cargo.toml new file mode 100644 index 0000000..18482de --- /dev/null +++ b/tests/binary/fn_ptr_closure_shims/Cargo.toml @@ -0,0 +1,8 @@ +cargo-features = ["profile-rustflags"] + +[package] +name = "fn_ptr_closure_shims" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/tests/binary/fn_ptr_closure_shims/src/main.rs b/tests/binary/fn_ptr_closure_shims/src/main.rs new file mode 100644 index 0000000..5cc60bd --- /dev/null +++ b/tests/binary/fn_ptr_closure_shims/src/main.rs @@ -0,0 +1,77 @@ +// Regression test: captureless closures coerced to fn pointers. +// +// Reifying a closure resolves to an FnOnce::call_once shim whose calling +// convention is (closure_zst, args_tuple) — not the fn pointer's flat args. +// The generated FnPtrImpl adapter previously forwarded the flat args +// unchanged, leaving the stack one value short at the invokestatic (caught by +// ProGuard). The adapter must construct the ZST closure receiver and wrap the +// fn-ptr args into the shim's tuple carrier. +// +// The same adapter generation runs in two places: MIR rvalue casts +// (`let f: fn(..) = |..| ..`) and const evaluation (fn-pointer fields in +// consts/statics, which is how the original failure surfaced via +// io_error::OsFunctions::DEFAULT in core). + +struct Ops { + unary: fn(i32) -> i32, + binary: fn(i32, i32) -> i32, +} + +fn named_add_one(x: i32) -> i32 { + x + 1 +} + +// Const-eval path: closures inside a const initializer. +const CLOSURE_OPS: Ops = Ops { + unary: |x| x * 3, + binary: |a, b| a + b, +}; + +// Const-eval path with a named fn, which must keep working alongside shims. +const NAMED_OPS: Ops = Ops { + unary: named_add_one, + binary: |a, b| a * b, +}; + +// Static (memory-read) path. +static STATIC_UNARY: fn(i32) -> i32 = |x| x - 4; + +fn apply(f: fn(i32) -> i32, v: i32) -> i32 { + f(v) +} + +fn apply2(f: fn(i32, i32) -> i32, a: i32, b: i32) -> i32 { + f(a, b) +} + +fn main() { + // MIR cast path: direct coercion in a let binding. + let double: fn(i32) -> i32 = |x| x * 2; + assert!(double(21) == 42); + + // MIR cast path: coercion at a call argument position. + assert!(apply(|x| x + 10, 5) == 15); + + // Multi-arg closure: the shim's args tuple has two fields. + let sub: fn(i32, i32) -> i32 = |a, b| a - b; + assert!(sub(10, 3) == 7); + assert!(apply2(|a, b| a * b + 1, 4, 5) == 21); + + // Const-eval path. + assert!((CLOSURE_OPS.unary)(7) == 21); + assert!((CLOSURE_OPS.binary)(20, 22) == 42); + assert!((NAMED_OPS.unary)(41) == 42); + assert!((NAMED_OPS.binary)(6, 7) == 42); + + // Static path. + assert!(STATIC_UNARY(46) == 42); + assert!(apply(STATIC_UNARY, 10) == 6); + + // Fn pointers stored and re-read through locals keep working. + let picked = if sub(1, 0) == 1 { + CLOSURE_OPS.unary + } else { + NAMED_OPS.unary + }; + assert!(picked(2) == 6); +} From ae5ccee20d2922f004a412cbe271101232fc3bc7 Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Sun, 12 Jul 2026 16:04:37 +0530 Subject: [PATCH 4/5] test: Add cast tests --- tests/binary/cast_test/.cargo/config.toml | 1 + tests/binary/cast_test/Cargo.lock | 7 +++ tests/binary/cast_test/Cargo.toml | 8 +++ tests/binary/cast_test/src/main.rs | 61 +++++++++++++++++++++++ 4 files changed, 77 insertions(+) create mode 120000 tests/binary/cast_test/.cargo/config.toml create mode 100644 tests/binary/cast_test/Cargo.lock create mode 100644 tests/binary/cast_test/Cargo.toml create mode 100644 tests/binary/cast_test/src/main.rs diff --git a/tests/binary/cast_test/.cargo/config.toml b/tests/binary/cast_test/.cargo/config.toml new file mode 120000 index 0000000..09e5e30 --- /dev/null +++ b/tests/binary/cast_test/.cargo/config.toml @@ -0,0 +1 @@ +../../../../config.toml \ No newline at end of file diff --git a/tests/binary/cast_test/Cargo.lock b/tests/binary/cast_test/Cargo.lock new file mode 100644 index 0000000..e35c798 --- /dev/null +++ b/tests/binary/cast_test/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "cast_test" +version = "0.1.0" diff --git a/tests/binary/cast_test/Cargo.toml b/tests/binary/cast_test/Cargo.toml new file mode 100644 index 0000000..b169b4d --- /dev/null +++ b/tests/binary/cast_test/Cargo.toml @@ -0,0 +1,8 @@ +cargo-features = ["profile-rustflags"] + +[package] +name = "cast_test" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/tests/binary/cast_test/src/main.rs b/tests/binary/cast_test/src/main.rs new file mode 100644 index 0000000..ae6b676 --- /dev/null +++ b/tests/binary/cast_test/src/main.rs @@ -0,0 +1,61 @@ +#[inline(never)] +fn opaque_i32(v: i32) -> i32 { v } + +#[inline(never)] +fn opaque_f64(v: f64) -> f64 { v } + +#[inline(never)] +fn opaque_u32(v: u32) -> u32 { v } + +#[inline(never)] +fn opaque_i64(v: i64) -> i64 { v } + +fn main() { + let x: i32 = opaque_i32(42); + let y: f64 = x as f64; + assert!(y == 42.0, "i32 -> f64: expected 42.0, got {}", y); + + let x: i32 = opaque_i32(-100); + let y: f64 = x as f64; + assert!(y == -100.0, "i32 -> f64 negative: expected -100.0, got {}", y); + + let x: i32 = opaque_i32(i32::MAX); + let y: f64 = x as f64; + assert!(y == 2147483647.0, "i32 -> f64 max: expected 2147483647.0, got {}", y); + + let x: i32 = opaque_i32(i32::MIN); + let y: f64 = x as f64; + assert!(y == -2147483648.0, "i32 -> f64 min: expected -2147483648.0, got {}", y); + + let x: i32 = opaque_i32(42); + let y: f32 = x as f32; + assert!(y == 42.0, "i32 -> f32: expected 42.0, got {}", y); + + let x: u32 = opaque_u32(42); + let y: f64 = x as f64; + assert!(y == 42.0, "u32 -> f64: expected 42.0, got {}", y); + + let x: i64 = opaque_i64(42); + let y: f64 = x as f64; + assert!(y == 42.0, "i64 -> f64: expected 42.0, got {}", y); + + let x: f64 = opaque_f64(42.0); + let y: i32 = x as i32; + assert!(y == 42, "f64 -> i32: expected 42, got {}", y); + + const C: i32 = 42; + const CF: f64 = C as f64; + assert!(CF == 42.0, "const i32 -> f64: expected 42.0, got {}", CF); + + const C2: i32 = -100; + const CF2: f64 = C2 as f64; + assert!(CF2 == -100.0, "const i32 -> f64 negative: expected -100.0, got {}", CF2); + + const C3: i32 = i32::MAX; + const CF3: f64 = C3 as f64; + assert!(CF3 == 2147483647.0, "const i32 -> f64 max: expected 2147483647.0, got {}", CF3); + + const C4: i32 = i32::MIN; + const CF4: f64 = C4 as f64; + assert!(CF4 == -2147483648.0, "const i32 -> f64 min: expected -2147483648.0, got {}", CF4); +} From a9ff2253f5540a7f0f119b518ee287d5f12f449f Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Sun, 12 Jul 2026 16:04:55 +0530 Subject: [PATCH 5/5] test: Add BigInteger <-> float cast tests --- .../bigint_float_casts/.cargo/config.toml | 1 + tests/binary/bigint_float_casts/Cargo.lock | 7 ++ tests/binary/bigint_float_casts/Cargo.toml | 8 +++ tests/binary/bigint_float_casts/src/main.rs | 72 +++++++++++++++++++ 4 files changed, 88 insertions(+) create mode 120000 tests/binary/bigint_float_casts/.cargo/config.toml create mode 100644 tests/binary/bigint_float_casts/Cargo.lock create mode 100644 tests/binary/bigint_float_casts/Cargo.toml create mode 100644 tests/binary/bigint_float_casts/src/main.rs diff --git a/tests/binary/bigint_float_casts/.cargo/config.toml b/tests/binary/bigint_float_casts/.cargo/config.toml new file mode 120000 index 0000000..09e5e30 --- /dev/null +++ b/tests/binary/bigint_float_casts/.cargo/config.toml @@ -0,0 +1 @@ +../../../../config.toml \ No newline at end of file diff --git a/tests/binary/bigint_float_casts/Cargo.lock b/tests/binary/bigint_float_casts/Cargo.lock new file mode 100644 index 0000000..8f4d09b --- /dev/null +++ b/tests/binary/bigint_float_casts/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bigint_float_casts" +version = "0.1.0" diff --git a/tests/binary/bigint_float_casts/Cargo.toml b/tests/binary/bigint_float_casts/Cargo.toml new file mode 100644 index 0000000..7243283 --- /dev/null +++ b/tests/binary/bigint_float_casts/Cargo.toml @@ -0,0 +1,8 @@ +cargo-features = ["profile-rustflags"] + +[package] +name = "bigint_float_casts" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/tests/binary/bigint_float_casts/src/main.rs b/tests/binary/bigint_float_casts/src/main.rs new file mode 100644 index 0000000..56b9821 --- /dev/null +++ b/tests/binary/bigint_float_casts/src/main.rs @@ -0,0 +1,72 @@ +// Regression test: conversions between BigInteger-backed integers (u128/i128) +// and JVM floats. +// +// The translator's BigInteger-to-primitive unboxing only knew intValue and +// longValue; when the declared type was f64 it emitted intValue()I and then +// stored the (1-slot) int into a (2-slot) double, corrupting the operand +// stack (caught by ProGuard's stack-size computation). Every declared +// primitive width must pick the matching *Value() accessor: floatValue / +// doubleValue for floats. + +// Generic hops force values through monomorphized call boundaries, where the +// definition-side and call-site-side types must agree on unboxing. +fn pass_through(v: T) -> T { + v +} + +fn as_f64(v: u128) -> f64 { + v as f64 +} + +fn as_f32(v: u128) -> f32 { + v as f32 +} + +fn ratio(a: u128, b: u128) -> f64 { + // Mirrors the num_bigint nth_root shape: two big-int derived doubles + // combined with float arithmetic. + a as f64 / b as f64 +} + +fn main() { + // Widening casts out of BigInteger-backed types. + let big: u128 = 1_000_000; + assert!(big as f64 == 1_000_000.0); + assert!(big as f32 == 1_000_000.0f32); + assert!(as_f64(big) == 1_000_000.0); + assert!(as_f32(big) == 1_000_000.0f32); + + let signed: i128 = -250; + assert!(signed as f64 == -250.0); + assert!(signed as f32 == -250.0f32); + + // Through a generic call boundary, then converted. + let hopped = pass_through(big); + assert!(hopped as f64 == 1_000_000.0); + let hopped_f = pass_through(big as f64); + assert!(hopped_f == 1_000_000.0); + + // Float arithmetic on big-int derived values. + assert!(ratio(84, 2) == 42.0); + let x: u128 = 7; + let y = x as f64 * 6.0; + assert!(y == 42.0); + + // Back into BigInteger-backed types. + let back = 42.9_f64 as u128; + assert!(back == 42); + let back_signed = -42.9_f64 as i128; + assert!(back_signed == -42); + + // Integer accessor paths must keep working alongside the float ones. + assert!(big as u64 == 1_000_000); + assert!(big as u32 == 1_000_000); + assert!(big as i64 == 1_000_000); + assert!((signed as i32) == -250); + + // Round trips. + let round: u128 = (big as f64) as u128; + assert!(round == 1_000_000); + let small: u128 = 3; + assert!(small as f32 as u128 == 3); +}