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 | // `IsZero` cannot be soundly implemented for pointers because of provenance |
44 | // (see #135338). |
45 | |
46 | unsafe 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. |
61 | macro_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 | |
84 | impl_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 | |
93 | unsafe impl<T: ?Sized> IsZero for Option<&T> { |
94 | #[inline ] |
95 | fn is_zero(&self) -> bool { |
96 | self.is_none() |
97 | } |
98 | } |
99 | |
100 | unsafe 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. |
113 | macro_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 | |
124 | impl_is_zero_option_of_nonzero_int!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize); |
125 | |
126 | macro_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 | |
141 | impl_is_zero_option_of_int!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize); |
142 | |
143 | unsafe impl<T: IsZero> IsZero for Wrapping<T> { |
144 | #[inline ] |
145 | fn is_zero(&self) -> bool { |
146 | self.0.is_zero() |
147 | } |
148 | } |
149 | |
150 | unsafe impl<T: IsZero> IsZero for Saturating<T> { |
151 | #[inline ] |
152 | fn is_zero(&self) -> bool { |
153 | self.0.is_zero() |
154 | } |
155 | } |
156 | |
157 | macro_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 | |
173 | impl_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 | } |
179 | |