diff --git a/library/core/src/convert/num.rs b/library/core/src/convert/num.rs index 40d61de50aaf2..3489852bbd8d3 100644 --- a/library/core/src/convert/num.rs +++ b/library/core/src/convert/num.rs @@ -1,4 +1,4 @@ -use crate::num::TryFromIntError; +use crate::num::{IntErrorKind, TryFromIntError}; mod private { /// This trait being unreachable from outside the crate @@ -274,7 +274,7 @@ macro_rules! impl_try_from_lower_bounded { if u >= 0 { Ok(u as Self) } else { - Err(TryFromIntError(())) + Err(TryFromIntError(IntErrorKind::NegOverflow)) } } } @@ -295,7 +295,7 @@ macro_rules! impl_try_from_upper_bounded { #[inline] fn try_from(u: $source) -> Result { if u > (Self::MAX as $source) { - Err(TryFromIntError(())) + Err(TryFromIntError(IntErrorKind::PosOverflow)) } else { Ok(u as Self) } @@ -319,8 +319,10 @@ macro_rules! impl_try_from_both_bounded { fn try_from(u: $source) -> Result { let min = Self::MIN as $source; let max = Self::MAX as $source; - if u < min || u > max { - Err(TryFromIntError(())) + if u < min { + Err(TryFromIntError(IntErrorKind::NegOverflow)) + } else if u > max { + Err(TryFromIntError(IntErrorKind::PosOverflow)) } else { Ok(u as Self) } @@ -331,7 +333,7 @@ macro_rules! impl_try_from_both_bounded { /// Implement `TryFrom` for `bool` macro_rules! impl_try_from_integer_for_bool { - ($($int:ty)+) => {$( + ($signedness:ident $($int:ty)+) => {$( #[stable(feature = "try_from", since = "1.34.0")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] impl const TryFrom<$int> for bool { @@ -351,10 +353,23 @@ macro_rules! impl_try_from_integer_for_bool { /// ``` #[inline] fn try_from(i: $int) -> Result { - match i { - 0 => Ok(false), - 1 => Ok(true), - _ => Err(TryFromIntError(())), + sign_dependent_expr!{ + $signedness ? + if signed { + match i { + 0 => Ok(false), + 1 => Ok(true), + ..0 => Err(TryFromIntError(IntErrorKind::NegOverflow)), + 2.. => Err(TryFromIntError(IntErrorKind::PosOverflow)), + } + } + if unsigned { + match i { + 0 => Ok(false), + 1 => Ok(true), + 2.. => Err(TryFromIntError(IntErrorKind::PosOverflow)), + } + } } } } @@ -368,8 +383,8 @@ macro_rules! rev { } // integer -> bool -impl_try_from_integer_for_bool!(u128 u64 u32 u16 u8); -impl_try_from_integer_for_bool!(i128 i64 i32 i16 i8); +impl_try_from_integer_for_bool!(unsigned u128 u64 u32 u16 u8); +impl_try_from_integer_for_bool!(signed i128 i64 i32 i16 i8); // unsigned integer -> unsigned integer impl_try_from_upper_bounded!(u16 => u8); @@ -407,7 +422,7 @@ impl_try_from_lower_bounded!(isize => usize); #[cfg(target_pointer_width = "16")] mod ptr_try_from_impls { - use super::TryFromIntError; + use super::{IntErrorKind, TryFromIntError}; impl_try_from_upper_bounded!(usize => u8); impl_try_from_unbounded!(usize => u16, u32, u64, u128); @@ -429,7 +444,7 @@ mod ptr_try_from_impls { #[cfg(target_pointer_width = "32")] mod ptr_try_from_impls { - use super::TryFromIntError; + use super::{IntErrorKind, TryFromIntError}; impl_try_from_upper_bounded!(usize => u8, u16); impl_try_from_unbounded!(usize => u32, u64, u128); @@ -454,7 +469,7 @@ mod ptr_try_from_impls { #[cfg(target_pointer_width = "64")] mod ptr_try_from_impls { - use super::TryFromIntError; + use super::{IntErrorKind, TryFromIntError}; impl_try_from_upper_bounded!(usize => u8, u16, u32); impl_try_from_unbounded!(usize => u64, u128); @@ -552,7 +567,7 @@ macro_rules! impl_nonzero_int_try_from_int { #[doc = concat!("to [NonZero]\\<[", stringify!($Int), "]>.")] #[inline] fn try_from(value: $Int) -> Result { - Self::new(value).ok_or(TryFromIntError(())) + Self::new(value).ok_or(TryFromIntError(IntErrorKind::Zero)) } } }; diff --git a/library/core/src/num/error.rs b/library/core/src/num/error.rs index 8a353dc0fbe99..8124bc42cdfba 100644 --- a/library/core/src/num/error.rs +++ b/library/core/src/num/error.rs @@ -7,7 +7,16 @@ use crate::fmt; /// The error type returned when a checked integral type conversion fails. #[stable(feature = "try_from", since = "1.34.0")] #[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct TryFromIntError(pub(crate) ()); +pub struct TryFromIntError(pub(crate) IntErrorKind); + +impl TryFromIntError { + /// Outputs the detailed cause of converting an integer failing. + #[must_use] + #[unstable(feature = "try_from_int_error_kind", issue = "153978")] + pub const fn kind(&self) -> &IntErrorKind { + &self.0 + } +} #[stable(feature = "try_from", since = "1.34.0")] impl fmt::Display for TryFromIntError { @@ -66,7 +75,8 @@ pub struct ParseIntError { pub(super) kind: IntErrorKind, } -/// Enum to store the various types of errors that can cause parsing an integer to fail. +/// Enum to store the various types of errors that can cause parsing or converting an +/// integer to fail. /// /// # Example /// @@ -103,10 +113,14 @@ pub enum IntErrorKind { NegOverflow, /// Value was Zero /// - /// This variant will be emitted when the parsing string has a value of zero, which - /// would be illegal for non-zero types. + /// This variant will be emitted when the parsing string or the converting integer + /// has a value of zero, which would be illegal for non-zero types. #[stable(feature = "int_error_matching", since = "1.55.0")] Zero, + /// Value is not a power of two. + #[unstable(feature = "try_from_int_error_kind", issue = "153978")] + // Also, #[unstable(feature = "ptr_alignment_type", issue = "102070")] + NotAPowerOfTwo, } impl ParseIntError { @@ -128,6 +142,7 @@ impl fmt::Display for ParseIntError { IntErrorKind::PosOverflow => "number too large to fit in target type", IntErrorKind::NegOverflow => "number too small to fit in target type", IntErrorKind::Zero => "number would be zero for non-zero type", + IntErrorKind::NotAPowerOfTwo => unreachable!(), } .fmt(f) } diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index b106314f14d12..feb2209c4e689 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -268,7 +268,7 @@ impl const TryFrom for Alignment { #[inline] fn try_from(align: usize) -> Result { - Self::new(align).ok_or(num::TryFromIntError(())) + Self::new(align).ok_or(num::TryFromIntError(num::IntErrorKind::NotAPowerOfTwo)) } } diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 72112f8b01133..93e9c786144fe 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -115,6 +115,7 @@ #![feature(trusted_random_access)] #![feature(try_blocks)] #![feature(try_find)] +#![feature(try_from_int_error_kind)] #![feature(try_trait_v2)] #![feature(type_info)] #![feature(uint_bit_width)] diff --git a/library/coretests/tests/nonzero.rs b/library/coretests/tests/nonzero.rs index 861e9e05081fc..55d479efb4b40 100644 --- a/library/coretests/tests/nonzero.rs +++ b/library/coretests/tests/nonzero.rs @@ -282,11 +282,11 @@ fn test_nonzero_from_int_on_success() { #[test] fn test_nonzero_from_int_on_err() { - assert!(NonZero::::try_from(0).is_err()); - assert!(NonZero::::try_from(0).is_err()); + assert_eq!(NonZero::::try_from(0).unwrap_err().kind(), &IntErrorKind::Zero); + assert_eq!(NonZero::::try_from(0).unwrap_err().kind(), &IntErrorKind::Zero); - assert!(NonZero::::try_from(0).is_err()); - assert!(NonZero::::try_from(0).is_err()); + assert_eq!(NonZero::::try_from(0).unwrap_err().kind(), &IntErrorKind::Zero); + assert_eq!(NonZero::::try_from(0).unwrap_err().kind(), &IntErrorKind::Zero); } #[test] diff --git a/library/coretests/tests/num/mod.rs b/library/coretests/tests/num/mod.rs index 73b0e2333feee..4955b6618cefd 100644 --- a/library/coretests/tests/num/mod.rs +++ b/library/coretests/tests/num/mod.rs @@ -371,6 +371,43 @@ fn test_f32f64() { assert!(nan.is_nan()); } +/// Conversions where $source can be represented as bool. +macro_rules! test_impl_try_from_integer_to_bool { + ($fn_name:ident, $source:ty) => { + #[test] + fn $fn_name() { + let max: $source = <$source>::MAX; + let min: $source = <$source>::MIN; + let zero: $source = 0; + let one: $source = 1; + let two: $source = 2; + assert_eq!(bool::try_from(max).unwrap_err().kind(), &IntErrorKind::PosOverflow); + if min != 0 { + assert_eq!(bool::try_from(min).unwrap_err().kind(), &IntErrorKind::NegOverflow); + assert_eq!( + bool::try_from(zero - 1).unwrap_err().kind(), + &IntErrorKind::NegOverflow + ); + } + assert_eq!(bool::try_from(zero).unwrap(), false); + assert_eq!(bool::try_from(one).unwrap(), true); + assert_eq!(bool::try_from(two).unwrap_err().kind(), &IntErrorKind::PosOverflow); + } + }; +} + +test_impl_try_from_integer_to_bool! { test_try_u8bool, u8 } +test_impl_try_from_integer_to_bool! { test_try_u16bool, u16 } +test_impl_try_from_integer_to_bool! { test_try_u32bool, u32 } +test_impl_try_from_integer_to_bool! { test_try_u64bool, u64 } +test_impl_try_from_integer_to_bool! { test_try_u128bool, u128 } + +test_impl_try_from_integer_to_bool! { test_try_i8bool, i8 } +test_impl_try_from_integer_to_bool! { test_try_i16bool, i16 } +test_impl_try_from_integer_to_bool! { test_try_i32bool, i32 } +test_impl_try_from_integer_to_bool! { test_try_i64bool, i64 } +test_impl_try_from_integer_to_bool! { test_try_i128bool, i128 } + /// Conversions where the full width of $source can be represented as $target macro_rules! test_impl_try_from_always_ok { ($fn_name:ident, $source:ty, $target: ty) => { @@ -494,9 +531,15 @@ macro_rules! test_impl_try_from_signed_to_unsigned_upper_ok { let zero: $source = 0; let neg_one: $source = -1; assert_eq!(<$target as TryFrom<$source>>::try_from(max).unwrap(), max as $target); - assert!(<$target as TryFrom<$source>>::try_from(min).is_err()); + assert_eq!( + <$target as TryFrom<$source>>::try_from(min).unwrap_err().kind(), + &IntErrorKind::NegOverflow + ); assert_eq!(<$target as TryFrom<$source>>::try_from(zero).unwrap(), zero as $target); - assert!(<$target as TryFrom<$source>>::try_from(neg_one).is_err()); + assert_eq!( + <$target as TryFrom<$source>>::try_from(neg_one).unwrap_err().kind(), + &IntErrorKind::NegOverflow + ); } }; } @@ -557,7 +600,10 @@ macro_rules! test_impl_try_from_unsigned_to_signed_upper_err { let max = <$source>::MAX; let min = <$source>::MIN; let zero: $source = 0; - assert!(<$target as TryFrom<$source>>::try_from(max).is_err()); + assert_eq!( + <$target as TryFrom<$source>>::try_from(max).unwrap_err().kind(), + &IntErrorKind::PosOverflow + ); assert_eq!(<$target as TryFrom<$source>>::try_from(min).unwrap(), min as $target); assert_eq!(<$target as TryFrom<$source>>::try_from(zero).unwrap(), zero as $target); } @@ -620,9 +666,15 @@ macro_rules! test_impl_try_from_same_sign_err { let zero: $source = 0; let t_max = <$target>::MAX; let t_min = <$target>::MIN; - assert!(<$target as TryFrom<$source>>::try_from(max).is_err()); + assert_eq!( + <$target as TryFrom<$source>>::try_from(max).unwrap_err().kind(), + &IntErrorKind::PosOverflow + ); if min != 0 { - assert!(<$target as TryFrom<$source>>::try_from(min).is_err()); + assert_eq!( + <$target as TryFrom<$source>>::try_from(min).unwrap_err().kind(), + &IntErrorKind::NegOverflow + ); } assert_eq!(<$target as TryFrom<$source>>::try_from(zero).unwrap(), zero as $target); assert_eq!( @@ -709,8 +761,14 @@ macro_rules! test_impl_try_from_signed_to_unsigned_err { let zero: $source = 0; let t_max = <$target>::MAX; let t_min = <$target>::MIN; - assert!(<$target as TryFrom<$source>>::try_from(max).is_err()); - assert!(<$target as TryFrom<$source>>::try_from(min).is_err()); + assert_eq!( + <$target as TryFrom<$source>>::try_from(max).unwrap_err().kind(), + &IntErrorKind::PosOverflow + ); + assert_eq!( + <$target as TryFrom<$source>>::try_from(min).unwrap_err().kind(), + &IntErrorKind::NegOverflow + ); assert_eq!(<$target as TryFrom<$source>>::try_from(zero).unwrap(), zero as $target); assert_eq!( <$target as TryFrom<$source>>::try_from(t_max as $source).unwrap(),