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