Skip to content

Commit a55463e

Browse files
authored
derive(IntoBytes): support repr(C) structs with explicit trailing slices (#2679)
For example: #[repr(C)] struct Example { leading: u8, trailing: [Trailing] } Makes progress towards #1112
1 parent e546da9 commit a55463e

File tree

11 files changed

+723
-265
lines changed

11 files changed

+723
-265
lines changed

src/layout.rs

Lines changed: 243 additions & 59 deletions
Large diffs are not rendered by default.

src/lib.rs

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5752,26 +5752,30 @@ mod tests {
57525752
};
57535753
}
57545754

5755-
let layout = |offset, align, _trailing_slice_elem_size| DstLayout {
5756-
align: NonZeroUsize::new(align).unwrap(),
5757-
size_info: match _trailing_slice_elem_size {
5758-
None => SizeInfo::Sized { size: offset },
5759-
Some(elem_size) => SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size }),
5760-
},
5761-
};
5755+
let layout =
5756+
|offset, align, trailing_slice_elem_size, statically_shallow_unpadded| DstLayout {
5757+
align: NonZeroUsize::new(align).unwrap(),
5758+
size_info: match trailing_slice_elem_size {
5759+
None => SizeInfo::Sized { size: offset },
5760+
Some(elem_size) => {
5761+
SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size })
5762+
}
5763+
},
5764+
statically_shallow_unpadded,
5765+
};
57625766

5763-
test!((), layout(0, 1, None));
5764-
test!(u8, layout(1, 1, None));
5767+
test!((), layout(0, 1, None, false));
5768+
test!(u8, layout(1, 1, None, false));
57655769
// Use `align_of` because `u64` alignment may be smaller than 8 on some
57665770
// platforms.
5767-
test!(u64, layout(8, mem::align_of::<u64>(), None));
5768-
test!(AU64, layout(8, 8, None));
5771+
test!(u64, layout(8, mem::align_of::<u64>(), None, false));
5772+
test!(AU64, layout(8, 8, None, false));
57695773

57705774
test!(Option<&'static ()>, usize::LAYOUT);
57715775

5772-
test!([()], layout(0, 1, Some(0)));
5773-
test!([u8], layout(0, 1, Some(1)));
5774-
test!(str, layout(0, 1, Some(1)));
5776+
test!([()], layout(0, 1, Some(0), true));
5777+
test!([u8], layout(0, 1, Some(1), true));
5778+
test!(str, layout(0, 1, Some(1), true));
57755779
}
57765780

57775781
#[cfg(feature = "derive")]
@@ -5826,11 +5830,13 @@ mod tests {
58265830
let sized_layout = |align, size| DstLayout {
58275831
align: NonZeroUsize::new(align).unwrap(),
58285832
size_info: SizeInfo::Sized { size },
5833+
statically_shallow_unpadded: false,
58295834
};
58305835

5831-
let unsized_layout = |align, elem_size, offset| DstLayout {
5836+
let unsized_layout = |align, elem_size, offset, statically_shallow_unpadded| DstLayout {
58325837
align: NonZeroUsize::new(align).unwrap(),
58335838
size_info: SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size }),
5839+
statically_shallow_unpadded,
58345840
};
58355841

