Skip to content

Commit 515906a

Browse files
committed
Use Add, Sub, Mul traits instead of unsafe
1 parent 82e9d9e commit 515906a

File tree

2 files changed

+51
-41
lines changed

2 files changed

+51
-41
lines changed

library/core/src/num/mod.rs

+20-40
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use crate::ascii;
66
use crate::intrinsics;
77
use crate::mem;
8+
use crate::ops::{Add, Mul, Sub};
89
use crate::str::FromStr;
910

1011
// Used because the `?` operator is not allowed in a const context.
@@ -969,14 +970,14 @@ pub enum FpCategory {
969970
}
970971

971972
#[doc(hidden)]
972-
trait FromStrRadixHelper: PartialOrd + Copy + Default {
973+
trait FromStrRadixHelper:
974+
PartialOrd + Copy + Default + Add<Output = Self> + Sub<Output = Self> + Mul<Output = Self>
975+
{
973976
const MIN: Self;
977+
fn from_u32(u: u32) -> Self;
974978
fn checked_mul(&self, other: u32) -> Option<Self>;
975979
fn checked_sub(&self, other: u32) -> Option<Self>;
976980
fn checked_add(&self, other: u32) -> Option<Self>;
977-
unsafe fn unchecked_mul(self, other: u32) -> Self;
978-
unsafe fn unchecked_sub(self, other: u32) -> Self;
979-
unsafe fn unchecked_add(self, other: u32) -> Self;
980981
}
981982

982983
macro_rules! from_str_radix_int_impl {
@@ -996,6 +997,8 @@ macro_rules! impl_helper_for {
996997
($($t:ty)*) => ($(impl FromStrRadixHelper for $t {
997998
const MIN: Self = Self::MIN;
998999
#[inline]
1000+
fn from_u32(u: u32) -> Self { u as Self }
1001+
#[inline]
9991002
fn checked_mul(&self, other: u32) -> Option<Self> {
10001003
Self::checked_mul(*self, other as Self)
10011004
}
@@ -1007,27 +1010,6 @@ macro_rules! impl_helper_for {
10071010
fn checked_add(&self, other: u32) -> Option<Self> {
10081011
Self::checked_add(*self, other as Self)
10091012
}
1010-
#[inline]
1011-
unsafe fn unchecked_mul(self, other: u32) -> Self {
1012-
// SAFETY: Conditions of `Self::unchecked_mul` must be upheld by the caller.
1013-
unsafe {
1014-
Self::unchecked_mul(self, other as Self)
1015-
}
1016-
}
1017-
#[inline]
1018-
unsafe fn unchecked_sub(self, other: u32) -> Self {
1019-
// SAFETY: Conditions of `Self::unchecked_sub` must be upheld by the caller.
1020-
unsafe {
1021-
Self::unchecked_sub(self, other as Self)
1022-
}
1023-
}
1024-
#[inline]
1025-
unsafe fn unchecked_add(self, other: u32) -> Self {
1026-
// SAFETY: Conditions of `Self::unchecked_add` must be upheld by the caller.
1027-
unsafe {
1028-
Self::unchecked_add(self, other as Self)
1029-
}
1030-
}
10311013
})*)
10321014
}
10331015
impl_helper_for! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize }
@@ -1077,30 +1059,28 @@ fn from_str_radix<T: FromStrRadixHelper>(src: &str, radix: u32) -> Result<T, Par
10771059
let mut result = T::default();
10781060

10791061
if can_not_overflow::<T>(radix, is_signed_ty, digits) {
1080-
// SAFETY: If the len of the str is short compared to the range of the type
1062+
// If the len of the str is short compared to the range of the type
10811063
// we are parsing into, then we can be certain that an overflow will not occur.
10821064
// This bound is when `radix.pow(digits.len()) - 1 <= T::MAX` but the condition
10831065
// above is a faster (conservative) approximation of this.
10841066
//
10851067
// Consider radix 16 as it has the highest information density per digit and will thus overflow the earliest:
10861068
// `u8::MAX` is `ff` - any str of len 2 is guaranteed to not overflow.
10871069
// `i8::MAX` is `7f` - only a str of len 1 is guaranteed to not overflow.
1088-
unsafe {
1089-
macro_rules! run_unchecked_loop {
1090-
($unchecked_additive_op:ident) => {
1091-
for &c in digits {
1092-
result = result.unchecked_mul(radix);
1093-
let x = (c as char).to_digit(radix).ok_or(PIE { kind: InvalidDigit })?;
1094-
result = T::$unchecked_additive_op(result, x);
1095-
}
1096-
};
1097-
}
1098-
if is_positive {
1099-
run_unchecked_loop!(unchecked_add)
1100-
} else {
1101-
run_unchecked_loop!(unchecked_sub)
1070+
macro_rules! run_unchecked_loop {
1071+
($unchecked_additive_op:expr) => {
1072+
for &c in digits {
1073+
result = result * T::from_u32(radix);
1074+
let x = (c as char).to_digit(radix).ok_or(PIE { kind: InvalidDigit })?;
1075+
result = $unchecked_additive_op(result, T::from_u32(x));
1076+
}
11021077
};
11031078
}
1079+
if is_positive {
1080+
run_unchecked_loop!(<T as core::ops::Add>::add)
1081+
} else {
1082+
run_unchecked_loop!(<T as core::ops::Sub>::sub)
1083+
};
11041084
} else {
11051085
macro_rules! run_checked_loop {
11061086
($checked_additive_op:ident, $overflow_err:expr) => {

library/core/tests/num/mod.rs

+31-1
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,28 @@ fn test_int_from_str_overflow() {
122122

123123
#[test]
124124
fn test_can_not_overflow() {
125+
fn can_overflow<T>(radix: u32, input: &str) -> bool
126+
where
127+
T: Default
128+
+ core::ops::Sub<Output = T>
129+
+ std::convert::From<bool>
130+
+ std::cmp::PartialOrd
131+
+ Copy,
132+
{
133+
let one = true.into();
134+
let zero = <T>::default();
135+
!can_not_overflow::<T>(radix, zero - one < zero, input.as_bytes())
136+
}
137+
138+
// Positive tests:
139+
assert!(!can_overflow::<i8>(16, "F"));
140+
assert!(!can_overflow::<u8>(16, "FF"));
141+
142+
assert!(!can_overflow::<i8>(10, "9"));
143+
assert!(!can_overflow::<u8>(10, "99"));
144+
145+
// Negative tests:
146+
125147
// Not currently in std lib (issue: #27728)
126148
fn format_radix<T>(mut x: T, radix: T) -> String
127149
where
@@ -157,12 +179,20 @@ fn test_can_not_overflow() {
157179
// Calcutate the string length for the smallest overflowing number:
158180
let max_len_string = format_radix(num, base as u128);
159181
// Ensure that that string length is deemed to potentially overflow:
160-
assert_eq!(can_not_overflow::<$t>(base, <$t>::default() > <$t>::MIN, max_len_string.as_bytes()), false);
182+
assert!(can_overflow::<$t>(base, &max_len_string));
161183
}
162184
)*)
163185
}
164186

165187
check! { i8 i16 i32 i64 i128 isize usize u8 u16 u32 u64 }
188+
189+
// Check u128 separately:
190+
for base in 2..=36 {
191+
let num = u128::MAX as u128;
192+
let max_len_string = format_radix(num, base as u128);
193+
// base 16 fits perfectly for u128 and won't overflow:
194+
assert_eq!(can_overflow::<u128>(base, &max_len_string), base != 16);
195+
}
166196
}
167197

168198
#[test]

0 commit comments

Comments
 (0)