Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions tests/binary/bigint_float_casts/.cargo/config.toml
7 changes: 7 additions & 0 deletions tests/binary/bigint_float_casts/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions tests/binary/bigint_float_casts/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
cargo-features = ["profile-rustflags"]

[package]
name = "bigint_float_casts"
version = "0.1.0"
edition = "2024"

[dependencies]
72 changes: 72 additions & 0 deletions tests/binary/bigint_float_casts/src/main.rs
Original file line number Diff line number Diff line change
@@ -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<T>(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);
}
1 change: 1 addition & 0 deletions tests/binary/cast_test/.cargo/config.toml
7 changes: 7 additions & 0 deletions tests/binary/cast_test/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions tests/binary/cast_test/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
cargo-features = ["profile-rustflags"]

[package]
name = "cast_test"
version = "0.1.0"
edition = "2024"

[dependencies]
61 changes: 61 additions & 0 deletions tests/binary/cast_test/src/main.rs
Original file line number Diff line number Diff line change
@@ -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);
}
1 change: 1 addition & 0 deletions tests/binary/fn_ptr_closure_shims/.cargo/config.toml
7 changes: 7 additions & 0 deletions tests/binary/fn_ptr_closure_shims/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions tests/binary/fn_ptr_closure_shims/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
cargo-features = ["profile-rustflags"]

[package]
name = "fn_ptr_closure_shims"
version = "0.1.0"
edition = "2024"

[dependencies]
77 changes: 77 additions & 0 deletions tests/binary/fn_ptr_closure_shims/src/main.rs
Original file line number Diff line number Diff line change
@@ -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);
}
1 change: 1 addition & 0 deletions tests/binary/unit_receiver_dispatch/.cargo/config.toml
7 changes: 7 additions & 0 deletions tests/binary/unit_receiver_dispatch/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions tests/binary/unit_receiver_dispatch/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
cargo-features = ["profile-rustflags"]

[package]
name = "unit_receiver_dispatch"
version = "0.1.0"
edition = "2024"

[dependencies]
75 changes: 75 additions & 0 deletions tests/binary/unit_receiver_dispatch/src/main.rs
Original file line number Diff line number Diff line change
@@ -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<T: Score> 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<T: Score>(value: T, base: i32) -> i32 {
value.score(base) + value.tag()
}

fn by_ref<T: Score>(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);
}
1 change: 1 addition & 0 deletions tests/multicrate/generic_reuse/.cargo/config.toml
14 changes: 14 additions & 0 deletions tests/multicrate/generic_reuse/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions tests/multicrate/generic_reuse/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
cargo-features = ["profile-rustflags"]

[package]
name = "generic_reuse"
version = "0.1.0"
edition = "2024"

[dependencies]
provider = { path = "provider" }
1 change: 1 addition & 0 deletions tests/multicrate/generic_reuse/provider/.cargo/config.toml
Loading