Skip to content

Commit 7d20958

Browse files
committed
Implement feature integer_casts
1 parent e164200 commit 7d20958

6 files changed

Lines changed: 350 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: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,26 @@ mod private {
66
/// which allows potentially adding more trait methods after the trait is `#[stable]`.
77
#[unstable(feature = "convert_float_to_int", issue = "67057")]
88
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]);
929
}
1030

1131
/// Supporting trait for inherent methods of `f32` and `f64` such as `to_int_unchecked`.
@@ -640,3 +660,98 @@ impl_nonzero_int_try_from_nonzero_int!(i32 => u8, u16, u32, u64, u128, usize);
640660
impl_nonzero_int_try_from_nonzero_int!(i64 => u8, u16, u32, u64, u128, usize);
641661
impl_nonzero_int_try_from_nonzero_int!(i128 => u8, u16, u32, u64, u128, usize);
642662
impl_nonzero_int_try_from_nonzero_int!(isize => u8, u16, u32, u64, u128, usize);
663+
664+
/// Conversion between integers, wrapping around or saturating at the target type's boundaries.
665+
#[unstable(feature = "integer_casts", issue = "157388")]
666+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
667+
pub const trait BoundedCastFromInt<T>: private::SealedCast<T> + Sized {
668+
/// Converts `value` to this type, wrapping around at the boundary of the type.
669+
#[unstable(feature = "integer_casts", issue = "157388")]
670+
fn wrapping_cast_from(value: T) -> Self;
671+
672+
/// Converts `value` to this type, saturating at the numeric bounds instead of overflowing.
673+
#[unstable(feature = "integer_casts", issue = "157388")]
674+
fn saturating_cast_from(value: T) -> Self;
675+
}
676+
677+
/// Fallible conversion between integers.
678+
#[unstable(feature = "integer_casts", issue = "157388")]
679+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
680+
pub const trait CheckedCastFromInt<T>: private::SealedCast<T> + Sized {
681+
/// Converts `value` to this type, returning `None` if overflow would have occurred.
682+
#[unstable(feature = "integer_casts", issue = "157388")]
683+
fn checked_cast_from(value: T) -> Option<Self>;
684+
685+
/// Converts `value` to this type, assuming overflow cannot occur.
686+
///
687+
/// # Safety
688+
///
689+
/// This results in undefined behavior when `value` will overflow when
690+
/// converted to this type.
691+
#[unstable(feature = "integer_casts", issue = "157388")]
692+
unsafe fn unchecked_cast_from(value: T) -> Self;
693+
694+
/// Converts `value` to this type, panicking on overflow.
695+
///
696+
/// # Panics
697+
///
698+
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
699+
#[unstable(feature = "integer_casts", issue = "157388")]
700+
fn strict_cast_from(value: T) -> Self;
701+
}
702+
703+
macro_rules! impl_int_cast {
704+
($Src:ty as [$($Dst:ty),*]) => {$(
705+
#[unstable(feature = "integer_casts", issue = "157388")]
706+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
707+
impl const CheckedCastFromInt<$Src> for $Dst {
708+
#[inline]
709+
fn checked_cast_from(value: $Src) -> Option<Self> {
710+
value.try_into().ok()
711+
}
712+
713+
#[inline(always)]
714+
unsafe fn unchecked_cast_from(value: $Src) -> Self {
715+
// SAFETY: the safety contract must be upheld by the caller.
716+
unsafe { value.try_into().unwrap_unchecked() }
717+
}
718+
719+
#[inline]
720+
#[track_caller]
721+
fn strict_cast_from(value: $Src) -> Self {
722+
match value.try_into() {
723+
Ok(x) => x,
724+
Err(_) => core::num::imp::overflow_panic::cast_integer()
725+
}
726+
}
727+
}
728+
729+
#[unstable(feature = "integer_casts", issue = "157388")]
730+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
731+
impl const BoundedCastFromInt<$Src> for $Dst {
732+
#[inline(always)]
733+
fn wrapping_cast_from(value: $Src) -> Self {
734+
value as Self
735+
}
736+
737+
#[inline]
738+
#[allow(unused_comparisons)]
739+
#[allow(irrefutable_let_patterns)]
740+
fn saturating_cast_from(value: $Src) -> Self {
741+
if let Ok(x) = value.try_into() {
742+
return x;
743+
}
744+
745+
if value < 0 { <$Dst>::MIN } else { <$Dst>::MAX }
746+
}
747+
}
748+
)*};
749+
}
750+
751+
macro_rules! impl_all_int_casts {
752+
([$($Src:ty),*]) => {$(
753+
impl_int_cast!($Src as [u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize]);
754+
)*};
755+
}
756+
757+
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;

