Auto merge of #148537 - oli-obk:push-yxuttqrqqyvu, r=dianqk

Start using pattern types in libcore (NonZero and friends)

part of rust-lang/rust#136006 

This PR only changes the internal representation of `NonZero`, `NonMax`, ... and other integral range types in libcore. This subsequently affects other types made up of it, but nothing really changes except that the field of `NonZero` is now accessible safely in contrast to the `rustc_layout_scalar_range_start` attribute, which has all kinds of obscure rules on how to properly access its field.
This commit is contained in:
bors 2026-02-12 13:23:22 +00:00
commit bb8b30a5fc
10 changed files with 65 additions and 75 deletions

View file

@ -5,45 +5,33 @@
)]
use crate::cmp::Ordering;
use crate::fmt;
use crate::hash::{Hash, Hasher};
use crate::marker::StructuralPartialEq;
use crate::{fmt, pattern_type};
macro_rules! define_valid_range_type {
($(
$(#[$m:meta])*
$vis:vis struct $name:ident($int:ident as $uint:ident in $low:literal..=$high:literal);
$vis:vis struct $name:ident($int:ident is $pat:pat);
)+) => {$(
#[derive(Clone, Copy, Eq)]
#[derive(Clone, Copy)]
#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start($low)]
#[rustc_layout_scalar_valid_range_end($high)]
$(#[$m])*
$vis struct $name($int);
const _: () = {
// With the `valid_range` attributes, it's always specified as unsigned
assert!(<$uint>::MIN == 0);
let ulow: $uint = $low;
let uhigh: $uint = $high;
assert!(ulow <= uhigh);
assert!(size_of::<$int>() == size_of::<$uint>());
};
$vis struct $name(pattern_type!($int is $pat));
impl $name {
#[inline]
pub const fn new(val: $int) -> Option<Self> {
if (val as $uint) >= ($low as $uint) && (val as $uint) <= ($high as $uint) {
// SAFETY: just checked the inclusive range
Some(unsafe { $name(val) })
#[allow(non_contiguous_range_endpoints)]
if let $pat = val {
// SAFETY: just checked that the value matches the pattern
Some(unsafe { $name(crate::mem::transmute(val)) })
} else {
None
}
}
/// Constructs an instance of this type from the underlying integer
/// primitive without checking whether its zero.
/// primitive without checking whether its valid.
///
/// # Safety
/// Immediate language UB if `val` is not within the valid range for this
@ -51,13 +39,13 @@ macro_rules! define_valid_range_type {
#[inline]
pub const unsafe fn new_unchecked(val: $int) -> Self {
// SAFETY: Caller promised that `val` is within the valid range.
unsafe { $name(val) }
unsafe { crate::mem::transmute(val) }
}
#[inline]
pub const fn as_inner(self) -> $int {
// SAFETY: This is a transparent wrapper, so unwrapping it is sound
// (Not using `.0` due to MCP#807.)
// SAFETY: pattern types are always legal values of their base type
// (Not using `.0` because that has perf regressions.)
unsafe { crate::mem::transmute(self) }
}
}
@ -67,6 +55,8 @@ macro_rules! define_valid_range_type {
// by <https://github.com/rust-lang/compiler-team/issues/807>.
impl StructuralPartialEq for $name {}
impl Eq for $name {}
impl PartialEq for $name {
#[inline]
fn eq(&self, other: &Self) -> bool {
@ -104,7 +94,7 @@ macro_rules! define_valid_range_type {
}
define_valid_range_type! {
pub struct Nanoseconds(u32 as u32 in 0..=999_999_999);
pub struct Nanoseconds(u32 is 0..=999_999_999);
}
impl Nanoseconds {
@ -120,47 +110,32 @@ impl const Default for Nanoseconds {
}
}
define_valid_range_type! {
pub struct NonZeroU8Inner(u8 as u8 in 1..=0xff);
pub struct NonZeroU16Inner(u16 as u16 in 1..=0xff_ff);
pub struct NonZeroU32Inner(u32 as u32 in 1..=0xffff_ffff);
pub struct NonZeroU64Inner(u64 as u64 in 1..=0xffffffff_ffffffff);
pub struct NonZeroU128Inner(u128 as u128 in 1..=0xffffffffffffffff_ffffffffffffffff);
pub struct NonZeroI8Inner(i8 as u8 in 1..=0xff);
pub struct NonZeroI16Inner(i16 as u16 in 1..=0xff_ff);
pub struct NonZeroI32Inner(i32 as u32 in 1..=0xffff_ffff);
pub struct NonZeroI64Inner(i64 as u64 in 1..=0xffffffff_ffffffff);
pub struct NonZeroI128Inner(i128 as u128 in 1..=0xffffffffffffffff_ffffffffffffffff);
pub struct NonZeroCharInner(char as u32 in 1..=0x10ffff);
}
#[cfg(target_pointer_width = "16")]
define_valid_range_type! {
pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff);
pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff);
pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff);
}
#[cfg(target_pointer_width = "32")]
define_valid_range_type! {
pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff_ffff);
pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff_ffff);
pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff_ffff);
}
#[cfg(target_pointer_width = "64")]
define_valid_range_type! {
pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff_ffff_ffff_ffff);
pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff_ffff_ffff_ffff);
pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff_ffff_ffff_ffff);
}
const HALF_USIZE: usize = usize::MAX >> 1;
define_valid_range_type! {
pub struct U32NotAllOnes(u32 as u32 in 0..=0xffff_fffe);
pub struct I32NotAllOnes(i32 as u32 in 0..=0xffff_fffe);
pub struct NonZeroU8Inner(u8 is 1..);
pub struct NonZeroU16Inner(u16 is 1..);
pub struct NonZeroU32Inner(u32 is 1..);
pub struct NonZeroU64Inner(u64 is 1..);
pub struct NonZeroU128Inner(u128 is 1..);
pub struct U64NotAllOnes(u64 as u64 in 0..=0xffff_ffff_ffff_fffe);
pub struct I64NotAllOnes(i64 as u64 in 0..=0xffff_ffff_ffff_fffe);
pub struct NonZeroI8Inner(i8 is ..0 | 1..);
pub struct NonZeroI16Inner(i16 is ..0 | 1..);
pub struct NonZeroI32Inner(i32 is ..0 | 1..);
pub struct NonZeroI64Inner(i64 is ..0 | 1..);
pub struct NonZeroI128Inner(i128 is ..0 | 1..);
pub struct UsizeNoHighBit(usize is 0..=HALF_USIZE);
pub struct NonZeroUsizeInner(usize is 1..);
pub struct NonZeroIsizeInner(isize is ..0 | 1..);
pub struct U32NotAllOnes(u32 is 0..u32::MAX);
pub struct I32NotAllOnes(i32 is ..-1 | 0..);
pub struct U64NotAllOnes(u64 is 0..u64::MAX);
pub struct I64NotAllOnes(i64 is ..-1 | 0..);
pub struct NonZeroCharInner(char is '\u{1}' ..= '\u{10ffff}');
}
pub trait NotAllOnesHelper {
@ -181,7 +156,7 @@ impl NotAllOnesHelper for i64 {
}
define_valid_range_type! {
pub struct CodePointInner(u32 as u32 in 0..=0x10ffff);
pub struct CodePointInner(u32 is 0..=0x10ffff);
}
impl CodePointInner {