diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 5bead39a3db..4768befcedd 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -5,6 +5,7 @@ use crate::ascii; use crate::intrinsics; use crate::mem; +use crate::ops::{Add, Mul, Sub}; use crate::str::FromStr; // Used because the `?` operator is not allowed in a const context. @@ -969,14 +970,14 @@ pub enum FpCategory { } #[doc(hidden)] -trait FromStrRadixHelper: PartialOrd + Copy + Default { +trait FromStrRadixHelper: + PartialOrd + Copy + Default + Add + Sub + Mul +{ const MIN: Self; + fn from_u32(u: u32) -> Self; fn checked_mul(&self, other: u32) -> Option; fn checked_sub(&self, other: u32) -> Option; fn checked_add(&self, other: u32) -> Option; - unsafe fn unchecked_mul(self, other: u32) -> Self; - unsafe fn unchecked_sub(self, other: u32) -> Self; - unsafe fn unchecked_add(self, other: u32) -> Self; } macro_rules! from_str_radix_int_impl { @@ -996,6 +997,8 @@ macro_rules! impl_helper_for { ($($t:ty)*) => ($(impl FromStrRadixHelper for $t { const MIN: Self = Self::MIN; #[inline] + fn from_u32(u: u32) -> Self { u as Self } + #[inline] fn checked_mul(&self, other: u32) -> Option { Self::checked_mul(*self, other as Self) } @@ -1007,27 +1010,6 @@ macro_rules! impl_helper_for { fn checked_add(&self, other: u32) -> Option { Self::checked_add(*self, other as Self) } - #[inline] - unsafe fn unchecked_mul(self, other: u32) -> Self { - // SAFETY: Conditions of `Self::unchecked_mul` must be upheld by the caller. - unsafe { - Self::unchecked_mul(self, other as Self) - } - } - #[inline] - unsafe fn unchecked_sub(self, other: u32) -> Self { - // SAFETY: Conditions of `Self::unchecked_sub` must be upheld by the caller. - unsafe { - Self::unchecked_sub(self, other as Self) - } - } - #[inline] - unsafe fn unchecked_add(self, other: u32) -> Self { - // SAFETY: Conditions of `Self::unchecked_add` must be upheld by the caller. - unsafe { - Self::unchecked_add(self, other as Self) - } - } })*) } impl_helper_for! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } @@ -1077,7 +1059,7 @@ fn from_str_radix(src: &str, radix: u32) -> Result(radix, is_signed_ty, digits) { - // SAFETY: If the len of the str is short compared to the range of the type + // If the len of the str is short compared to the range of the type // we are parsing into, then we can be certain that an overflow will not occur. // This bound is when `radix.pow(digits.len()) - 1 <= T::MAX` but the condition // above is a faster (conservative) approximation of this. @@ -1085,22 +1067,20 @@ fn from_str_radix(src: &str, radix: u32) -> Result { - for &c in digits { - result = result.unchecked_mul(radix); - let x = (c as char).to_digit(radix).ok_or(PIE { kind: InvalidDigit })?; - result = T::$unchecked_additive_op(result, x); - } - }; - } - if is_positive { - run_unchecked_loop!(unchecked_add) - } else { - run_unchecked_loop!(unchecked_sub) + macro_rules! run_unchecked_loop { + ($unchecked_additive_op:expr) => { + for &c in digits { + result = result * T::from_u32(radix); + let x = (c as char).to_digit(radix).ok_or(PIE { kind: InvalidDigit })?; + result = $unchecked_additive_op(result, T::from_u32(x)); + } }; } + if is_positive { + run_unchecked_loop!(::add) + } else { + run_unchecked_loop!(::sub) + }; } else { macro_rules! run_checked_loop { ($checked_additive_op:ident, $overflow_err:expr) => { diff --git a/library/core/tests/num/mod.rs b/library/core/tests/num/mod.rs index 0abc88f21fc..10b8d975442 100644 --- a/library/core/tests/num/mod.rs +++ b/library/core/tests/num/mod.rs @@ -122,6 +122,28 @@ fn test_int_from_str_overflow() { #[test] fn test_can_not_overflow() { + fn can_overflow(radix: u32, input: &str) -> bool + where + T: Default + + core::ops::Sub + + std::convert::From + + std::cmp::PartialOrd + + Copy, + { + let one = true.into(); + let zero = ::default(); + !can_not_overflow::(radix, zero - one < zero, input.as_bytes()) + } + + // Positive tests: + assert!(!can_overflow::(16, "F")); + assert!(!can_overflow::(16, "FF")); + + assert!(!can_overflow::(10, "9")); + assert!(!can_overflow::(10, "99")); + + // Negative tests: + // Not currently in std lib (issue: #27728) fn format_radix(mut x: T, radix: T) -> String where @@ -157,12 +179,20 @@ fn test_can_not_overflow() { // Calcutate the string length for the smallest overflowing number: let max_len_string = format_radix(num, base as u128); // Ensure that that string length is deemed to potentially overflow: - assert_eq!(can_not_overflow::<$t>(base, <$t>::default() > <$t>::MIN, max_len_string.as_bytes()), false); + assert!(can_overflow::<$t>(base, &max_len_string)); } )*) } check! { i8 i16 i32 i64 i128 isize usize u8 u16 u32 u64 } + + // Check u128 separately: + for base in 2..=36 { + let num = u128::MAX as u128; + let max_len_string = format_radix(num, base as u128); + // base 16 fits perfectly for u128 and won't overflow: + assert_eq!(can_overflow::(base, &max_len_string), base != 16); + } } #[test]