1 | use core::num::{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_for_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_for_tuples!($($rest),*); |
92 | } |
93 | } |
94 | |
95 | impl_for_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<num::NonZeroU32>` 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 `NonZeroU32` and `Option<num::NonZeroU32>` 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 | |
125 | macro_rules! impl_is_zero_option_of_nonzero { |
126 | ($($t:ident,)+) => {$( |
127 | unsafe impl IsZero for Option<core::num::$t> { |
128 | #[inline] |
129 | fn is_zero(&self) -> bool { |
130 | self.is_none() |
131 | } |
132 | } |
133 | )+}; |
134 | } |
135 | |
136 | impl_is_zero_option_of_nonzero!( |
137 | NonZeroU8, |
138 | NonZeroU16, |
139 | NonZeroU32, |
140 | NonZeroU64, |
141 | NonZeroU128, |
142 | NonZeroI8, |
143 | NonZeroI16, |
144 | NonZeroI32, |
145 | NonZeroI64, |
146 | NonZeroI128, |
147 | NonZeroUsize, |
148 | NonZeroIsize, |
149 | ); |
150 | |
151 | macro_rules! impl_is_zero_option_of_num { |
152 | ($($t:ty,)+) => {$( |
153 | unsafe impl IsZero for Option<$t> { |
154 | #[inline] |
155 | fn is_zero(&self) -> bool { |
156 | const { |
157 | let none: Self = unsafe { core::mem::MaybeUninit::zeroed().assume_init() }; |
158 | assert!(none.is_none()); |
159 | } |
160 | self.is_none() |
161 | } |
162 | } |
163 | )+}; |
164 | } |
165 | |
166 | impl_is_zero_option_of_num!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize,); |
167 | |
168 | unsafe impl<T: IsZero> IsZero for Wrapping<T> { |
169 | #[inline ] |
170 | fn is_zero(&self) -> bool { |
171 | self.0.is_zero() |
172 | } |
173 | } |
174 | |
175 | unsafe impl<T: IsZero> IsZero for Saturating<T> { |
176 | #[inline ] |
177 | fn is_zero(&self) -> bool { |
178 | self.0.is_zero() |
179 | } |
180 | } |
181 | |
182 | macro_rules! impl_for_optional_bool { |
183 | ($($t:ty,)+) => {$( |
184 | unsafe impl IsZero for $t { |
185 | #[inline] |
186 | fn is_zero(&self) -> bool { |
187 | // SAFETY: This is *not* a stable layout guarantee, but |
188 | // inside `core` we're allowed to rely on the current rustc |
189 | // behaviour that options of bools will be one byte with |
190 | // no padding, so long as they're nested less than 254 deep. |
191 | let raw: u8 = unsafe { core::mem::transmute(*self) }; |
192 | raw == 0 |
193 | } |
194 | } |
195 | )+}; |
196 | } |
197 | impl_for_optional_bool! { |
198 | Option<bool>, |
199 | Option<Option<bool>>, |
200 | Option<Option<Option<bool>>>, |
201 | // Could go further, but not worth the metadata overhead |
202 | } |
203 | |