Skip to content

Commit ac729a4

Browse files
authored
Rollup merge of #149207 - EFanZh:add-ilog10-result-range-hints, r=Mark-Simulacrum
Add `ilog10` result range hints This PR adds hints that the return value of `T::ilog10` will never exceed `T::MAX.ilog10()`. This works because `ilog10` is a monotonically nondecreasing function, the maximum return value is reached at the max input value.
2 parents 80b4c44 + 4046385 commit ac729a4

File tree

4 files changed

+279
-47
lines changed

4 files changed

+279
-47
lines changed

library/core/src/num/int_log10.rs

Lines changed: 64 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
//! These functions compute the integer logarithm of their type, assuming
22
//! that someone has already checked that the value is strictly positive.
33
4+
use crate::num::NonZero;
5+
46
// 0 < val <= u8::MAX
57
#[inline]
6-
pub(super) const fn u8(val: u8) -> u32 {
8+
const fn u8_impl(val: u8) -> u32 {
79
let val = val as u32;
810

911
// For better performance, avoid branches by assembling the solution
@@ -45,13 +47,13 @@ const fn less_than_5(val: u32) -> u32 {
4547

4648
// 0 < val <= u16::MAX
4749
#[inline]
48-
pub(super) const fn u16(val: u16) -> u32 {
50+
const fn u16_impl(val: u16) -> u32 {
4951
less_than_5(val as u32)
5052
}
5153

5254
// 0 < val <= u32::MAX
5355
#[inline]
54-
pub(super) const fn u32(mut val: u32) -> u32 {
56+
const fn u32_impl(mut val: u32) -> u32 {
5557
let mut log = 0;
5658
if val >= 100_000 {
5759
val /= 100_000;
@@ -62,7 +64,7 @@ pub(super) const fn u32(mut val: u32) -> u32 {
6264

6365
// 0 < val <= u64::MAX
6466
#[inline]
65-
pub(super) const fn u64(mut val: u64) -> u32 {
67+
const fn u64_impl(mut val: u64) -> u32 {
6668
let mut log = 0;
6769
if val >= 10_000_000_000 {
6870
val /= 10_000_000_000;
@@ -77,66 +79,87 @@ pub(super) const fn u64(mut val: u64) -> u32 {
7779

7880
// 0 < val <= u128::MAX
7981
#[inline]
80-
pub(super) const fn u128(mut val: u128) -> u32 {
82+
const fn u128_impl(mut val: u128) -> u32 {
8183
let mut log = 0;
8284
if val >= 100_000_000_000_000_000_000_000_000_000_000 {
8385
val /= 100_000_000_000_000_000_000_000_000_000_000;
8486
log += 32;
85-
return log + u32(val as u32);
87+
return log + u32_impl(val as u32);
8688
}
8789
if val >= 10_000_000_000_000_000 {
8890
val /= 10_000_000_000_000_000;
8991
log += 16;
9092
}
91-
log + u64(val as u64)
93+
log + u64_impl(val as u64)
9294
}
9395

94-
#[cfg(target_pointer_width = "16")]
95-
#[inline]
96-
pub(super) const fn usize(val: usize) -> u32 {
97-
u16(val as _)
98-
}
96+
macro_rules! define_unsigned_ilog10 {
97+
($($ty:ident => $impl_fn:ident,)*) => {$(
98+
#[inline]
99+
pub(super) const fn $ty(val: NonZero<$ty>) -> u32 {
100+
let result = $impl_fn(val.get());
99101

100-
#[cfg(target_pointer_width = "32")]
101-
#[inline]
102-
pub(super) const fn usize(val: usize) -> u32 {
103-
u32(val as _)
104-
}
102+
// SAFETY: Integer logarithm is monotonic non-decreasing, so the computed `result` cannot
103+
// exceed the value produced for the maximum input.
104+
unsafe { crate::hint::assert_unchecked(result <= const { $impl_fn($ty::MAX) }) };
105105

106-
#[cfg(target_pointer_width = "64")]
107-
#[inline]
108-
pub(super) const fn usize(val: usize) -> u32 {
109-
u64(val as _)
106+
result
107+
}
108+
)*};
110109
}
111110

112-
// 0 < val <= i8::MAX
113-
#[inline]
114-
pub(super) const fn i8(val: i8) -> u32 {
115-
u8(val as u8)
111+
define_unsigned_ilog10! {
112+
u8 => u8_impl,
113+
u16 => u16_impl,
114+
u32 => u32_impl,
115+
u64 => u64_impl,
116+
u128 => u128_impl,
116117
}
117118

118-
// 0 < val <= i16::MAX
119119
#[inline]
120-
pub(super) const fn i16(val: i16) -> u32 {
121-
u16(val as u16)
122-
}
120+
pub(super) const fn usize(val: NonZero<usize>) -> u32 {
121+
#[cfg(target_pointer_width = "16")]
122+
let impl_fn = u16;
123123

124-
// 0 < val <= i32::MAX
125-
#[inline]
126-
pub(super) const fn i32(val: i32) -> u32 {
127-
u32(val as u32)
124+
#[cfg(target_pointer_width = "32")]
125+
let impl_fn = u32;
126+
127+
#[cfg(target_pointer_width = "64")]
128+
let impl_fn = u64;
129+
130+
// SAFETY: We have selected the correct `impl_fn`, so the converting `val` to the argument is
131+
// safe.
132+
impl_fn(unsafe { NonZero::new_unchecked(val.get() as _) })
128133
}
129134

130-
// 0 < val <= i64::MAX
131-
#[inline]
132-
pub(super) const fn i64(val: i64) -> u32 {
133-
u64(val as u64)
135+
macro_rules! define_signed_ilog10 {
136+
($($ty:ident => $impl_fn:ident,)*) => {$(
137+
// 0 < val <= $ty::MAX
138+
#[inline]
139+
pub(super) const fn $ty(val: $ty) -> Option<u32> {
140+
if val > 0 {
141+
let result = $impl_fn(val.cast_unsigned());
142+
143+
// SAFETY: Integer logarithm is monotonic non-decreasing, so the computed `result`
144+
// cannot exceed the value produced for the maximum input.
145+
unsafe {
146+
crate::hint::assert_unchecked(result <= const { $impl_fn($ty::MAX.cast_unsigned()) });
147+
}
148+
149+
Some(result)
150+
} else {
151+
None
152+
}
153+
}
154+
)*};
134155
}
135156

136-
// 0 < val <= i128::MAX
137-
#[inline]
138-
pub(super) const fn i128(val: i128) -> u32 {
139-
u128(val as u128)
157+
define_signed_ilog10! {
158+
i8 => u8_impl,
159+
i16 => u16_impl,
160+
i32 => u32_impl,
161+
i64 => u64_impl,
162+
i128 => u128_impl,
140163
}
141164

142165
/// Instantiate this panic logic once, rather than for all the ilog methods

library/core/src/num/int_macros.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3528,11 +3528,7 @@ macro_rules! int_impl {
35283528
without modifying the original"]
35293529
#[inline]
35303530
pub const fn checked_ilog10(self) -> Option<u32> {
3531-
if self > 0 {
3532-
Some(int_log10::$ActualT(self as $ActualT))
3533-
} else {
3534-
None
3535-
}
3531+
int_log10::$ActualT(self as $ActualT)
35363532
}
35373533

35383534
/// Computes the absolute value of `self`.

library/core/src/num/nonzero.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1657,7 +1657,7 @@ macro_rules! nonzero_integer_signedness_dependent_methods {
16571657
without modifying the original"]
16581658
#[inline]
16591659
pub const fn ilog10(self) -> u32 {
1660-
super::int_log10::$Int(self.get())
1660+
super::int_log10::$Int(self)
16611661
}
16621662

16631663
/// Calculates the midpoint (average) between `self` and `rhs`.
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
//! Make sure the compiler knows the result range of `ilog10`.
2+
3+
//@ compile-flags: -O -Z merge-functions=disabled
4+
5+
#![crate_type = "lib"]
6+
7+
use std::num::NonZero;
8+
9+
// Signed integers.
10+
11+
#[no_mangle]
12+
fn i8_ilog10_range(value: i8) {
13+
const MAX_RESULT: u32 = i8::MAX.ilog10();
14+
15+
// CHECK-LABEL: @i8_ilog10_range(
16+
// CHECK-NOT: panic
17+
// CHECK: ret void
18+
// CHECK-NEXT: }
19+
assert!(value <= 0 || value.ilog10() <= MAX_RESULT);
20+
assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT));
21+
}
22+
23+
#[no_mangle]
24+
fn i16_ilog10_range(value: i16) {
25+
const MAX_RESULT: u32 = i16::MAX.ilog10();
26+
27+
// CHECK-LABEL: @i16_ilog10_range(
28+
// CHECK-NOT: panic
29+
// CHECK: ret void
30+
// CHECK-NEXT: }
31+
assert!(value <= 0 || value.ilog10() <= MAX_RESULT);
32+
assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT));
33+
}
34+
35+
#[no_mangle]
36+
fn i32_ilog10_range(value: i32) {
37+
const MAX_RESULT: u32 = i32::MAX.ilog10();
38+
39+
// CHECK-LABEL: @i32_ilog10_range(
40+
// CHECK-NOT: panic
41+
// CHECK: ret void
42+
// CHECK-NEXT: }
43+
assert!(value <= 0 || value.ilog10() <= MAX_RESULT);
44+
assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT));
45+
}
46+
47+
#[no_mangle]
48+
fn i64_ilog10_range(value: i64) {
49+
const MAX_RESULT: u32 = i64::MAX.ilog10();
50+
51+
// CHECK-LABEL: @i64_ilog10_range(
52+
// CHECK-NOT: panic
53+
// CHECK: ret void
54+
// CHECK-NEXT: }
55+
assert!(value <= 0 || value.ilog10() <= MAX_RESULT);
56+
assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT));
57+
}
58+
59+
#[no_mangle]
60+
fn i128_ilog10_range(value: i128) {
61+
const MAX_RESULT: u32 = i128::MAX.ilog10();
62+
63+
// CHECK-LABEL: @i128_ilog10_range(
64+
// CHECK-NOT: panic
65+
// CHECK: ret void
66+
// CHECK-NEXT: }
67+
assert!(value <= 0 || value.ilog10() <= MAX_RESULT);
68+
assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT));
69+
}
70+
71+
#[no_mangle]
72+
fn isize_ilog10_range(value: isize) {
73+
const MAX_RESULT: u32 = isize::MAX.ilog10();
74+
75+
// CHECK-LABEL: @isize_ilog10_range(
76+
// CHECK-NOT: panic
77+
// CHECK: ret void
78+
// CHECK-NEXT: }
79+
assert!(value <= 0 || value.ilog10() <= MAX_RESULT);
80+
assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT));
81+
}
82+
83+
// Unsigned integer types.
84+
85+
#[no_mangle]
86+
fn u8_ilog10_range(value: u8) {
87+
const MAX_RESULT: u32 = u8::MAX.ilog10();
88+
89+
// CHECK-LABEL: @u8_ilog10_range(
90+
// CHECK-NOT: panic
91+
// CHECK: ret void
92+
// CHECK-NEXT: }
93+
assert!(value == 0 || value.ilog10() <= MAX_RESULT);
94+
assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT));
95+
}
96+
97+
#[no_mangle]
98+
fn u16_ilog10_range(value: u16) {
99+
const MAX_RESULT: u32 = u16::MAX.ilog10();
100+
101+
// CHECK-LABEL: @u16_ilog10_range(
102+
// CHECK-NOT: panic
103+
// CHECK: ret void
104+
// CHECK-NEXT: }
105+
assert!(value == 0 || value.ilog10() <= MAX_RESULT);
106+
assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT));
107+
}
108+
109+
#[no_mangle]
110+
fn u32_ilog10_range(value: u32) {
111+
const MAX_RESULT: u32 = u32::MAX.ilog10();
112+
113+
// CHECK-LABEL: @u32_ilog10_range(
114+
// CHECK-NOT: panic
115+
// CHECK: ret void
116+
// CHECK-NEXT: }
117+
assert!(value == 0 || value.ilog10() <= MAX_RESULT);
118+
assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT));
119+
}
120+
121+
#[no_mangle]
122+
fn u64_ilog10_range(value: u64) {
123+
const MAX_RESULT: u32 = u64::MAX.ilog10();
124+
125+
// CHECK-LABEL: @u64_ilog10_range(
126+
// CHECK-NOT: panic
127+
// CHECK: ret void
128+
// CHECK-NEXT: }
129+
assert!(value == 0 || value.ilog10() <= MAX_RESULT);
130+
assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT));
131+
}
132+
133+
#[no_mangle]
134+
fn u128_ilog10_range(value: u128) {
135+
const MAX_RESULT: u32 = u128::MAX.ilog10();
136+
137+
// CHECK-LABEL: @u128_ilog10_range(
138+
// CHECK-NOT: panic
139+
// CHECK: ret void
140+
// CHECK-NEXT: }
141+
assert!(value == 0 || value.ilog10() <= MAX_RESULT);
142+
assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT));
143+
}
144+
145+
#[no_mangle]
146+
fn usize_ilog10_range(value: usize) {
147+
const MAX_RESULT: u32 = usize::MAX.ilog10();
148+
149+
// CHECK-LABEL: @usize_ilog10_range(
150+
// CHECK-NOT: panic
151+
// CHECK: ret void
152+
// CHECK-NEXT: }
153+
assert!(value == 0 || value.ilog10() <= MAX_RESULT);
154+
assert!(value.checked_ilog10().is_none_or(|result| result <= MAX_RESULT));
155+
}
156+
157+
// Signed non-zero integers do not have `ilog10` methods.
158+
159+
// Unsigned non-zero integers.
160+
161+
#[no_mangle]
162+
fn non_zero_u8_ilog10_range(value: NonZero<u8>) {
163+
// CHECK-LABEL: @non_zero_u8_ilog10_range(
164+
// CHECK-NOT: panic
165+
// CHECK: ret void
166+
// CHECK-NEXT: }
167+
assert!(value.ilog10() <= const { u8::MAX.ilog10() });
168+
}
169+
170+
#[no_mangle]
171+
fn non_zero_u16_ilog10_range(value: NonZero<u16>) {
172+
// CHECK-LABEL: @non_zero_u16_ilog10_range(
173+
// CHECK-NOT: panic
174+
// CHECK: ret void
175+
// CHECK-NEXT: }
176+
assert!(value.ilog10() <= const { u16::MAX.ilog10() });
177+
}
178+
179+
#[no_mangle]
180+
fn non_zero_u32_ilog10_range(value: NonZero<u32>) {
181+
// CHECK-LABEL: @non_zero_u32_ilog10_range(
182+
// CHECK-NOT: panic
183+
// CHECK: ret void
184+
// CHECK-NEXT: }
185+
assert!(value.ilog10() <= const { u32::MAX.ilog10() });
186+
}
187+
188+
#[no_mangle]
189+
fn non_zero_u64_ilog10_range(value: NonZero<u64>) {
190+
// CHECK-LABEL: @non_zero_u64_ilog10_range(
191+
// CHECK-NOT: panic
192+
// CHECK: ret void
193+
// CHECK-NEXT: }
194+
assert!(value.ilog10() <= const { u64::MAX.ilog10() });
195+
}
196+
197+
#[no_mangle]
198+
fn non_zero_u128_ilog10_range(value: NonZero<u128>) {
199+
// CHECK-LABEL: @non_zero_u128_ilog10_range(
200+
// CHECK-NOT: panic
201+
// CHECK: ret void
202+
// CHECK-NEXT: }
203+
assert!(value.ilog10() <= const { u128::MAX.ilog10() });
204+
}
205+
206+
#[no_mangle]
207+
fn non_zero_usize_ilog10_range(value: NonZero<usize>) {
208+
// CHECK-LABEL: @non_zero_usize_ilog10_range(
209+
// CHECK-NOT: panic
210+
// CHECK: ret void
211+
// CHECK-NEXT: }
212+
assert!(value.ilog10() <= const { usize::MAX.ilog10() });
213+
}

0 commit comments

Comments
 (0)