Skip to content

Commit d65ab4d

Browse files
authored
Unrolled build for #157402
Rollup merge of #157402 - orlp:int-cast, r=jhpratt Implement feature `integer_casts` Tracking issue: #157388
2 parents 06293ff + d79b87b commit d65ab4d

9 files changed

Lines changed: 436 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: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,3 +630,98 @@ impl_nonzero_int_try_from_nonzero_int!(i32 => u8, u16, u32, u64, u128, usize);
630630
impl_nonzero_int_try_from_nonzero_int!(i64 => u8, u16, u32, u64, u128, usize);
631631
impl_nonzero_int_try_from_nonzero_int!(i128 => u8, u16, u32, u64, u128, usize);
632632
impl_nonzero_int_try_from_nonzero_int!(isize => u8, u16, u32, u64, u128, usize);
633+
634+
/// Conversion between integers, wrapping around or saturating at the target type's boundaries.
635+
#[unstable(feature = "integer_casts", issue = "157388")]
636+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
637+
pub impl(self) const trait BoundedCastFromInt<T>: Sized {
638+
/// Converts `value` to this type, wrapping around at the boundary of the type.
639+
#[unstable(feature = "integer_casts", issue = "157388")]
640+
fn wrapping_cast_from(value: T) -> Self;
641+
642+
/// Converts `value` to this type, saturating at the numeric bounds instead of overflowing.
643+
#[unstable(feature = "integer_casts", issue = "157388")]
644+
fn saturating_cast_from(value: T) -> Self;
645+
}
646+
647+
/// Fallible conversion between integers.
648+
#[unstable(feature = "integer_casts", issue = "157388")]
649+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
650+
pub impl(self) const trait CheckedCastFromInt<T>: Sized {
651+
/// Converts `value` to this type, returning `None` if overflow would have occurred.
652+
#[unstable(feature = "integer_casts", issue = "157388")]
653+
fn checked_cast_from(value: T) -> Option<Self>;
654+
655+
/// Converts `value` to this type, assuming overflow cannot occur.
656+
///
657+
/// # Safety
658+
///
659+
/// This results in undefined behavior when `value` will overflow when
660+
/// converted to this type.
661+
#[unstable(feature = "integer_casts", issue = "157388")]
662+
unsafe fn unchecked_cast_from(value: T) -> Self;
663+
664+
/// Converts `value` to this type, panicking on overflow.
665+
///
666+
/// # Panics
667+
///
668+
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
669+
#[unstable(feature = "integer_casts", issue = "157388")]
670+
fn strict_cast_from(value: T) -> Self;
671+
}
672+
673+
macro_rules! impl_int_cast {
674+
($Src:ty as [$($Dst:ty),*]) => {$(
675+
#[unstable(feature = "integer_casts", issue = "157388")]
676+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
677+
impl const CheckedCastFromInt<$Src> for $Dst {
678+
#[inline]
679+
fn checked_cast_from(value: $Src) -> Option<Self> {
680+
value.try_into().ok()
681+
}
682+
683+
#[inline(always)]
684+
unsafe fn unchecked_cast_from(value: $Src) -> Self {
685+
// SAFETY: the safety contract must be upheld by the caller.
686+
unsafe { value.try_into().unwrap_unchecked() }
687+
}
688+
689+
#[inline]
690+
#[track_caller]
691+
fn strict_cast_from(value: $Src) -> Self {
692+
match value.try_into() {
693+
Ok(x) => x,
694+
Err(_) => core::num::imp::overflow_panic::cast_integer()
695+
}
696+
}
697+
}
698+
699+
#[unstable(feature = "integer_casts", issue = "157388")]
700+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
701+
impl const BoundedCastFromInt<$Src> for $Dst {
702+
#[inline(always)]
703+
fn wrapping_cast_from(value: $Src) -> Self {
704+
value as Self
705+
}
706+
707+
#[inline]
708+
#[allow(unused_comparisons)]
709+
#[allow(irrefutable_let_patterns)]
710+
fn saturating_cast_from(value: $Src) -> Self {
711+
if let Ok(x) = value.try_into() {
712+
return x;
713+
}
714+
715+
if value < 0 { <$Dst>::MIN } else { <$Dst>::MAX }
716+
}
717+
}
718+
)*};
719+
}
720+
721+
macro_rules! impl_all_int_casts {
722+
([$($Src:ty),*]) => {$(
723+
impl_int_cast!($Src as [u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize]);
724+
)*};
725+
}
726+
727+
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
@@ -58,6 +58,6 @@ pub(in crate::num) const fn pow() -> ! {
5858

5959
#[cold]
6060
#[track_caller]
61-
pub(in crate::num) const fn cast_integer() -> ! {
61+
pub(crate) const fn cast_integer() -> ! {
6262
panic!("attempt to cast integer with overflow")
6363
}

library/core/src/num/int_macros.rs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4048,5 +4048,123 @@ macro_rules! int_impl {
40484048
{
40494049
traits::WidenTarget::internal_widen(self)
40504050
}
4051+
4052+
4053+
/// Converts `self` to the target integer type, saturating at the numeric
4054+
/// bounds instead of overflowing.
4055+
///
4056+
/// # Examples
4057+
///
4058+
/// ```
4059+
/// #![feature(integer_casts)]
4060+
#[doc = concat!("assert_eq!(i8::MAX, ", stringify!($SelfT), "::MAX.saturating_cast());")]
4061+
#[doc = concat!("assert_eq!(i8::MIN, ", stringify!($SelfT), "::MIN.saturating_cast());")]
4062+
#[doc = concat!("assert_eq!(42u8, 42", stringify!($SelfT), ".saturating_cast());")]
4063+
#[doc = concat!("assert_eq!(0u8, (-42", stringify!($SelfT), ").saturating_cast());")]
4064+
/// ```
4065+
#[must_use = "this returns the cast result and does not modify the original"]
4066+
#[unstable(feature = "integer_casts", issue = "157388")]
4067+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
4068+
#[inline(always)]
4069+
pub const fn saturating_cast<T: [const] BoundedCastFromInt<Self>>(self) -> T {
4070+
T::saturating_cast_from(self)
4071+
}
4072+
4073+
/// Converts `self` to the target integer type, wrapping around at the
4074+
/// boundary of the target type.
4075+
///
4076+
/// # Examples
4077+
///
4078+
/// ```
4079+
/// #![feature(integer_casts)]
4080+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX as i8, ", stringify!($SelfT), "::MAX.wrapping_cast());")]
4081+
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN as i8, ", stringify!($SelfT), "::MIN.wrapping_cast());")]
4082+
#[doc = concat!("assert_eq!(42u8, 42", stringify!($SelfT), ".wrapping_cast());")]
4083+
#[doc = concat!("assert_eq!(u8::MAX - 41, (-42", stringify!($SelfT), ").wrapping_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 wrapping_cast<T: [const] BoundedCastFromInt<Self>>(self) -> T {
4090+
T::wrapping_cast_from(self)
4091+
}
4092+
4093+
/// Converts `self` to the target integer type, returning `None` if the value
4094+
/// is not representable by the target type.
4095+
///
4096+
/// # Examples
4097+
///
4098+
/// ```
4099+
/// #![feature(integer_casts)]
4100+
#[doc = concat!("assert_eq!(Some(42u8), 42", stringify!($SelfT), ".checked_cast());")]
4101+
#[doc = concat!("assert_eq!((-42", stringify!($SelfT), ").checked_cast::<u8>(), None);")]
4102+
/// ```
4103+
#[must_use = "this returns the cast result and does not modify the original"]
4104+
#[unstable(feature = "integer_casts", issue = "157388")]
4105+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
4106+
#[inline(always)]
4107+
pub const fn checked_cast<T: [const] CheckedCastFromInt<Self>>(self) -> Option<T> {
4108+
T::checked_cast_from(self)
4109+
}
4110+
4111+
/// Converts `self` to the target integer type, panicking if the value
4112+
/// is not representable by the target type.
4113+
///
4114+
/// # Panics
4115+
///
4116+
/// This function will panic if the value is not representable by the target type.
4117+
///
4118+
/// # Examples
4119+
///
4120+
/// ```
4121+
/// #![feature(integer_casts)]
4122+
#[doc = concat!("assert_eq!(42u8, 42", stringify!($SelfT), ".strict_cast());")]
4123+
/// ```
4124+
///
4125+
/// The following will panic:
4126+
///
4127+
/// ```should_panic
4128+
/// #![feature(integer_casts)]
4129+
#[doc = concat!("let _ = (-42", stringify!($SelfT), ").strict_cast::<u8>();")]
4130+
/// ```
4131+
#[must_use = "this returns the cast result and does not modify the original"]
4132+
#[unstable(feature = "integer_casts", issue = "157388")]
4133+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
4134+
#[inline(always)]
4135+
#[track_caller]
4136+
pub const fn strict_cast<T: [const] CheckedCastFromInt<Self>>(self) -> T {
4137+
T::strict_cast_from(self)
4138+
}
4139+
4140+
/// Converts `self` to the target integer type, assuming the value is
4141+
/// representable by the target type.
4142+
///
4143+
/// # Safety
4144+
///
4145+
/// This results in undefined behavior if the integer value of `self` is bigger than `T::MAX`,
4146+
/// or smaller than `T::MIN`, where `T` is the target type.
4147+
#[must_use = "this returns the cast result and does not modify the original"]
4148+
#[unstable(feature = "integer_casts", issue = "157388")]
4149+
#[rustc_const_unstable(feature = "integer_casts", issue = "157388")]
4150+
#[inline(always)]
4151+
pub const unsafe fn unchecked_cast<T: [const] CheckedCastFromInt<Self>>(self) -> T {
4152+
assert_unsafe_precondition!(
4153+
check_language_ub,
4154+
concat!(stringify!($SelfT), "::unchecked_cast must fit in the target type"),
4155+
(
4156+
// Check has to be performed up-front because it depends on generic T.
4157+
in_bounds: bool = {
4158+
let cast_val = self.checked_cast::<T>();
4159+
let ret = cast_val.is_some();
4160+
core::mem::forget(cast_val); // We don't have const Drop, but we know it's an int.
4161+
ret
4162+
},
4163+
) => in_bounds,
4164+
);
4165+
4166+
// SAFETY: this is guaranteed to be safe by the caller.
4167+
unsafe { T::unchecked_cast_from(self) }
4168+
}
40514169
}
40524170
}

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

library/coretests/tests/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
#![feature(hashmap_internals)]
6969
#![feature(int_from_ascii)]
7070
#![feature(int_roundings)]
71+
#![feature(integer_casts)]
7172
#![feature(io_slice_as_bytes)]
7273
#![feature(ip)]
7374
#![feature(is_ascii_octdigit)]
@@ -83,6 +84,7 @@
8384
#![feature(iterator_try_collect)]
8485
#![feature(iterator_try_reduce)]
8586
#![feature(layout_for_ptr)]
87+
#![feature(macro_metavar_expr_concat)]
8688
#![feature(maybe_uninit_fill)]
8789
#![feature(maybe_uninit_uninit_array_transpose)]
8890
#![feature(min_specialization)]

0 commit comments

Comments
 (0)