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")]
106impl const Default for Nanoseconds {
107    #[inline]
108    fn default() -> Self {
109        Self::ZERO
110    }
111}
112
113const HALF_USIZE: usize = usize::MAX >> 1;
114
115define_valid_range_type! {
116    pub struct NonZeroU8Inner(u8 is 1..);
117    pub struct NonZeroU16Inner(u16 is 1..);
118    pub struct NonZeroU32Inner(u32 is 1..);
119    pub struct NonZeroU64Inner(u64 is 1..);
120    pub struct NonZeroU128Inner(u128 is 1..);
121
122    pub struct NonZeroI8Inner(i8 is ..0 | 1..);
123    pub struct NonZeroI16Inner(i16 is ..0 | 1..);
124    pub struct NonZeroI32Inner(i32 is ..0 | 1..);
125    pub struct NonZeroI64Inner(i64 is ..0 | 1..);
126    pub struct NonZeroI128Inner(i128 is ..0 | 1..);
127
128    pub struct UsizeNoHighBit(usize is 0..=HALF_USIZE);
129    pub struct NonZeroUsizeInner(usize is 1..);
130    pub struct NonZeroIsizeInner(isize is ..0 | 1..);
131
132    pub struct U32NotAllOnes(u32 is 0..u32::MAX);
133    pub struct I32NotAllOnes(i32 is ..-1 | 0..);
134
135    pub struct U64NotAllOnes(u64 is 0..u64::MAX);
136    pub struct I64NotAllOnes(i64 is ..-1 | 0..);
137
138    pub struct NonZeroCharInner(char is '\u{1}' ..= '\u{10ffff}');
139}
140
141pub trait NotAllOnesHelper {
142    type Type;
143}
144pub type NotAllOnes<T> = <T as NotAllOnesHelper>::Type;
145impl NotAllOnesHelper for u32 {
146    type Type = U32NotAllOnes;
147}
148impl NotAllOnesHelper for i32 {
149    type Type = I32NotAllOnes;
150}
151impl NotAllOnesHelper for u64 {
152    type Type = U64NotAllOnes;
153}
154impl NotAllOnesHelper for i64 {
155    type Type = I64NotAllOnes;
156}
157
158define_valid_range_type! {
159    pub struct CodePointInner(u32 is 0..=0x10ffff);
160}
161
162impl CodePointInner {
163    pub const ZERO: Self = CodePointInner::new(0).unwrap();
164}
165
166impl Default for CodePointInner {
167    #[inline]
168    fn default() -> Self {
169        Self::ZERO
170    }
171}