From 1ce044b732fbae373c0bba4633f3c6f456464379 Mon Sep 17 00:00:00 2001 From: EFanZh Date: Sat, 22 Nov 2025 18:23:13 +0800 Subject: [PATCH] Add `ilog10` result range hints --- library/core/src/num/int_macros.rs | 10 +- library/core/src/num/nonzero.rs | 10 +- .../lib-optimizations/ilog10-range.rs | 213 ++++++++++++++++++ 3 files changed, 231 insertions(+), 2 deletions(-) create mode 100644 tests/codegen-llvm/lib-optimizations/ilog10-range.rs diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 93fdf2823aeb7..69df73f65c677 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -3520,8 +3520,16 @@ macro_rules! int_impl { without modifying the original"] #[inline] pub const fn checked_ilog10(self) -> Option { + const MAX_RESULT: u32 = int_log10::$ActualT(<$SelfT>::MAX as $ActualT); + if self > 0 { - Some(int_log10::$ActualT(self as $ActualT)) + let result = int_log10::$ActualT(self as $ActualT); + + // SAFETY: `ilog10` is a monotonically nondecreasing function, so the result for + // positive inputs is always in the range [0, `MAX_RESULT`]. + unsafe { crate::hint::assert_unchecked(result <= MAX_RESULT) }; + + Some(result) } else { None } diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index ee375dbaaab2d..0029d826de916 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1657,7 +1657,15 @@ macro_rules! nonzero_integer_signedness_dependent_methods { without modifying the original"] #[inline] pub const fn ilog10(self) -> u32 { - super::int_log10::$Int(self.get()) + const MAX_RESULT: u32 = super::int_log10::$Int(<$Int>::MAX); + + let result = super::int_log10::$Int(self.get()); + + // SAFETY: `ilog10` is a monotonically nondecreasing function, so the result for + // positive inputs is always in the range [0, `MAX_RESULT`]. + unsafe { crate::hint::assert_unchecked(result <= MAX_RESULT) }; + + result } /// Calculates the midpoint (average) between `self` and `rhs`. diff --git a/tests/codegen-llvm/lib-optimizations/ilog10-range.rs b/tests/codegen-llvm/lib-optimizations/ilog10-range.rs new file mode 100644 index 0000000000000..b466855389fb4 --- /dev/null +++ b/tests/codegen-llvm/lib-optimizations/ilog10-range.rs @@ -0,0 +1,213 @@ +//! Make sure the compiler knows the result range of `ilog10`. + +//@ compile-flags: -O -Z merge-functions=disabled + +#![crate_type = "lib"] + +use std::num::NonZero; + +// Signed integers. + +#[no_mangle] +fn i8_ilog10_range(value: i8) { + const MAX_RESULT: u32 = i8::MAX.ilog10(); + + // CHECK-LABEL: @i8_ilog10_range( + // CHECK-NOT: panic + // CHECK: ret void + // CHECK-NEXT: } + assert!(value <= 0 || value.ilog10() <= MAX_RESULT); + assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT)); +} + +#[no_mangle] +fn i16_ilog10_range(value: i16) { + const MAX_RESULT: u32 = i16::MAX.ilog10(); + + // CHECK-LABEL: @i16_ilog10_range( + // CHECK-NOT: panic + // CHECK: ret void + // CHECK-NEXT: } + assert!(value <= 0 || value.ilog10() <= MAX_RESULT); + assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT)); +} + +#[no_mangle] +fn i32_ilog10_range(value: i32) { + const MAX_RESULT: u32 = i32::MAX.ilog10(); + + // CHECK-LABEL: @i32_ilog10_range( + // CHECK-NOT: panic + // CHECK: ret void + // CHECK-NEXT: } + assert!(value <= 0 || value.ilog10() <= MAX_RESULT); + assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT)); +} + +#[no_mangle] +fn i64_ilog10_range(value: i64) { + const MAX_RESULT: u32 = i64::MAX.ilog10(); + + // CHECK-LABEL: @i64_ilog10_range( + // CHECK-NOT: panic + // CHECK: ret void + // CHECK-NEXT: } + assert!(value <= 0 || value.ilog10() <= MAX_RESULT); + assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT)); +} + +#[no_mangle] +fn i128_ilog10_range(value: i128) { + const MAX_RESULT: u32 = i128::MAX.ilog10(); + + // CHECK-LABEL: @i128_ilog10_range( + // CHECK-NOT: panic + // CHECK: ret void + // CHECK-NEXT: } + assert!(value <= 0 || value.ilog10() <= MAX_RESULT); + assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT)); +} + +#[no_mangle] +fn isize_ilog10_range(value: isize) { + const MAX_RESULT: u32 = isize::MAX.ilog10(); + + // CHECK-LABEL: @isize_ilog10_range( + // CHECK-NOT: panic + // CHECK: ret void + // CHECK-NEXT: } + assert!(value <= 0 || value.ilog10() <= MAX_RESULT); + assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT)); +} + +// Unsigned integer types. + +#[no_mangle] +fn u8_ilog10_range(value: u8) { + const MAX_RESULT: u32 = u8::MAX.ilog10(); + + // CHECK-LABEL: @u8_ilog10_range( + // CHECK-NOT: panic + // CHECK: ret void + // CHECK-NEXT: } + assert!(value == 0 || value.ilog10() <= MAX_RESULT); + assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT)); +} + +#[no_mangle] +fn u16_ilog10_range(value: u16) { + const MAX_RESULT: u32 = u16::MAX.ilog10(); + + // CHECK-LABEL: @u16_ilog10_range( + // CHECK-NOT: panic + // CHECK: ret void + // CHECK-NEXT: } + assert!(value == 0 || value.ilog10() <= MAX_RESULT); + assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT)); +} + +#[no_mangle] +fn u32_ilog10_range(value: u32) { + const MAX_RESULT: u32 = u32::MAX.ilog10(); + + // CHECK-LABEL: @u32_ilog10_range( + // CHECK-NOT: panic + // CHECK: ret void + // CHECK-NEXT: } + assert!(value == 0 || value.ilog10() <= MAX_RESULT); + assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT)); +} + +#[no_mangle] +fn u64_ilog10_range(value: u64) { + const MAX_RESULT: u32 = u64::MAX.ilog10(); + + // CHECK-LABEL: @u64_ilog10_range( + // CHECK-NOT: panic + // CHECK: ret void + // CHECK-NEXT: } + assert!(value == 0 || value.ilog10() <= MAX_RESULT); + assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT)); +} + +#[no_mangle] +fn u128_ilog10_range(value: u128) { + const MAX_RESULT: u32 = u128::MAX.ilog10(); + + // CHECK-LABEL: @u128_ilog10_range( + // CHECK-NOT: panic + // CHECK: ret void + // CHECK-NEXT: } + assert!(value == 0 || value.ilog10() <= MAX_RESULT); + assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT)); +} + +#[no_mangle] +fn usize_ilog10_range(value: usize) { + const MAX_RESULT: u32 = usize::MAX.ilog10(); + + // CHECK-LABEL: @usize_ilog10_range( + // CHECK-NOT: panic + // CHECK: ret void + // CHECK-NEXT: } + assert!(value == 0 || value.ilog10() <= MAX_RESULT); + assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT)); +} + +// Signed non-zero integers do not have `ilog10` methods. + +// Unsigned non-zero integers. + +#[no_mangle] +fn non_zero_u8_ilog10_range(value: NonZero) { + // CHECK-LABEL: @non_zero_u8_ilog10_range( + // CHECK-NOT: panic + // CHECK: ret void + // CHECK-NEXT: } + assert!(value.ilog10() <= const { u8::MAX.ilog10() }); +} + +#[no_mangle] +fn non_zero_u16_ilog10_range(value: NonZero) { + // CHECK-LABEL: @non_zero_u16_ilog10_range( + // CHECK-NOT: panic + // CHECK: ret void + // CHECK-NEXT: } + assert!(value.ilog10() <= const { u16::MAX.ilog10() }); +} + +#[no_mangle] +fn non_zero_u32_ilog10_range(value: NonZero) { + // CHECK-LABEL: @non_zero_u32_ilog10_range( + // CHECK-NOT: panic + // CHECK: ret void + // CHECK-NEXT: } + assert!(value.ilog10() <= const { u32::MAX.ilog10() }); +} + +#[no_mangle] +fn non_zero_u64_ilog10_range(value: NonZero) { + // CHECK-LABEL: @non_zero_u64_ilog10_range( + // CHECK-NOT: panic + // CHECK: ret void + // CHECK-NEXT: } + assert!(value.ilog10() <= const { u64::MAX.ilog10() }); +} + +#[no_mangle] +fn non_zero_u128_ilog10_range(value: NonZero) { + // CHECK-LABEL: @non_zero_u128_ilog10_range( + // CHECK-NOT: panic + // CHECK: ret void + // CHECK-NEXT: } + assert!(value.ilog10() <= const { u128::MAX.ilog10() }); +} + +#[no_mangle] +fn non_zero_usize_ilog10_range(value: NonZero) { + // CHECK-LABEL: @non_zero_usize_ilog10_range( + // CHECK-NOT: panic + // CHECK: ret void + // CHECK-NEXT: } + assert!(value.ilog10() <= const { usize::MAX.ilog10() }); +}