Skip to content

Commit 973b0e5

Browse files
committed
Implement feature integer_casts
1 parent b354133 commit 973b0e5

6 files changed

Lines changed: 358 additions & 1 deletion

File tree

library/core/src/convert/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ mod num;
4343

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

4749
/// The identity function.
4850
///

library/core/src/convert/num.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,33 @@
11
use crate::num::{IntErrorKind, TryFromIntError};
22

3+
mod private {
4+
/// This trait being unreachable from outside the crate
5+
/// prevents other implementations of the `FloatToInt` trait,
6+
/// which allows potentially adding more trait methods after the trait is `#[stable]`.
7+
#[unstable(feature = "convert_float_to_int", issue = "67057")]
8+
pub trait Sealed {}
9+
10+
/// This trait being unreachable from outside the crate prevents other
11+
/// implementations of the integer cast traits.
12+
///
13+
/// `Cast<T> : SealedCast<T>` avoids the orphan rule, which would otherwise
14+
/// allow e.g. implementing `Cast<Foo>` for `u8`.
15+
#[unstable(feature = "integer_casts", issue = "157388")]
16+
pub trait SealedCast<T>: Sealed {}
17+
18+
#[unstable(feature = "integer_casts", issue = "157388")]
19+
impl<T: Sealed, U: Sealed> SealedCast<T> for U {}
20+
21+
macro_rules! impl_sealed_int {
22+
([$($T:ty),*]) => {$(
23+
#[unstable(feature = "integer_casts", issue = "157388")]
24+
impl Sealed for $T { }
25+
)*};
26+
}
27+
28+
impl_sealed_int!([u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize]);
29+
}
30+
331
/// Supporting trait for inherent methods of `f32` and `f64` such as `to_int_unchecked`.
432
/// Typically doesn’t need to be used directly.
533
#[unstable(feature = "convert_float_to_int", issue = "67057")]
@@ -630,3 +658,98 @@ impl_nonzero_int_try_from_nonzero_int!(i32 => u8, u16, u32, u64, u128, usize);
630658
impl_nonzero_int_try_from_nonzero_int!(i64 => u8, u16, u32, u64, u128, usize);
631659
impl_nonzero_int_try_from_nonzero_int!(i128 => u8, u16, u32, u64, u128, usize);
632660
impl_nonzero_int_try_from_nonzero_int!(isize => u8, u16, u32, u64, u128, usize);
661+
662+
/// Conversion between integers, wrapping around or saturating at the target type's boundaries.
663+
#[unstable(feature = "integer_casts", issue = "157388")]
664+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
665+
pub const trait BoundedCastFromInt<T>: private::SealedCast<T> + Sized {
666+
/// Converts `value` to this type, wrapping around at the boundary of the type.
667+
#[unstable(feature = "integer_casts", issue = "157388")]
668+
fn wrapping_cast_from(value: T) -> Self;
669+
670+
/// Converts `value` to this type, saturating at the numeric bounds instead of overflowing.
671+
#[unstable(feature = "integer_casts", issue = "157388")]
672+
fn saturating_cast_from(value: T) -> Self;
673+
}
674+
675+
/// Fallible conversion between integers.
676+
#[unstable(feature = "integer_casts", issue = "157388")]
677+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
678+
pub const trait CheckedCastFromInt<T>: private::SealedCast<T> + Sized {
679+
/// Converts `value` to this type, returning `None` if overflow would have occurred.
680+
#[unstable(feature = "integer_casts", issue = "157388")]
681+
fn checked_cast_from(value: T) -> Option<Self>;
682+
683+
/// Converts `value` to this type, assuming overflow cannot occur.
684+
///
685+
/// # Safety
686+
///
687+
/// This results in undefined behavior when `value` will overflow when
688+
/// converted to this type.
689+
#[unstable(feature = "integer_casts", issue = "157388")]
690+
unsafe fn unchecked_cast_from(value: T) -> Self;
691+
692+
/// Converts `value` to this type, panicking on overflow.
693+
///
694+
/// # Panics
695+
///
696+
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
697+
#[unstable(feature = "integer_casts", issue = "157388")]
698+
fn strict_cast_from(value: T) -> Self;
699+
}
700+
701+
macro_rules! impl_int_cast {
702+
($Src:ty as [$($Dst:ty),*]) => {$(
703+
#[unstable(feature = "integer_casts", issue = "157388")]
704+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
705+
impl const CheckedCastFromInt<$Src> for $Dst {
706+
#[inline]
707+
fn checked_cast_from(value: $Src) -> Option<Self> {
708+
value.try_into().ok()
709+
}
710+
711+
#[inline(always)]
712+
unsafe fn unchecked_cast_from(value: $Src) -> Self {
713+
// SAFETY: the safety contract must be upheld by the caller.
714+
unsafe { value.try_into().unwrap_unchecked() }
715+
}
716+
717+
#[inline]
718+
#[track_caller]
719+
fn strict_cast_from(value: $Src) -> Self {
720+
match value.try_into() {
721+
Ok(x) => x,
722+
Err(_) => core::num::imp::overflow_panic::cast_integer()
723+
}
724+
}
725+
}
726+
727+
#[unstable(feature = "integer_casts", issue = "157388")]
728+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
729+
impl const BoundedCastFromInt<$Src> for $Dst {
730+
#[inline(always)]
731+
fn wrapping_cast_from(value: $Src) -> Self {
732+
value as Self
733+
}
734+
735+
#[inline]
736+
#[allow(unused_comparisons)]
737+
#[allow(irrefutable_let_patterns)]
738+
fn saturating_cast_from(value: $Src) -> Self {
739+
if let Ok(x) = value.try_into() {
740+
return x;
741+
}
742+
743+
if value < 0 { <$Dst>::MIN } else { <$Dst>::MAX }
744+
}
745+
}
746+
)*};
747+
}
748+
749+
macro_rules! impl_all_int_casts {
750+
([$($Src:ty),*]) => {$(
751+
impl_int_cast!($Src as [u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize]);
752+
)*};
753+
}
754+
755+
impl_all_int_casts!([u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize]);

