1 | // Copyright 2022 The Fuchsia Authors |
2 | // |
3 | // Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 |
4 | // <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT |
5 | // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. |
6 | // This file may not be copied, modified, or distributed except according to |
7 | // those terms. |
8 | |
9 | //! Utilities used by macros and by `zerocopy-derive`. |
10 | //! |
11 | //! These are defined here `zerocopy` rather than in code generated by macros or |
12 | //! by `zerocopy-derive` so that they can be compiled once rather than |
13 | //! recompiled for every invocation (e.g., if they were defined in generated |
14 | //! code, then deriving `IntoBytes` and `FromBytes` on three different types |
15 | //! would result in the code in question being emitted and compiled six |
16 | //! different times). |
17 | |
18 | #![allow (missing_debug_implementations)] |
19 | |
20 | use core::mem::{self, ManuallyDrop}; |
21 | |
22 | // TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove this |
23 | // `cfg` when `size_of_val_raw` is stabilized. |
24 | #[cfg (__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] |
25 | use core::ptr::{self, NonNull}; |
26 | |
27 | use crate::{ |
28 | pointer::{ |
29 | invariant::{self, AtLeast, Invariants}, |
30 | AliasingSafe, AliasingSafeReason, BecauseExclusive, BecauseImmutable, |
31 | }, |
32 | FromBytes, Immutable, IntoBytes, Ptr, TryFromBytes, Unalign, ValidityError, |
33 | }; |
34 | |
35 | /// Projects the type of the field at `Index` in `Self`. |
36 | /// |
37 | /// The `Index` parameter is any sort of handle that identifies the field; its |
38 | /// definition is the obligation of the implementer. |
39 | /// |
40 | /// # Safety |
41 | /// |
42 | /// Unsafe code may assume that this accurately reflects the definition of |
43 | /// `Self`. |
44 | pub unsafe trait Field<Index> { |
45 | /// The type of the field at `Index`. |
46 | type Type: ?Sized; |
47 | } |
48 | |
49 | #[cfg_attr ( |
50 | zerocopy_diagnostic_on_unimplemented_1_78_0, |
51 | diagnostic::on_unimplemented( |
52 | message = "`{T}` has inter-field padding" , |
53 | label = "types with padding cannot implement `IntoBytes`" , |
54 | note = "consider using `zerocopy::Unalign` to lower the alignment of individual fields" , |
55 | note = "consider adding explicit fields where padding would be" , |
56 | note = "consider using `#[repr(packed)]` to remove inter-field padding" |
57 | ) |
58 | )] |
59 | pub trait PaddingFree<T: ?Sized, const HAS_PADDING: bool> {} |
60 | impl<T: ?Sized> PaddingFree<T, false> for () {} |
61 | |
62 | /// A type whose size is equal to `align_of::<T>()`. |
63 | #[repr (C)] |
64 | pub struct AlignOf<T> { |
65 | // This field ensures that: |
66 | // - The size is always at least 1 (the minimum possible alignment). |
67 | // - If the alignment is greater than 1, Rust has to round up to the next |
68 | // multiple of it in order to make sure that `Align`'s size is a multiple |
69 | // of that alignment. Without this field, its size could be 0, which is a |
70 | // valid multiple of any alignment. |
71 | _u: u8, |
72 | _a: [T; 0], |
73 | } |
74 | |
75 | impl<T> AlignOf<T> { |
76 | #[inline (never)] // Make `missing_inline_in_public_items` happy. |
77 | #[cfg_attr ( |
78 | all(coverage_nightly, __ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS), |
79 | coverage(off) |
80 | )] |
81 | pub fn into_t(self) -> T { |
82 | unreachable!() |
83 | } |
84 | } |
85 | |
86 | /// A type whose size is equal to `max(align_of::<T>(), align_of::<U>())`. |
87 | #[repr (C)] |
88 | pub union MaxAlignsOf<T, U> { |
89 | _t: ManuallyDrop<AlignOf<T>>, |
90 | _u: ManuallyDrop<AlignOf<U>>, |
91 | } |
92 | |
93 | impl<T, U> MaxAlignsOf<T, U> { |
94 | #[inline (never)] // Make `missing_inline_in_public_items` happy. |
95 | #[cfg_attr ( |
96 | all(coverage_nightly, __ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS), |
97 | coverage(off) |
98 | )] |
99 | pub fn new(_t: T, _u: U) -> MaxAlignsOf<T, U> { |
100 | unreachable!() |
101 | } |
102 | } |
103 | |
104 | const _64K: usize = 1 << 16; |
105 | |
106 | // TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove this |
107 | // `cfg` when `size_of_val_raw` is stabilized. |
108 | #[cfg (__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] |
109 | #[repr (C, align(65536))] |
110 | struct Aligned64kAllocation([u8; _64K]); |
111 | |
112 | /// A pointer to an aligned allocation of size 2^16. |
113 | /// |
114 | /// # Safety |
115 | /// |
116 | /// `ALIGNED_64K_ALLOCATION` is guaranteed to point to the entirety of an |
117 | /// allocation with size and alignment 2^16, and to have valid provenance. |
118 | // TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove this |
119 | // `cfg` when `size_of_val_raw` is stabilized. |
120 | #[cfg (__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] |
121 | pub const ALIGNED_64K_ALLOCATION: NonNull<[u8]> = { |
122 | const REF: &Aligned64kAllocation = &Aligned64kAllocation([0; _64K]); |
123 | let ptr: *const Aligned64kAllocation = REF; |
124 | let ptr: *const [u8] = ptr::slice_from_raw_parts(ptr.cast(), _64K); |
125 | // SAFETY: |
126 | // - `ptr` is derived from a Rust reference, which is guaranteed to be |
127 | // non-null. |
128 | // - `ptr` is derived from an `&Aligned64kAllocation`, which has size and |
129 | // alignment `_64K` as promised. Its length is initialized to `_64K`, |
130 | // which means that it refers to the entire allocation. |
131 | // - `ptr` is derived from a Rust reference, which is guaranteed to have |
132 | // valid provenance. |
133 | // |
134 | // TODO(#429): Once `NonNull::new_unchecked` docs document that it preserves |
135 | // provenance, cite those docs. |
136 | // TODO: Replace this `as` with `ptr.cast_mut()` once our MSRV >= 1.65 |
137 | #[allow (clippy::as_conversions)] |
138 | unsafe { |
139 | NonNull::new_unchecked(ptr as *mut _) |
140 | } |
141 | }; |
142 | |
143 | /// Computes the offset of the base of the field `$trailing_field_name` within |
144 | /// the type `$ty`. |
145 | /// |
146 | /// `trailing_field_offset!` produces code which is valid in a `const` context. |
147 | // TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove this |
148 | // `cfg` when `size_of_val_raw` is stabilized. |
149 | #[cfg (__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] |
150 | #[doc (hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. |
151 | #[macro_export ] |
152 | macro_rules! trailing_field_offset { |
153 | ($ty:ty, $trailing_field_name:tt) => {{ |
154 | let min_size = { |
155 | let zero_elems: *const [()] = |
156 | $crate::util::macro_util::core_reexport::ptr::slice_from_raw_parts( |
157 | // Work around https://github.com/rust-lang/rust-clippy/issues/12280 |
158 | #[allow(clippy::incompatible_msrv)] |
159 | $crate::util::macro_util::core_reexport::ptr::NonNull::<()>::dangling() |
160 | .as_ptr() |
161 | .cast_const(), |
162 | 0, |
163 | ); |
164 | // SAFETY: |
165 | // - If `$ty` is `Sized`, `size_of_val_raw` is always safe to call. |
166 | // - Otherwise: |
167 | // - If `$ty` is not a slice DST, this pointer conversion will |
168 | // fail due to "mismatched vtable kinds", and compilation will |
169 | // fail. |
170 | // - If `$ty` is a slice DST, we have constructed `zero_elems` to |
171 | // have zero trailing slice elements. Per the `size_of_val_raw` |
172 | // docs, "For the special case where the dynamic tail length is |
173 | // 0, this function is safe to call." [1] |
174 | // |
175 | // [1] https://doc.rust-lang.org/nightly/std/mem/fn.size_of_val_raw.html |
176 | unsafe { |
177 | #[allow(clippy::as_conversions)] |
178 | $crate::util::macro_util::core_reexport::mem::size_of_val_raw( |
179 | zero_elems as *const $ty, |
180 | ) |
181 | } |
182 | }; |
183 | |
184 | assert!(min_size <= _64K); |
185 | |
186 | #[allow(clippy::as_conversions)] |
187 | let ptr = ALIGNED_64K_ALLOCATION.as_ptr() as *const $ty; |
188 | |
189 | // SAFETY: |
190 | // - Thanks to the preceding `assert!`, we know that the value with zero |
191 | // elements fits in `_64K` bytes, and thus in the allocation addressed |
192 | // by `ALIGNED_64K_ALLOCATION`. The offset of the trailing field is |
193 | // guaranteed to be no larger than this size, so this field projection |
194 | // is guaranteed to remain in-bounds of its allocation. |
195 | // - Because the minimum size is no larger than `_64K` bytes, and |
196 | // because an object's size must always be a multiple of its alignment |
197 | // [1], we know that `$ty`'s alignment is no larger than `_64K`. The |
198 | // allocation addressed by `ALIGNED_64K_ALLOCATION` is guaranteed to |
199 | // be aligned to `_64K`, so `ptr` is guaranteed to satisfy `$ty`'s |
200 | // alignment. |
201 | // - As required by `addr_of!`, we do not write through `field`. |
202 | // |
203 | // Note that, as of [2], this requirement is technically unnecessary |
204 | // for Rust versions >= 1.75.0, but no harm in guaranteeing it anyway |
205 | // until we bump our MSRV. |
206 | // |
207 | // [1] Per https://doc.rust-lang.org/reference/type-layout.html: |
208 | // |
209 | // The size of a value is always a multiple of its alignment. |
210 | // |
211 | // [2] https://github.com/rust-lang/reference/pull/1387 |
212 | let field = unsafe { |
213 | $crate::util::macro_util::core_reexport::ptr::addr_of!((*ptr).$trailing_field_name) |
214 | }; |
215 | // SAFETY: |
216 | // - Both `ptr` and `field` are derived from the same allocated object. |
217 | // - By the preceding safety comment, `field` is in bounds of that |
218 | // allocated object. |
219 | // - The distance, in bytes, between `ptr` and `field` is required to be |
220 | // a multiple of the size of `u8`, which is trivially true because |
221 | // `u8`'s size is 1. |
222 | // - The distance, in bytes, cannot overflow `isize`. This is guaranteed |
223 | // because no allocated object can have a size larger than can fit in |
224 | // `isize`. [1] |
225 | // - The distance being in-bounds cannot rely on wrapping around the |
226 | // address space. This is guaranteed because the same is guaranteed of |
227 | // allocated objects. [1] |
228 | // |
229 | // [1] TODO(#429), TODO(https://github.com/rust-lang/rust/pull/116675): |
230 | // Once these are guaranteed in the Reference, cite it. |
231 | let offset = unsafe { field.cast::<u8>().offset_from(ptr.cast::<u8>()) }; |
232 | // Guaranteed not to be lossy: `field` comes after `ptr`, so the offset |
233 | // from `ptr` to `field` is guaranteed to be positive. |
234 | assert!(offset >= 0); |
235 | Some( |
236 | #[allow(clippy::as_conversions)] |
237 | { |
238 | offset as usize |
239 | }, |
240 | ) |
241 | }}; |
242 | } |
243 | |
244 | /// Computes alignment of `$ty: ?Sized`. |
245 | /// |
246 | /// `align_of!` produces code which is valid in a `const` context. |
247 | // TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove this |
248 | // `cfg` when `size_of_val_raw` is stabilized. |
249 | #[cfg (__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] |
250 | #[doc (hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. |
251 | #[macro_export ] |
252 | macro_rules! align_of { |
253 | ($ty:ty) => {{ |
254 | // SAFETY: `OffsetOfTrailingIsAlignment` is `repr(C)`, and its layout is |
255 | // guaranteed [1] to begin with the single-byte layout for `_byte`, |
256 | // followed by the padding needed to align `_trailing`, then the layout |
257 | // for `_trailing`, and finally any trailing padding bytes needed to |
258 | // correctly-align the entire struct. |
259 | // |
260 | // This macro computes the alignment of `$ty` by counting the number of |
261 | // bytes preceeding `_trailing`. For instance, if the alignment of `$ty` |
262 | // is `1`, then no padding is required align `_trailing` and it will be |
263 | // located immediately after `_byte` at offset 1. If the alignment of |
264 | // `$ty` is 2, then a single padding byte is required before |
265 | // `_trailing`, and `_trailing` will be located at offset 2. |
266 | |
267 | // This correspondence between offset and alignment holds for all valid |
268 | // Rust alignments, and we confirm this exhaustively (or, at least up to |
269 | // the maximum alignment supported by `trailing_field_offset!`) in |
270 | // `test_align_of_dst`. |
271 | // |
272 | // [1]: https://doc.rust-lang.org/nomicon/other-reprs.html#reprc |
273 | |
274 | #[repr(C)] |
275 | struct OffsetOfTrailingIsAlignment { |
276 | _byte: u8, |
277 | _trailing: $ty, |
278 | } |
279 | |
280 | trailing_field_offset!(OffsetOfTrailingIsAlignment, _trailing) |
281 | }}; |
282 | } |
283 | |
284 | mod size_to_tag { |
285 | pub trait SizeToTag<const SIZE: usize> { |
286 | type Tag; |
287 | } |
288 | |
289 | impl SizeToTag<1> for () { |
290 | type Tag = u8; |
291 | } |
292 | impl SizeToTag<2> for () { |
293 | type Tag = u16; |
294 | } |
295 | impl SizeToTag<4> for () { |
296 | type Tag = u32; |
297 | } |
298 | impl SizeToTag<8> for () { |
299 | type Tag = u64; |
300 | } |
301 | impl SizeToTag<16> for () { |
302 | type Tag = u128; |
303 | } |
304 | } |
305 | |
306 | /// An alias for the unsigned integer of the given size in bytes. |
307 | #[doc (hidden)] |
308 | pub type SizeToTag<const SIZE: usize> = <() as size_to_tag::SizeToTag<SIZE>>::Tag; |
309 | |
310 | // We put `Sized` in its own module so it can have the same name as the standard |
311 | // library `Sized` without shadowing it in the parent module. |
312 | #[cfg (zerocopy_diagnostic_on_unimplemented_1_78_0)] |
313 | mod __size_of { |
314 | #[diagnostic::on_unimplemented( |
315 | message = "`{Self}` is unsized" , |
316 | label = "`IntoBytes` needs all field types to be `Sized` in order to determine whether there is inter-field padding" , |
317 | note = "consider using `#[repr(packed)]` to remove inter-field padding" , |
318 | note = "`IntoBytes` does not require the fields of `#[repr(packed)]` types to be `Sized`" |
319 | )] |
320 | pub trait Sized: core::marker::Sized {} |
321 | impl<T: core::marker::Sized> Sized for T {} |
322 | |
323 | #[inline (always)] |
324 | #[must_use ] |
325 | #[allow (clippy::needless_maybe_sized)] |
326 | pub const fn size_of<T: Sized + ?core::marker::Sized>() -> usize { |
327 | core::mem::size_of::<T>() |
328 | } |
329 | } |
330 | |
331 | #[cfg (zerocopy_diagnostic_on_unimplemented_1_78_0)] |
332 | pub use __size_of::size_of; |
333 | #[cfg (not(zerocopy_diagnostic_on_unimplemented_1_78_0))] |
334 | pub use core::mem::size_of; |
335 | |
336 | /// Does the struct type `$t` have padding? |
337 | /// |
338 | /// `$ts` is the list of the type of every field in `$t`. `$t` must be a |
339 | /// struct type, or else `struct_has_padding!`'s result may be meaningless. |
340 | /// |
341 | /// Note that `struct_has_padding!`'s results are independent of `repcr` since |
342 | /// they only consider the size of the type and the sizes of the fields. |
343 | /// Whatever the repr, the size of the type already takes into account any |
344 | /// padding that the compiler has decided to add. Structs with well-defined |
345 | /// representations (such as `repr(C)`) can use this macro to check for padding. |
346 | /// Note that while this may yield some consistent value for some `repr(Rust)` |
347 | /// structs, it is not guaranteed across platforms or compilations. |
348 | #[doc (hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. |
349 | #[macro_export ] |
350 | macro_rules! struct_has_padding { |
351 | ($t:ty, [$($ts:ty),*]) => { |
352 | ::zerocopy::util::macro_util::size_of::<$t>() > 0 $(+ ::zerocopy::util::macro_util::size_of::<$ts>())* |
353 | }; |
354 | } |
355 | |
356 | /// Does the union type `$t` have padding? |
357 | /// |
358 | /// `$ts` is the list of the type of every field in `$t`. `$t` must be a |
359 | /// union type, or else `union_has_padding!`'s result may be meaningless. |
360 | /// |
361 | /// Note that `union_has_padding!`'s results are independent of `repr` since |
362 | /// they only consider the size of the type and the sizes of the fields. |
363 | /// Whatever the repr, the size of the type already takes into account any |
364 | /// padding that the compiler has decided to add. Unions with well-defined |
365 | /// representations (such as `repr(C)`) can use this macro to check for padding. |
366 | /// Note that while this may yield some consistent value for some `repr(Rust)` |
367 | /// unions, it is not guaranteed across platforms or compilations. |
368 | #[doc (hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. |
369 | #[macro_export ] |
370 | macro_rules! union_has_padding { |
371 | ($t:ty, [$($ts:ty),*]) => { |
372 | false $(|| ::zerocopy::util::macro_util::size_of::<$t>() != ::zerocopy::util::macro_util::size_of::<$ts>())* |
373 | }; |
374 | } |
375 | |
376 | /// Does the enum type `$t` have padding? |
377 | /// |
378 | /// `$disc` is the type of the enum tag, and `$ts` is a list of fields in each |
379 | /// square-bracket-delimited variant. `$t` must be an enum, or else |
380 | /// `enum_has_padding!`'s result may be meaningless. An enum has padding if any |
381 | /// of its variant structs [1][2] contain padding, and so all of the variants of |
382 | /// an enum must be "full" in order for the enum to not have padding. |
383 | /// |
384 | /// The results of `enum_has_padding!` require that the enum is not |
385 | /// `repr(Rust)`, as `repr(Rust)` enums may niche the enum's tag and reduce the |
386 | /// total number of bytes required to represent the enum as a result. As long as |
387 | /// the enum is `repr(C)`, `repr(int)`, or `repr(C, int)`, this will |
388 | /// consistently return whether the enum contains any padding bytes. |
389 | /// |
390 | /// [1]: https://doc.rust-lang.org/1.81.0/reference/type-layout.html#reprc-enums-with-fields |
391 | /// [2]: https://doc.rust-lang.org/1.81.0/reference/type-layout.html#primitive-representation-of-enums-with-fields |
392 | #[doc (hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. |
393 | #[macro_export ] |
394 | macro_rules! enum_has_padding { |
395 | ($t:ty, $disc:ty, $([$($ts:ty),*]),*) => { |
396 | false $( |
397 | || ::zerocopy::util::macro_util::size_of::<$t>() |
398 | != ( |
399 | ::zerocopy::util::macro_util::size_of::<$disc>() |
400 | $(+ ::zerocopy::util::macro_util::size_of::<$ts>())* |
401 | ) |
402 | )* |
403 | } |
404 | } |
405 | |
406 | /// Does `t` have alignment greater than or equal to `u`? If not, this macro |
407 | /// produces a compile error. It must be invoked in a dead codepath. This is |
408 | /// used in `transmute_ref!` and `transmute_mut!`. |
409 | #[doc (hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. |
410 | #[macro_export ] |
411 | macro_rules! assert_align_gt_eq { |
412 | ($t:ident, $u: ident) => {{ |
413 | // The comments here should be read in the context of this macro's |
414 | // invocations in `transmute_ref!` and `transmute_mut!`. |
415 | if false { |
416 | // The type wildcard in this bound is inferred to be `T` because |
417 | // `align_of.into_t()` is assigned to `t` (which has type `T`). |
418 | let align_of: $crate::util::macro_util::AlignOf<_> = unreachable!(); |
419 | $t = align_of.into_t(); |
420 | // `max_aligns` is inferred to have type `MaxAlignsOf<T, U>` because |
421 | // of the inferred types of `t` and `u`. |
422 | let mut max_aligns = $crate::util::macro_util::MaxAlignsOf::new($t, $u); |
423 | |
424 | // This transmute will only compile successfully if |
425 | // `align_of::<T>() == max(align_of::<T>(), align_of::<U>())` - in |
426 | // other words, if `align_of::<T>() >= align_of::<U>()`. |
427 | // |
428 | // SAFETY: This code is never run. |
429 | max_aligns = unsafe { |
430 | // Clippy: We can't annotate the types; this macro is designed |
431 | // to infer the types from the calling context. |
432 | #[allow(clippy::missing_transmute_annotations)] |
433 | $crate::util::macro_util::core_reexport::mem::transmute(align_of) |
434 | }; |
435 | } else { |
436 | loop {} |
437 | } |
438 | }}; |
439 | } |
440 | |
441 | /// Do `t` and `u` have the same size? If not, this macro produces a compile |
442 | /// error. It must be invoked in a dead codepath. This is used in |
443 | /// `transmute_ref!` and `transmute_mut!`. |
444 | #[doc (hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. |
445 | #[macro_export ] |
446 | macro_rules! assert_size_eq { |
447 | ($t:ident, $u: ident) => {{ |
448 | // The comments here should be read in the context of this macro's |
449 | // invocations in `transmute_ref!` and `transmute_mut!`. |
450 | if false { |
451 | // SAFETY: This code is never run. |
452 | $u = unsafe { |
453 | // Clippy: |
454 | // - It's okay to transmute a type to itself. |
455 | // - We can't annotate the types; this macro is designed to |
456 | // infer the types from the calling context. |
457 | #[allow(clippy::useless_transmute, clippy::missing_transmute_annotations)] |
458 | $crate::util::macro_util::core_reexport::mem::transmute($t) |
459 | }; |
460 | } else { |
461 | loop {} |
462 | } |
463 | }}; |
464 | } |
465 | |
466 | /// Transmutes a reference of one type to a reference of another type. |
467 | /// |
468 | /// # Safety |
469 | /// |
470 | /// The caller must guarantee that: |
471 | /// - `Src: IntoBytes + Immutable` |
472 | /// - `Dst: FromBytes + Immutable` |
473 | /// - `size_of::<Src>() == size_of::<Dst>()` |
474 | /// - `align_of::<Src>() >= align_of::<Dst>()` |
475 | #[inline (always)] |
476 | pub const unsafe fn transmute_ref<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( |
477 | src: &'src Src, |
478 | ) -> &'dst Dst { |
479 | let src: *const Src = src; |
480 | let dst = src.cast::<Dst>(); |
481 | // SAFETY: |
482 | // - We know that it is sound to view the target type of the input reference |
483 | // (`Src`) as the target type of the output reference (`Dst`) because the |
484 | // caller has guaranteed that `Src: IntoBytes`, `Dst: FromBytes`, and |
485 | // `size_of::<Src>() == size_of::<Dst>()`. |
486 | // - We know that there are no `UnsafeCell`s, and thus we don't have to |
487 | // worry about `UnsafeCell` overlap, because `Src: Immutable` and `Dst: |
488 | // Immutable`. |
489 | // - The caller has guaranteed that alignment is not increased. |
490 | // - We know that the returned lifetime will not outlive the input lifetime |
491 | // thanks to the lifetime bounds on this function. |
492 | // |
493 | // TODO(#67): Once our MSRV is 1.58, replace this `transmute` with `&*dst`. |
494 | #[allow (clippy::transmute_ptr_to_ref)] |
495 | unsafe { |
496 | mem::transmute(dst) |
497 | } |
498 | } |
499 | |
500 | /// Transmutes a mutable reference of one type to a mutable reference of another |
501 | /// type. |
502 | /// |
503 | /// # Safety |
504 | /// |
505 | /// The caller must guarantee that: |
506 | /// - `Src: FromBytes + IntoBytes` |
507 | /// - `Dst: FromBytes + IntoBytes` |
508 | /// - `size_of::<Src>() == size_of::<Dst>()` |
509 | /// - `align_of::<Src>() >= align_of::<Dst>()` |
510 | // TODO(#686): Consider removing the `Immutable` requirement. |
511 | #[inline (always)] |
512 | pub unsafe fn transmute_mut<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( |
513 | src: &'src mut Src, |
514 | ) -> &'dst mut Dst { |
515 | let src: *mut Src = src; |
516 | let dst = src.cast::<Dst>(); |
517 | // SAFETY: |
518 | // - We know that it is sound to view the target type of the input reference |
519 | // (`Src`) as the target type of the output reference (`Dst`) and |
520 | // vice-versa because the caller has guaranteed that `Src: FromBytes + |
521 | // IntoBytes`, `Dst: FromBytes + IntoBytes`, and `size_of::<Src>() == |
522 | // size_of::<Dst>()`. |
523 | // - The caller has guaranteed that alignment is not increased. |
524 | // - We know that the returned lifetime will not outlive the input lifetime |
525 | // thanks to the lifetime bounds on this function. |
526 | unsafe { &mut *dst } |
527 | } |
528 | |
529 | /// Is a given source a valid instance of `Dst`? |
530 | /// |
531 | /// If so, returns `src` casted to a `Ptr<Dst, _>`. Otherwise returns `None`. |
532 | /// |
533 | /// # Safety |
534 | /// |
535 | /// Unsafe code may assume that, if `try_cast_or_pme(src)` returns `Some`, |
536 | /// `*src` is a bit-valid instance of `Dst`, and that the size of `Src` is |
537 | /// greater than or equal to the size of `Dst`. |
538 | /// |
539 | /// # Panics |
540 | /// |
541 | /// `try_cast_or_pme` may either produce a post-monomorphization error or a |
542 | /// panic if `Dst` not the same size as `Src`. Otherwise, `try_cast_or_pme` |
543 | /// panics under the same circumstances as [`is_bit_valid`]. |
544 | /// |
545 | /// [`is_bit_valid`]: TryFromBytes::is_bit_valid |
546 | #[doc (hidden)] |
547 | #[inline ] |
548 | fn try_cast_or_pme<Src, Dst, I, R>( |
549 | src: Ptr<'_, Src, I>, |
550 | ) -> Result< |
551 | Ptr<'_, Dst, (I::Aliasing, invariant::Any, invariant::Valid)>, |
552 | ValidityError<Ptr<'_, Src, I>, Dst>, |
553 | > |
554 | where |
555 | // TODO(#2226): There should be a `Src: FromBytes` bound here, but doing so |
556 | // requires deeper surgery. |
557 | Src: IntoBytes, |
558 | Dst: TryFromBytes + AliasingSafe<Src, I::Aliasing, R>, |
559 | I: Invariants<Validity = invariant::Valid>, |
560 | I::Aliasing: AtLeast<invariant::Shared>, |
561 | R: AliasingSafeReason, |
562 | { |
563 | static_assert!(Src, Dst => mem::size_of::<Dst>() == mem::size_of::<Src>()); |
564 | |
565 | // SAFETY: This is a pointer cast, satisfying the following properties: |
566 | // - `p as *mut Dst` addresses a subset of the `bytes` addressed by `src`, |
567 | // because we assert above that the size of `Dst` equal to the size of |
568 | // `Src`. |
569 | // - `p as *mut Dst` is a provenance-preserving cast |
570 | // - Because `Dst: AliasingSafe<Src, I::Aliasing, _>`, either: |
571 | // - `I::Aliasing` is `Exclusive` |
572 | // - `Src` and `Dst` are both `Immutable`, in which case they |
573 | // trivially contain `UnsafeCell`s at identical locations |
574 | #[allow (clippy::as_conversions)] |
575 | let c_ptr = unsafe { src.cast_unsized(|p| p as *mut Dst) }; |
576 | |
577 | // SAFETY: `c_ptr` is derived from `src` which is `IntoBytes`. By |
578 | // invariant on `IntoByte`s, `c_ptr`'s referent consists entirely of |
579 | // initialized bytes. |
580 | let c_ptr = unsafe { c_ptr.assume_initialized() }; |
581 | |
582 | match c_ptr.try_into_valid() { |
583 | Ok(ptr) => Ok(ptr), |
584 | Err(err) => { |
585 | // Re-cast `Ptr<Dst>` to `Ptr<Src>`. |
586 | let ptr = err.into_src(); |
587 | // SAFETY: This is a pointer cast, satisfying the following |
588 | // properties: |
589 | // - `p as *mut Src` addresses a subset of the `bytes` addressed by |
590 | // `ptr`, because we assert above that the size of `Dst` is equal |
591 | // to the size of `Src`. |
592 | // - `p as *mut Src` is a provenance-preserving cast |
593 | // - Because `Dst: AliasingSafe<Src, I::Aliasing, _>`, either: |
594 | // - `I::Aliasing` is `Exclusive` |
595 | // - `Src` and `Dst` are both `Immutable`, in which case they |
596 | // trivially contain `UnsafeCell`s at identical locations |
597 | #[allow (clippy::as_conversions)] |
598 | let ptr = unsafe { ptr.cast_unsized(|p| p as *mut Src) }; |
599 | // SAFETY: `ptr` is `src`, and has the same alignment invariant. |
600 | let ptr = unsafe { ptr.assume_alignment::<I::Alignment>() }; |
601 | // SAFETY: `ptr` is `src` and has the same validity invariant. |
602 | let ptr = unsafe { ptr.assume_validity::<I::Validity>() }; |
603 | Err(ValidityError::new(ptr.unify_invariants())) |
604 | } |
605 | } |
606 | } |
607 | |
608 | /// Attempts to transmute `Src` into `Dst`. |
609 | /// |
610 | /// A helper for `try_transmute!`. |
611 | /// |
612 | /// # Panics |
613 | /// |
614 | /// `try_transmute` may either produce a post-monomorphization error or a panic |
615 | /// if `Dst` is bigger than `Src`. Otherwise, `try_transmute` panics under the |
616 | /// same circumstances as [`is_bit_valid`]. |
617 | /// |
618 | /// [`is_bit_valid`]: TryFromBytes::is_bit_valid |
619 | #[inline (always)] |
620 | pub fn try_transmute<Src, Dst>(src: Src) -> Result<Dst, ValidityError<Src, Dst>> |
621 | where |
622 | Src: IntoBytes, |
623 | Dst: TryFromBytes, |
624 | { |
625 | let mut src = ManuallyDrop::new(src); |
626 | let ptr: Ptr<'_, {unknown}, (Exclusive, …)> = Ptr::from_mut(&mut src); |
627 | // Wrapping `Dst` in `Unalign` ensures that this cast does not fail due to |
628 | // alignment requirements. |
629 | match try_cast_or_pme::<_, ManuallyDrop<Unalign<Dst>>, _, BecauseExclusive>(src:ptr) { |
630 | Ok(ptr) => { |
631 | let dst = ptr.bikeshed_recall_aligned().as_mut(); |
632 | // SAFETY: By shadowing `dst`, we ensure that `dst` is not re-used |
633 | // after taking its inner value. |
634 | let dst = unsafe { ManuallyDrop::take(dst) }; |
635 | Ok(dst.into_inner()) |
636 | } |
637 | Err(_) => Err(ValidityError::new(src:ManuallyDrop::into_inner(src))), |
638 | } |
639 | } |
640 | |
641 | /// Attempts to transmute `&Src` into `&Dst`. |
642 | /// |
643 | /// A helper for `try_transmute_ref!`. |
644 | /// |
645 | /// # Panics |
646 | /// |
647 | /// `try_transmute_ref` may either produce a post-monomorphization error or a |
648 | /// panic if `Dst` is bigger or has a stricter alignment requirement than `Src`. |
649 | /// Otherwise, `try_transmute_ref` panics under the same circumstances as |
650 | /// [`is_bit_valid`]. |
651 | /// |
652 | /// [`is_bit_valid`]: TryFromBytes::is_bit_valid |
653 | #[inline (always)] |
654 | pub fn try_transmute_ref<Src, Dst>(src: &Src) -> Result<&Dst, ValidityError<&Src, Dst>> |
655 | where |
656 | Src: IntoBytes + Immutable, |
657 | Dst: TryFromBytes + Immutable, |
658 | { |
659 | match try_cast_or_pme::<Src, Dst, _, BecauseImmutable>(src:Ptr::from_ref(ptr:src)) { |
660 | Ok(ptr) => { |
661 | static_assert!(Src, Dst => mem::align_of::<Dst>() <= mem::align_of::<Src>()); |
662 | // SAFETY: We have checked that `Dst` does not have a stricter |
663 | // alignment requirement than `Src`. |
664 | let ptr = unsafe { ptr.assume_alignment::<invariant::Aligned>() }; |
665 | Ok(ptr.as_ref()) |
666 | } |
667 | Err(err) => Err(err.map_src(Ptr::as_ref)), |
668 | } |
669 | } |
670 | |
671 | /// Attempts to transmute `&mut Src` into `&mut Dst`. |
672 | /// |
673 | /// A helper for `try_transmute_mut!`. |
674 | /// |
675 | /// # Panics |
676 | /// |
677 | /// `try_transmute_mut` may either produce a post-monomorphization error or a |
678 | /// panic if `Dst` is bigger or has a stricter alignment requirement than `Src`. |
679 | /// Otherwise, `try_transmute_mut` panics under the same circumstances as |
680 | /// [`is_bit_valid`]. |
681 | /// |
682 | /// [`is_bit_valid`]: TryFromBytes::is_bit_valid |
683 | #[inline (always)] |
684 | pub fn try_transmute_mut<Src, Dst>(src: &mut Src) -> Result<&mut Dst, ValidityError<&mut Src, Dst>> |
685 | where |
686 | Src: FromBytes + IntoBytes, |
687 | Dst: TryFromBytes, |
688 | { |
689 | match try_cast_or_pme::<Src, Dst, _, BecauseExclusive>(src:Ptr::from_mut(ptr:src)) { |
690 | Ok(ptr) => { |
691 | static_assert!(Src, Dst => mem::align_of::<Dst>() <= mem::align_of::<Src>()); |
692 | // SAFETY: We have checked that `Dst` does not have a stricter |
693 | // alignment requirement than `Src`. |
694 | let ptr = unsafe { ptr.assume_alignment::<invariant::Aligned>() }; |
695 | Ok(ptr.as_mut()) |
696 | } |
697 | Err(err) => Err(err.map_src(Ptr::as_mut)), |
698 | } |
699 | } |
700 | |
701 | /// A function which emits a warning if its return value is not used. |
702 | #[must_use ] |
703 | #[inline (always)] |
704 | pub const fn must_use<T>(t: T) -> T { |
705 | t |
706 | } |
707 | |
708 | // NOTE: We can't change this to a `pub use core as core_reexport` until [1] is |
709 | // fixed or we update to a semver-breaking version (as of this writing, 0.8.0) |
710 | // on the `main` branch. |
711 | // |
712 | // [1] https://github.com/obi1kenobi/cargo-semver-checks/issues/573 |
713 | pub mod core_reexport { |
714 | pub use core::*; |
715 | |
716 | pub mod mem { |
717 | pub use core::mem::*; |
718 | } |
719 | } |
720 | |
721 | #[cfg (test)] |
722 | mod tests { |
723 | use super::*; |
724 | use crate::util::testutil::*; |
725 | |
726 | #[test] |
727 | fn test_align_of() { |
728 | macro_rules! test { |
729 | ($ty:ty) => { |
730 | assert_eq!(mem::size_of::<AlignOf<$ty>>(), mem::align_of::<$ty>()); |
731 | }; |
732 | } |
733 | |
734 | test!(()); |
735 | test!(u8); |
736 | test!(AU64); |
737 | test!([AU64; 2]); |
738 | } |
739 | |
740 | #[test] |
741 | fn test_max_aligns_of() { |
742 | macro_rules! test { |
743 | ($t:ty, $u:ty) => { |
744 | assert_eq!( |
745 | mem::size_of::<MaxAlignsOf<$t, $u>>(), |
746 | core::cmp::max(mem::align_of::<$t>(), mem::align_of::<$u>()) |
747 | ); |
748 | }; |
749 | } |
750 | |
751 | test!(u8, u8); |
752 | test!(u8, AU64); |
753 | test!(AU64, u8); |
754 | } |
755 | |
756 | #[test] |
757 | fn test_typed_align_check() { |
758 | // Test that the type-based alignment check used in |
759 | // `assert_align_gt_eq!` behaves as expected. |
760 | |
761 | macro_rules! assert_t_align_gteq_u_align { |
762 | ($t:ty, $u:ty, $gteq:expr) => { |
763 | assert_eq!( |
764 | mem::size_of::<MaxAlignsOf<$t, $u>>() == mem::size_of::<AlignOf<$t>>(), |
765 | $gteq |
766 | ); |
767 | }; |
768 | } |
769 | |
770 | assert_t_align_gteq_u_align!(u8, u8, true); |
771 | assert_t_align_gteq_u_align!(AU64, AU64, true); |
772 | assert_t_align_gteq_u_align!(AU64, u8, true); |
773 | assert_t_align_gteq_u_align!(u8, AU64, false); |
774 | } |
775 | |
776 | // TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove |
777 | // this `cfg` when `size_of_val_raw` is stabilized. |
778 | #[allow (clippy::decimal_literal_representation)] |
779 | #[cfg (__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] |
780 | #[test] |
781 | fn test_trailing_field_offset() { |
782 | assert_eq!(mem::align_of::<Aligned64kAllocation>(), _64K); |
783 | |
784 | macro_rules! test { |
785 | (#[$cfg:meta] ($($ts:ty),* ; $trailing_field_ty:ty) => $expect:expr) => {{ |
786 | #[$cfg] |
787 | struct Test($(#[allow(dead_code)] $ts,)* #[allow(dead_code)] $trailing_field_ty); |
788 | assert_eq!(test!(@offset $($ts),* ; $trailing_field_ty), $expect); |
789 | }}; |
790 | (#[$cfg:meta] $(#[$cfgs:meta])* ($($ts:ty),* ; $trailing_field_ty:ty) => $expect:expr) => { |
791 | test!(#[$cfg] ($($ts),* ; $trailing_field_ty) => $expect); |
792 | test!($(#[$cfgs])* ($($ts),* ; $trailing_field_ty) => $expect); |
793 | }; |
794 | (@offset ; $_trailing:ty) => { trailing_field_offset!(Test, 0) }; |
795 | (@offset $_t:ty ; $_trailing:ty) => { trailing_field_offset!(Test, 1) }; |
796 | } |
797 | |
798 | test!(#[repr(C)] #[repr(transparent)] #[repr(packed)](; u8) => Some(0)); |
799 | test!(#[repr(C)] #[repr(transparent)] #[repr(packed)](; [u8]) => Some(0)); |
800 | test!(#[repr(C)] #[repr(C, packed)] (u8; u8) => Some(1)); |
801 | test!(#[repr(C)] (; AU64) => Some(0)); |
802 | test!(#[repr(C)] (; [AU64]) => Some(0)); |
803 | test!(#[repr(C)] (u8; AU64) => Some(8)); |
804 | test!(#[repr(C)] (u8; [AU64]) => Some(8)); |
805 | test!(#[repr(C)] (; Nested<u8, AU64>) => Some(0)); |
806 | test!(#[repr(C)] (; Nested<u8, [AU64]>) => Some(0)); |
807 | test!(#[repr(C)] (u8; Nested<u8, AU64>) => Some(8)); |
808 | test!(#[repr(C)] (u8; Nested<u8, [AU64]>) => Some(8)); |
809 | |
810 | // Test that `packed(N)` limits the offset of the trailing field. |
811 | test!(#[repr(C, packed( 1))] (u8; elain::Align< 2>) => Some( 1)); |
812 | test!(#[repr(C, packed( 2))] (u8; elain::Align< 4>) => Some( 2)); |
813 | test!(#[repr(C, packed( 4))] (u8; elain::Align< 8>) => Some( 4)); |
814 | test!(#[repr(C, packed( 8))] (u8; elain::Align< 16>) => Some( 8)); |
815 | test!(#[repr(C, packed( 16))] (u8; elain::Align< 32>) => Some( 16)); |
816 | test!(#[repr(C, packed( 32))] (u8; elain::Align< 64>) => Some( 32)); |
817 | test!(#[repr(C, packed( 64))] (u8; elain::Align< 128>) => Some( 64)); |
818 | test!(#[repr(C, packed( 128))] (u8; elain::Align< 256>) => Some( 128)); |
819 | test!(#[repr(C, packed( 256))] (u8; elain::Align< 512>) => Some( 256)); |
820 | test!(#[repr(C, packed( 512))] (u8; elain::Align< 1024>) => Some( 512)); |
821 | test!(#[repr(C, packed( 1024))] (u8; elain::Align< 2048>) => Some( 1024)); |
822 | test!(#[repr(C, packed( 2048))] (u8; elain::Align< 4096>) => Some( 2048)); |
823 | test!(#[repr(C, packed( 4096))] (u8; elain::Align< 8192>) => Some( 4096)); |
824 | test!(#[repr(C, packed( 8192))] (u8; elain::Align< 16384>) => Some( 8192)); |
825 | test!(#[repr(C, packed( 16384))] (u8; elain::Align< 32768>) => Some( 16384)); |
826 | test!(#[repr(C, packed( 32768))] (u8; elain::Align< 65536>) => Some( 32768)); |
827 | test!(#[repr(C, packed( 65536))] (u8; elain::Align< 131072>) => Some( 65536)); |
828 | /* Alignments above 65536 are not yet supported. |
829 | test!(#[repr(C, packed( 131072))] (u8; elain::Align< 262144>) => Some( 131072)); |
830 | test!(#[repr(C, packed( 262144))] (u8; elain::Align< 524288>) => Some( 262144)); |
831 | test!(#[repr(C, packed( 524288))] (u8; elain::Align< 1048576>) => Some( 524288)); |
832 | test!(#[repr(C, packed( 1048576))] (u8; elain::Align< 2097152>) => Some( 1048576)); |
833 | test!(#[repr(C, packed( 2097152))] (u8; elain::Align< 4194304>) => Some( 2097152)); |
834 | test!(#[repr(C, packed( 4194304))] (u8; elain::Align< 8388608>) => Some( 4194304)); |
835 | test!(#[repr(C, packed( 8388608))] (u8; elain::Align< 16777216>) => Some( 8388608)); |
836 | test!(#[repr(C, packed( 16777216))] (u8; elain::Align< 33554432>) => Some( 16777216)); |
837 | test!(#[repr(C, packed( 33554432))] (u8; elain::Align< 67108864>) => Some( 33554432)); |
838 | test!(#[repr(C, packed( 67108864))] (u8; elain::Align< 33554432>) => Some( 67108864)); |
839 | test!(#[repr(C, packed( 33554432))] (u8; elain::Align<134217728>) => Some( 33554432)); |
840 | test!(#[repr(C, packed(134217728))] (u8; elain::Align<268435456>) => Some(134217728)); |
841 | test!(#[repr(C, packed(268435456))] (u8; elain::Align<268435456>) => Some(268435456)); |
842 | */ |
843 | |
844 | // Test that `align(N)` does not limit the offset of the trailing field. |
845 | test!(#[repr(C, align( 1))] (u8; elain::Align< 2>) => Some( 2)); |
846 | test!(#[repr(C, align( 2))] (u8; elain::Align< 4>) => Some( 4)); |
847 | test!(#[repr(C, align( 4))] (u8; elain::Align< 8>) => Some( 8)); |
848 | test!(#[repr(C, align( 8))] (u8; elain::Align< 16>) => Some( 16)); |
849 | test!(#[repr(C, align( 16))] (u8; elain::Align< 32>) => Some( 32)); |
850 | test!(#[repr(C, align( 32))] (u8; elain::Align< 64>) => Some( 64)); |
851 | test!(#[repr(C, align( 64))] (u8; elain::Align< 128>) => Some( 128)); |
852 | test!(#[repr(C, align( 128))] (u8; elain::Align< 256>) => Some( 256)); |
853 | test!(#[repr(C, align( 256))] (u8; elain::Align< 512>) => Some( 512)); |
854 | test!(#[repr(C, align( 512))] (u8; elain::Align< 1024>) => Some( 1024)); |
855 | test!(#[repr(C, align( 1024))] (u8; elain::Align< 2048>) => Some( 2048)); |
856 | test!(#[repr(C, align( 2048))] (u8; elain::Align< 4096>) => Some( 4096)); |
857 | test!(#[repr(C, align( 4096))] (u8; elain::Align< 8192>) => Some( 8192)); |
858 | test!(#[repr(C, align( 8192))] (u8; elain::Align< 16384>) => Some( 16384)); |
859 | test!(#[repr(C, align( 16384))] (u8; elain::Align< 32768>) => Some( 32768)); |
860 | test!(#[repr(C, align( 32768))] (u8; elain::Align< 65536>) => Some( 65536)); |
861 | /* Alignments above 65536 are not yet supported. |
862 | test!(#[repr(C, align( 65536))] (u8; elain::Align< 131072>) => Some( 131072)); |
863 | test!(#[repr(C, align( 131072))] (u8; elain::Align< 262144>) => Some( 262144)); |
864 | test!(#[repr(C, align( 262144))] (u8; elain::Align< 524288>) => Some( 524288)); |
865 | test!(#[repr(C, align( 524288))] (u8; elain::Align< 1048576>) => Some( 1048576)); |
866 | test!(#[repr(C, align( 1048576))] (u8; elain::Align< 2097152>) => Some( 2097152)); |
867 | test!(#[repr(C, align( 2097152))] (u8; elain::Align< 4194304>) => Some( 4194304)); |
868 | test!(#[repr(C, align( 4194304))] (u8; elain::Align< 8388608>) => Some( 8388608)); |
869 | test!(#[repr(C, align( 8388608))] (u8; elain::Align< 16777216>) => Some( 16777216)); |
870 | test!(#[repr(C, align( 16777216))] (u8; elain::Align< 33554432>) => Some( 33554432)); |
871 | test!(#[repr(C, align( 33554432))] (u8; elain::Align< 67108864>) => Some( 67108864)); |
872 | test!(#[repr(C, align( 67108864))] (u8; elain::Align< 33554432>) => Some( 33554432)); |
873 | test!(#[repr(C, align( 33554432))] (u8; elain::Align<134217728>) => Some(134217728)); |
874 | test!(#[repr(C, align(134217728))] (u8; elain::Align<268435456>) => Some(268435456)); |
875 | */ |
876 | } |
877 | |
878 | // TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove |
879 | // this `cfg` when `size_of_val_raw` is stabilized. |
880 | #[allow (clippy::decimal_literal_representation)] |
881 | #[cfg (__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] |
882 | #[test] |
883 | fn test_align_of_dst() { |
884 | // Test that `align_of!` correctly computes the alignment of DSTs. |
885 | assert_eq!(align_of!([elain::Align<1>]), Some(1)); |
886 | assert_eq!(align_of!([elain::Align<2>]), Some(2)); |
887 | assert_eq!(align_of!([elain::Align<4>]), Some(4)); |
888 | assert_eq!(align_of!([elain::Align<8>]), Some(8)); |
889 | assert_eq!(align_of!([elain::Align<16>]), Some(16)); |
890 | assert_eq!(align_of!([elain::Align<32>]), Some(32)); |
891 | assert_eq!(align_of!([elain::Align<64>]), Some(64)); |
892 | assert_eq!(align_of!([elain::Align<128>]), Some(128)); |
893 | assert_eq!(align_of!([elain::Align<256>]), Some(256)); |
894 | assert_eq!(align_of!([elain::Align<512>]), Some(512)); |
895 | assert_eq!(align_of!([elain::Align<1024>]), Some(1024)); |
896 | assert_eq!(align_of!([elain::Align<2048>]), Some(2048)); |
897 | assert_eq!(align_of!([elain::Align<4096>]), Some(4096)); |
898 | assert_eq!(align_of!([elain::Align<8192>]), Some(8192)); |
899 | assert_eq!(align_of!([elain::Align<16384>]), Some(16384)); |
900 | assert_eq!(align_of!([elain::Align<32768>]), Some(32768)); |
901 | assert_eq!(align_of!([elain::Align<65536>]), Some(65536)); |
902 | /* Alignments above 65536 are not yet supported. |
903 | assert_eq!(align_of!([elain::Align<131072>]), Some(131072)); |
904 | assert_eq!(align_of!([elain::Align<262144>]), Some(262144)); |
905 | assert_eq!(align_of!([elain::Align<524288>]), Some(524288)); |
906 | assert_eq!(align_of!([elain::Align<1048576>]), Some(1048576)); |
907 | assert_eq!(align_of!([elain::Align<2097152>]), Some(2097152)); |
908 | assert_eq!(align_of!([elain::Align<4194304>]), Some(4194304)); |
909 | assert_eq!(align_of!([elain::Align<8388608>]), Some(8388608)); |
910 | assert_eq!(align_of!([elain::Align<16777216>]), Some(16777216)); |
911 | assert_eq!(align_of!([elain::Align<33554432>]), Some(33554432)); |
912 | assert_eq!(align_of!([elain::Align<67108864>]), Some(67108864)); |
913 | assert_eq!(align_of!([elain::Align<33554432>]), Some(33554432)); |
914 | assert_eq!(align_of!([elain::Align<134217728>]), Some(134217728)); |
915 | assert_eq!(align_of!([elain::Align<268435456>]), Some(268435456)); |
916 | */ |
917 | } |
918 | |
919 | #[test] |
920 | fn test_enum_casts() { |
921 | // Test that casting the variants of enums with signed integer reprs to |
922 | // unsigned integers obeys expected signed -> unsigned casting rules. |
923 | |
924 | #[repr (i8)] |
925 | enum ReprI8 { |
926 | MinusOne = -1, |
927 | Zero = 0, |
928 | Min = i8::MIN, |
929 | Max = i8::MAX, |
930 | } |
931 | |
932 | #[allow (clippy::as_conversions)] |
933 | let x = ReprI8::MinusOne as u8; |
934 | assert_eq!(x, u8::MAX); |
935 | |
936 | #[allow (clippy::as_conversions)] |
937 | let x = ReprI8::Zero as u8; |
938 | assert_eq!(x, 0); |
939 | |
940 | #[allow (clippy::as_conversions)] |
941 | let x = ReprI8::Min as u8; |
942 | assert_eq!(x, 128); |
943 | |
944 | #[allow (clippy::as_conversions)] |
945 | let x = ReprI8::Max as u8; |
946 | assert_eq!(x, 127); |
947 | } |
948 | |
949 | #[test] |
950 | fn test_struct_has_padding() { |
951 | // Test that, for each provided repr, `struct_has_padding!` reports the |
952 | // expected value. |
953 | macro_rules! test { |
954 | (#[$cfg:meta] ($($ts:ty),*) => $expect:expr) => {{ |
955 | #[$cfg] |
956 | struct Test($(#[allow(dead_code)] $ts),*); |
957 | assert_eq!(struct_has_padding!(Test, [$($ts),*]), $expect); |
958 | }}; |
959 | (#[$cfg:meta] $(#[$cfgs:meta])* ($($ts:ty),*) => $expect:expr) => { |
960 | test!(#[$cfg] ($($ts),*) => $expect); |
961 | test!($(#[$cfgs])* ($($ts),*) => $expect); |
962 | }; |
963 | } |
964 | |
965 | test!(#[repr(C)] #[repr(transparent)] #[repr(packed)] () => false); |
966 | test!(#[repr(C)] #[repr(transparent)] #[repr(packed)] (u8) => false); |
967 | test!(#[repr(C)] #[repr(transparent)] #[repr(packed)] (u8, ()) => false); |
968 | test!(#[repr(C)] #[repr(packed)] (u8, u8) => false); |
969 | |
970 | test!(#[repr(C)] (u8, AU64) => true); |
971 | // Rust won't let you put `#[repr(packed)]` on a type which contains a |
972 | // `#[repr(align(n > 1))]` type (`AU64`), so we have to use `u64` here. |
973 | // It's not ideal, but it definitely has align > 1 on /some/ of our CI |
974 | // targets, and this isn't a particularly complex macro we're testing |
975 | // anyway. |
976 | test!(#[repr(packed)] (u8, u64) => false); |
977 | } |
978 | |
979 | #[test] |
980 | fn test_union_has_padding() { |
981 | // Test that, for each provided repr, `union_has_padding!` reports the |
982 | // expected value. |
983 | macro_rules! test { |
984 | (#[$cfg:meta] {$($fs:ident: $ts:ty),*} => $expect:expr) => {{ |
985 | #[$cfg] |
986 | #[allow(unused)] // fields are never read |
987 | union Test{ $($fs: $ts),* } |
988 | assert_eq!(union_has_padding!(Test, [$($ts),*]), $expect); |
989 | }}; |
990 | (#[$cfg:meta] $(#[$cfgs:meta])* {$($fs:ident: $ts:ty),*} => $expect:expr) => { |
991 | test!(#[$cfg] {$($fs: $ts),*} => $expect); |
992 | test!($(#[$cfgs])* {$($fs: $ts),*} => $expect); |
993 | }; |
994 | } |
995 | |
996 | test!(#[repr(C)] #[repr(packed)] {a: u8} => false); |
997 | test!(#[repr(C)] #[repr(packed)] {a: u8, b: u8} => false); |
998 | |
999 | // Rust won't let you put `#[repr(packed)]` on a type which contains a |
1000 | // `#[repr(align(n > 1))]` type (`AU64`), so we have to use `u64` here. |
1001 | // It's not ideal, but it definitely has align > 1 on /some/ of our CI |
1002 | // targets, and this isn't a particularly complex macro we're testing |
1003 | // anyway. |
1004 | test!(#[repr(C)] #[repr(packed)] {a: u8, b: u64} => true); |
1005 | } |
1006 | |
1007 | #[test] |
1008 | fn test_enum_has_padding() { |
1009 | // Test that, for each provided repr, `enum_has_padding!` reports the |
1010 | // expected value. |
1011 | macro_rules! test { |
1012 | (#[repr($disc:ident $(, $c:ident)?)] { $($vs:ident ($($ts:ty),*),)* } => $expect:expr) => { |
1013 | test!(@case #[repr($disc $(, $c)?)] { $($vs ($($ts),*),)* } => $expect); |
1014 | }; |
1015 | (#[repr($disc:ident $(, $c:ident)?)] #[$cfg:meta] $(#[$cfgs:meta])* { $($vs:ident ($($ts:ty),*),)* } => $expect:expr) => { |
1016 | test!(@case #[repr($disc $(, $c)?)] #[$cfg] { $($vs ($($ts),*),)* } => $expect); |
1017 | test!(#[repr($disc $(, $c)?)] $(#[$cfgs])* { $($vs ($($ts),*),)* } => $expect); |
1018 | }; |
1019 | (@case #[repr($disc:ident $(, $c:ident)?)] $(#[$cfg:meta])? { $($vs:ident ($($ts:ty),*),)* } => $expect:expr) => {{ |
1020 | #[repr($disc $(, $c)?)] |
1021 | $(#[$cfg])? |
1022 | #[allow(unused)] // variants and fields are never used |
1023 | enum Test { |
1024 | $($vs ($($ts),*),)* |
1025 | } |
1026 | assert_eq!( |
1027 | enum_has_padding!(Test, $disc, $([$($ts),*]),*), |
1028 | $expect |
1029 | ); |
1030 | }}; |
1031 | } |
1032 | |
1033 | #[allow (unused)] |
1034 | #[repr (align(2))] |
1035 | struct U16(u16); |
1036 | |
1037 | #[allow (unused)] |
1038 | #[repr (align(4))] |
1039 | struct U32(u32); |
1040 | |
1041 | test!(#[repr(u8)] #[repr(C)] { |
1042 | A(u8), |
1043 | } => false); |
1044 | test!(#[repr(u16)] #[repr(C)] { |
1045 | A(u8, u8), |
1046 | B(U16), |
1047 | } => false); |
1048 | test!(#[repr(u32)] #[repr(C)] { |
1049 | A(u8, u8, u8, u8), |
1050 | B(U16, u8, u8), |
1051 | C(u8, u8, U16), |
1052 | D(U16, U16), |
1053 | E(U32), |
1054 | } => false); |
1055 | |
1056 | // `repr(int)` can pack the discriminant more efficiently |
1057 | test!(#[repr(u8)] { |
1058 | A(u8, U16), |
1059 | } => false); |
1060 | test!(#[repr(u8)] { |
1061 | A(u8, U16, U32), |
1062 | } => false); |
1063 | |
1064 | // `repr(C)` cannot |
1065 | test!(#[repr(u8, C)] { |
1066 | A(u8, U16), |
1067 | } => true); |
1068 | test!(#[repr(u8, C)] { |
1069 | A(u8, u8, u8, U32), |
1070 | } => true); |
1071 | |
1072 | // And field ordering can always cause problems |
1073 | test!(#[repr(u8)] #[repr(C)] { |
1074 | A(U16, u8), |
1075 | } => true); |
1076 | test!(#[repr(u8)] #[repr(C)] { |
1077 | A(U32, u8, u8, u8), |
1078 | } => true); |
1079 | } |
1080 | } |
1081 | |