alloc/vec/
is_zero.rs

1use core::num::{NonZero, Saturating, Wrapping};
2
3use crate::boxed::Box;
4
5#[rustc_specialization_trait]
6pub(super) unsafe trait IsZero {
7    /// Whether this value's representation is all zeros,
8    /// or can be represented with all zeroes.
9    fn is_zero(&self) -> bool;
10}
11
12macro_rules! impl_is_zero {
13    ($t:ty, $is_zero:expr) => {
14        unsafe impl IsZero for $t {
15            #[inline]
16            fn is_zero(&self) -> bool {
17                $is_zero(*self)
18            }
19        }
20    };
21}
22
23impl_is_zero!(i8, |x| x == 0); // It is needed to impl for arrays and tuples of i8.
24impl_is_zero!(i16, |x| x == 0);
25impl_is_zero!(i32, |x| x == 0);
26impl_is_zero!(i64, |x| x == 0);
27impl_is_zero!(i128, |x| x == 0);
28impl_is_zero!(isize, |x| x == 0);
29
30impl_is_zero!(u8, |x| x == 0); // It is needed to impl for arrays and tuples of u8.
31impl_is_zero!(u16, |x| x == 0);
32impl_is_zero!(u32, |x| x == 0);
33impl_is_zero!(u64, |x| x == 0);
34impl_is_zero!(u128, |x| x == 0);
35impl_is_zero!(usize, |x| x == 0);
36
37impl_is_zero!(bool, |x| x == false);
38impl_is_zero!(char, |x| x == '\0');
39
40impl_is_zero!(f32, |x: f32| x.to_bits() == 0);
41impl_is_zero!(f64, |x: f64| x.to_bits() == 0);
42
43// `IsZero` cannot be soundly implemented for pointers because of provenance
44// (see #135338).
45
46unsafe impl<T: IsZero, const N: usize> IsZero for [T; N] {
47    #[inline]
48    fn is_zero(&self) -> bool {
49        // Because this is generated as a runtime check, it's not obvious that
50        // it's worth doing if the array is really long. The threshold here
51        // is largely arbitrary, but was picked because as of 2022-07-01 LLVM
52        // fails to const-fold the check in `vec![[1; 32]; n]`
53        // See https://github.com/rust-lang/rust/pull/97581#issuecomment-1166628022
54        // Feel free to tweak if you have better evidence.
55
56        N <= 16 && self.iter().all(IsZero::is_zero)
57    }
58}
59
60// This is recursive macro.
61macro_rules! impl_is_zero_tuples {
62    // Stopper
63    () => {
64        // No use for implementing for empty tuple because it is ZST.
65    };
66    ($first_arg:ident $(,$rest:ident)*) => {
67        unsafe impl <$first_arg: IsZero, $($rest: IsZero,)*> IsZero for ($first_arg, $($rest,)*){
68            #[inline]
69            fn is_zero(&self) -> bool{
70                // Destructure tuple to N references
71                // Rust allows to hide generic params by local variable names.
72                #[allow(non_snake_case)]
73                let ($first_arg, $($rest,)*) = self;
74
75                $first_arg.is_zero()
76                    $( && $rest.is_zero() )*
77            }
78        }
79
80        impl_is_zero_tuples!($($rest),*);
81    }
82}
83
84impl_is_zero_tuples!(A, B, C, D, E, F, G, H);
85
86// `Option<&T>` and `Option<Box<T>>` are guaranteed to represent `None` as null.
87// For fat pointers, the bytes that would be the pointer metadata in the `Some`
88// variant are padding in the `None` variant, so ignoring them and
89// zero-initializing instead is ok.
90// `Option<&mut T>` never implements `Clone`, so there's no need for an impl of
91// `SpecFromElem`.
92
93unsafe impl<T: ?Sized> IsZero for Option<&T> {
94    #[inline]
95    fn is_zero(&self) -> bool {
96        self.is_none()
97    }
98}
99
100unsafe impl<T: ?Sized> IsZero for Option<Box<T>> {
101    #[inline]
102    fn is_zero(&self) -> bool {
103        self.is_none()
104    }
105}
106
107// `Option<NonZero<u32>>` and similar have a representation guarantee that
108// they're the same size as the corresponding `u32` type, as well as a guarantee
109// that transmuting between `NonZero<u32>` and `Option<NonZero<u32>>` works.
110// While the documentation officially makes it UB to transmute from `None`,
111// we're the standard library so we can make extra inferences, and we know that
112// the only niche available to represent `None` is the one that's all zeros.
113macro_rules! impl_is_zero_option_of_nonzero_int {
114    ($($t:ty),+ $(,)?) => {$(
115        unsafe impl IsZero for Option<NonZero<$t>> {
116            #[inline]
117            fn is_zero(&self) -> bool {
118                self.is_none()
119            }
120        }
121    )+};
122}
123
124impl_is_zero_option_of_nonzero_int!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
125
126macro_rules! impl_is_zero_option_of_int {
127    ($($t:ty),+ $(,)?) => {$(
128        unsafe impl IsZero for Option<$t> {
129            #[inline]
130            fn is_zero(&self) -> bool {
131                const {
132                    let none: Self = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
133                    assert!(none.is_none());
134                }
135                self.is_none()
136            }
137        }
138    )+};
139}
140
141impl_is_zero_option_of_int!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize);
142
143unsafe impl<T: IsZero> IsZero for Wrapping<T> {
144    #[inline]
145    fn is_zero(&self) -> bool {
146        self.0.is_zero()
147    }
148}
149
150unsafe impl<T: IsZero> IsZero for Saturating<T> {
151    #[inline]
152    fn is_zero(&self) -> bool {
153        self.0.is_zero()
154    }
155}
156
157macro_rules! impl_is_zero_option_of_bool {
158    ($($t:ty),+ $(,)?) => {$(
159        unsafe impl IsZero for $t {
160            #[inline]
161            fn is_zero(&self) -> bool {
162                // SAFETY: This is *not* a stable layout guarantee, but
163                // inside `core` we're allowed to rely on the current rustc
164                // behavior that options of bools will be one byte with
165                // no padding, so long as they're nested less than 254 deep.
166                let raw: u8 = unsafe { core::mem::transmute(*self) };
167                raw == 0
168            }
169        }
170    )+};
171}
172
173impl_is_zero_option_of_bool! {
174    Option<bool>,
175    Option<Option<bool>>,
176    Option<Option<Option<bool>>>,
177    // Could go further, but not worth the metadata overhead.
178}