library/core/src/num/imp/overflow_panic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,6 @@ pub(in crate::num) const fn shl() -> ! {
5252

5353
#[cold]
5454
#[track_caller]
55-
pub(in crate::num) const fn cast_integer() -> ! {
55+
pub(crate) const fn cast_integer() -> ! {
5656
panic!("attempt to cast integer with overflow")
5757
}

library/core/src/num/int_macros.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4068,5 +4068,122 @@ macro_rules! int_impl {
40684068
{
40694069
traits::WidenTarget::internal_widen(self)
40704070
}
4071+
4072+
4073+
/// Converts `self` to the target integer type, saturating at the numeric
4074+
/// bounds instead of overflowing.
4075+
///
4076+
/// # Examples
4077+
///
4078+
/// ```
4079+
/// #![feature(integer_casts)]
4080+
#[doc = concat!("assert_eq!(i8::MAX, ", stringify!($SelfT), "::MAX.saturating_cast());")]
4081+
#[doc = concat!("assert_eq!(i8::MIN, ", stringify!($SelfT), "::MIN.saturating_cast());")]
4082+
#[doc = concat!("assert_eq!(42u8, 42", stringify!($SelfT), ".saturating_cast());")]
4083+
#[doc = concat!("assert_eq!(0u8, (-42", stringify!($SelfT), ").saturating_cast());")]
4084+
/// ```
4085+
#[must_use = "this returns the cast result and does not modify the original"]
4086+
#[unstable(feature = "integer_casts", issue = "157388")]
4087+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
4088+
#[inline(always)]
4089+
pub const fn saturating_cast<T: [const] BoundedCastFromInt<Self>>(self) -> T {
4090+
T::saturating_cast_from(self)
4091+
}
4092+
4093+
/// Converts `self` to the target integer type, wrapping around at the
4094+
/// boundary of the target type.
4095+
///
4096+
/// # Examples
4097+
///
4098+
/// ```
4099+
/// #![feature(integer_casts)]
4100+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX as i8, ", stringify!($SelfT), "::MAX.wrapping_cast());")]
4101+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN as i8, ", stringify!($SelfT), "::MIN.wrapping_cast());")]
4102+
#[doc = concat!("assert_eq!(42u8, 42", stringify!($SelfT), ".wrapping_cast());")]
4103+
#[doc = concat!("assert_eq!(u8::MAX - 41, (-42", stringify!($SelfT), ").wrapping_cast());")]
4104+
/// ```
4105+
#[must_use = "this returns the cast result and does not modify the original"]
4106+
#[unstable(feature = "integer_casts", issue = "157388")]
4107+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
4108+
#[inline(always)]
4109+
pub const fn wrapping_cast<T: [const] BoundedCastFromInt<Self>>(self) -> T {
4110+
T::wrapping_cast_from(self)
4111+
}
4112+
4113+
/// Converts `self` to the target integer type, returning `None` if the value
4114+
/// does not lie in the target type's domain.
4115+
///
4116+
/// # Examples
4117+
///
4118+
/// ```
4119+
/// #![feature(integer_casts)]
4120+
#[doc = concat!("assert_eq!(Some(42u8), 42", stringify!($SelfT), ".checked_cast());")]
4121+
#[doc = concat!("assert_eq!((-42", stringify!($SelfT), ").checked_cast::<u8>(), None);")]
4122+
/// ```
4123+
#[must_use = "this returns the cast result and does not modify the original"]
4124+
#[unstable(feature = "integer_casts", issue = "157388")]
4125+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
4126+
#[inline(always)]
4127+
pub const fn checked_cast<T: [const] CheckedCastFromInt<Self>>(self) -> Option<T> {
4128+
T::checked_cast_from(self)
4129+
}
4130+
4131+
/// Converts `self` to the target integer type, panicking if the value
4132+
/// does not lie in the target type's domain.
4133+
///
4134+
/// # Panics
4135+
///
4136+
/// This function will panic if the value does not lie in the target type's domain.
4137+
///
4138+
/// # Examples
4139+
///
4140+
/// ```
4141+
/// #![feature(integer_casts)]
4142+
#[doc = concat!("assert_eq!(42u8, 42", stringify!($SelfT), ".strict_cast());")]
4143+
/// ```
4144+
///
4145+
/// The following will panic:
4146+
///
4147+
/// ```should_panic
4148+
/// #![feature(integer_casts)]
4149+
#[doc = concat!("let _ = (-42", stringify!($SelfT), ").strict_cast::<u8>();")]
4150+
/// ```
4151+
#[must_use = "this returns the cast result and does not modify the original"]
4152+
#[unstable(feature = "integer_casts", issue = "157388")]
4153+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
4154+
#[inline(always)]
4155+
#[track_caller]
4156+
pub const fn strict_cast<T: [const] CheckedCastFromInt<Self>>(self) -> T {
4157+
T::strict_cast_from(self)
4158+
}
4159+
4160+
/// Converts `self` to the target integer type, assuming the value lies in the target type's domain.
4161+
///
4162+
/// # Safety
4163+
///
4164+
/// This results in undefined behavior if the integer value of `self` is bigger than `T::MAX`,
4165+
/// or smaller than `T::MIN`, where `T` is the target type.
4166+
#[must_use = "this returns the cast result and does not modify the original"]
4167+
#[unstable(feature = "integer_casts", issue = "157388")]
4168+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
4169+
#[inline(always)]
4170+
pub const unsafe fn unchecked_cast<T: [const] CheckedCastFromInt<Self>>(self) -> T {
4171+
assert_unsafe_precondition!(
4172+
check_language_ub,
4173+
concat!(stringify!($SelfT), "::unchecked_cast must fit in the target type"),
4174+
(
4175+
// Check has to be performed up-front because it depends on generic T.
4176+
in_bounds: bool = {
4177+
let cast_val = self.checked_cast::<T>();
4178+
let ret = cast_val.is_some();
4179+
core::mem::forget(cast_val); // We don't have const Drop, but we know it's an int.
4180+
ret
4181+
},
4182+
) => in_bounds,
4183+
);
4184+
4185+
// SAFETY: this is guaranteed to be safe by the caller.
4186+
unsafe { T::unchecked_cast_from(self) }
4187+
}
40714188
}
40724189
}

library/core/src/num/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
#![stable(feature = "rust1", since = "1.0.0")]
44

5+
use crate::convert::{BoundedCastFromInt, CheckedCastFromInt};
56
use crate::panic::const_panic;
67
use crate::str::FromStr;
78
use crate::ub_checks::assert_unsafe_precondition;

0 commit comments

Comments
 (0)