Skip to content

Commit 3993d61

Browse files
committed
Check reconstructed layouts against compiler layout queries
Reconstructed layouts must agree with rustc's objects. Check that boundary for ordinary, aligned, and packed repr(C) fixtures: compile-time assertions compare complete size, alignment, and field offsets. Nested DST fixtures safely unsize real array tails and compare the metadata-size calculation with size_of_val. The checks cover the selected compiler, target, and concrete instantiations; they do not establish a universal result across types or compilers. MSRV runtime comparisons remain available where layout queries are not yet const. Agent-authored-by: AI agent acting on joshlf's behalf gherrit-pr-id: Gkxoay4mfhudcnfy2cuk46iu6gnltvxg3
1 parent 8d8ed8d commit 3993d61

2 files changed

Lines changed: 155 additions & 10 deletions

File tree

‎zerocopy/src/layout.rs‎

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2332,6 +2332,110 @@ mod tests {
23322332
use super::*;
23332333
use crate::PointerMetadata as _;
23342334

2335+
// The MSRV's dead-code analysis does not follow uses in unnamed consts.
2336+
#[allow(dead_code)]
2337+
const fn sized_size(layout: DstLayout) -> usize {
2338+
match layout.size_info {
2339+
SizeInfo::Sized { size } => size,
2340+
SizeInfo::SliceDst(_) => const_panic!("expected a sized layout"),
2341+
}
2342+
}
2343+
2344+
// Compare reconstructed layouts directly with rustc's size and alignment
2345+
// of the complete type.
2346+
#[allow(dead_code)]
2347+
const fn assert_compiler_layout<T>(layout: DstLayout) {
2348+
const_assert!(layout.align.get() == mem::align_of::<T>());
2349+
const_assert!(sized_size(layout) == mem::size_of::<T>());
2350+
}
2351+
2352+
// Keep the type declaration, ordered field reconstruction, and compiler
2353+
// comparisons together. Evaluating the associated constants below checks
2354+
// those concrete generic instantiations without generic const expressions
2355+
// on the MSRV.
2356+
macro_rules! assert_repr_c_layout {
2357+
($name:ident, $repr:meta, $align:expr, $packed:expr) => {
2358+
#[derive(crate::KnownLayout)]
2359+
#[$repr]
2360+
struct $name<T, const N: usize> {
2361+
prefix: u8,
2362+
data: [T; N],
2363+
tag: u16,
2364+
tail: T,
2365+
}
2366+
2367+
impl<T: crate::KnownLayout, const N: usize> $name<T, N> {
2368+
#[allow(dead_code)]
2369+
const ASSERT_LAYOUT: () = {
2370+
let reconstructed = DstLayout::for_repr_c_struct(
2371+
$align,
2372+
$packed,
2373+
&[
2374+
DstLayout::for_type::<u8>(),
2375+
DstLayout::for_type::<[T; N]>(),
2376+
DstLayout::for_type::<u16>(),
2377+
DstLayout::for_type::<T>(),
2378+
],
2379+
);
2380+
assert_compiler_layout::<Self>(reconstructed);
2381+
assert_compiler_layout::<Self>(<Self as crate::KnownLayout>::LAYOUT);
2382+
};
2383+
2384+
#[rustversion::since(1.77)]
2385+
const ASSERT_OFFSETS: () = {
2386+
let prefix =
2387+
DstLayout::new_zst($align).extend(DstLayout::for_type::<u8>(), $packed);
2388+
let data = prefix.extend(DstLayout::for_type::<[T; N]>(), $packed);
2389+
let tag = data.extend(DstLayout::for_type::<u16>(), $packed);
2390+
let tail = tag.extend(DstLayout::for_type::<T>(), $packed);
2391+
const_assert!(mem::offset_of!(Self, prefix) == 0);
2392+
// `extend` has not added trailing padding, so subtracting
2393+
// the last field's size gives its reconstructed offset.
2394+
#[allow(clippy::arithmetic_side_effects)]
2395+
{
2396+
const_assert!(
2397+
mem::offset_of!(Self, data)
2398+
== sized_size(data) - mem::size_of::<[T; N]>()
2399+
);
2400+
const_assert!(
2401+
mem::offset_of!(Self, tag) == sized_size(tag) - mem::size_of::<u16>()
2402+
);
2403+
const_assert!(
2404+
mem::offset_of!(Self, tail) == sized_size(tail) - mem::size_of::<T>()
2405+
);
2406+
}
2407+
};
2408+
}
2409+
2410+
const _: () = $name::<u64, 3>::ASSERT_LAYOUT;
2411+
const _: () = $name::<[u8; 3], 2>::ASSERT_LAYOUT;
2412+
const _: () = $name::<(), 0>::ASSERT_LAYOUT;
2413+
2414+
#[rustversion::since(1.77)]
2415+
const _: () = $name::<u64, 3>::ASSERT_OFFSETS;
2416+
#[rustversion::since(1.77)]
2417+
const _: () = $name::<[u8; 3], 2>::ASSERT_OFFSETS;
2418+
#[rustversion::since(1.77)]
2419+
const _: () = $name::<(), 0>::ASSERT_OFFSETS;
2420+
};
2421+
}
2422+
2423+
assert_repr_c_layout!(NativeC, repr(C), None, None);
2424+
assert_repr_c_layout!(NativeAlignedC, repr(C, align(16)), NonZeroUsize::new(16), None);
2425+
assert_repr_c_layout!(NativePackedC, repr(C, packed(2)), None, NonZeroUsize::new(2));
2426+
2427+
#[derive(crate::KnownLayout, crate::Unaligned)]
2428+
#[repr(C)]
2429+
struct UnalignedByte {
2430+
byte: u8,
2431+
}
2432+
2433+
const _: () = {
2434+
const_assert!(mem::align_of::<u8>() == 1);
2435+
const_assert!(mem::align_of::<UnalignedByte>() == 1);
2436+
assert_compiler_layout::<UnalignedByte>(<UnalignedByte as crate::KnownLayout>::LAYOUT);
2437+
};
2438+
23352439
/// Models the size of nested `repr(C)` structs ending in a slice.
23362440
///
23372441
/// `leading` lists structs from outermost to innermost. Each tuple gives

‎zerocopy/src/lib.rs‎

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6772,6 +6772,44 @@ mod tests {
67726772
inner: ManuallyDrop<NestedPackedDstInner<T>>,
67736773
}
67746774

6775+
// `size_of_val` and `align_of_val` became const in 1.85. Expected sizes and
6776+
// alignments come from references safely unsized from real sized objects.
6777+
#[rustversion::since(1.85)]
6778+
const _: () = {
6779+
macro_rules! assert_native_dst_layout {
6780+
($elems:expr) => {{
6781+
let value = NestedPackedDstOuter {
6782+
z: 0,
6783+
inner: ManuallyDrop::new(NestedPackedDstInner {
6784+
a: NestedPackedDstWord([0; 4]),
6785+
b: 0,
6786+
tail: [0; $elems],
6787+
}),
6788+
};
6789+
let value: &NestedPackedDstOuter<[u8]> = &value;
6790+
let layout = NestedPackedDstOuter::<[u8]>::LAYOUT;
6791+
const_assert!(layout.align.get() == mem::align_of_val(value));
6792+
match layout.size_info {
6793+
// This is the const implementation called by
6794+
// `PointerMetadata::size_for_metadata` for slice DSTs.
6795+
SizeInfo::SliceDst(trailing) => match trailing.size_for_elems($elems) {
6796+
Some(size) => const_assert!(size == mem::size_of_val(value)),
6797+
None => const_panic!("native DST size must fit in usize"),
6798+
},
6799+
SizeInfo::Sized { .. } => const_panic!("expected a slice DST layout"),
6800+
}
6801+
}};
6802+
}
6803+
6804+
assert_native_dst_layout!(0);
6805+
assert_native_dst_layout!(1);
6806+
assert_native_dst_layout!(2);
6807+
assert_native_dst_layout!(3);
6808+
assert_native_dst_layout!(4);
6809+
assert_native_dst_layout!(7);
6810+
assert_native_dst_layout!(8);
6811+
};
6812+
67756813
#[repr(C, align(2))]
67766814
struct Align2<T>(T);
67776815

@@ -6877,7 +6915,7 @@ mod tests {
68776915

68786916
#[test]
68796917
fn test_nested_packed_dst_layout() {
6880-
fn assert_native_size<const N: usize>(expected: usize) {
6918+
fn assert_native_size<const N: usize>() {
68816919
let value = NestedPackedDstOuter {
68826920
z: 0,
68836921
inner: ManuallyDrop::new(NestedPackedDstInner {
@@ -6887,17 +6925,20 @@ mod tests {
68876925
}),
68886926
};
68896927
let value: &NestedPackedDstOuter<[u8]> = &value;
6890-
assert_eq!(mem::size_of_val(value), expected);
6891-
assert_eq!(NestedPackedDstOuter::<[u8]>::size_for_metadata(N), Some(expected));
6928+
assert_eq!(
6929+
NestedPackedDstOuter::<[u8]>::size_for_metadata(N),
6930+
Some(mem::size_of_val(value))
6931+
);
6932+
assert_eq!(NestedPackedDstOuter::<[u8]>::LAYOUT.align.get(), mem::align_of_val(value));
68926933
}
68936934

6894-
assert_native_size::<0>(10);
6895-
assert_native_size::<1>(10);
6896-
assert_native_size::<2>(10);
6897-
assert_native_size::<3>(10);
6898-
assert_native_size::<4>(14);
6899-
assert_native_size::<7>(14);
6900-
assert_native_size::<8>(18);
6935+
assert_native_size::<0>();
6936+
assert_native_size::<1>();
6937+
assert_native_size::<2>();
6938+
assert_native_size::<3>();
6939+
assert_native_size::<4>();
6940+
assert_native_size::<7>();
6941+
assert_native_size::<8>();
69016942

69026943
let layout = NestedPackedDstOuter::<[u8]>::LAYOUT;
69036944
assert_eq!(layout.align.get(), 2);

0 commit comments

Comments
 (0)