diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 12ab12d7ee03..5bead39a3dba 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -969,9 +969,8 @@ pub enum FpCategory { } #[doc(hidden)] -trait FromStrRadixHelper: PartialOrd + Copy { +trait FromStrRadixHelper: PartialOrd + Copy + Default { 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; @@ -997,8 +996,6 @@ 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) } @@ -1035,8 +1032,14 @@ macro_rules! impl_helper_for { } impl_helper_for! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } +/// Determins if a string of text of that length of that radix could be guaranteed to be +/// stored in the given type T. +/// Note that if the radix is known to the compiler, it is just the check of digits.len that +/// is done at runtime. +#[doc(hidden)] #[inline(always)] -pub(crate) fn can_not_overflow(radix: u32, is_signed_ty: bool, digits:&[u8]) -> bool { +#[unstable(issue = "none", feature = "std_internals")] +pub fn can_not_overflow(radix: u32, is_signed_ty: bool, digits: &[u8]) -> bool { radix <= 16 && digits.len() <= mem::size_of::() * 2 - is_signed_ty as usize } @@ -1054,7 +1057,7 @@ fn from_str_radix(src: &str, radix: u32) -> Result T::MIN; + let is_signed_ty = T::default() > T::MIN; // all valid digits are ascii, so we will just iterate over the utf8 bytes // and cast them to chars. .to_digit() will safely return None for anything @@ -1071,7 +1074,7 @@ fn from_str_radix(src: &str, radix: u32) -> Result (true, src), }; - let mut result = T::from_u32(0); + let mut result = T::default(); if can_not_overflow::(radix, is_signed_ty, digits) { // SAFETY: If the len of the str is short compared to the range of the type @@ -1127,11 +1130,3 @@ fn from_str_radix(src: &str, radix: u32) -> Result(10, true, "99".as_bytes()), true); - assert_eq!(can_not_overflow::(10, true, "129".as_bytes()), false); - } -} \ No newline at end of file diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 5c861236e86f..bc2ab7489a16 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -55,6 +55,7 @@ #![feature(numfmt)] #![feature(step_trait)] #![feature(str_internals)] +#![feature(std_internals)] #![feature(test)] #![feature(trusted_len)] #![feature(try_blocks)] diff --git a/library/core/tests/num/mod.rs b/library/core/tests/num/mod.rs index 4f773a824efd..0abc88f21fcb 100644 --- a/library/core/tests/num/mod.rs +++ b/library/core/tests/num/mod.rs @@ -2,7 +2,7 @@ use core::cmp::PartialEq; use core::convert::{TryFrom, TryInto}; use core::fmt::Debug; use core::marker::Copy; -use core::num::{IntErrorKind, ParseIntError, TryFromIntError}; +use core::num::{can_not_overflow, IntErrorKind, ParseIntError, TryFromIntError}; use core::ops::{Add, Div, Mul, Rem, Sub}; use core::option::Option; use core::option::Option::None; @@ -120,6 +120,51 @@ fn test_int_from_str_overflow() { test_parse::("-9223372036854775809", Err(IntErrorKind::NegOverflow)); } +#[test] +fn test_can_not_overflow() { + // Not currently in std lib (issue: #27728) + fn format_radix(mut x: T, radix: T) -> String + where + T: std::ops::Rem, + T: std::ops::Div, + T: std::cmp::PartialEq, + T: std::default::Default, + T: Copy, + T: Default, + u32: TryFrom, + { + let mut result = vec![]; + + loop { + let m = x % radix; + x = x / radix; + result.push( + std::char::from_digit(m.try_into().ok().unwrap(), radix.try_into().ok().unwrap()) + .unwrap(), + ); + if x == T::default() { + break; + } + } + result.into_iter().rev().collect() + } + + macro_rules! check { + ($($t:ty)*) => ($( + for base in 2..=36 { + let num = (<$t>::MAX as u128) + 1; + + // 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); + } + )*) + } + + check! { i8 i16 i32 i64 i128 isize usize u8 u16 u32 u64 } +} + #[test] fn test_leading_plus() { test_parse::("+127", Ok(127));