Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 31 additions & 16 deletions library/core/src/convert/num.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::num::TryFromIntError;
use crate::num::{IntErrorKind, TryFromIntError};

mod private {
/// This trait being unreachable from outside the crate
Expand Down Expand Up @@ -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))
}
}
}
Expand All @@ -295,7 +295,7 @@ macro_rules! impl_try_from_upper_bounded {
#[inline]
fn try_from(u: $source) -> Result<Self, Self::Error> {
if u > (Self::MAX as $source) {
Err(TryFromIntError(()))
Err(TryFromIntError(IntErrorKind::PosOverflow))
} else {
Ok(u as Self)
}
Expand All @@ -319,8 +319,10 @@ macro_rules! impl_try_from_both_bounded {
fn try_from(u: $source) -> Result<Self, Self::Error> {
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)
}
Expand All @@ -331,7 +333,7 @@ macro_rules! impl_try_from_both_bounded {

/// Implement `TryFrom<integer>` 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 {
Expand All @@ -351,10 +353,23 @@ macro_rules! impl_try_from_integer_for_bool {
/// ```
#[inline]
fn try_from(i: $int) -> Result<Self, Self::Error> {
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)),
}
}
}
}
}
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -552,7 +567,7 @@ macro_rules! impl_nonzero_int_try_from_int {
#[doc = concat!("to <code>[NonZero]\\<[", stringify!($Int), "]></code>.")]
#[inline]
fn try_from(value: $Int) -> Result<Self, Self::Error> {
Self::new(value).ok_or(TryFromIntError(()))
Self::new(value).ok_or(TryFromIntError(IntErrorKind::Zero))
}
}
};
Expand Down
23 changes: 19 additions & 4 deletions library/core/src/num/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
///
Expand Down Expand Up @@ -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 {
Expand All @@ -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)
}
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/ptr/alignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ impl const TryFrom<usize> for Alignment {

#[inline]
fn try_from(align: usize) -> Result<Alignment, Self::Error> {
Self::new(align).ok_or(num::TryFromIntError(()))
Self::new(align).ok_or(num::TryFromIntError(num::IntErrorKind::NotAPowerOfTwo))
}
}

Expand Down
1 change: 1 addition & 0 deletions library/coretests/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
8 changes: 4 additions & 4 deletions library/coretests/tests/nonzero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,11 +282,11 @@ fn test_nonzero_from_int_on_success() {

#[test]
fn test_nonzero_from_int_on_err() {
assert!(NonZero::<u8>::try_from(0).is_err());
assert!(NonZero::<u32>::try_from(0).is_err());
assert_eq!(NonZero::<u8>::try_from(0).unwrap_err().kind(), &IntErrorKind::Zero);
assert_eq!(NonZero::<u32>::try_from(0).unwrap_err().kind(), &IntErrorKind::Zero);

assert!(NonZero::<i8>::try_from(0).is_err());
assert!(NonZero::<i32>::try_from(0).is_err());
assert_eq!(NonZero::<i8>::try_from(0).unwrap_err().kind(), &IntErrorKind::Zero);
assert_eq!(NonZero::<i32>::try_from(0).unwrap_err().kind(), &IntErrorKind::Zero);
}

#[test]
Expand Down
72 changes: 65 additions & 7 deletions library/coretests/tests/num/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -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
);
}
};
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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!(
Expand Down Expand Up @@ -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(),
Expand Down
Loading