Skip to main content

core/num/
niche_types.rs

1#![unstable(
2    feature = "temporary_niche_types",
3    issue = "none",
4    reason = "for core, alloc, and std internals until pattern types are further along"
5)]
6
7use crate::cmp::Ordering;
8use crate::hash::{Hash, Hasher};
9use crate::marker::StructuralPartialEq;
10use crate::{fmt, pattern_type};
11
12macro_rules! define_valid_range_type {
13    ($(
14        $(#[$m:meta])*
15        $vis:vis struct $name:ident($int:ident is $pat:pat);
16    )+) => {$(
17        #[derive(Clone, Copy)]
18        #[repr(transparent)]
19        $(#[$m])*
20        $vis struct $name(pattern_type!($int is $pat));
21        impl $name {
22            #[inline]
23            pub const fn new(val: $int) -> Option<Self> {
24                #[allow(non_contiguous_range_endpoints)]
25                if let $pat = val {
26                    // SAFETY: just checked that the value matches the pattern
27                    Some(unsafe { $name(crate::mem::transmute(val)) })
28                } else {
29                    None
30                }
31            }
32
33            /// Constructs an instance of this type from the underlying integer
34            /// primitive without checking whether its valid.
35            ///
36            /// # Safety
37            /// Immediate language UB if `val` is not within the valid range for this
38            /// type, as it violates the validity invariant.
39            #[inline]
40            pub const unsafe fn new_unchecked(val: $int) -> Self {
41                // SAFETY: Caller promised that `val` is within the valid range.
42                unsafe { crate::mem::transmute(val) }
43            }
44
45            #[inline]
46            pub const fn as_inner(self) -> $int {
47                // SAFETY: pattern types are always legal values of their base type
48                // (Not using `.0` because that has perf regressions.)
49                unsafe { crate::mem::transmute(self) }
50            }
51        }
52
53        // This is required to allow matching a constant.  We don't get it from a derive
54        // because the derived `PartialEq` would do a field projection, which is banned
55        // by <https://github.com/rust-lang/compiler-team/issues/807>.
56        impl StructuralPartialEq for $name {}
57
58        impl Eq for $name {}
59
60        impl PartialEq for $name {
61            #[inline]
62            fn eq(&self, other: &Self) -> bool {
63                self.as_inner() == other.as_inner()
64            }
65        }
66
67        impl Ord for $name {
68            #[inline]
69            fn cmp(&self, other: &Self) -> Ordering {
70                Ord::cmp(&self.as_inner(), &other.as_inner())
71            }
72        }
73
74        impl PartialOrd for $name {
75            #[inline]
76            fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
77                Some(Ord::cmp(self, other))
78            }
79        }
80
81        impl Hash for $name {
82            // Required method
83            fn hash<H: Hasher>(&self, state: &mut H) {
84                Hash::hash(&self.as_inner(), state);
85            }
86        }
87
88        impl fmt::Debug for $name {
89            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90                <$int as fmt::Debug>::fmt(&self.as_inner(), f)
91            }
92        }
93    )+};
94}
95
96define_valid_range_type! {
97    pub struct Nanoseconds(u32 is 0..=999_999_999);
98}
99
100impl Nanoseconds {
101    // SAFETY: 0 is within the valid range
102    pub const ZERO: Self = unsafe { Nanoseconds::new_unchecked(0) };
103}
104
105#[rustc_const_unstable(feature = "const_default", issue = "143894")]
106const impl Default for Nanoseconds {
107    #[inline]
108    fn default() -> Self {
109        Self::ZERO
110    }
111}
112
113const HALF_USIZE: usize = usize::MAX >> 1;
114const HALF_USIZE_MINUS_ONE: usize = HALF_USIZE - 1;
115
116define_valid_range_type! {
117    pub struct NonZeroU8Inner(u8 is 1..);
118    pub struct NonZeroU16Inner(u16 is 1..);
119    pub struct NonZeroU32Inner(u32 is 1..);
120    pub struct NonZeroU64Inner(u64 is 1..);
121    pub struct NonZeroU128Inner(u128 is 1..);
122
123    pub struct NonZeroI8Inner(i8 is ..0 | 1..);
124    pub struct NonZeroI16Inner(i16 is ..0 | 1..);
125    pub struct NonZeroI32Inner(i32 is ..0 | 1..);
126    pub struct NonZeroI64Inner(i64 is ..0 | 1..);
127    pub struct NonZeroI128Inner(i128 is ..0 | 1..);
128
129    pub struct UsizeNoHighBit(usize is 0..=HALF_USIZE);
130    pub struct UsizeNoHighBitMinusOne(usize is 0..=HALF_USIZE_MINUS_ONE);
131    pub struct NonZeroUsizeInner(usize is 1..);
132    pub struct NonZeroIsizeInner(isize is ..0 | 1..);
133
134    pub struct U32NotAllOnes(u32 is 0..u32::MAX);
135    pub struct I32NotAllOnes(i32 is ..-1 | 0..);
136
137    pub struct U64NotAllOnes(u64 is 0..u64::MAX);
138    pub struct I64NotAllOnes(i64 is ..-1 | 0..);
139
140    pub struct NonZeroCharInner(char is '\u{1}' ..= '\u{10ffff}');
141}
142
143pub trait NotAllOnesHelper {
144    type Type;
145}
146pub type NotAllOnes<T> = <T as NotAllOnesHelper>::Type;
147impl NotAllOnesHelper for u32 {
148    type Type = U32NotAllOnes;
149}
150impl NotAllOnesHelper for i32 {
151    type Type = I32NotAllOnes;
152}
153impl NotAllOnesHelper for u64 {
154    type Type = U64NotAllOnes;
155}
156impl NotAllOnesHelper for i64 {
157    type Type = I64NotAllOnes;
158}
159
160define_valid_range_type! {
161    pub struct CodePointInner(u32 is 0..=0x10ffff);
162}
163
164impl CodePointInner {
165    pub const ZERO: Self = CodePointInner::new(0).unwrap();
166}
167
168impl Default for CodePointInner {
169    #[inline]
170    fn default() -> Self {
171        Self::ZERO
172    }
173}