Skip to content

Commit 2e37819

Browse files
committed
[derive] support derive(TryFromBytes) on packed structs
Now that #715 supports unaligned `Ptr`s, we get support for `repr(packed)` 'for free' — this commit merely removes the anti-`packed` check from the derive. Makes progress towards #5.
1 parent 5948354 commit 2e37819

File tree

2 files changed

+72
-13
lines changed

2 files changed

+72
-13
lines changed

zerocopy-derive/src/lib.rs

-12
Original file line numberDiff line numberDiff line change
@@ -332,20 +332,8 @@ pub fn derive_unaligned(ts: proc_macro::TokenStream) -> proc_macro::TokenStream
332332

333333
// A struct is `TryFromBytes` if:
334334
// - all fields are `TryFromBytes`
335-
// - the struct is NOT `repr(packed)` or `repr(packed(N))`
336335

337336
fn derive_try_from_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream {
338-
let reprs = try_or_print!(repr::reprs::<Repr>(&ast.attrs));
339-
340-
for (meta, repr) in reprs {
341-
if matches!(repr, Repr::Packed | Repr::PackedN(_)) {
342-
try_or_print!(Err(vec![Error::new_spanned(
343-
meta,
344-
"cannot derive TryFromBytes with repr(packed)",
345-
)]));
346-
}
347-
}
348-
349337
let extras = Some({
350338
let fields = strct.fields();
351339
let field_names = fields.iter().map(|(name, _ty)| name);

zerocopy-derive/tests/struct_try_from_bytes.rs

+72-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use std::{marker::PhantomData, option::IntoIter};
1414

1515
use {
1616
static_assertions::assert_impl_all,
17-
zerocopy::{FromBytes, FromZeros, TryFromBytes},
17+
zerocopy::{FromBytes, FromZeros, KnownLayout, TryFromBytes},
1818
};
1919

2020
use crate::util::AU16;
@@ -145,3 +145,74 @@ where
145145
T: 'a + 'b + TryFromBytes;
146146

147147
assert_impl_all!(WithParams<'static, 'static, 42, u8>: TryFromBytes);
148+
149+
#[derive(Debug, PartialEq, Eq, TryFromBytes, KnownLayout)]
150+
#[repr(C, packed)]
151+
struct CPacked {
152+
a: u8,
153+
// NOTE: The `u32` type is not guaranteed to have alignment 4, although it
154+
// does on many platforms. However, to fix this would require a custom type
155+
// with a `#[repr(align(4))]` attribute, and `#[repr(packed)]` types are not
156+
// allowed to transitively contain `#[repr(align(...))]` types. Thus, we
157+
// have no choice but to use `u32` here. Luckily, these tests run in CI on
158+
// platforms on which `u32` has alignment 4, so this isn't that big of a
159+
// deal.
160+
b: u32,
161+
}
162+
163+
#[test]
164+
fn c_packed() {
165+
let candidate = &[42u8, 0xFF, 0xFF, 0xFF, 0xFF];
166+
let converted = CPacked::try_from_ref(candidate);
167+
assert_eq!(converted, Some(&CPacked { a: 42, b: u32::MAX }));
168+
}
169+
170+
#[derive(TryFromBytes, KnownLayout)]
171+
#[repr(C, packed)]
172+
struct CPackedUnsized {
173+
a: u8,
174+
// NOTE: The `u32` type is not guaranteed to have alignment 4, although it
175+
// does on many platforms. However, to fix this would require a custom type
176+
// with a `#[repr(align(4))]` attribute, and `#[repr(packed)]` types are not
177+
// allowed to transitively contain `#[repr(align(...))]` types. Thus, we
178+
// have no choice but to use `u32` here. Luckily, these tests run in CI on
179+
// platforms on which `u32` has alignment 4, so this isn't that big of a
180+
// deal.
181+
b: [u32],
182+
}
183+
184+
#[test]
185+
fn c_packed_unsized() {
186+
let candidate = &[42u8, 0xFF, 0xFF, 0xFF, 0xFF];
187+
let converted = CPackedUnsized::try_from_ref(candidate);
188+
assert!(converted.is_some());
189+
}
190+
191+
#[derive(TryFromBytes)]
192+
#[repr(packed)]
193+
struct PackedUnsized {
194+
a: u8,
195+
// NOTE: The `u32` type is not guaranteed to have alignment 4, although it
196+
// does on many platforms. However, to fix this would require a custom type
197+
// with a `#[repr(align(4))]` attribute, and `#[repr(packed)]` types are not
198+
// allowed to transitively contain `#[repr(align(...))]` types. Thus, we
199+
// have no choice but to use `u32` here. Luckily, these tests run in CI on
200+
// platforms on which `u32` has alignment 4, so this isn't that big of a
201+
// deal.
202+
b: [u32],
203+
}
204+
205+
#[test]
206+
fn packed_unsized() {
207+
let candidate = &[42u8, 0xFF, 0xFF, 0xFF, 0xFF];
208+
let converted = CPackedUnsized::try_from_ref(candidate);
209+
assert!(converted.is_some());
210+
211+
let candidate = &[42u8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
212+
let converted = CPackedUnsized::try_from_ref(candidate);
213+
assert!(converted.is_none());
214+
215+
let candidate = &[42u8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
216+
let converted = CPackedUnsized::try_from_ref(candidate);
217+
assert!(converted.is_some());
218+
}

0 commit comments

Comments
 (0)