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