Repository navigation
Check reconstructed layouts against compiler layout queries #3860
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: Gxizdpw52bljtxjfng3ervgk5xnca6mow
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2332,6 +2332,112 @@ mod tests { | |
| use super::*; | ||
| use crate::PointerMetadata as _; | ||
|
|
||
| // The MSRV's dead-code analysis does not follow uses in unnamed consts. | ||
| #[allow(dead_code)] | ||
| const fn sized_size(layout: DstLayout) -> usize { | ||
| match layout.size_info { | ||
| SizeInfo::Sized { size } => size, | ||
| SizeInfo::SliceDst(_) => const_panic!("expected a sized layout"), | ||
| } | ||
| } | ||
|
|
||
| // Compare reconstructed layouts directly with rustc's size and alignment | ||
| // of the complete type. | ||
| #[allow(dead_code)] | ||
| const fn assert_compiler_layout<T>(layout: DstLayout) { | ||
| const_assert!(layout.align.get() == mem::align_of::<T>()); | ||
| const_assert!(sized_size(layout) == mem::size_of::<T>()); | ||
| } | ||
|
|
||
| // Keep the type declaration, ordered field reconstruction, and compiler | ||
| // comparisons together. Evaluating the associated constants below checks | ||
| // those concrete generic instantiations without generic const expressions | ||
| // on the MSRV. | ||
| macro_rules! assert_repr_c_layout { | ||
| ($name:ident, $repr:meta, $align:expr, $packed:expr) => { | ||
| #[derive(crate::KnownLayout)] | ||
| #[$repr] | ||
| struct $name<T, const N: usize> { | ||
| prefix: u8, | ||
| data: [T; N], | ||
| tag: u16, | ||
| tail: T, | ||
| } | ||
|
|
||
| impl<T: crate::KnownLayout, const N: usize> $name<T, N> { | ||
| #[allow(dead_code)] | ||
| const ASSERT_LAYOUT: () = { | ||
| let reconstructed = DstLayout::for_repr_c_struct( | ||
| $align, | ||
| $packed, | ||
| &[ | ||
| DstLayout::for_type::<u8>(), | ||
| DstLayout::for_type::<[T; N]>(), | ||
| DstLayout::for_type::<u16>(), | ||
| DstLayout::for_type::<T>(), | ||
| ], | ||
| ); | ||
| assert_compiler_layout::<Self>(reconstructed); | ||
| assert_compiler_layout::<Self>(<Self as crate::KnownLayout>::LAYOUT); | ||
| }; | ||
|
|
||
| // Older compilers miss uses through the const assertions below. | ||
| #[allow(dead_code)] | ||
| #[rustversion::since(1.77)] | ||
| const ASSERT_OFFSETS: () = { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
On the supported Rust 1.77–1.88 range, dead-code analysis does not follow the uses of this associated constant from the unnamed Useful? React with 👍 / 👎. |
||
| let prefix = | ||
| DstLayout::new_zst($align).extend(DstLayout::for_type::<u8>(), $packed); | ||
| let data = prefix.extend(DstLayout::for_type::<[T; N]>(), $packed); | ||
| let tag = data.extend(DstLayout::for_type::<u16>(), $packed); | ||
| let tail = tag.extend(DstLayout::for_type::<T>(), $packed); | ||
| const_assert!(mem::offset_of!(Self, prefix) == 0); | ||
| // `extend` has not added trailing padding, so subtracting | ||
| // the last field's size gives its reconstructed offset. | ||
| #[allow(clippy::arithmetic_side_effects)] | ||
| { | ||
| const_assert!( | ||
| mem::offset_of!(Self, data) | ||
| == sized_size(data) - mem::size_of::<[T; N]>() | ||
| ); | ||
| const_assert!( | ||
| mem::offset_of!(Self, tag) == sized_size(tag) - mem::size_of::<u16>() | ||
| ); | ||
| const_assert!( | ||
| mem::offset_of!(Self, tail) == sized_size(tail) - mem::size_of::<T>() | ||
| ); | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| const _: () = $name::<u64, 3>::ASSERT_LAYOUT; | ||
| const _: () = $name::<[u8; 3], 2>::ASSERT_LAYOUT; | ||
| const _: () = $name::<(), 0>::ASSERT_LAYOUT; | ||
|
|
||
| #[rustversion::since(1.77)] | ||
| const _: () = $name::<u64, 3>::ASSERT_OFFSETS; | ||
| #[rustversion::since(1.77)] | ||
| const _: () = $name::<[u8; 3], 2>::ASSERT_OFFSETS; | ||
| #[rustversion::since(1.77)] | ||
| const _: () = $name::<(), 0>::ASSERT_OFFSETS; | ||
| }; | ||
| } | ||
|
|
||
| assert_repr_c_layout!(NativeC, repr(C), None, None); | ||
| assert_repr_c_layout!(NativeAlignedC, repr(C, align(16)), NonZeroUsize::new(16), None); | ||
| assert_repr_c_layout!(NativePackedC, repr(C, packed(2)), None, NonZeroUsize::new(2)); | ||
|
|
||
| #[derive(crate::KnownLayout, crate::Unaligned)] | ||
| #[repr(C)] | ||
| struct UnalignedByte { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The CI matrix compiles tests with Useful? React with 👍 / 👎. |
||
| byte: u8, | ||
| } | ||
|
|
||
| const _: () = { | ||
| const_assert!(mem::align_of::<u8>() == 1); | ||
| const_assert!(mem::align_of::<UnalignedByte>() == 1); | ||
| assert_compiler_layout::<UnalignedByte>(<UnalignedByte as crate::KnownLayout>::LAYOUT); | ||
| }; | ||
|
|
||
| /// Models the size of nested `repr(C)` structs ending in a slice. | ||
| /// | ||
| /// `leading` lists structs from outermost to innermost. Each tuple gives | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the repository's
RUSTFLAGS=-Dwarningstest matrix, these fixtures fail to compile on supported older toolchains. Before Rust 1.77 theASSERT_OFFSETSitems are removed entirely, and through Rust 1.88offset_of!does not make the type count as constructed; the layout constants only use the type and therefore triggerdead_codefor every macro invocation. Put#[allow(dead_code)]on the generated struct, rather than only onASSERT_LAYOUT, so the MSRV and intermediate-version jobs can compile the tests.Useful? React with 👍 / 👎.