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
2 changes: 2 additions & 0 deletions library/core/src/convert/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ mod num;

#[unstable(feature = "convert_float_to_int", issue = "67057")]
pub use num::FloatToInt;
#[unstable(feature = "integer_casts", issue = "157388")]
pub use num::{BoundedCastFromInt, CheckedCastFromInt};

/// The identity function.
///
Expand Down
122 changes: 122 additions & 0 deletions library/core/src/convert/num.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
use crate::num::{IntErrorKind, TryFromIntError};

mod private {
/// This trait being unreachable from outside the crate prevents other
/// implementations of the integer cast traits.
#[unstable(feature = "integer_casts", issue = "157388")]
pub trait Sealed {}

/// This trait being unreachable from outside the crate prevents other
/// implementations of the integer cast traits.
///
/// `Cast<T> : SealedCast<T>` avoids the orphan rule, which would otherwise
/// allow e.g. implementing `Cast<Foo>` for `u8`.
#[unstable(feature = "integer_casts", issue = "157388")]
pub trait SealedCast<T>: Sealed {}

#[unstable(feature = "integer_casts", issue = "157388")]
impl<T: Sealed, U: Sealed> SealedCast<T> for U {}

macro_rules! impl_sealed_int {
([$($T:ty),*]) => {$(
#[unstable(feature = "integer_casts", issue = "157388")]
impl Sealed for $T { }
)*};
}

impl_sealed_int!([u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize]);
}

/// Supporting trait for inherent methods of `f32` and `f64` such as `to_int_unchecked`.
/// Typically doesn’t need to be used directly.
#[unstable(feature = "convert_float_to_int", issue = "67057")]
Expand Down Expand Up @@ -630,3 +657,98 @@ impl_nonzero_int_try_from_nonzero_int!(i32 => u8, u16, u32, u64, u128, usize);
impl_nonzero_int_try_from_nonzero_int!(i64 => u8, u16, u32, u64, u128, usize);
impl_nonzero_int_try_from_nonzero_int!(i128 => u8, u16, u32, u64, u128, usize);
impl_nonzero_int_try_from_nonzero_int!(isize => u8, u16, u32, u64, u128, usize);

/// Conversion between integers, wrapping around or saturating at the target type's boundaries.
#[unstable(feature = "integer_casts", issue = "157388")]
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
pub const trait BoundedCastFromInt<T>: private::SealedCast<T> + Sized {
/// Converts `value` to this type, wrapping around at the boundary of the type.
#[unstable(feature = "integer_casts", issue = "157388")]
fn wrapping_cast_from(value: T) -> Self;

/// Converts `value` to this type, saturating at the numeric bounds instead of overflowing.
#[unstable(feature = "integer_casts", issue = "157388")]
fn saturating_cast_from(value: T) -> Self;
}

/// Fallible conversion between integers.
#[unstable(feature = "integer_casts", issue = "157388")]
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
pub const trait CheckedCastFromInt<T>: private::SealedCast<T> + Sized {
/// Converts `value` to this type, returning `None` if overflow would have occurred.
#[unstable(feature = "integer_casts", issue = "157388")]
fn checked_cast_from(value: T) -> Option<Self>;

/// Converts `value` to this type, assuming overflow cannot occur.
///
/// # Safety
///
/// This results in undefined behavior when `value` will overflow when
/// converted to this type.
#[unstable(feature = "integer_casts", issue = "157388")]
unsafe fn unchecked_cast_from(value: T) -> Self;

/// Converts `value` to this type, panicking on overflow.
///
/// # Panics
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
#[unstable(feature = "integer_casts", issue = "157388")]
fn strict_cast_from(value: T) -> Self;
}

macro_rules! impl_int_cast {
($Src:ty as [$($Dst:ty),*]) => {$(
#[unstable(feature = "integer_casts", issue = "157388")]
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
impl const CheckedCastFromInt<$Src> for $Dst {
#[inline]
fn checked_cast_from(value: $Src) -> Option<Self> {
value.try_into().ok()
}

#[inline(always)]
unsafe fn unchecked_cast_from(value: $Src) -> Self {
// SAFETY: the safety contract must be upheld by the caller.
unsafe { value.try_into().unwrap_unchecked() }
}

#[inline]
#[track_caller]
fn strict_cast_from(value: $Src) -> Self {
match value.try_into() {
Ok(x) => x,
Err(_) => core::num::imp::overflow_panic::cast_integer()
}
}
}

#[unstable(feature = "integer_casts", issue = "157388")]
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
impl const BoundedCastFromInt<$Src> for $Dst {
#[inline(always)]
fn wrapping_cast_from(value: $Src) -> Self {
value as Self
}

#[inline]
#[allow(unused_comparisons)]
#[allow(irrefutable_let_patterns)]
fn saturating_cast_from(value: $Src) -> Self {
if let Ok(x) = value.try_into() {
return x;
}

if value < 0 { <$Dst>::MIN } else { <$Dst>::MAX }
}
}
)*};
}

