1*499dc02cSMiguel Ojeda // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT 2*499dc02cSMiguel Ojeda 3c3739801SMiguel Ojeda // Copyright 2022 The Fuchsia Authors 4c3739801SMiguel Ojeda // 5c3739801SMiguel Ojeda // Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 6c3739801SMiguel Ojeda // <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT 7c3739801SMiguel Ojeda // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. 8c3739801SMiguel Ojeda // This file may not be copied, modified, or distributed except according to 9c3739801SMiguel Ojeda // those terms. 10c3739801SMiguel Ojeda 11c3739801SMiguel Ojeda //! Utilities used by macros and by `zerocopy-derive`. 12c3739801SMiguel Ojeda //! 13c3739801SMiguel Ojeda //! These are defined here `zerocopy` rather than in code generated by macros or 14c3739801SMiguel Ojeda //! by `zerocopy-derive` so that they can be compiled once rather than 15c3739801SMiguel Ojeda //! recompiled for every invocation (e.g., if they were defined in generated 16c3739801SMiguel Ojeda //! code, then deriving `IntoBytes` and `FromBytes` on three different types 17c3739801SMiguel Ojeda //! would result in the code in question being emitted and compiled six 18c3739801SMiguel Ojeda //! different times). 19c3739801SMiguel Ojeda 20c3739801SMiguel Ojeda #![allow(missing_debug_implementations)] 21c3739801SMiguel Ojeda 22c3739801SMiguel Ojeda // FIXME(#29), FIXME(https://github.com/rust-lang/rust/issues/69835): Remove 23c3739801SMiguel Ojeda // this `cfg` when `size_of_val_raw` is stabilized. 24c3739801SMiguel Ojeda #[cfg(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] 25c3739801SMiguel Ojeda #[cfg(not(target_pointer_width = "16"))] 26c3739801SMiguel Ojeda use core::ptr::{self, NonNull}; 27c3739801SMiguel Ojeda use core::{marker::PhantomData, mem, num::Wrapping}; 28c3739801SMiguel Ojeda 29c3739801SMiguel Ojeda use crate::{ 30c3739801SMiguel Ojeda pointer::{ 31c3739801SMiguel Ojeda cast::CastSized, 32c3739801SMiguel Ojeda invariant::{Aligned, Initialized, Valid}, 33c3739801SMiguel Ojeda BecauseImmutable, 34c3739801SMiguel Ojeda }, 35c3739801SMiguel Ojeda FromBytes, Immutable, IntoBytes, KnownLayout, Ptr, ReadOnly, TryFromBytes, ValidityError, 36c3739801SMiguel Ojeda }; 37c3739801SMiguel Ojeda 38c3739801SMiguel Ojeda /// Projects the type of the field at `Index` in `Self` without regard for field 39c3739801SMiguel Ojeda /// privacy. 40c3739801SMiguel Ojeda /// 41c3739801SMiguel Ojeda /// The `Index` parameter is any sort of handle that identifies the field; its 42c3739801SMiguel Ojeda /// definition is the obligation of the implementer. 43c3739801SMiguel Ojeda /// 44c3739801SMiguel Ojeda /// # Safety 45c3739801SMiguel Ojeda /// 46c3739801SMiguel Ojeda /// Unsafe code may assume that this accurately reflects the definition of 47c3739801SMiguel Ojeda /// `Self`. 48c3739801SMiguel Ojeda pub unsafe trait Field<Index> { 49c3739801SMiguel Ojeda /// The type of the field at `Index`. 50c3739801SMiguel Ojeda type Type: ?Sized; 51c3739801SMiguel Ojeda } 52c3739801SMiguel Ojeda 53c3739801SMiguel Ojeda #[cfg_attr( 54c3739801SMiguel Ojeda not(no_zerocopy_diagnostic_on_unimplemented_1_78_0), 55c3739801SMiguel Ojeda diagnostic::on_unimplemented( 56c3739801SMiguel Ojeda message = "`{T}` has {PADDING_BYTES} total byte(s) of padding", 57c3739801SMiguel Ojeda label = "types with padding cannot implement `IntoBytes`", 58c3739801SMiguel Ojeda note = "consider using `zerocopy::Unalign` to lower the alignment of individual fields", 59c3739801SMiguel Ojeda note = "consider adding explicit fields where padding would be", 60c3739801SMiguel Ojeda note = "consider using `#[repr(packed)]` to remove padding" 61c3739801SMiguel Ojeda ) 62c3739801SMiguel Ojeda )] 63c3739801SMiguel Ojeda pub trait PaddingFree<T: ?Sized, const PADDING_BYTES: usize> {} 64c3739801SMiguel Ojeda impl<T: ?Sized> PaddingFree<T, 0> for () {} 65c3739801SMiguel Ojeda 66c3739801SMiguel Ojeda // FIXME(#1112): In the slice DST case, we should delegate to *both* 67c3739801SMiguel Ojeda // `PaddingFree` *and* `DynamicPaddingFree` (and probably rename `PaddingFree` 68c3739801SMiguel Ojeda // to `StaticPaddingFree` or something - or introduce a third trait with that 69c3739801SMiguel Ojeda // name) so that we can have more clear error messages. 70c3739801SMiguel Ojeda 71c3739801SMiguel Ojeda #[cfg_attr( 72c3739801SMiguel Ojeda not(no_zerocopy_diagnostic_on_unimplemented_1_78_0), 73c3739801SMiguel Ojeda diagnostic::on_unimplemented( 74c3739801SMiguel Ojeda message = "`{T}` has one or more padding bytes", 75c3739801SMiguel Ojeda label = "types with padding cannot implement `IntoBytes`", 76c3739801SMiguel Ojeda note = "consider using `zerocopy::Unalign` to lower the alignment of individual fields", 77c3739801SMiguel Ojeda note = "consider adding explicit fields where padding would be", 78c3739801SMiguel Ojeda note = "consider using `#[repr(packed)]` to remove padding" 79c3739801SMiguel Ojeda ) 80c3739801SMiguel Ojeda )] 81c3739801SMiguel Ojeda pub trait DynamicPaddingFree<T: ?Sized, const HAS_PADDING: bool> {} 82c3739801SMiguel Ojeda impl<T: ?Sized> DynamicPaddingFree<T, false> for () {} 83c3739801SMiguel Ojeda 84c3739801SMiguel Ojeda #[cfg(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] 85c3739801SMiguel Ojeda #[cfg(not(target_pointer_width = "16"))] 86c3739801SMiguel Ojeda const _64K: usize = 1 << 16; 87c3739801SMiguel Ojeda 88c3739801SMiguel Ojeda // FIXME(#29), FIXME(https://github.com/rust-lang/rust/issues/69835): Remove 89c3739801SMiguel Ojeda // this `cfg` when `size_of_val_raw` is stabilized. 90c3739801SMiguel Ojeda #[cfg(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] 91c3739801SMiguel Ojeda #[cfg(not(target_pointer_width = "16"))] 92c3739801SMiguel Ojeda #[repr(C, align(65536))] 93c3739801SMiguel Ojeda struct Aligned64kAllocation([u8; _64K]); 94c3739801SMiguel Ojeda 95c3739801SMiguel Ojeda /// A pointer to an aligned allocation of size 2^16. 96c3739801SMiguel Ojeda /// 97c3739801SMiguel Ojeda /// # Safety 98c3739801SMiguel Ojeda /// 99c3739801SMiguel Ojeda /// `ALIGNED_64K_ALLOCATION` is guaranteed to point to the entirety of an 100c3739801SMiguel Ojeda /// allocation with size and alignment 2^16, and to have valid provenance. 101c3739801SMiguel Ojeda // FIXME(#29), FIXME(https://github.com/rust-lang/rust/issues/69835): Remove 102c3739801SMiguel Ojeda // this `cfg` when `size_of_val_raw` is stabilized. 103c3739801SMiguel Ojeda #[cfg(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] 104c3739801SMiguel Ojeda #[cfg(not(target_pointer_width = "16"))] 105c3739801SMiguel Ojeda pub const ALIGNED_64K_ALLOCATION: NonNull<[u8]> = { 106c3739801SMiguel Ojeda const REF: &Aligned64kAllocation = &Aligned64kAllocation([0; _64K]); 107c3739801SMiguel Ojeda let ptr: *const Aligned64kAllocation = REF; 108c3739801SMiguel Ojeda let ptr: *const [u8] = ptr::slice_from_raw_parts(ptr.cast(), _64K); 109c3739801SMiguel Ojeda // SAFETY: 110c3739801SMiguel Ojeda // - `ptr` is derived from a Rust reference, which is guaranteed to be 111c3739801SMiguel Ojeda // non-null. 112c3739801SMiguel Ojeda // - `ptr` is derived from an `&Aligned64kAllocation`, which has size and 113c3739801SMiguel Ojeda // alignment `_64K` as promised. Its length is initialized to `_64K`, 114c3739801SMiguel Ojeda // which means that it refers to the entire allocation. 115c3739801SMiguel Ojeda // - `ptr` is derived from a Rust reference, which is guaranteed to have 116c3739801SMiguel Ojeda // valid provenance. 117c3739801SMiguel Ojeda // 118c3739801SMiguel Ojeda // FIXME(#429): Once `NonNull::new_unchecked` docs document that it 119c3739801SMiguel Ojeda // preserves provenance, cite those docs. 120c3739801SMiguel Ojeda // FIXME: Replace this `as` with `ptr.cast_mut()` once our MSRV >= 1.65 121c3739801SMiguel Ojeda #[allow(clippy::as_conversions)] 122c3739801SMiguel Ojeda unsafe { 123c3739801SMiguel Ojeda NonNull::new_unchecked(ptr as *mut _) 124c3739801SMiguel Ojeda } 125c3739801SMiguel Ojeda }; 126c3739801SMiguel Ojeda 127c3739801SMiguel Ojeda /// Computes the offset of the base of the field `$trailing_field_name` within 128c3739801SMiguel Ojeda /// the type `$ty`. 129c3739801SMiguel Ojeda /// 130c3739801SMiguel Ojeda /// `trailing_field_offset!` produces code which is valid in a `const` context. 131c3739801SMiguel Ojeda // FIXME(#29), FIXME(https://github.com/rust-lang/rust/issues/69835): Remove 132c3739801SMiguel Ojeda // this `cfg` when `size_of_val_raw` is stabilized. 133c3739801SMiguel Ojeda #[cfg(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] 134c3739801SMiguel Ojeda #[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. 135c3739801SMiguel Ojeda #[macro_export] 136c3739801SMiguel Ojeda macro_rules! trailing_field_offset { 137c3739801SMiguel Ojeda ($ty:ty, $trailing_field_name:tt) => {{ 138c3739801SMiguel Ojeda let min_size = { 139c3739801SMiguel Ojeda let zero_elems: *const [()] = 140c3739801SMiguel Ojeda $crate::util::macro_util::core_reexport::ptr::slice_from_raw_parts( 141c3739801SMiguel Ojeda $crate::util::macro_util::core_reexport::ptr::NonNull::<()>::dangling() 142c3739801SMiguel Ojeda .as_ptr() 143c3739801SMiguel Ojeda .cast_const(), 144c3739801SMiguel Ojeda 0, 145c3739801SMiguel Ojeda ); 146c3739801SMiguel Ojeda // SAFETY: 147c3739801SMiguel Ojeda // - If `$ty` is `Sized`, `size_of_val_raw` is always safe to call. 148c3739801SMiguel Ojeda // - Otherwise: 149c3739801SMiguel Ojeda // - If `$ty` is not a slice DST, this pointer conversion will 150c3739801SMiguel Ojeda // fail due to "mismatched vtable kinds", and compilation will 151c3739801SMiguel Ojeda // fail. 152c3739801SMiguel Ojeda // - If `$ty` is a slice DST, we have constructed `zero_elems` to 153c3739801SMiguel Ojeda // have zero trailing slice elements. Per the `size_of_val_raw` 154c3739801SMiguel Ojeda // docs, "For the special case where the dynamic tail length is 155c3739801SMiguel Ojeda // 0, this function is safe to call." [1] 156c3739801SMiguel Ojeda // 157c3739801SMiguel Ojeda // [1] https://doc.rust-lang.org/nightly/std/mem/fn.size_of_val_raw.html 158c3739801SMiguel Ojeda unsafe { 159c3739801SMiguel Ojeda #[allow(clippy::as_conversions)] 160c3739801SMiguel Ojeda $crate::util::macro_util::core_reexport::mem::size_of_val_raw( 161c3739801SMiguel Ojeda zero_elems as *const $ty, 162c3739801SMiguel Ojeda ) 163c3739801SMiguel Ojeda } 164c3739801SMiguel Ojeda }; 165c3739801SMiguel Ojeda 166c3739801SMiguel Ojeda assert!(min_size <= _64K); 167c3739801SMiguel Ojeda 168c3739801SMiguel Ojeda #[allow(clippy::as_conversions)] 169c3739801SMiguel Ojeda let ptr = ALIGNED_64K_ALLOCATION.as_ptr() as *const $ty; 170c3739801SMiguel Ojeda 171c3739801SMiguel Ojeda // SAFETY: 172c3739801SMiguel Ojeda // - Thanks to the preceding `assert!`, we know that the value with zero 173c3739801SMiguel Ojeda // elements fits in `_64K` bytes, and thus in the allocation addressed 174c3739801SMiguel Ojeda // by `ALIGNED_64K_ALLOCATION`. The offset of the trailing field is 175c3739801SMiguel Ojeda // guaranteed to be no larger than this size, so this field projection 176c3739801SMiguel Ojeda // is guaranteed to remain in-bounds of its allocation. 177c3739801SMiguel Ojeda // - Because the minimum size is no larger than `_64K` bytes, and 178c3739801SMiguel Ojeda // because an object's size must always be a multiple of its alignment 179c3739801SMiguel Ojeda // [1], we know that `$ty`'s alignment is no larger than `_64K`. The 180c3739801SMiguel Ojeda // allocation addressed by `ALIGNED_64K_ALLOCATION` is guaranteed to 181c3739801SMiguel Ojeda // be aligned to `_64K`, so `ptr` is guaranteed to satisfy `$ty`'s 182c3739801SMiguel Ojeda // alignment. 183c3739801SMiguel Ojeda // - As required by `addr_of!`, we do not write through `field`. 184c3739801SMiguel Ojeda // 185c3739801SMiguel Ojeda // Note that, as of [2], this requirement is technically unnecessary 186c3739801SMiguel Ojeda // for Rust versions >= 1.75.0, but no harm in guaranteeing it anyway 187c3739801SMiguel Ojeda // until we bump our MSRV. 188c3739801SMiguel Ojeda // 189c3739801SMiguel Ojeda // [1] Per https://doc.rust-lang.org/reference/type-layout.html: 190c3739801SMiguel Ojeda // 191c3739801SMiguel Ojeda // The size of a value is always a multiple of its alignment. 192c3739801SMiguel Ojeda // 193c3739801SMiguel Ojeda // [2] https://github.com/rust-lang/reference/pull/1387 194c3739801SMiguel Ojeda let field = unsafe { 195c3739801SMiguel Ojeda $crate::util::macro_util::core_reexport::ptr::addr_of!((*ptr).$trailing_field_name) 196c3739801SMiguel Ojeda }; 197c3739801SMiguel Ojeda // SAFETY: 198c3739801SMiguel Ojeda // - Both `ptr` and `field` are derived from the same allocated object. 199c3739801SMiguel Ojeda // - By the preceding safety comment, `field` is in bounds of that 200c3739801SMiguel Ojeda // allocated object. 201c3739801SMiguel Ojeda // - The distance, in bytes, between `ptr` and `field` is required to be 202c3739801SMiguel Ojeda // a multiple of the size of `u8`, which is trivially true because 203c3739801SMiguel Ojeda // `u8`'s size is 1. 204c3739801SMiguel Ojeda // - The distance, in bytes, cannot overflow `isize`. This is guaranteed 205c3739801SMiguel Ojeda // because no allocated object can have a size larger than can fit in 206c3739801SMiguel Ojeda // `isize`. [1] 207c3739801SMiguel Ojeda // - The distance being in-bounds cannot rely on wrapping around the 208c3739801SMiguel Ojeda // address space. This is guaranteed because the same is guaranteed of 209c3739801SMiguel Ojeda // allocated objects. [1] 210c3739801SMiguel Ojeda // 211c3739801SMiguel Ojeda // [1] FIXME(#429), FIXME(https://github.com/rust-lang/rust/pull/116675): 212c3739801SMiguel Ojeda // Once these are guaranteed in the Reference, cite it. 213c3739801SMiguel Ojeda let offset = unsafe { field.cast::<u8>().offset_from(ptr.cast::<u8>()) }; 214c3739801SMiguel Ojeda // Guaranteed not to be lossy: `field` comes after `ptr`, so the offset 215c3739801SMiguel Ojeda // from `ptr` to `field` is guaranteed to be positive. 216c3739801SMiguel Ojeda assert!(offset >= 0); 217c3739801SMiguel Ojeda Some( 218c3739801SMiguel Ojeda #[allow(clippy::as_conversions)] 219c3739801SMiguel Ojeda { 220c3739801SMiguel Ojeda offset as usize 221c3739801SMiguel Ojeda }, 222c3739801SMiguel Ojeda ) 223c3739801SMiguel Ojeda }}; 224c3739801SMiguel Ojeda } 225c3739801SMiguel Ojeda 226c3739801SMiguel Ojeda /// Computes alignment of `$ty: ?Sized`. 227c3739801SMiguel Ojeda /// 228c3739801SMiguel Ojeda /// `align_of!` produces code which is valid in a `const` context. 229c3739801SMiguel Ojeda // FIXME(#29), FIXME(https://github.com/rust-lang/rust/issues/69835): Remove 230c3739801SMiguel Ojeda // this `cfg` when `size_of_val_raw` is stabilized. 231c3739801SMiguel Ojeda #[cfg(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] 232c3739801SMiguel Ojeda #[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. 233c3739801SMiguel Ojeda #[macro_export] 234c3739801SMiguel Ojeda macro_rules! align_of { 235c3739801SMiguel Ojeda ($ty:ty) => {{ 236c3739801SMiguel Ojeda // SAFETY: `OffsetOfTrailingIsAlignment` is `repr(C)`, and its layout is 237c3739801SMiguel Ojeda // guaranteed [1] to begin with the single-byte layout for `_byte`, 238c3739801SMiguel Ojeda // followed by the padding needed to align `_trailing`, then the layout 239c3739801SMiguel Ojeda // for `_trailing`, and finally any trailing padding bytes needed to 240c3739801SMiguel Ojeda // correctly-align the entire struct. 241c3739801SMiguel Ojeda // 242c3739801SMiguel Ojeda // This macro computes the alignment of `$ty` by counting the number of 243c3739801SMiguel Ojeda // bytes preceding `_trailing`. For instance, if the alignment of `$ty` 244c3739801SMiguel Ojeda // is `1`, then no padding is required align `_trailing` and it will be 245c3739801SMiguel Ojeda // located immediately after `_byte` at offset 1. If the alignment of 246c3739801SMiguel Ojeda // `$ty` is 2, then a single padding byte is required before 247c3739801SMiguel Ojeda // `_trailing`, and `_trailing` will be located at offset 2. 248c3739801SMiguel Ojeda 249c3739801SMiguel Ojeda // This correspondence between offset and alignment holds for all valid 250c3739801SMiguel Ojeda // Rust alignments, and we confirm this exhaustively (or, at least up to 251c3739801SMiguel Ojeda // the maximum alignment supported by `trailing_field_offset!`) in 252c3739801SMiguel Ojeda // `test_align_of_dst`. 253c3739801SMiguel Ojeda // 254c3739801SMiguel Ojeda // [1]: https://doc.rust-lang.org/nomicon/other-reprs.html#reprc 255c3739801SMiguel Ojeda 256c3739801SMiguel Ojeda #[repr(C)] 257c3739801SMiguel Ojeda struct OffsetOfTrailingIsAlignment { 258c3739801SMiguel Ojeda _byte: u8, 259c3739801SMiguel Ojeda _trailing: $ty, 260c3739801SMiguel Ojeda } 261c3739801SMiguel Ojeda 262c3739801SMiguel Ojeda trailing_field_offset!(OffsetOfTrailingIsAlignment, _trailing) 263c3739801SMiguel Ojeda }}; 264c3739801SMiguel Ojeda } 265c3739801SMiguel Ojeda 266c3739801SMiguel Ojeda mod size_to_tag { 267c3739801SMiguel Ojeda pub trait SizeToTag<const SIZE: usize> { 268c3739801SMiguel Ojeda type Tag; 269c3739801SMiguel Ojeda } 270c3739801SMiguel Ojeda 271c3739801SMiguel Ojeda impl SizeToTag<1> for () { 272c3739801SMiguel Ojeda type Tag = u8; 273c3739801SMiguel Ojeda } 274c3739801SMiguel Ojeda impl SizeToTag<2> for () { 275c3739801SMiguel Ojeda type Tag = u16; 276c3739801SMiguel Ojeda } 277c3739801SMiguel Ojeda impl SizeToTag<4> for () { 278c3739801SMiguel Ojeda type Tag = u32; 279c3739801SMiguel Ojeda } 280c3739801SMiguel Ojeda impl SizeToTag<8> for () { 281c3739801SMiguel Ojeda type Tag = u64; 282c3739801SMiguel Ojeda } 283c3739801SMiguel Ojeda impl SizeToTag<16> for () { 284c3739801SMiguel Ojeda type Tag = u128; 285c3739801SMiguel Ojeda } 286c3739801SMiguel Ojeda } 287c3739801SMiguel Ojeda 288c3739801SMiguel Ojeda /// An alias for the unsigned integer of the given size in bytes. 289c3739801SMiguel Ojeda #[doc(hidden)] 290c3739801SMiguel Ojeda pub type SizeToTag<const SIZE: usize> = <() as size_to_tag::SizeToTag<SIZE>>::Tag; 291c3739801SMiguel Ojeda 292c3739801SMiguel Ojeda // We put `Sized` in its own module so it can have the same name as the standard 293c3739801SMiguel Ojeda // library `Sized` without shadowing it in the parent module. 294c3739801SMiguel Ojeda #[cfg(not(no_zerocopy_diagnostic_on_unimplemented_1_78_0))] 295c3739801SMiguel Ojeda mod __size_of { 296c3739801SMiguel Ojeda #[diagnostic::on_unimplemented( 297c3739801SMiguel Ojeda message = "`{Self}` is unsized", 298c3739801SMiguel Ojeda label = "`IntoBytes` needs all field types to be `Sized` in order to determine whether there is padding", 299c3739801SMiguel Ojeda note = "consider using `#[repr(packed)]` to remove padding", 300c3739801SMiguel Ojeda note = "`IntoBytes` does not require the fields of `#[repr(packed)]` types to be `Sized`" 301c3739801SMiguel Ojeda )] 302c3739801SMiguel Ojeda pub trait Sized: core::marker::Sized {} 303c3739801SMiguel Ojeda impl<T: core::marker::Sized> Sized for T {} 304c3739801SMiguel Ojeda 305c3739801SMiguel Ojeda #[inline(always)] 306c3739801SMiguel Ojeda #[must_use] 307c3739801SMiguel Ojeda #[allow(clippy::needless_maybe_sized)] 308c3739801SMiguel Ojeda pub const fn size_of<T: Sized + ?core::marker::Sized>() -> usize { 309c3739801SMiguel Ojeda core::mem::size_of::<T>() 310c3739801SMiguel Ojeda } 311c3739801SMiguel Ojeda } 312c3739801SMiguel Ojeda 313c3739801SMiguel Ojeda #[cfg(no_zerocopy_diagnostic_on_unimplemented_1_78_0)] 314c3739801SMiguel Ojeda pub use core::mem::size_of; 315c3739801SMiguel Ojeda 316c3739801SMiguel Ojeda #[cfg(not(no_zerocopy_diagnostic_on_unimplemented_1_78_0))] 317c3739801SMiguel Ojeda pub use __size_of::size_of; 318c3739801SMiguel Ojeda 319c3739801SMiguel Ojeda /// How many padding bytes does the struct type `$t` have? 320c3739801SMiguel Ojeda /// 321c3739801SMiguel Ojeda /// `$ts` is the list of the type of every field in `$t`. `$t` must be a struct 322c3739801SMiguel Ojeda /// type, or else `struct_padding!`'s result may be meaningless. 323c3739801SMiguel Ojeda /// 324c3739801SMiguel Ojeda /// Note that `struct_padding!`'s results are independent of `repcr` since they 325c3739801SMiguel Ojeda /// only consider the size of the type and the sizes of the fields. Whatever the 326c3739801SMiguel Ojeda /// repr, the size of the type already takes into account any padding that the 327c3739801SMiguel Ojeda /// compiler has decided to add. Structs with well-defined representations (such 328c3739801SMiguel Ojeda /// as `repr(C)`) can use this macro to check for padding. Note that while this 329c3739801SMiguel Ojeda /// may yield some consistent value for some `repr(Rust)` structs, it is not 330c3739801SMiguel Ojeda /// guaranteed across platforms or compilations. 331c3739801SMiguel Ojeda #[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. 332c3739801SMiguel Ojeda #[macro_export] 333c3739801SMiguel Ojeda macro_rules! struct_padding { 334c3739801SMiguel Ojeda ($t:ty, $_align:expr, $_packed:expr, [$($ts:ty),*]) => {{ 335c3739801SMiguel Ojeda // The `align` and `packed` directives can be ignored here. Regardless 336c3739801SMiguel Ojeda // of if and how they are set, comparing the size of `$t` to the sum of 337c3739801SMiguel Ojeda // its field sizes is a reliable indicator of the presence of padding. 338c3739801SMiguel Ojeda $crate::util::macro_util::size_of::<$t>() - (0 $(+ $crate::util::macro_util::size_of::<$ts>())*) 339c3739801SMiguel Ojeda }}; 340c3739801SMiguel Ojeda } 341c3739801SMiguel Ojeda 342c3739801SMiguel Ojeda /// Does the `repr(C)` struct type `$t` have padding? 343c3739801SMiguel Ojeda /// 344c3739801SMiguel Ojeda /// `$ts` is the list of the type of every field in `$t`. `$t` must be a 345c3739801SMiguel Ojeda /// `repr(C)` struct type, or else `struct_has_padding!`'s result may be 346c3739801SMiguel Ojeda /// meaningless. 347c3739801SMiguel Ojeda #[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. 348c3739801SMiguel Ojeda #[macro_export] 349c3739801SMiguel Ojeda macro_rules! repr_c_struct_has_padding { 350c3739801SMiguel Ojeda ($t:ty, $align:expr, $packed:expr, [$($ts:tt),*]) => {{ 351c3739801SMiguel Ojeda let layout = $crate::DstLayout::for_repr_c_struct( 352c3739801SMiguel Ojeda $align, 353c3739801SMiguel Ojeda $packed, 354c3739801SMiguel Ojeda &[$($crate::repr_c_struct_has_padding!(@field $ts),)*] 355c3739801SMiguel Ojeda ); 356c3739801SMiguel Ojeda layout.requires_static_padding() || layout.requires_dynamic_padding() 357c3739801SMiguel Ojeda }}; 358c3739801SMiguel Ojeda (@field ([$t:ty])) => { 359c3739801SMiguel Ojeda <[$t] as $crate::KnownLayout>::LAYOUT 360c3739801SMiguel Ojeda }; 361c3739801SMiguel Ojeda (@field ($t:ty)) => { 362c3739801SMiguel Ojeda $crate::DstLayout::for_unpadded_type::<$t>() 363c3739801SMiguel Ojeda }; 364c3739801SMiguel Ojeda (@field [$t:ty]) => { 365c3739801SMiguel Ojeda <[$t] as $crate::KnownLayout>::LAYOUT 366c3739801SMiguel Ojeda }; 367c3739801SMiguel Ojeda (@field $t:ty) => { 368c3739801SMiguel Ojeda $crate::DstLayout::for_unpadded_type::<$t>() 369c3739801SMiguel Ojeda }; 370c3739801SMiguel Ojeda } 371c3739801SMiguel Ojeda 372c3739801SMiguel Ojeda /// Does the union type `$t` have padding? 373c3739801SMiguel Ojeda /// 374c3739801SMiguel Ojeda /// `$ts` is the list of the type of every field in `$t`. `$t` must be a union 375c3739801SMiguel Ojeda /// type, or else `union_padding!`'s result may be meaningless. 376c3739801SMiguel Ojeda /// 377c3739801SMiguel Ojeda /// Note that `union_padding!`'s results are independent of `repr` since they 378c3739801SMiguel Ojeda /// only consider the size of the type and the sizes of the fields. Whatever the 379c3739801SMiguel Ojeda /// repr, the size of the type already takes into account any padding that the 380c3739801SMiguel Ojeda /// compiler has decided to add. Unions with well-defined representations (such 381c3739801SMiguel Ojeda /// as `repr(C)`) can use this macro to check for padding. Note that while this 382c3739801SMiguel Ojeda /// may yield some consistent value for some `repr(Rust)` unions, it is not 383c3739801SMiguel Ojeda /// guaranteed across platforms or compilations. 384c3739801SMiguel Ojeda #[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. 385c3739801SMiguel Ojeda #[macro_export] 386c3739801SMiguel Ojeda macro_rules! union_padding { 387c3739801SMiguel Ojeda ($t:ty, $_align:expr, $_packed:expr, [$($ts:ty),*]) => {{ 388c3739801SMiguel Ojeda // The `align` and `packed` directives can be ignored here. Regardless 389c3739801SMiguel Ojeda // of if and how they are set, comparing the size of `$t` to each of its 390c3739801SMiguel Ojeda // field sizes is a reliable indicator of the presence of padding. 391c3739801SMiguel Ojeda let mut max = 0; 392c3739801SMiguel Ojeda $({ 393c3739801SMiguel Ojeda let padding = $crate::util::macro_util::size_of::<$t>() - $crate::util::macro_util::size_of::<$ts>(); 394c3739801SMiguel Ojeda if padding > max { 395c3739801SMiguel Ojeda max = padding; 396c3739801SMiguel Ojeda } 397c3739801SMiguel Ojeda })* 398c3739801SMiguel Ojeda max 399c3739801SMiguel Ojeda }}; 400c3739801SMiguel Ojeda } 401c3739801SMiguel Ojeda 402c3739801SMiguel Ojeda /// How many padding bytes does the enum type `$t` have? 403c3739801SMiguel Ojeda /// 404c3739801SMiguel Ojeda /// `$disc` is the type of the enum tag, and `$ts` is a list of fields in each 405c3739801SMiguel Ojeda /// square-bracket-delimited variant. `$t` must be an enum, or else 406c3739801SMiguel Ojeda /// `enum_padding!`'s result may be meaningless. An enum has padding if any of 407c3739801SMiguel Ojeda /// its variant structs [1][2] contain padding, and so all of the variants of an 408c3739801SMiguel Ojeda /// enum must be "full" in order for the enum to not have padding. 409c3739801SMiguel Ojeda /// 410c3739801SMiguel Ojeda /// The results of `enum_padding!` require that the enum is not `repr(Rust)`, as 411c3739801SMiguel Ojeda /// `repr(Rust)` enums may niche the enum's tag and reduce the total number of 412c3739801SMiguel Ojeda /// bytes required to represent the enum as a result. As long as the enum is 413c3739801SMiguel Ojeda /// `repr(C)`, `repr(int)`, or `repr(C, int)`, this will consistently return 414c3739801SMiguel Ojeda /// whether the enum contains any padding bytes. 415c3739801SMiguel Ojeda /// 416c3739801SMiguel Ojeda /// [1]: https://doc.rust-lang.org/1.81.0/reference/type-layout.html#reprc-enums-with-fields 417c3739801SMiguel Ojeda /// [2]: https://doc.rust-lang.org/1.81.0/reference/type-layout.html#primitive-representation-of-enums-with-fields 418c3739801SMiguel Ojeda #[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. 419c3739801SMiguel Ojeda #[macro_export] 420c3739801SMiguel Ojeda macro_rules! enum_padding { 421c3739801SMiguel Ojeda ($t:ty, $_align:expr, $packed:expr, $disc:ty, $([$($ts:ty),*]),*) => {{ 422c3739801SMiguel Ojeda // The `align` and `packed` directives are irrelevant. `$align` can be 423c3739801SMiguel Ojeda // ignored because regardless of if and how it is set, comparing the 424c3739801SMiguel Ojeda // size of `$t` to each of its field sizes is a reliable indicator of 425c3739801SMiguel Ojeda // the presence of padding. `$packed` is irrelevant because it is 426c3739801SMiguel Ojeda // forbidden on enums. 427c3739801SMiguel Ojeda #[allow(clippy::as_conversions)] 428c3739801SMiguel Ojeda const _: [(); 1] = [(); $packed.is_none() as usize]; 429c3739801SMiguel Ojeda let mut max = 0; 430c3739801SMiguel Ojeda $({ 431c3739801SMiguel Ojeda let padding = $crate::util::macro_util::size_of::<$t>() 432c3739801SMiguel Ojeda - ( 433c3739801SMiguel Ojeda $crate::util::macro_util::size_of::<$disc>() 434c3739801SMiguel Ojeda $(+ $crate::util::macro_util::size_of::<$ts>())* 435c3739801SMiguel Ojeda ); 436c3739801SMiguel Ojeda if padding > max { 437c3739801SMiguel Ojeda max = padding; 438c3739801SMiguel Ojeda } 439c3739801SMiguel Ojeda })* 440c3739801SMiguel Ojeda max 441c3739801SMiguel Ojeda }}; 442c3739801SMiguel Ojeda } 443c3739801SMiguel Ojeda 444c3739801SMiguel Ojeda /// Unwraps an infallible `Result`. 445c3739801SMiguel Ojeda #[doc(hidden)] 446c3739801SMiguel Ojeda #[macro_export] 447c3739801SMiguel Ojeda macro_rules! into_inner { 448c3739801SMiguel Ojeda ($e:expr) => { 449c3739801SMiguel Ojeda match $e { 450c3739801SMiguel Ojeda $crate::util::macro_util::core_reexport::result::Result::Ok(e) => e, 451c3739801SMiguel Ojeda $crate::util::macro_util::core_reexport::result::Result::Err(i) => match i {}, 452c3739801SMiguel Ojeda } 453c3739801SMiguel Ojeda }; 454c3739801SMiguel Ojeda } 455c3739801SMiguel Ojeda 456c3739801SMiguel Ojeda /// Translates an identifier or tuple index into a numeric identifier. 457c3739801SMiguel Ojeda #[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. 458c3739801SMiguel Ojeda #[macro_export] 459c3739801SMiguel Ojeda macro_rules! ident_id { 460c3739801SMiguel Ojeda ($field:ident) => { 461c3739801SMiguel Ojeda $crate::util::macro_util::hash_name(stringify!($field)) 462c3739801SMiguel Ojeda }; 463c3739801SMiguel Ojeda ($field:literal) => { 464c3739801SMiguel Ojeda $field 465c3739801SMiguel Ojeda }; 466c3739801SMiguel Ojeda } 467c3739801SMiguel Ojeda 468c3739801SMiguel Ojeda /// Computes the hash of a string. 469c3739801SMiguel Ojeda /// 470c3739801SMiguel Ojeda /// NOTE(#2749) on hash collisions: This function's output only needs to be 471c3739801SMiguel Ojeda /// deterministic within a particular compilation. Thus, if a user ever reports 472c3739801SMiguel Ojeda /// a hash collision (very unlikely given the <= 16-byte special case), we can 473c3739801SMiguel Ojeda /// strengthen the hash function at that point and publish a new version. Since 474c3739801SMiguel Ojeda /// this is computed at compile time on small strings, we can easily use more 475c3739801SMiguel Ojeda /// expensive and higher-quality hash functions if need be. 476c3739801SMiguel Ojeda #[inline(always)] 477c3739801SMiguel Ojeda #[must_use] 478c3739801SMiguel Ojeda #[allow(clippy::as_conversions, clippy::indexing_slicing, clippy::arithmetic_side_effects)] 479c3739801SMiguel Ojeda pub const fn hash_name(name: &str) -> i128 { 480c3739801SMiguel Ojeda let name = name.as_bytes(); 481c3739801SMiguel Ojeda 482c3739801SMiguel Ojeda // We guarantee freedom from hash collisions between any two strings of 483c3739801SMiguel Ojeda // length 16 or less by having the hashes of such strings be equal to 484c3739801SMiguel Ojeda // their value. There is still a possibility that such strings will have 485c3739801SMiguel Ojeda // the same value as the hash of a string of length > 16. 486c3739801SMiguel Ojeda if name.len() <= size_of::<u128>() { 487c3739801SMiguel Ojeda let mut bytes = [0u8; 16]; 488c3739801SMiguel Ojeda 489c3739801SMiguel Ojeda let mut i = 0; 490c3739801SMiguel Ojeda while i < name.len() { 491c3739801SMiguel Ojeda bytes[i] = name[i]; 492c3739801SMiguel Ojeda i += 1; 493c3739801SMiguel Ojeda } 494c3739801SMiguel Ojeda 495c3739801SMiguel Ojeda return i128::from_ne_bytes(bytes); 496c3739801SMiguel Ojeda }; 497c3739801SMiguel Ojeda 498c3739801SMiguel Ojeda // An implementation of FxHasher, although returning a u128. Probably 499c3739801SMiguel Ojeda // not as strong as it could be, but probably more collision resistant 500c3739801SMiguel Ojeda // than normal 64-bit FxHasher. 501c3739801SMiguel Ojeda let mut hash = 0u128; 502c3739801SMiguel Ojeda let mut i = 0; 503c3739801SMiguel Ojeda while i < name.len() { 504c3739801SMiguel Ojeda // This is just FxHasher's `0x517cc1b727220a95` constant 505c3739801SMiguel Ojeda // concatenated back-to-back. 506c3739801SMiguel Ojeda const K: u128 = 0x517cc1b727220a95517cc1b727220a95; 507c3739801SMiguel Ojeda hash = (hash.rotate_left(5) ^ (name[i] as u128)).wrapping_mul(K); 508c3739801SMiguel Ojeda i += 1; 509c3739801SMiguel Ojeda } 510c3739801SMiguel Ojeda i128::from_ne_bytes(hash.to_ne_bytes()) 511c3739801SMiguel Ojeda } 512c3739801SMiguel Ojeda 513c3739801SMiguel Ojeda /// Attempts to transmute `Src` into `Dst`. 514c3739801SMiguel Ojeda /// 515c3739801SMiguel Ojeda /// A helper for `try_transmute!`. 516c3739801SMiguel Ojeda /// 517c3739801SMiguel Ojeda /// # Panics 518c3739801SMiguel Ojeda /// 519c3739801SMiguel Ojeda /// `try_transmute` may either produce a post-monomorphization error or a panic 520c3739801SMiguel Ojeda /// if `Dst` is bigger than `Src`. Otherwise, `try_transmute` panics under the 521c3739801SMiguel Ojeda /// same circumstances as [`is_bit_valid`]. 522c3739801SMiguel Ojeda /// 523c3739801SMiguel Ojeda /// [`is_bit_valid`]: TryFromBytes::is_bit_valid 524c3739801SMiguel Ojeda #[inline(always)] 525c3739801SMiguel Ojeda pub fn try_transmute<Src, Dst>(src: Src) -> Result<Dst, ValidityError<Src, Dst>> 526c3739801SMiguel Ojeda where 527c3739801SMiguel Ojeda Src: IntoBytes, 528c3739801SMiguel Ojeda Dst: TryFromBytes, 529c3739801SMiguel Ojeda { 530c3739801SMiguel Ojeda static_assert!(Src, Dst => mem::size_of::<Dst>() == mem::size_of::<Src>()); 531c3739801SMiguel Ojeda 532c3739801SMiguel Ojeda let mu_src = mem::MaybeUninit::new(src); 533c3739801SMiguel Ojeda // SAFETY: `MaybeUninit` has no validity requirements. 534c3739801SMiguel Ojeda let mu_dst: mem::MaybeUninit<ReadOnly<Dst>> = 535c3739801SMiguel Ojeda unsafe { crate::util::transmute_unchecked(mu_src) }; 536c3739801SMiguel Ojeda 537c3739801SMiguel Ojeda let ptr = Ptr::from_ref(&mu_dst); 538c3739801SMiguel Ojeda 539c3739801SMiguel Ojeda // SAFETY: Since `Src: IntoBytes`, and since `size_of::<Src>() == 540c3739801SMiguel Ojeda // size_of::<Dst>()` by the preceding assertion, all of `mu_dst`'s bytes are 541c3739801SMiguel Ojeda // initialized. `MaybeUninit` has no validity requirements, so even if 542c3739801SMiguel Ojeda // `ptr` is used to mutate its referent (which it actually can't be - it's 543c3739801SMiguel Ojeda // a shared `ReadOnly` pointer), that won't violate its referent's validity. 544c3739801SMiguel Ojeda let ptr = unsafe { ptr.assume_validity::<Initialized>() }; 545c3739801SMiguel Ojeda if Dst::is_bit_valid(ptr.cast::<_, CastSized, _>()) { 546c3739801SMiguel Ojeda // SAFETY: Since `Dst::is_bit_valid`, we know that `ptr`'s referent is 547c3739801SMiguel Ojeda // bit-valid for `Dst`. `ptr` points to `mu_dst`, and no intervening 548c3739801SMiguel Ojeda // operations have mutated it, so it is a bit-valid `Dst`. 549c3739801SMiguel Ojeda Ok(ReadOnly::into_inner(unsafe { mu_dst.assume_init() })) 550c3739801SMiguel Ojeda } else { 551c3739801SMiguel Ojeda // SAFETY: `MaybeUninit` has no validity requirements. 552c3739801SMiguel Ojeda let mu_src: mem::MaybeUninit<Src> = unsafe { crate::util::transmute_unchecked(mu_dst) }; 553c3739801SMiguel Ojeda // SAFETY: `mu_dst`/`mu_src` was constructed from `src` and never 554c3739801SMiguel Ojeda // modified, so it is still bit-valid. 555c3739801SMiguel Ojeda Err(ValidityError::new(unsafe { mu_src.assume_init() })) 556c3739801SMiguel Ojeda } 557c3739801SMiguel Ojeda } 558c3739801SMiguel Ojeda 559c3739801SMiguel Ojeda /// See `try_transmute_ref!` documentation. 560c3739801SMiguel Ojeda pub trait TryTransmuteRefDst<'a> { 561c3739801SMiguel Ojeda type Dst: ?Sized; 562c3739801SMiguel Ojeda 563c3739801SMiguel Ojeda /// See `try_transmute_ref!` documentation. 564c3739801SMiguel Ojeda fn try_transmute_ref(self) -> Result<&'a Self::Dst, ValidityError<&'a Self::Src, Self::Dst>> 565c3739801SMiguel Ojeda where 566c3739801SMiguel Ojeda Self: TryTransmuteRefSrc<'a>, 567c3739801SMiguel Ojeda Self::Src: IntoBytes + Immutable + KnownLayout, 568c3739801SMiguel Ojeda Self::Dst: TryFromBytes + Immutable + KnownLayout; 569c3739801SMiguel Ojeda } 570c3739801SMiguel Ojeda 571c3739801SMiguel Ojeda pub trait TryTransmuteRefSrc<'a> { 572c3739801SMiguel Ojeda type Src: ?Sized; 573c3739801SMiguel Ojeda } 574c3739801SMiguel Ojeda 575c3739801SMiguel Ojeda impl<'a, Src, Dst> TryTransmuteRefSrc<'a> for Wrap<&'a Src, &'a Dst> 576c3739801SMiguel Ojeda where 577c3739801SMiguel Ojeda Src: ?Sized, 578c3739801SMiguel Ojeda Dst: ?Sized, 579c3739801SMiguel Ojeda { 580c3739801SMiguel Ojeda type Src = Src; 581c3739801SMiguel Ojeda } 582c3739801SMiguel Ojeda 583c3739801SMiguel Ojeda impl<'a, Src, Dst> TryTransmuteRefDst<'a> for Wrap<&'a Src, &'a Dst> 584c3739801SMiguel Ojeda where 585c3739801SMiguel Ojeda Src: IntoBytes + Immutable + KnownLayout + ?Sized, 586c3739801SMiguel Ojeda Dst: TryFromBytes + Immutable + KnownLayout + ?Sized, 587c3739801SMiguel Ojeda { 588c3739801SMiguel Ojeda type Dst = Dst; 589c3739801SMiguel Ojeda 590c3739801SMiguel Ojeda #[inline(always)] 591c3739801SMiguel Ojeda fn try_transmute_ref( 592c3739801SMiguel Ojeda self, 593c3739801SMiguel Ojeda ) -> Result< 594c3739801SMiguel Ojeda &'a Dst, 595c3739801SMiguel Ojeda ValidityError<&'a <Wrap<&'a Src, &'a Dst> as TryTransmuteRefSrc<'a>>::Src, Dst>, 596c3739801SMiguel Ojeda > { 597c3739801SMiguel Ojeda let ptr = Ptr::from_ref(self.0); 598c3739801SMiguel Ojeda #[rustfmt::skip] 599c3739801SMiguel Ojeda let res = ptr.try_with(#[inline(always)] |ptr| { 600c3739801SMiguel Ojeda let ptr = ptr.recall_validity::<Initialized, _>(); 601c3739801SMiguel Ojeda let ptr = ptr.cast::<_, crate::layout::CastFrom<Dst>, _>(); 602c3739801SMiguel Ojeda ptr.try_into_valid() 603c3739801SMiguel Ojeda }); 604c3739801SMiguel Ojeda match res { 605c3739801SMiguel Ojeda Ok(ptr) => { 606c3739801SMiguel Ojeda static_assert!(Src: ?Sized + KnownLayout, Dst: ?Sized + KnownLayout => { 607c3739801SMiguel Ojeda Src::LAYOUT.align.get() >= Dst::LAYOUT.align.get() 608c3739801SMiguel Ojeda }, "cannot transmute reference when destination type has higher alignment than source type"); 609c3739801SMiguel Ojeda // SAFETY: We have checked that `Dst` does not have a stricter 610c3739801SMiguel Ojeda // alignment requirement than `Src`. 611c3739801SMiguel Ojeda let ptr = unsafe { ptr.assume_alignment::<Aligned>() }; 612c3739801SMiguel Ojeda Ok(ptr.as_ref()) 613c3739801SMiguel Ojeda } 614c3739801SMiguel Ojeda Err(err) => Err(err.map_src(Ptr::as_ref)), 615c3739801SMiguel Ojeda } 616c3739801SMiguel Ojeda } 617c3739801SMiguel Ojeda } 618c3739801SMiguel Ojeda 619c3739801SMiguel Ojeda pub trait TryTransmuteMutDst<'a> { 620c3739801SMiguel Ojeda type Dst: ?Sized; 621c3739801SMiguel Ojeda 622c3739801SMiguel Ojeda /// See `try_transmute_mut!` documentation. 623c3739801SMiguel Ojeda fn try_transmute_mut( 624c3739801SMiguel Ojeda self, 625c3739801SMiguel Ojeda ) -> Result<&'a mut Self::Dst, ValidityError<&'a mut Self::Src, Self::Dst>> 626c3739801SMiguel Ojeda where 627c3739801SMiguel Ojeda Self: TryTransmuteMutSrc<'a>, 628c3739801SMiguel Ojeda Self::Src: IntoBytes, 629c3739801SMiguel Ojeda Self::Dst: TryFromBytes; 630c3739801SMiguel Ojeda } 631c3739801SMiguel Ojeda 632c3739801SMiguel Ojeda pub trait TryTransmuteMutSrc<'a> { 633c3739801SMiguel Ojeda type Src: ?Sized; 634c3739801SMiguel Ojeda } 635c3739801SMiguel Ojeda 636c3739801SMiguel Ojeda impl<'a, Src, Dst> TryTransmuteMutSrc<'a> for Wrap<&'a mut Src, &'a mut Dst> 637c3739801SMiguel Ojeda where 638c3739801SMiguel Ojeda Src: ?Sized, 639c3739801SMiguel Ojeda Dst: ?Sized, 640c3739801SMiguel Ojeda { 641c3739801SMiguel Ojeda type Src = Src; 642c3739801SMiguel Ojeda } 643c3739801SMiguel Ojeda 644c3739801SMiguel Ojeda impl<'a, Src, Dst> TryTransmuteMutDst<'a> for Wrap<&'a mut Src, &'a mut Dst> 645c3739801SMiguel Ojeda where 646c3739801SMiguel Ojeda Src: FromBytes + IntoBytes + KnownLayout + ?Sized, 647c3739801SMiguel Ojeda Dst: TryFromBytes + IntoBytes + KnownLayout + ?Sized, 648c3739801SMiguel Ojeda { 649c3739801SMiguel Ojeda type Dst = Dst; 650c3739801SMiguel Ojeda 651c3739801SMiguel Ojeda #[inline(always)] 652c3739801SMiguel Ojeda fn try_transmute_mut( 653c3739801SMiguel Ojeda self, 654c3739801SMiguel Ojeda ) -> Result< 655c3739801SMiguel Ojeda &'a mut Dst, 656c3739801SMiguel Ojeda ValidityError<&'a mut <Wrap<&'a mut Src, &'a mut Dst> as TryTransmuteMutSrc<'a>>::Src, Dst>, 657c3739801SMiguel Ojeda > { 658c3739801SMiguel Ojeda let ptr = Ptr::from_mut(self.0); 659c3739801SMiguel Ojeda // SAFETY: The provided closure returns the only copy of `ptr`. 660c3739801SMiguel Ojeda #[rustfmt::skip] 661c3739801SMiguel Ojeda let res = unsafe { 662c3739801SMiguel Ojeda ptr.try_with_unchecked(#[inline(always)] |ptr| { 663c3739801SMiguel Ojeda let ptr = ptr.recall_validity::<Initialized, (_, (_, _))>(); 664c3739801SMiguel Ojeda let ptr = ptr.cast::<_, crate::layout::CastFrom<Dst>, _>(); 665c3739801SMiguel Ojeda ptr.try_into_valid() 666c3739801SMiguel Ojeda }) 667c3739801SMiguel Ojeda }; 668c3739801SMiguel Ojeda match res { 669c3739801SMiguel Ojeda Ok(ptr) => { 670c3739801SMiguel Ojeda static_assert!(Src: ?Sized + KnownLayout, Dst: ?Sized + KnownLayout => { 671c3739801SMiguel Ojeda Src::LAYOUT.align.get() >= Dst::LAYOUT.align.get() 672c3739801SMiguel Ojeda }, "cannot transmute reference when destination type has higher alignment than source type"); 673c3739801SMiguel Ojeda // SAFETY: We have checked that `Dst` does not have a stricter 674c3739801SMiguel Ojeda // alignment requirement than `Src`. 675c3739801SMiguel Ojeda let ptr = unsafe { ptr.assume_alignment::<Aligned>() }; 676c3739801SMiguel Ojeda Ok(ptr.as_mut()) 677c3739801SMiguel Ojeda } 678c3739801SMiguel Ojeda Err(err) => Err(err.map_src(Ptr::as_mut)), 679c3739801SMiguel Ojeda } 680c3739801SMiguel Ojeda } 681c3739801SMiguel Ojeda } 682c3739801SMiguel Ojeda 683c3739801SMiguel Ojeda // Used in `transmute_ref!` and friends. 684c3739801SMiguel Ojeda // 685c3739801SMiguel Ojeda // This permits us to use the autoref specialization trick to dispatch to 686c3739801SMiguel Ojeda // associated functions for `transmute_ref` and `transmute_mut` when both `Src` 687c3739801SMiguel Ojeda // and `Dst` are `Sized`, and to trait methods otherwise. The associated 688c3739801SMiguel Ojeda // functions, unlike the trait methods, do not require a `KnownLayout` bound. 689c3739801SMiguel Ojeda // This permits us to add support for transmuting references to unsized types 690c3739801SMiguel Ojeda // without breaking backwards-compatibility (on v0.8.x) with the old 691c3739801SMiguel Ojeda // implementation, which did not require a `KnownLayout` bound to transmute 692c3739801SMiguel Ojeda // sized types. 693c3739801SMiguel Ojeda #[derive(Copy, Clone)] 694c3739801SMiguel Ojeda pub struct Wrap<Src, Dst>(pub Src, pub PhantomData<Dst>); 695c3739801SMiguel Ojeda 696c3739801SMiguel Ojeda impl<Src, Dst> Wrap<Src, Dst> { 697c3739801SMiguel Ojeda #[inline(always)] 698c3739801SMiguel Ojeda pub const fn new(src: Src) -> Self { 699c3739801SMiguel Ojeda Wrap(src, PhantomData) 700c3739801SMiguel Ojeda } 701c3739801SMiguel Ojeda } 702c3739801SMiguel Ojeda 703c3739801SMiguel Ojeda impl<'a, Src, Dst> Wrap<&'a Src, &'a Dst> 704c3739801SMiguel Ojeda where 705c3739801SMiguel Ojeda Src: ?Sized, 706c3739801SMiguel Ojeda Dst: ?Sized, 707c3739801SMiguel Ojeda { 708c3739801SMiguel Ojeda #[allow(clippy::must_use_candidate, clippy::missing_inline_in_public_items, clippy::empty_loop)] 709c3739801SMiguel Ojeda pub const fn transmute_ref_inference_helper(self) -> &'a Dst { 710c3739801SMiguel Ojeda loop {} 711c3739801SMiguel Ojeda } 712c3739801SMiguel Ojeda } 713c3739801SMiguel Ojeda 714c3739801SMiguel Ojeda impl<'a, Src, Dst> Wrap<&'a Src, &'a Dst> { 715c3739801SMiguel Ojeda /// # Safety 716c3739801SMiguel Ojeda /// The caller must guarantee that: 717c3739801SMiguel Ojeda /// - `Src: IntoBytes + Immutable` 718c3739801SMiguel Ojeda /// - `Dst: FromBytes + Immutable` 719c3739801SMiguel Ojeda /// 720c3739801SMiguel Ojeda /// # PME 721c3739801SMiguel Ojeda /// 722c3739801SMiguel Ojeda /// Instantiating this method PMEs unless both: 723c3739801SMiguel Ojeda /// - `mem::size_of::<Dst>() == mem::size_of::<Src>()` 724c3739801SMiguel Ojeda /// - `mem::align_of::<Dst>() <= mem::align_of::<Src>()` 725c3739801SMiguel Ojeda #[inline(always)] 726c3739801SMiguel Ojeda #[must_use] 727c3739801SMiguel Ojeda pub const unsafe fn transmute_ref(self) -> &'a Dst { 728c3739801SMiguel Ojeda static_assert!(Src, Dst => mem::size_of::<Dst>() == mem::size_of::<Src>()); 729c3739801SMiguel Ojeda static_assert!(Src, Dst => mem::align_of::<Dst>() <= mem::align_of::<Src>()); 730c3739801SMiguel Ojeda 731c3739801SMiguel Ojeda let src: *const Src = self.0; 732c3739801SMiguel Ojeda let dst = src.cast::<Dst>(); 733c3739801SMiguel Ojeda // SAFETY: 734c3739801SMiguel Ojeda // - We know that it is sound to view the target type of the input 735c3739801SMiguel Ojeda // reference (`Src`) as the target type of the output reference 736c3739801SMiguel Ojeda // (`Dst`) because the caller has guaranteed that `Src: IntoBytes`, 737c3739801SMiguel Ojeda // `Dst: FromBytes`, and `size_of::<Src>() == size_of::<Dst>()`. 738c3739801SMiguel Ojeda // - We know that there are no `UnsafeCell`s, and thus we don't have to 739c3739801SMiguel Ojeda // worry about `UnsafeCell` overlap, because `Src: Immutable` and 740c3739801SMiguel Ojeda // `Dst: Immutable`. 741c3739801SMiguel Ojeda // - The caller has guaranteed that alignment is not increased. 742c3739801SMiguel Ojeda // - We know that the returned lifetime will not outlive the input 743c3739801SMiguel Ojeda // lifetime thanks to the lifetime bounds on this function. 744c3739801SMiguel Ojeda // 745c3739801SMiguel Ojeda // FIXME(#67): Once our MSRV is 1.58, replace this `transmute` with 746c3739801SMiguel Ojeda // `&*dst`. 747c3739801SMiguel Ojeda #[allow(clippy::transmute_ptr_to_ref)] 748c3739801SMiguel Ojeda unsafe { 749c3739801SMiguel Ojeda mem::transmute(dst) 750c3739801SMiguel Ojeda } 751c3739801SMiguel Ojeda } 752c3739801SMiguel Ojeda 753c3739801SMiguel Ojeda #[inline(always)] 754c3739801SMiguel Ojeda pub fn try_transmute_ref(self) -> Result<&'a Dst, ValidityError<&'a Src, Dst>> 755c3739801SMiguel Ojeda where 756c3739801SMiguel Ojeda Src: IntoBytes + Immutable, 757c3739801SMiguel Ojeda Dst: TryFromBytes + Immutable, 758c3739801SMiguel Ojeda { 759c3739801SMiguel Ojeda static_assert!(Src => mem::align_of::<Src>() == mem::align_of::<Wrapping<Src>>()); 760c3739801SMiguel Ojeda static_assert!(Dst => mem::align_of::<Dst>() == mem::align_of::<Wrapping<Dst>>()); 761c3739801SMiguel Ojeda 762c3739801SMiguel Ojeda // SAFETY: By the preceding assert, `Src` and `Wrapping<Src>` have the 763c3739801SMiguel Ojeda // same alignment. 764c3739801SMiguel Ojeda let src: &Wrapping<Src> = 765c3739801SMiguel Ojeda unsafe { crate::util::transmute_ref::<_, _, BecauseImmutable>(self.0) }; 766c3739801SMiguel Ojeda let src = Wrap::new(src); 767c3739801SMiguel Ojeda <Wrap<&'a Wrapping<Src>, &'a Wrapping<Dst>> as TryTransmuteRefDst<'a>>::try_transmute_ref( 768c3739801SMiguel Ojeda src, 769c3739801SMiguel Ojeda ) 770c3739801SMiguel Ojeda .map( 771c3739801SMiguel Ojeda // SAFETY: By the preceding assert, `Dst` and `Wrapping<Dst>` have 772c3739801SMiguel Ojeda // the same alignment. 773c3739801SMiguel Ojeda #[inline(always)] 774c3739801SMiguel Ojeda |dst| unsafe { crate::util::transmute_ref::<_, _, BecauseImmutable>(dst) }, 775c3739801SMiguel Ojeda ) 776c3739801SMiguel Ojeda .map_err( 777c3739801SMiguel Ojeda #[inline(always)] 778c3739801SMiguel Ojeda |err| { 779c3739801SMiguel Ojeda // SAFETY: By the preceding assert, `Src` and `Wrapping<Src>` have the 780c3739801SMiguel Ojeda // same alignment. 781c3739801SMiguel Ojeda ValidityError::new(unsafe { 782c3739801SMiguel Ojeda crate::util::transmute_ref::<_, _, BecauseImmutable>(err.into_src()) 783c3739801SMiguel Ojeda }) 784c3739801SMiguel Ojeda }, 785c3739801SMiguel Ojeda ) 786c3739801SMiguel Ojeda } 787c3739801SMiguel Ojeda } 788c3739801SMiguel Ojeda 789c3739801SMiguel Ojeda impl<'a, Src, Dst> Wrap<&'a mut Src, &'a mut Dst> 790c3739801SMiguel Ojeda where 791c3739801SMiguel Ojeda Src: ?Sized, 792c3739801SMiguel Ojeda Dst: ?Sized, 793c3739801SMiguel Ojeda { 794c3739801SMiguel Ojeda #[allow(clippy::must_use_candidate, clippy::missing_inline_in_public_items, clippy::empty_loop)] 795c3739801SMiguel Ojeda pub fn transmute_mut_inference_helper(self) -> &'a mut Dst { 796c3739801SMiguel Ojeda loop {} 797c3739801SMiguel Ojeda } 798c3739801SMiguel Ojeda } 799c3739801SMiguel Ojeda 800c3739801SMiguel Ojeda impl<'a, Src, Dst> Wrap<&'a mut Src, &'a mut Dst> { 801c3739801SMiguel Ojeda /// Transmutes a mutable reference of one type to a mutable reference of 802c3739801SMiguel Ojeda /// another type. 803c3739801SMiguel Ojeda /// 804c3739801SMiguel Ojeda /// # PME 805c3739801SMiguel Ojeda /// 806c3739801SMiguel Ojeda /// Instantiating this method PMEs unless both: 807c3739801SMiguel Ojeda /// - `mem::size_of::<Dst>() == mem::size_of::<Src>()` 808c3739801SMiguel Ojeda /// - `mem::align_of::<Dst>() <= mem::align_of::<Src>()` 809c3739801SMiguel Ojeda #[inline(always)] 810c3739801SMiguel Ojeda #[must_use] 811c3739801SMiguel Ojeda pub fn transmute_mut(self) -> &'a mut Dst 812c3739801SMiguel Ojeda where 813c3739801SMiguel Ojeda Src: FromBytes + IntoBytes, 814c3739801SMiguel Ojeda Dst: FromBytes + IntoBytes, 815c3739801SMiguel Ojeda { 816c3739801SMiguel Ojeda static_assert!(Src, Dst => mem::size_of::<Dst>() == mem::size_of::<Src>()); 817c3739801SMiguel Ojeda static_assert!(Src, Dst => mem::align_of::<Dst>() <= mem::align_of::<Src>()); 818c3739801SMiguel Ojeda 819c3739801SMiguel Ojeda let src: *mut Src = self.0; 820c3739801SMiguel Ojeda let dst = src.cast::<Dst>(); 821c3739801SMiguel Ojeda // SAFETY: 822c3739801SMiguel Ojeda // - We know that it is sound to view the target type of the input 823c3739801SMiguel Ojeda // reference (`Src`) as the target type of the output reference 824c3739801SMiguel Ojeda // (`Dst`) and vice-versa because `Src: FromBytes + IntoBytes`, `Dst: 825c3739801SMiguel Ojeda // FromBytes + IntoBytes`, and (as asserted above) `size_of::<Src>() 826c3739801SMiguel Ojeda // == size_of::<Dst>()`. 827c3739801SMiguel Ojeda // - We asserted above that alignment will not increase. 828c3739801SMiguel Ojeda // - We know that the returned lifetime will not outlive the input 829c3739801SMiguel Ojeda // lifetime thanks to the lifetime bounds on this function. 830c3739801SMiguel Ojeda unsafe { &mut *dst } 831c3739801SMiguel Ojeda } 832c3739801SMiguel Ojeda 833c3739801SMiguel Ojeda #[inline(always)] 834c3739801SMiguel Ojeda pub fn try_transmute_mut(self) -> Result<&'a mut Dst, ValidityError<&'a mut Src, Dst>> 835c3739801SMiguel Ojeda where 836c3739801SMiguel Ojeda Src: FromBytes + IntoBytes, 837c3739801SMiguel Ojeda Dst: TryFromBytes + IntoBytes, 838c3739801SMiguel Ojeda { 839c3739801SMiguel Ojeda static_assert!(Src => mem::align_of::<Src>() == mem::align_of::<Wrapping<Src>>()); 840c3739801SMiguel Ojeda static_assert!(Dst => mem::align_of::<Dst>() == mem::align_of::<Wrapping<Dst>>()); 841c3739801SMiguel Ojeda 842c3739801SMiguel Ojeda // SAFETY: By the preceding assert, `Src` and `Wrapping<Src>` have the 843c3739801SMiguel Ojeda // same alignment. 844c3739801SMiguel Ojeda let src: &mut Wrapping<Src> = 845c3739801SMiguel Ojeda unsafe { crate::util::transmute_mut::<_, _, (_, (_, _))>(self.0) }; 846c3739801SMiguel Ojeda let src = Wrap::new(src); 847c3739801SMiguel Ojeda <Wrap<&'a mut Wrapping<Src>, &'a mut Wrapping<Dst>> as TryTransmuteMutDst<'a>> 848c3739801SMiguel Ojeda ::try_transmute_mut(src) 849c3739801SMiguel Ojeda // SAFETY: By the preceding assert, `Dst` and `Wrapping<Dst>` have the 850c3739801SMiguel Ojeda // same alignment. 851c3739801SMiguel Ojeda .map(|dst| unsafe { crate::util::transmute_mut::<_, _, (_, (_, _))>(dst) }) 852c3739801SMiguel Ojeda .map_err(|err| { 853c3739801SMiguel Ojeda // SAFETY: By the preceding assert, `Src` and `Wrapping<Src>` have the 854c3739801SMiguel Ojeda // same alignment. 855c3739801SMiguel Ojeda ValidityError::new(unsafe { 856c3739801SMiguel Ojeda crate::util::transmute_mut::<_, _, (_, (_, _))>(err.into_src()) 857c3739801SMiguel Ojeda }) 858c3739801SMiguel Ojeda }) 859c3739801SMiguel Ojeda } 860c3739801SMiguel Ojeda } 861c3739801SMiguel Ojeda 862c3739801SMiguel Ojeda pub trait TransmuteRefDst<'a> { 863c3739801SMiguel Ojeda type Dst: ?Sized; 864c3739801SMiguel Ojeda 865c3739801SMiguel Ojeda #[must_use] 866c3739801SMiguel Ojeda fn transmute_ref(self) -> &'a Self::Dst; 867c3739801SMiguel Ojeda } 868c3739801SMiguel Ojeda 869c3739801SMiguel Ojeda impl<'a, Src: ?Sized, Dst: ?Sized> TransmuteRefDst<'a> for Wrap<&'a Src, &'a Dst> 870c3739801SMiguel Ojeda where 871c3739801SMiguel Ojeda Src: KnownLayout + IntoBytes + Immutable, 872c3739801SMiguel Ojeda Dst: KnownLayout<PointerMetadata = usize> + FromBytes + Immutable, 873c3739801SMiguel Ojeda { 874c3739801SMiguel Ojeda type Dst = Dst; 875c3739801SMiguel Ojeda 876c3739801SMiguel Ojeda #[inline(always)] 877c3739801SMiguel Ojeda fn transmute_ref(self) -> &'a Dst { 878c3739801SMiguel Ojeda let ptr = Ptr::from_ref(self.0) 879c3739801SMiguel Ojeda .recall_validity::<Initialized, _>() 880c3739801SMiguel Ojeda .transmute_with::<Dst, Initialized, crate::layout::CastFrom<Dst>, (crate::pointer::BecauseMutationCompatible, _)>() 881c3739801SMiguel Ojeda .recall_validity::<Valid, _>(); 882c3739801SMiguel Ojeda 883c3739801SMiguel Ojeda static_assert!(Src: ?Sized + KnownLayout, Dst: ?Sized + KnownLayout => { 884c3739801SMiguel Ojeda Src::LAYOUT.align.get() >= Dst::LAYOUT.align.get() 885c3739801SMiguel Ojeda }, "cannot transmute reference when destination type has higher alignment than source type"); 886c3739801SMiguel Ojeda 887c3739801SMiguel Ojeda // SAFETY: The preceding `static_assert!` ensures that 888c3739801SMiguel Ojeda // `Src::LAYOUT.align >= Dst::LAYOUT.align`. Since `self` is 889c3739801SMiguel Ojeda // validly-aligned for `Src`, it is also validly-aligned for `Dst`. 890c3739801SMiguel Ojeda let ptr = unsafe { ptr.assume_alignment() }; 891c3739801SMiguel Ojeda 892c3739801SMiguel Ojeda ptr.as_ref() 893c3739801SMiguel Ojeda } 894c3739801SMiguel Ojeda } 895c3739801SMiguel Ojeda 896c3739801SMiguel Ojeda pub trait TransmuteMutDst<'a> { 897c3739801SMiguel Ojeda type Dst: ?Sized; 898c3739801SMiguel Ojeda #[must_use] 899c3739801SMiguel Ojeda fn transmute_mut(self) -> &'a mut Self::Dst; 900c3739801SMiguel Ojeda } 901c3739801SMiguel Ojeda 902c3739801SMiguel Ojeda impl<'a, Src: ?Sized, Dst: ?Sized> TransmuteMutDst<'a> for Wrap<&'a mut Src, &'a mut Dst> 903c3739801SMiguel Ojeda where 904c3739801SMiguel Ojeda Src: KnownLayout + FromBytes + IntoBytes, 905c3739801SMiguel Ojeda Dst: KnownLayout<PointerMetadata = usize> + FromBytes + IntoBytes, 906c3739801SMiguel Ojeda { 907c3739801SMiguel Ojeda type Dst = Dst; 908c3739801SMiguel Ojeda 909c3739801SMiguel Ojeda #[inline(always)] 910c3739801SMiguel Ojeda fn transmute_mut(self) -> &'a mut Dst { 911c3739801SMiguel Ojeda let ptr = Ptr::from_mut(self.0) 912c3739801SMiguel Ojeda .recall_validity::<Initialized, (_, (_, _))>() 913c3739801SMiguel Ojeda .transmute_with::<Dst, Initialized, crate::layout::CastFrom<Dst>, _>() 914c3739801SMiguel Ojeda .recall_validity::<Valid, (_, (_, _))>(); 915c3739801SMiguel Ojeda 916c3739801SMiguel Ojeda static_assert!(Src: ?Sized + KnownLayout, Dst: ?Sized + KnownLayout => { 917c3739801SMiguel Ojeda Src::LAYOUT.align.get() >= Dst::LAYOUT.align.get() 918c3739801SMiguel Ojeda }, "cannot transmute reference when destination type has higher alignment than source type"); 919c3739801SMiguel Ojeda 920c3739801SMiguel Ojeda // SAFETY: The preceding `static_assert!` ensures that 921c3739801SMiguel Ojeda // `Src::LAYOUT.align >= Dst::LAYOUT.align`. Since `self` is 922c3739801SMiguel Ojeda // validly-aligned for `Src`, it is also validly-aligned for `Dst`. 923c3739801SMiguel Ojeda let ptr = unsafe { ptr.assume_alignment() }; 924c3739801SMiguel Ojeda 925c3739801SMiguel Ojeda ptr.as_mut() 926c3739801SMiguel Ojeda } 927c3739801SMiguel Ojeda } 928c3739801SMiguel Ojeda 929c3739801SMiguel Ojeda /// A function which emits a warning if its return value is not used. 930c3739801SMiguel Ojeda #[must_use] 931c3739801SMiguel Ojeda #[inline(always)] 932c3739801SMiguel Ojeda pub const fn must_use<T>(t: T) -> T { 933c3739801SMiguel Ojeda t 934c3739801SMiguel Ojeda } 935c3739801SMiguel Ojeda 936c3739801SMiguel Ojeda // NOTE: We can't change this to a `pub use core as core_reexport` until [1] is 937c3739801SMiguel Ojeda // fixed or we update to a semver-breaking version (as of this writing, 0.8.0) 938c3739801SMiguel Ojeda // on the `main` branch. 939c3739801SMiguel Ojeda // 940c3739801SMiguel Ojeda // [1] https://github.com/obi1kenobi/cargo-semver-checks/issues/573 941c3739801SMiguel Ojeda pub mod core_reexport { 942c3739801SMiguel Ojeda pub use core::*; 943c3739801SMiguel Ojeda 944c3739801SMiguel Ojeda pub mod mem { 945c3739801SMiguel Ojeda pub use core::mem::*; 946c3739801SMiguel Ojeda } 947c3739801SMiguel Ojeda } 948c3739801SMiguel Ojeda 949c3739801SMiguel Ojeda #[cfg(test)] 950c3739801SMiguel Ojeda mod tests { 951c3739801SMiguel Ojeda use core::num::NonZeroUsize; 952c3739801SMiguel Ojeda 953c3739801SMiguel Ojeda use crate::util::testutil::*; 954c3739801SMiguel Ojeda 955c3739801SMiguel Ojeda #[cfg(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] 956c3739801SMiguel Ojeda mod nightly { 957c3739801SMiguel Ojeda use super::super::*; 958c3739801SMiguel Ojeda use crate::util::testutil::*; 959c3739801SMiguel Ojeda 960c3739801SMiguel Ojeda // FIXME(#29), FIXME(https://github.com/rust-lang/rust/issues/69835): 961c3739801SMiguel Ojeda // Remove this `cfg` when `size_of_val_raw` is stabilized. 962c3739801SMiguel Ojeda #[allow(clippy::decimal_literal_representation)] 963c3739801SMiguel Ojeda #[test] 964c3739801SMiguel Ojeda fn test_trailing_field_offset() { 965c3739801SMiguel Ojeda assert_eq!(mem::align_of::<Aligned64kAllocation>(), _64K); 966c3739801SMiguel Ojeda 967c3739801SMiguel Ojeda macro_rules! test { 968c3739801SMiguel Ojeda (#[$cfg:meta] ($($ts:ty),* ; $trailing_field_ty:ty) => $expect:expr) => {{ 969c3739801SMiguel Ojeda #[$cfg] 970c3739801SMiguel Ojeda struct Test($(#[allow(dead_code)] $ts,)* #[allow(dead_code)] $trailing_field_ty); 971c3739801SMiguel Ojeda assert_eq!(test!(@offset $($ts),* ; $trailing_field_ty), $expect); 972c3739801SMiguel Ojeda }}; 973c3739801SMiguel Ojeda (#[$cfg:meta] $(#[$cfgs:meta])* ($($ts:ty),* ; $trailing_field_ty:ty) => $expect:expr) => { 974c3739801SMiguel Ojeda test!(#[$cfg] ($($ts),* ; $trailing_field_ty) => $expect); 975c3739801SMiguel Ojeda test!($(#[$cfgs])* ($($ts),* ; $trailing_field_ty) => $expect); 976c3739801SMiguel Ojeda }; 977c3739801SMiguel Ojeda (@offset ; $_trailing:ty) => { trailing_field_offset!(Test, 0) }; 978c3739801SMiguel Ojeda (@offset $_t:ty ; $_trailing:ty) => { trailing_field_offset!(Test, 1) }; 979c3739801SMiguel Ojeda } 980c3739801SMiguel Ojeda 981c3739801SMiguel Ojeda test!(#[repr(C)] #[repr(transparent)] #[repr(packed)](; u8) => Some(0)); 982c3739801SMiguel Ojeda test!(#[repr(C)] #[repr(transparent)] #[repr(packed)](; [u8]) => Some(0)); 983c3739801SMiguel Ojeda test!(#[repr(C)] #[repr(C, packed)] (u8; u8) => Some(1)); 984c3739801SMiguel Ojeda test!(#[repr(C)] (; AU64) => Some(0)); 985c3739801SMiguel Ojeda test!(#[repr(C)] (; [AU64]) => Some(0)); 986c3739801SMiguel Ojeda test!(#[repr(C)] (u8; AU64) => Some(8)); 987c3739801SMiguel Ojeda test!(#[repr(C)] (u8; [AU64]) => Some(8)); 988c3739801SMiguel Ojeda 989c3739801SMiguel Ojeda #[derive( 990c3739801SMiguel Ojeda Immutable, FromBytes, Eq, PartialEq, Ord, PartialOrd, Default, Debug, Copy, Clone, 991c3739801SMiguel Ojeda )] 992c3739801SMiguel Ojeda #[repr(C)] 993c3739801SMiguel Ojeda pub(crate) struct Nested<T, U: ?Sized> { 994c3739801SMiguel Ojeda _t: T, 995c3739801SMiguel Ojeda _u: U, 996c3739801SMiguel Ojeda } 997c3739801SMiguel Ojeda 998c3739801SMiguel Ojeda test!(#[repr(C)] (; Nested<u8, AU64>) => Some(0)); 999c3739801SMiguel Ojeda test!(#[repr(C)] (; Nested<u8, [AU64]>) => Some(0)); 1000c3739801SMiguel Ojeda test!(#[repr(C)] (u8; Nested<u8, AU64>) => Some(8)); 1001c3739801SMiguel Ojeda test!(#[repr(C)] (u8; Nested<u8, [AU64]>) => Some(8)); 1002c3739801SMiguel Ojeda 1003c3739801SMiguel Ojeda // Test that `packed(N)` limits the offset of the trailing field. 1004c3739801SMiguel Ojeda test!(#[repr(C, packed( 1))] (u8; elain::Align< 2>) => Some( 1)); 1005c3739801SMiguel Ojeda test!(#[repr(C, packed( 2))] (u8; elain::Align< 4>) => Some( 2)); 1006c3739801SMiguel Ojeda test!(#[repr(C, packed( 4))] (u8; elain::Align< 8>) => Some( 4)); 1007c3739801SMiguel Ojeda test!(#[repr(C, packed( 8))] (u8; elain::Align< 16>) => Some( 8)); 1008c3739801SMiguel Ojeda test!(#[repr(C, packed( 16))] (u8; elain::Align< 32>) => Some( 16)); 1009c3739801SMiguel Ojeda test!(#[repr(C, packed( 32))] (u8; elain::Align< 64>) => Some( 32)); 1010c3739801SMiguel Ojeda test!(#[repr(C, packed( 64))] (u8; elain::Align< 128>) => Some( 64)); 1011c3739801SMiguel Ojeda test!(#[repr(C, packed( 128))] (u8; elain::Align< 256>) => Some( 128)); 1012c3739801SMiguel Ojeda test!(#[repr(C, packed( 256))] (u8; elain::Align< 512>) => Some( 256)); 1013c3739801SMiguel Ojeda test!(#[repr(C, packed( 512))] (u8; elain::Align< 1024>) => Some( 512)); 1014c3739801SMiguel Ojeda test!(#[repr(C, packed( 1024))] (u8; elain::Align< 2048>) => Some( 1024)); 1015c3739801SMiguel Ojeda test!(#[repr(C, packed( 2048))] (u8; elain::Align< 4096>) => Some( 2048)); 1016c3739801SMiguel Ojeda test!(#[repr(C, packed( 4096))] (u8; elain::Align< 8192>) => Some( 4096)); 1017c3739801SMiguel Ojeda test!(#[repr(C, packed( 8192))] (u8; elain::Align< 16384>) => Some( 8192)); 1018c3739801SMiguel Ojeda test!(#[repr(C, packed( 16384))] (u8; elain::Align< 32768>) => Some( 16384)); 1019c3739801SMiguel Ojeda test!(#[repr(C, packed( 32768))] (u8; elain::Align< 65536>) => Some( 32768)); 1020c3739801SMiguel Ojeda test!(#[repr(C, packed( 65536))] (u8; elain::Align< 131072>) => Some( 65536)); 1021c3739801SMiguel Ojeda /* Alignments above 65536 are not yet supported. 1022c3739801SMiguel Ojeda test!(#[repr(C, packed( 131072))] (u8; elain::Align< 262144>) => Some( 131072)); 1023c3739801SMiguel Ojeda test!(#[repr(C, packed( 262144))] (u8; elain::Align< 524288>) => Some( 262144)); 1024c3739801SMiguel Ojeda test!(#[repr(C, packed( 524288))] (u8; elain::Align< 1048576>) => Some( 524288)); 1025c3739801SMiguel Ojeda test!(#[repr(C, packed( 1048576))] (u8; elain::Align< 2097152>) => Some( 1048576)); 1026c3739801SMiguel Ojeda test!(#[repr(C, packed( 2097152))] (u8; elain::Align< 4194304>) => Some( 2097152)); 1027c3739801SMiguel Ojeda test!(#[repr(C, packed( 4194304))] (u8; elain::Align< 8388608>) => Some( 4194304)); 1028c3739801SMiguel Ojeda test!(#[repr(C, packed( 8388608))] (u8; elain::Align< 16777216>) => Some( 8388608)); 1029c3739801SMiguel Ojeda test!(#[repr(C, packed( 16777216))] (u8; elain::Align< 33554432>) => Some( 16777216)); 1030c3739801SMiguel Ojeda test!(#[repr(C, packed( 33554432))] (u8; elain::Align< 67108864>) => Some( 33554432)); 1031c3739801SMiguel Ojeda test!(#[repr(C, packed( 67108864))] (u8; elain::Align< 33554432>) => Some( 67108864)); 1032c3739801SMiguel Ojeda test!(#[repr(C, packed( 33554432))] (u8; elain::Align<134217728>) => Some( 33554432)); 1033c3739801SMiguel Ojeda test!(#[repr(C, packed(134217728))] (u8; elain::Align<268435456>) => Some(134217728)); 1034c3739801SMiguel Ojeda test!(#[repr(C, packed(268435456))] (u8; elain::Align<268435456>) => Some(268435456)); 1035c3739801SMiguel Ojeda */ 1036c3739801SMiguel Ojeda 1037c3739801SMiguel Ojeda // Test that `align(N)` does not limit the offset of the trailing field. 1038c3739801SMiguel Ojeda test!(#[repr(C, align( 1))] (u8; elain::Align< 2>) => Some( 2)); 1039c3739801SMiguel Ojeda test!(#[repr(C, align( 2))] (u8; elain::Align< 4>) => Some( 4)); 1040c3739801SMiguel Ojeda test!(#[repr(C, align( 4))] (u8; elain::Align< 8>) => Some( 8)); 1041c3739801SMiguel Ojeda test!(#[repr(C, align( 8))] (u8; elain::Align< 16>) => Some( 16)); 1042c3739801SMiguel Ojeda test!(#[repr(C, align( 16))] (u8; elain::Align< 32>) => Some( 32)); 1043c3739801SMiguel Ojeda test!(#[repr(C, align( 32))] (u8; elain::Align< 64>) => Some( 64)); 1044c3739801SMiguel Ojeda test!(#[repr(C, align( 64))] (u8; elain::Align< 128>) => Some( 128)); 1045c3739801SMiguel Ojeda test!(#[repr(C, align( 128))] (u8; elain::Align< 256>) => Some( 256)); 1046c3739801SMiguel Ojeda test!(#[repr(C, align( 256))] (u8; elain::Align< 512>) => Some( 512)); 1047c3739801SMiguel Ojeda test!(#[repr(C, align( 512))] (u8; elain::Align< 1024>) => Some( 1024)); 1048c3739801SMiguel Ojeda test!(#[repr(C, align( 1024))] (u8; elain::Align< 2048>) => Some( 2048)); 1049c3739801SMiguel Ojeda test!(#[repr(C, align( 2048))] (u8; elain::Align< 4096>) => Some( 4096)); 1050c3739801SMiguel Ojeda test!(#[repr(C, align( 4096))] (u8; elain::Align< 8192>) => Some( 8192)); 1051c3739801SMiguel Ojeda test!(#[repr(C, align( 8192))] (u8; elain::Align< 16384>) => Some( 16384)); 1052c3739801SMiguel Ojeda test!(#[repr(C, align( 16384))] (u8; elain::Align< 32768>) => Some( 32768)); 1053c3739801SMiguel Ojeda test!(#[repr(C, align( 32768))] (u8; elain::Align< 65536>) => Some( 65536)); 1054c3739801SMiguel Ojeda /* Alignments above 65536 are not yet supported. 1055c3739801SMiguel Ojeda test!(#[repr(C, align( 65536))] (u8; elain::Align< 131072>) => Some( 131072)); 1056c3739801SMiguel Ojeda test!(#[repr(C, align( 131072))] (u8; elain::Align< 262144>) => Some( 262144)); 1057c3739801SMiguel Ojeda test!(#[repr(C, align( 262144))] (u8; elain::Align< 524288>) => Some( 524288)); 1058c3739801SMiguel Ojeda test!(#[repr(C, align( 524288))] (u8; elain::Align< 1048576>) => Some( 1048576)); 1059c3739801SMiguel Ojeda test!(#[repr(C, align( 1048576))] (u8; elain::Align< 2097152>) => Some( 2097152)); 1060c3739801SMiguel Ojeda test!(#[repr(C, align( 2097152))] (u8; elain::Align< 4194304>) => Some( 4194304)); 1061c3739801SMiguel Ojeda test!(#[repr(C, align( 4194304))] (u8; elain::Align< 8388608>) => Some( 8388608)); 1062c3739801SMiguel Ojeda test!(#[repr(C, align( 8388608))] (u8; elain::Align< 16777216>) => Some( 16777216)); 1063c3739801SMiguel Ojeda test!(#[repr(C, align( 16777216))] (u8; elain::Align< 33554432>) => Some( 33554432)); 1064c3739801SMiguel Ojeda test!(#[repr(C, align( 33554432))] (u8; elain::Align< 67108864>) => Some( 67108864)); 1065c3739801SMiguel Ojeda test!(#[repr(C, align( 67108864))] (u8; elain::Align< 33554432>) => Some( 33554432)); 1066c3739801SMiguel Ojeda test!(#[repr(C, align( 33554432))] (u8; elain::Align<134217728>) => Some(134217728)); 1067c3739801SMiguel Ojeda test!(#[repr(C, align(134217728))] (u8; elain::Align<268435456>) => Some(268435456)); 1068c3739801SMiguel Ojeda */ 1069c3739801SMiguel Ojeda } 1070c3739801SMiguel Ojeda 1071c3739801SMiguel Ojeda // FIXME(#29), FIXME(https://github.com/rust-lang/rust/issues/69835): 1072c3739801SMiguel Ojeda // Remove this `cfg` when `size_of_val_raw` is stabilized. 1073c3739801SMiguel Ojeda #[allow(clippy::decimal_literal_representation)] 1074c3739801SMiguel Ojeda #[test] 1075c3739801SMiguel Ojeda fn test_align_of_dst() { 1076c3739801SMiguel Ojeda // Test that `align_of!` correctly computes the alignment of DSTs. 1077c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<1>]), Some(1)); 1078c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<2>]), Some(2)); 1079c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<4>]), Some(4)); 1080c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<8>]), Some(8)); 1081c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<16>]), Some(16)); 1082c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<32>]), Some(32)); 1083c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<64>]), Some(64)); 1084c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<128>]), Some(128)); 1085c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<256>]), Some(256)); 1086c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<512>]), Some(512)); 1087c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<1024>]), Some(1024)); 1088c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<2048>]), Some(2048)); 1089c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<4096>]), Some(4096)); 1090c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<8192>]), Some(8192)); 1091c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<16384>]), Some(16384)); 1092c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<32768>]), Some(32768)); 1093c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<65536>]), Some(65536)); 1094c3739801SMiguel Ojeda /* Alignments above 65536 are not yet supported. 1095c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<131072>]), Some(131072)); 1096c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<262144>]), Some(262144)); 1097c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<524288>]), Some(524288)); 1098c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<1048576>]), Some(1048576)); 1099c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<2097152>]), Some(2097152)); 1100c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<4194304>]), Some(4194304)); 1101c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<8388608>]), Some(8388608)); 1102c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<16777216>]), Some(16777216)); 1103c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<33554432>]), Some(33554432)); 1104c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<67108864>]), Some(67108864)); 1105c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<33554432>]), Some(33554432)); 1106c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<134217728>]), Some(134217728)); 1107c3739801SMiguel Ojeda assert_eq!(align_of!([elain::Align<268435456>]), Some(268435456)); 1108c3739801SMiguel Ojeda */ 1109c3739801SMiguel Ojeda } 1110c3739801SMiguel Ojeda } 1111c3739801SMiguel Ojeda 1112c3739801SMiguel Ojeda #[test] 1113c3739801SMiguel Ojeda fn test_enum_casts() { 1114c3739801SMiguel Ojeda // Test that casting the variants of enums with signed integer reprs to 1115c3739801SMiguel Ojeda // unsigned integers obeys expected signed -> unsigned casting rules. 1116c3739801SMiguel Ojeda 1117c3739801SMiguel Ojeda #[repr(i8)] 1118c3739801SMiguel Ojeda enum ReprI8 { 1119c3739801SMiguel Ojeda MinusOne = -1, 1120c3739801SMiguel Ojeda Zero = 0, 1121c3739801SMiguel Ojeda Min = i8::MIN, 1122c3739801SMiguel Ojeda Max = i8::MAX, 1123c3739801SMiguel Ojeda } 1124c3739801SMiguel Ojeda 1125c3739801SMiguel Ojeda #[allow(clippy::as_conversions)] 1126c3739801SMiguel Ojeda let x = ReprI8::MinusOne as u8; 1127c3739801SMiguel Ojeda assert_eq!(x, u8::MAX); 1128c3739801SMiguel Ojeda 1129c3739801SMiguel Ojeda #[allow(clippy::as_conversions)] 1130c3739801SMiguel Ojeda let x = ReprI8::Zero as u8; 1131c3739801SMiguel Ojeda assert_eq!(x, 0); 1132c3739801SMiguel Ojeda 1133c3739801SMiguel Ojeda #[allow(clippy::as_conversions)] 1134c3739801SMiguel Ojeda let x = ReprI8::Min as u8; 1135c3739801SMiguel Ojeda assert_eq!(x, 128); 1136c3739801SMiguel Ojeda 1137c3739801SMiguel Ojeda #[allow(clippy::as_conversions)] 1138c3739801SMiguel Ojeda let x = ReprI8::Max as u8; 1139c3739801SMiguel Ojeda assert_eq!(x, 127); 1140c3739801SMiguel Ojeda } 1141c3739801SMiguel Ojeda 1142c3739801SMiguel Ojeda #[test] 1143c3739801SMiguel Ojeda fn test_struct_padding() { 1144c3739801SMiguel Ojeda // Test that, for each provided repr, `struct_padding!` reports the 1145c3739801SMiguel Ojeda // expected value. 1146c3739801SMiguel Ojeda macro_rules! test { 1147c3739801SMiguel Ojeda (#[$cfg:meta] ($($ts:ty),*) => $expect:expr) => {{ 1148c3739801SMiguel Ojeda #[$cfg] 1149c3739801SMiguel Ojeda #[allow(dead_code)] 1150c3739801SMiguel Ojeda struct Test($($ts),*); 1151c3739801SMiguel Ojeda assert_eq!(struct_padding!(Test, None::<NonZeroUsize>, None::<NonZeroUsize>, [$($ts),*]), $expect); 1152c3739801SMiguel Ojeda }}; 1153c3739801SMiguel Ojeda (#[$cfg:meta] $(#[$cfgs:meta])* ($($ts:ty),*) => $expect:expr) => { 1154c3739801SMiguel Ojeda test!(#[$cfg] ($($ts),*) => $expect); 1155c3739801SMiguel Ojeda test!($(#[$cfgs])* ($($ts),*) => $expect); 1156c3739801SMiguel Ojeda }; 1157c3739801SMiguel Ojeda } 1158c3739801SMiguel Ojeda 1159c3739801SMiguel Ojeda test!(#[repr(C)] #[repr(transparent)] #[repr(packed)] () => 0); 1160c3739801SMiguel Ojeda test!(#[repr(C)] #[repr(transparent)] #[repr(packed)] (u8) => 0); 1161c3739801SMiguel Ojeda test!(#[repr(C)] #[repr(transparent)] #[repr(packed)] (u8, ()) => 0); 1162c3739801SMiguel Ojeda test!(#[repr(C)] #[repr(packed)] (u8, u8) => 0); 1163c3739801SMiguel Ojeda 1164c3739801SMiguel Ojeda test!(#[repr(C)] (u8, AU64) => 7); 1165c3739801SMiguel Ojeda // Rust won't let you put `#[repr(packed)]` on a type which contains a 1166c3739801SMiguel Ojeda // `#[repr(align(n > 1))]` type (`AU64`), so we have to use `u64` here. 1167c3739801SMiguel Ojeda // It's not ideal, but it definitely has align > 1 on /some/ of our CI 1168c3739801SMiguel Ojeda // targets, and this isn't a particularly complex macro we're testing 1169c3739801SMiguel Ojeda // anyway. 1170c3739801SMiguel Ojeda test!(#[repr(packed)] (u8, u64) => 0); 1171c3739801SMiguel Ojeda } 1172c3739801SMiguel Ojeda 1173c3739801SMiguel Ojeda #[test] 1174c3739801SMiguel Ojeda fn test_repr_c_struct_padding() { 1175c3739801SMiguel Ojeda // Test that, for each provided repr, `repr_c_struct_padding!` reports 1176c3739801SMiguel Ojeda // the expected value. 1177c3739801SMiguel Ojeda macro_rules! test { 1178c3739801SMiguel Ojeda (($($ts:tt),*) => $expect:expr) => {{ 1179c3739801SMiguel Ojeda #[repr(C)] 1180c3739801SMiguel Ojeda #[allow(dead_code)] 1181c3739801SMiguel Ojeda struct Test($($ts),*); 1182c3739801SMiguel Ojeda assert_eq!(repr_c_struct_has_padding!(Test, None::<NonZeroUsize>, None::<NonZeroUsize>, [$($ts),*]), $expect); 1183c3739801SMiguel Ojeda }}; 1184c3739801SMiguel Ojeda } 1185c3739801SMiguel Ojeda 1186c3739801SMiguel Ojeda // Test static padding 1187c3739801SMiguel Ojeda test!(() => false); 1188c3739801SMiguel Ojeda test!(([u8]) => false); 1189c3739801SMiguel Ojeda test!((u8) => false); 1190c3739801SMiguel Ojeda test!((u8, [u8]) => false); 1191c3739801SMiguel Ojeda test!((u8, ()) => false); 1192c3739801SMiguel Ojeda test!((u8, (), [u8]) => false); 1193c3739801SMiguel Ojeda test!((u8, u8) => false); 1194c3739801SMiguel Ojeda test!((u8, u8, [u8]) => false); 1195c3739801SMiguel Ojeda 1196c3739801SMiguel Ojeda test!((u8, AU64) => true); 1197c3739801SMiguel Ojeda test!((u8, AU64, [u8]) => true); 1198c3739801SMiguel Ojeda 1199c3739801SMiguel Ojeda // Test dynamic padding 1200c3739801SMiguel Ojeda test!((AU64, [AU64]) => false); 1201c3739801SMiguel Ojeda test!((u8, [AU64]) => true); 1202c3739801SMiguel Ojeda 1203c3739801SMiguel Ojeda #[repr(align(4))] 1204c3739801SMiguel Ojeda struct AU32(#[allow(unused)] u32); 1205c3739801SMiguel Ojeda test!((AU64, [AU64]) => false); 1206c3739801SMiguel Ojeda test!((AU64, [AU32]) => true); 1207c3739801SMiguel Ojeda } 1208c3739801SMiguel Ojeda 1209c3739801SMiguel Ojeda #[test] 1210c3739801SMiguel Ojeda fn test_union_padding() { 1211c3739801SMiguel Ojeda // Test that, for each provided repr, `union_padding!` reports the 1212c3739801SMiguel Ojeda // expected value. 1213c3739801SMiguel Ojeda macro_rules! test { 1214c3739801SMiguel Ojeda (#[$cfg:meta] {$($fs:ident: $ts:ty),*} => $expect:expr) => {{ 1215c3739801SMiguel Ojeda #[$cfg] 1216c3739801SMiguel Ojeda #[allow(unused)] // fields are never read 1217c3739801SMiguel Ojeda union Test{ $($fs: $ts),* } 1218c3739801SMiguel Ojeda assert_eq!(union_padding!(Test, None::<NonZeroUsize>, None::<usize>, [$($ts),*]), $expect); 1219c3739801SMiguel Ojeda }}; 1220c3739801SMiguel Ojeda (#[$cfg:meta] $(#[$cfgs:meta])* {$($fs:ident: $ts:ty),*} => $expect:expr) => { 1221c3739801SMiguel Ojeda test!(#[$cfg] {$($fs: $ts),*} => $expect); 1222c3739801SMiguel Ojeda test!($(#[$cfgs])* {$($fs: $ts),*} => $expect); 1223c3739801SMiguel Ojeda }; 1224c3739801SMiguel Ojeda } 1225c3739801SMiguel Ojeda 1226c3739801SMiguel Ojeda test!(#[repr(C)] #[repr(packed)] {a: u8} => 0); 1227c3739801SMiguel Ojeda test!(#[repr(C)] #[repr(packed)] {a: u8, b: u8} => 0); 1228c3739801SMiguel Ojeda 1229c3739801SMiguel Ojeda // Rust won't let you put `#[repr(packed)]` on a type which contains a 1230c3739801SMiguel Ojeda // `#[repr(align(n > 1))]` type (`AU64`), so we have to use `u64` here. 1231c3739801SMiguel Ojeda // It's not ideal, but it definitely has align > 1 on /some/ of our CI 1232c3739801SMiguel Ojeda // targets, and this isn't a particularly complex macro we're testing 1233c3739801SMiguel Ojeda // anyway. 1234c3739801SMiguel Ojeda test!(#[repr(C)] #[repr(packed)] {a: u8, b: u64} => 7); 1235c3739801SMiguel Ojeda } 1236c3739801SMiguel Ojeda 1237c3739801SMiguel Ojeda #[test] 1238c3739801SMiguel Ojeda fn test_enum_padding() { 1239c3739801SMiguel Ojeda // Test that, for each provided repr, `enum_has_padding!` reports the 1240c3739801SMiguel Ojeda // expected value. 1241c3739801SMiguel Ojeda macro_rules! test { 1242c3739801SMiguel Ojeda (#[repr($disc:ident $(, $c:ident)?)] { $($vs:ident ($($ts:ty),*),)* } => $expect:expr) => { 1243c3739801SMiguel Ojeda test!(@case #[repr($disc $(, $c)?)] { $($vs ($($ts),*),)* } => $expect); 1244c3739801SMiguel Ojeda }; 1245c3739801SMiguel Ojeda (#[repr($disc:ident $(, $c:ident)?)] #[$cfg:meta] $(#[$cfgs:meta])* { $($vs:ident ($($ts:ty),*),)* } => $expect:expr) => { 1246c3739801SMiguel Ojeda test!(@case #[repr($disc $(, $c)?)] #[$cfg] { $($vs ($($ts),*),)* } => $expect); 1247c3739801SMiguel Ojeda test!(#[repr($disc $(, $c)?)] $(#[$cfgs])* { $($vs ($($ts),*),)* } => $expect); 1248c3739801SMiguel Ojeda }; 1249c3739801SMiguel Ojeda (@case #[repr($disc:ident $(, $c:ident)?)] $(#[$cfg:meta])? { $($vs:ident ($($ts:ty),*),)* } => $expect:expr) => {{ 1250c3739801SMiguel Ojeda #[repr($disc $(, $c)?)] 1251c3739801SMiguel Ojeda $(#[$cfg])? 1252c3739801SMiguel Ojeda #[allow(unused)] // variants and fields are never used 1253c3739801SMiguel Ojeda enum Test { 1254c3739801SMiguel Ojeda $($vs ($($ts),*),)* 1255c3739801SMiguel Ojeda } 1256c3739801SMiguel Ojeda assert_eq!( 1257c3739801SMiguel Ojeda enum_padding!(Test, None::<NonZeroUsize>, None::<NonZeroUsize>, $disc, $([$($ts),*]),*), 1258c3739801SMiguel Ojeda $expect 1259c3739801SMiguel Ojeda ); 1260c3739801SMiguel Ojeda }}; 1261c3739801SMiguel Ojeda } 1262c3739801SMiguel Ojeda 1263c3739801SMiguel Ojeda #[allow(unused)] 1264c3739801SMiguel Ojeda #[repr(align(2))] 1265c3739801SMiguel Ojeda struct U16(u16); 1266c3739801SMiguel Ojeda 1267c3739801SMiguel Ojeda #[allow(unused)] 1268c3739801SMiguel Ojeda #[repr(align(4))] 1269c3739801SMiguel Ojeda struct U32(u32); 1270c3739801SMiguel Ojeda 1271c3739801SMiguel Ojeda test!(#[repr(u8)] #[repr(C)] { 1272c3739801SMiguel Ojeda A(u8), 1273c3739801SMiguel Ojeda } => 0); 1274c3739801SMiguel Ojeda test!(#[repr(u16)] #[repr(C)] { 1275c3739801SMiguel Ojeda A(u8, u8), 1276c3739801SMiguel Ojeda B(U16), 1277c3739801SMiguel Ojeda } => 0); 1278c3739801SMiguel Ojeda test!(#[repr(u32)] #[repr(C)] { 1279c3739801SMiguel Ojeda A(u8, u8, u8, u8), 1280c3739801SMiguel Ojeda B(U16, u8, u8), 1281c3739801SMiguel Ojeda C(u8, u8, U16), 1282c3739801SMiguel Ojeda D(U16, U16), 1283c3739801SMiguel Ojeda E(U32), 1284c3739801SMiguel Ojeda } => 0); 1285c3739801SMiguel Ojeda 1286c3739801SMiguel Ojeda // `repr(int)` can pack the discriminant more efficiently 1287c3739801SMiguel Ojeda test!(#[repr(u8)] { 1288c3739801SMiguel Ojeda A(u8, U16), 1289c3739801SMiguel Ojeda } => 0); 1290c3739801SMiguel Ojeda test!(#[repr(u8)] { 1291c3739801SMiguel Ojeda A(u8, U16, U32), 1292c3739801SMiguel Ojeda } => 0); 1293c3739801SMiguel Ojeda 1294c3739801SMiguel Ojeda // `repr(C)` cannot 1295c3739801SMiguel Ojeda test!(#[repr(u8, C)] { 1296c3739801SMiguel Ojeda A(u8, U16), 1297c3739801SMiguel Ojeda } => 2); 1298c3739801SMiguel Ojeda test!(#[repr(u8, C)] { 1299c3739801SMiguel Ojeda A(u8, u8, u8, U32), 1300c3739801SMiguel Ojeda } => 4); 1301c3739801SMiguel Ojeda 1302c3739801SMiguel Ojeda // And field ordering can always cause problems 1303c3739801SMiguel Ojeda test!(#[repr(u8)] #[repr(C)] { 1304c3739801SMiguel Ojeda A(U16, u8), 1305c3739801SMiguel Ojeda } => 2); 1306c3739801SMiguel Ojeda test!(#[repr(u8)] #[repr(C)] { 1307c3739801SMiguel Ojeda A(U32, u8, u8, u8), 1308c3739801SMiguel Ojeda } => 4); 1309c3739801SMiguel Ojeda } 1310c3739801SMiguel Ojeda } 1311