library/core/src/num/uint_macros.rs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4229,5 +4229,119 @@ macro_rules! uint_impl {
42294229
{
42304230
traits::WidenTarget::internal_widen(self)
42314231
}
4232+
4233+
/// Converts `self` to the target integer type, saturating at the numeric
4234+
/// bounds instead of overflowing.
4235+
///
4236+
/// # Examples
4237+
///
4238+
/// ```
4239+
/// #![feature(integer_casts)]
4240+
#[doc = concat!("assert_eq!(255u8, ", stringify!($SelfT), "::MAX.saturating_cast());")]
4241+
#[doc = concat!("assert_eq!(127i8, ", stringify!($SelfT), "::MAX.saturating_cast());")]
4242+
#[doc = concat!("assert_eq!(42i8, 42", stringify!($SelfT), ".saturating_cast());")]
4243+
/// ```
4244+
#[must_use = "this returns the cast result and does not modify the original"]
4245+
#[unstable(feature = "integer_casts", issue = "157388")]
4246+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
4247+
#[inline(always)]
4248+
pub const fn saturating_cast<T: [const] BoundedCastFromInt<Self>>(self) -> T {
4249+
T::saturating_cast_from(self)
4250+
}
4251+
4252+
/// Converts `self` to the target integer type, wrapping around at the
4253+
/// boundary of the target type.
4254+
///
4255+
/// # Examples
4256+
///
4257+
/// ```
4258+
/// #![feature(integer_casts)]
4259+
#[doc = concat!("assert_eq!(255u8, ", stringify!($SelfT), "::MAX.wrapping_cast());")]
4260+
#[doc = concat!("assert_eq!(42i8, 42", stringify!($SelfT), ".wrapping_cast());")]
4261+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX as i8, ", stringify!($SelfT), "::MAX.wrapping_cast());")]
4262+
/// ```
4263+
#[must_use = "this returns the cast result and does not modify the original"]
4264+
#[unstable(feature = "integer_casts", issue = "157388")]
4265+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
4266+
#[inline(always)]
4267+
pub const fn wrapping_cast<T: [const] BoundedCastFromInt<Self>>(self) -> T {
4268+
T::wrapping_cast_from(self)
4269+
}
4270+
4271+
/// Converts `self` to the target integer type, returning `None` if the value
4272+
/// does not lie in the target type's domain.
4273+
///
4274+
/// # Examples
4275+
///
4276+
/// ```
4277+
/// #![feature(integer_casts)]
4278+
#[doc = concat!("assert_eq!(Some(42u8), 42", stringify!($SelfT), ".checked_cast());")]
4279+
#[doc = concat!("assert_eq!(128", stringify!($SelfT), ".checked_cast::<i8>(), None);")]
4280+
/// ```
4281+
#[must_use = "this returns the cast result and does not modify the original"]
4282+
#[unstable(feature = "integer_casts", issue = "157388")]
4283+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
4284+
#[inline(always)]
4285+
pub const fn checked_cast<T: [const] CheckedCastFromInt<Self>>(self) -> Option<T> {
4286+
T::checked_cast_from(self)
4287+
}
4288+
4289+
/// Converts `self` to the target integer type, panicking if the value
4290+
/// does not lie in the target type's domain.
4291+
///
4292+
/// # Panics
4293+
///
4294+
/// This function will panic if the value does not lie in the target type's domain.
4295+
///
4296+
/// # Examples
4297+
///
4298+
/// ```
4299+
/// #![feature(integer_casts)]
4300+
#[doc = concat!("assert_eq!(42u8, 42", stringify!($SelfT), ".strict_cast());")]
4301+
/// ```
4302+
///
4303+
/// The following will panic:
4304+
///
4305+
/// ```should_panic
4306+
/// #![feature(integer_casts)]
4307+
#[doc = concat!("let _ = 128", stringify!($SelfT), ".strict_cast::<i8>();")]
4308+
/// ```
4309+
#[must_use = "this returns the cast result and does not modify the original"]
4310+
#[unstable(feature = "integer_casts", issue = "157388")]
4311+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
4312+
#[inline(always)]
4313+
#[track_caller]
4314+
pub const fn strict_cast<T: [const] CheckedCastFromInt<Self>>(self) -> T {
4315+
T::strict_cast_from(self)
4316+
}
4317+
4318+
/// Converts `self` to the target integer type, assuming the value lies in the target type's domain.
4319+
///
4320+
/// # Safety
4321+
///
4322+
/// This results in undefined behavior if the integer value of `self` is bigger than `T::MAX`,
4323+
/// or smaller than `T::MIN`, where `T` is the target type.
4324+
#[must_use = "this returns the cast result and does not modify the original"]
4325+
#[unstable(feature = "integer_casts", issue = "157388")]
4326+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
4327+
#[inline(always)]
4328+
pub const unsafe fn unchecked_cast<T: [const] CheckedCastFromInt<Self>>(self) -> T {
4329+
assert_unsafe_precondition!(
4330+
check_language_ub,
4331+
concat!(stringify!($SelfT), "::unchecked_cast must fit in the target type"),
4332+
(
4333+
// Check has to be performed up-front because it depends on generic T.
4334+
in_bounds: bool = {
4335+
let cast_val = self.checked_cast::<T>();
4336+
let ret = cast_val.is_some();
4337+
core::mem::forget(cast_val); // We don't have const Drop, but we know it's an int.
4338+
ret
4339+
},
4340+
) => in_bounds,
4341+
);
4342+
4343+
// SAFETY: this is guaranteed to be safe by the caller.
4344+
unsafe { T::unchecked_cast_from(self) }
4345+
}
42324346
}
42334347
}

0 commit comments

Comments
 (0)