macro_rules! impl_all_int_casts {
([$($Src:ty),*]) => {$(
impl_int_cast!($Src as [u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize]);
)*};
}

impl_all_int_casts!([u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize]);
2 changes: 1 addition & 1 deletion library/core/src/num/imp/overflow_panic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,6 @@ pub(in crate::num) const fn shl() -> ! {

#[cold]
#[track_caller]
pub(in crate::num) const fn cast_integer() -> ! {
pub(crate) const fn cast_integer() -> ! {
panic!("attempt to cast integer with overflow")
}
117 changes: 117 additions & 0 deletions library/core/src/num/int_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4068,5 +4068,122 @@ macro_rules! int_impl {
{
traits::WidenTarget::internal_widen(self)
}


/// Converts `self` to the target integer type, saturating at the numeric
/// bounds instead of overflowing.
///
/// # Examples
///
/// ```
/// #![feature(integer_casts)]
#[doc = concat!("assert_eq!(i8::MAX, ", stringify!($SelfT), "::MAX.saturating_cast());")]
#[doc = concat!("assert_eq!(i8::MIN, ", stringify!($SelfT), "::MIN.saturating_cast());")]
#[doc = concat!("assert_eq!(42u8, 42", stringify!($SelfT), ".saturating_cast());")]
#[doc = concat!("assert_eq!(0u8, (-42", stringify!($SelfT), ").saturating_cast());")]
/// ```
#[must_use = "this returns the cast result and does not modify the original"]
#[unstable(feature = "integer_casts", issue = "157388")]
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
#[inline(always)]
pub const fn saturating_cast<T: [const] BoundedCastFromInt<Self>>(self) -> T {
T::saturating_cast_from(self)
}

/// Converts `self` to the target integer type, wrapping around at the
/// boundary of the target type.
///
/// # Examples
///
/// ```
/// #![feature(integer_casts)]
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX as i8, ", stringify!($SelfT), "::MAX.wrapping_cast());")]
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN as i8, ", stringify!($SelfT), "::MIN.wrapping_cast());")]
#[doc = concat!("assert_eq!(42u8, 42", stringify!($SelfT), ".wrapping_cast());")]
#[doc = concat!("assert_eq!(u8::MAX - 41, (-42", stringify!($SelfT), ").wrapping_cast());")]
/// ```
#[must_use = "this returns the cast result and does not modify the original"]
#[unstable(feature = "integer_casts", issue = "157388")]
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
#[inline(always)]
pub const fn wrapping_cast<T: [const] BoundedCastFromInt<Self>>(self) -> T {
T::wrapping_cast_from(self)
}

/// Converts `self` to the target integer type, returning `None` if the value
/// does not lie in the target type's domain.
///
/// # Examples
///
/// ```
/// #![feature(integer_casts)]
#[doc = concat!("assert_eq!(Some(42u8), 42", stringify!($SelfT), ".checked_cast());")]
#[doc = concat!("assert_eq!((-42", stringify!($SelfT), ").checked_cast::<u8>(), None);")]
/// ```
#[must_use = "this returns the cast result and does not modify the original"]
#[unstable(feature = "integer_casts", issue = "157388")]
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
#[inline(always)]
pub const fn checked_cast<T: [const] CheckedCastFromInt<Self>>(self) -> Option<T> {
T::checked_cast_from(self)
}

/// Converts `self` to the target integer type, panicking if the value
/// does not lie in the target type's domain.
///
/// # Panics
///
/// This function will panic if the value does not lie in the target type's domain.
///
/// # Examples
///
/// ```
/// #![feature(integer_casts)]
#[doc = concat!("assert_eq!(42u8, 42", stringify!($SelfT), ".strict_cast());")]
/// ```
///
/// The following will panic:
///
/// ```should_panic
/// #![feature(integer_casts)]
#[doc = concat!("let _ = (-42", stringify!($SelfT), ").strict_cast::<u8>();")]
/// ```
#[must_use = "this returns the cast result and does not modify the original"]
#[unstable(feature = "integer_casts", issue = "157388")]
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
#[inline(always)]
#[track_caller]
pub const fn strict_cast<T: [const] CheckedCastFromInt<Self>>(self) -> T {
T::strict_cast_from(self)
}

/// Converts `self` to the target integer type, assuming the value lies in the target type's domain.
///
/// # Safety
///
/// This results in undefined behavior if the integer value of `self` is bigger than `T::MAX`,
/// or smaller than `T::MIN`, where `T` is the target type.
#[must_use = "this returns the cast result and does not modify the original"]
#[unstable(feature = "integer_casts", issue = "157388")]
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
#[inline(always)]
pub const unsafe fn unchecked_cast<T: [const] CheckedCastFromInt<Self>>(self) -> T {
assert_unsafe_precondition!(
check_language_ub,
concat!(stringify!($SelfT), "::unchecked_cast must fit in the target type"),
(
// Check has to be performed up-front because it depends on generic T.
in_bounds: bool = {
let cast_val = self.checked_cast::<T>();
let ret = cast_val.is_some();
core::mem::forget(cast_val); // We don't have const Drop, but we know it's an int.
ret
},
) => in_bounds,
);

// SAFETY: this is guaranteed to be safe by the caller.
unsafe { T::unchecked_cast_from(self) }
}
}
}
1 change: 1 addition & 0 deletions library/core/src/num/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#![stable(feature = "rust1", since = "1.0.0")]