58365842
// | `repr(C)`? | generic? | `KnownLayout`? | `Sized`? | Type Name |
@@ -5958,7 +5964,7 @@ mod tests {
59585964
.pad_to_align();
59595965

59605966
assert_eq!(<KL10 as KnownLayout>::LAYOUT, expected);
5961-
assert_eq!(<KL10 as KnownLayout>::LAYOUT, unsized_layout(4, 1, 4));
5967+
assert_eq!(<KL10 as KnownLayout>::LAYOUT, unsized_layout(4, 1, 4, false));
59625968

59635969
// ...with `align(N)`:
59645970
#[allow(dead_code)]
@@ -5974,7 +5980,7 @@ mod tests {
59745980
.pad_to_align();
59755981

59765982
assert_eq!(<KL10Align as KnownLayout>::LAYOUT, expected);
5977-
assert_eq!(<KL10Align as KnownLayout>::LAYOUT, unsized_layout(64, 1, 4));
5983+
assert_eq!(<KL10Align as KnownLayout>::LAYOUT, unsized_layout(64, 1, 4, false));
59785984

59795985
// ...with `packed`:
59805986
#[allow(dead_code)]
@@ -5990,7 +5996,7 @@ mod tests {
59905996
.pad_to_align();
59915997

59925998
assert_eq!(<KL10Packed as KnownLayout>::LAYOUT, expected);
5993-
assert_eq!(<KL10Packed as KnownLayout>::LAYOUT, unsized_layout(1, 1, 4));
5999+
assert_eq!(<KL10Packed as KnownLayout>::LAYOUT, unsized_layout(1, 1, 4, false));
59946000

59956001
// ...with `packed(N)`:
59966002
#[allow(dead_code)]
@@ -6006,7 +6012,7 @@ mod tests {
60066012
.pad_to_align();
60076013

60086014
assert_eq!(<KL10PackedN as KnownLayout>::LAYOUT, expected);
6009-
assert_eq!(<KL10PackedN as KnownLayout>::LAYOUT, unsized_layout(2, 1, 4));
6015+
assert_eq!(<KL10PackedN as KnownLayout>::LAYOUT, unsized_layout(2, 1, 4, false));
60106016

60116017
// | `repr(C)`? | generic? | `KnownLayout`? | `Sized`? | Type Name |
60126018
// | Y | N | Y | Y | KL11 |
@@ -6112,35 +6118,35 @@ mod tests {
61126118

61136119
assert_eq!(<KLTU<(), AU16> as KnownLayout>::LAYOUT, sized_layout(2, 2));
61146120

6115-
assert_eq!(<KLTU<(), [()]> as KnownLayout>::LAYOUT, unsized_layout(1, 0, 0));
6121+
assert_eq!(<KLTU<(), [()]> as KnownLayout>::LAYOUT, unsized_layout(1, 0, 0, false));
61166122

6117-
assert_eq!(<KLTU<(), [u8]> as KnownLayout>::LAYOUT, unsized_layout(1, 1, 0));
6123+
assert_eq!(<KLTU<(), [u8]> as KnownLayout>::LAYOUT, unsized_layout(1, 1, 0, false));
61186124

6119-
assert_eq!(<KLTU<(), [AU16]> as KnownLayout>::LAYOUT, unsized_layout(2, 2, 0));
6125+
assert_eq!(<KLTU<(), [AU16]> as KnownLayout>::LAYOUT, unsized_layout(2, 2, 0, false));
61206126

61216127
assert_eq!(<KLTU<u8, ()> as KnownLayout>::LAYOUT, sized_layout(1, 1));
61226128

61236129
assert_eq!(<KLTU<u8, u8> as KnownLayout>::LAYOUT, sized_layout(1, 2));
61246130

61256131
assert_eq!(<KLTU<u8, AU16> as KnownLayout>::LAYOUT, sized_layout(2, 4));
61266132

6127-
assert_eq!(<KLTU<u8, [()]> as KnownLayout>::LAYOUT, unsized_layout(1, 0, 1));
6133+
assert_eq!(<KLTU<u8, [()]> as KnownLayout>::LAYOUT, unsized_layout(1, 0, 1, false));
61286134

6129-
assert_eq!(<KLTU<u8, [u8]> as KnownLayout>::LAYOUT, unsized_layout(1, 1, 1));
6135+
assert_eq!(<KLTU<u8, [u8]> as KnownLayout>::LAYOUT, unsized_layout(1, 1, 1, false));
61306136

6131-
assert_eq!(<KLTU<u8, [AU16]> as KnownLayout>::LAYOUT, unsized_layout(2, 2, 2));
6137+
assert_eq!(<KLTU<u8, [AU16]> as KnownLayout>::LAYOUT, unsized_layout(2, 2, 2, false));
61326138

61336139
assert_eq!(<KLTU<AU16, ()> as KnownLayout>::LAYOUT, sized_layout(2, 2));
61346140

61356141
assert_eq!(<KLTU<AU16, u8> as KnownLayout>::LAYOUT, sized_layout(2, 4));
61366142

61376143
assert_eq!(<KLTU<AU16, AU16> as KnownLayout>::LAYOUT, sized_layout(2, 4));
61386144

6139-
assert_eq!(<KLTU<AU16, [()]> as KnownLayout>::LAYOUT, unsized_layout(2, 0, 2));
6145+
assert_eq!(<KLTU<AU16, [()]> as KnownLayout>::LAYOUT, unsized_layout(2, 0, 2, false));
61406146

6141-
assert_eq!(<KLTU<AU16, [u8]> as KnownLayout>::LAYOUT, unsized_layout(2, 1, 2));
6147+
assert_eq!(<KLTU<AU16, [u8]> as KnownLayout>::LAYOUT, unsized_layout(2, 1, 2, false));
61426148

6143-
assert_eq!(<KLTU<AU16, [AU16]> as KnownLayout>::LAYOUT, unsized_layout(2, 2, 2));
6149+
assert_eq!(<KLTU<AU16, [AU16]> as KnownLayout>::LAYOUT, unsized_layout(2, 2, 2, false));
61446150

61456151
// Test a variety of field counts.
61466152

@@ -6154,25 +6160,25 @@ mod tests {
61546160
#[repr(C)]
61556161
struct KLF1([u8]);
61566162

6157-
assert_eq!(<KLF1 as KnownLayout>::LAYOUT, unsized_layout(1, 1, 0));
6163+
assert_eq!(<KLF1 as KnownLayout>::LAYOUT, unsized_layout(1, 1, 0, true));
61586164

61596165
#[derive(KnownLayout)]
61606166
#[repr(C)]
61616167
struct KLF2(NotKnownLayout<u8>, [u8]);
61626168

6163-
assert_eq!(<KLF2 as KnownLayout>::LAYOUT, unsized_layout(1, 1, 1));
6169+
assert_eq!(<KLF2 as KnownLayout>::LAYOUT, unsized_layout(1, 1, 1, false));
61646170

61656171
#[derive(KnownLayout)]
61666172
#[repr(C)]
61676173
struct KLF3(NotKnownLayout<u8>, NotKnownLayout<AU16>, [u8]);
61686174

6169-
assert_eq!(<KLF3 as KnownLayout>::LAYOUT, unsized_layout(2, 1, 4));
6175+
assert_eq!(<KLF3 as KnownLayout>::LAYOUT, unsized_layout(2, 1, 4, false));
61706176

61716177
#[derive(KnownLayout)]
61726178
#[repr(C)]
61736179
struct KLF4(NotKnownLayout<u8>, NotKnownLayout<AU16>, NotKnownLayout<AU32>, [u8]);
61746180

6175-
assert_eq!(<KLF4 as KnownLayout>::LAYOUT, unsized_layout(4, 1, 8));
6181+
assert_eq!(<KLF4 as KnownLayout>::LAYOUT, unsized_layout(4, 1, 8, false));
61766182
}
61776183

61786184
#[test]

src/util/macro_util.rs

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,24 @@ pub unsafe trait Field<Index> {
6262
pub trait PaddingFree<T: ?Sized, const PADDING_BYTES: usize> {}
6363
impl<T: ?Sized> PaddingFree<T, 0> for () {}
6464

65+
// FIXME(#1112): In the slice DST case, we should delegate to *both*
66+
// `PaddingFree` *and* `DynamicPaddingFree` (and probably rename `PaddingFree`
67+
// to `StaticPaddingFree` or something - or introduce a third trait with that
68+
// name) so that we can have more clear error messages.
69+
70+
#[cfg_attr(
71+
zerocopy_diagnostic_on_unimplemented_1_78_0,
72+
diagnostic::on_unimplemented(
73+
message = "`{T}` has one or more padding bytes",
74+
label = "types with padding cannot implement `IntoBytes`",
75+
note = "consider using `zerocopy::Unalign` to lower the alignment of individual fields",
76+
note = "consider adding explicit fields where padding would be",
77+
note = "consider using `#[repr(packed)]` to remove padding"
78+
)
79+
)]
80+
pub trait DynamicPaddingFree<T: ?Sized, const HAS_PADDING: bool> {}
81+
impl<T: ?Sized> DynamicPaddingFree<T, false> for () {}
82+
6583
/// A type whose size is equal to `align_of::<T>()`.
6684
#[repr(C)]
6785
pub struct AlignOf<T> {
@@ -359,7 +377,31 @@ macro_rules! struct_padding {
359377
};
360378
}
361379

362-
/// How many padding bytes does the union type `$t` have?
380+
/// Does the `repr(C)` struct type `$t` have padding?
381+
///
382+
/// `$ts` is the list of the type of every field in `$t`. `$t` must be a
383+
/// `repr(C)` struct type, or else `struct_has_padding!`'s result may be
384+
/// meaningless.
385+
#[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`.
386+
#[macro_export]
387+
macro_rules! repr_c_struct_has_padding {
388+
($t:ty, [$($ts:tt),*]) => {{
389+
let layout = $crate::DstLayout::for_repr_c_struct(
390+
$crate::util::macro_util::core_reexport::option::Option::None,
391+
$crate::util::macro_util::core_reexport::option::Option::None,
392+
&[$($crate::repr_c_struct_has_padding!(@field $ts),)*]
393+
);
394+
layout.requires_static_padding() || layout.requires_dynamic_padding()
395+
}};
396+
(@field [$t:ty]) => {
397+
<[$t] as $crate::KnownLayout>::LAYOUT
398+
};
399+
(@field $t:ty) => {
400+
$crate::DstLayout::for_unpadded_type::<$t>()
401+
};
402+
}
403+
404+
/// Does the union type `$t` have padding?
363405
///
364406
/// `$ts` is the list of the type of every field in `$t`. `$t` must be a union
365407
/// type, or else `union_padding!`'s result may be meaningless.
@@ -1129,7 +1171,8 @@ mod tests {
11291171
macro_rules! test {
11301172
(#[$cfg:meta] ($($ts:ty),*) => $expect:expr) => {{
11311173
#[$cfg]
1132-
struct Test($(#[allow(dead_code)] $ts),*);
1174+
#[allow(dead_code)]
1175+
struct Test($($ts),*);
11331176
assert_eq!(struct_padding!(Test, [$($ts),*]), $expect);
11341177
}};
11351178
(#[$cfg:meta] $(#[$cfgs:meta])* ($($ts:ty),*) => $expect:expr) => {
@@ -1152,6 +1195,42 @@ mod tests {
11521195
test!(#[repr(packed)] (u8, u64) => 0);
11531196
}
11541197

1198+
#[test]
1199+
fn test_repr_c_struct_padding() {
1200+
// Test that, for each provided repr, `repr_c_struct_padding!` reports
1201+
// the expected value.
1202+
macro_rules! test {
1203+
(($($ts:tt),*) => $expect:expr) => {{
1204+
#[repr(C)]
1205+
#[allow(dead_code)]
1206+
struct Test($($ts),*);
1207+
assert_eq!(repr_c_struct_has_padding!(Test, [$($ts),*]), $expect);
1208+
}};
1209+
}
1210+
1211+
// Test static padding
1212+
test!(() => false);
1213+
test!(([u8]) => false);
1214+
test!((u8) => false);
1215+
test!((u8, [u8]) => false);
1216+
test!((u8, ()) => false);
1217+
test!((u8, (), [u8]) => false);
1218+
test!((u8, u8) => false);
1219+
test!((u8, u8, [u8]) => false);
1220+
1221+
test!((u8, AU64) => true);
1222+
test!((u8, AU64, [u8]) => true);
1223+
1224+
// Test dynamic padding
1225+
test!((AU64, [AU64]) => false);
1226+
test!((u8, [AU64]) => true);
1227+
1228+
#[repr(align(4))]
1229+
struct AU32(#[allow(unused)] u32);
1230+
test!((AU64, [AU64]) => false);
1231+
test!((AU64, [AU32]) => true);
1232+
}
1233+
11551234
#[test]
11561235
fn test_union_padding() {
11571236
// Test that, for each provided repr, `union_padding!` reports the

src/util/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,11 @@ mod len_of {
520520
Some(size) => size,
521521
None => return Err(MetadataCastError::Size),
522522
};
523-
DstLayout { align: T::LAYOUT.align, size_info: crate::SizeInfo::Sized { size } }
523+
DstLayout {
524+
align: T::LAYOUT.align,
525+
size_info: crate::SizeInfo::Sized { size },
526+
statically_shallow_unpadded: false,
527+
}
524528
}
525529
};
526530
// Lemma 0: By contract on `validate_cast_and_convert_metadata`, if

0 commit comments

Comments
 (0)