use crate::convert::{BoundedCastFromInt, CheckedCastFromInt};
use crate::panic::const_panic;
use crate::str::FromStr;
use crate::ub_checks::assert_unsafe_precondition;
Expand Down
114 changes: 114 additions & 0 deletions library/core/src/num/uint_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4229,5 +4229,119 @@ macro_rules! uint_impl {
{
traits::WidenTarget::internal_widen(self)
}

/// Converts `self` to the target integer type, saturating at the numeric
/// bounds instead of overflowing.
///
/// # Examples
///
/// ```
/// #![feature(integer_casts)]
#[doc = concat!("assert_eq!(255u8, ", stringify!($SelfT), "::MAX.saturating_cast());")]
#[doc = concat!("assert_eq!(127i8, ", stringify!($SelfT), "::MAX.saturating_cast());")]
#[doc = concat!("assert_eq!(42i8, 42", stringify!($SelfT), ".saturating_cast());")]
/// ```
#[must_use = "this returns the cast result and does not modify the original"]
#[unstable(feature = "integer_casts", issue = "157388")]
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
#[inline(always)]
pub const fn saturating_cast<T: [const] BoundedCastFromInt<Self>>(self) -> T {
T::saturating_cast_from(self)
}

/// Converts `self` to the target integer type, wrapping around at the
/// boundary of the target type.
///
/// # Examples
///
/// ```
/// #![feature(integer_casts)]
#[doc = concat!("assert_eq!(255u8, ", stringify!($SelfT), "::MAX.wrapping_cast());")]
#[doc = concat!("assert_eq!(42i8, 42", stringify!($SelfT), ".wrapping_cast());")]
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX as i8, ", stringify!($SelfT), "::MAX.wrapping_cast());")]
/// ```
#[must_use = "this returns the cast result and does not modify the original"]
#[unstable(feature = "integer_casts", issue = "157388")]
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
#[inline(always)]
pub const fn wrapping_cast<T: [const] BoundedCastFromInt<Self>>(self) -> T {
T::wrapping_cast_from(self)
}

/// Converts `self` to the target integer type, returning `None` if the value
/// does not lie in the target type's domain.
///
/// # Examples
///
/// ```
/// #![feature(integer_casts)]
#[doc = concat!("assert_eq!(Some(42u8), 42", stringify!($SelfT), ".checked_cast());")]
#[doc = concat!("assert_eq!(128", stringify!($SelfT), ".checked_cast::<i8>(), None);")]
/// ```
#[must_use = "this returns the cast result and does not modify the original"]
#[unstable(feature = "integer_casts", issue = "157388")]
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
#[inline(always)]
pub const fn checked_cast<T: [const] CheckedCastFromInt<Self>>(self) -> Option<T> {
T::checked_cast_from(self)
}

/// Converts `self` to the target integer type, panicking if the value
/// does not lie in the target type's domain.
///
/// # Panics
///
/// This function will panic if the value does not lie in the target type's domain.
///
/// # Examples
///
/// ```
/// #![feature(integer_casts)]
#[doc = concat!("assert_eq!(42u8, 42", stringify!($SelfT), ".strict_cast());")]
/// ```
///
/// The following will panic:
///
/// ```should_panic
/// #![feature(integer_casts)]
#[doc = concat!("let _ = 128", stringify!($SelfT), ".strict_cast::<i8>();")]
/// ```
#[must_use = "this returns the cast result and does not modify the original"]
#[unstable(feature = "integer_casts", issue = "157388")]
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
#[inline(always)]
#[track_caller]
pub const fn strict_cast<T: [const] CheckedCastFromInt<Self>>(self) -> T {
T::strict_cast_from(self)
}

/// Converts `self` to the target integer type, assuming the value lies in the target type's domain.
///
/// # Safety
///
/// This results in undefined behavior if the integer value of `self` is bigger than `T::MAX`,
/// or smaller than `T::MIN`, where `T` is the target type.
#[must_use = "this returns the cast result and does not modify the original"]
#[unstable(feature = "integer_casts", issue = "157388")]
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
#[inline(always)]
pub const unsafe fn unchecked_cast<T: [const] CheckedCastFromInt<Self>>(self) -> T {
assert_unsafe_precondition!(
check_language_ub,
concat!(stringify!($SelfT), "::unchecked_cast must fit in the target type"),
(
// Check has to be performed up-front because it depends on generic T.
in_bounds: bool = {
let cast_val = self.checked_cast::<T>();
let ret = cast_val.is_some();
core::mem::forget(cast_val); // We don't have const Drop, but we know it's an int.
ret
},
) => in_bounds,
);

// SAFETY: this is guaranteed to be safe by the caller.
unsafe { T::unchecked_cast_from(self) }
}
}
}
Loading