Skip to content

Commit

Permalink
[derive] Fix AsBytes for #[repr(C, packed(N))] (#672)
Browse files Browse the repository at this point in the history
`packed(N)` does not gaurantee no padding, but it doesn't prevent it
either. This was previously supported.

Co-authored-by: Joshua Liebow-Feeser <joshlf@users.noreply.github.com>
  • Loading branch information
maurer and joshlf authored Dec 5, 2023
1 parent d18d233 commit 941c83f
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 45 deletions.
18 changes: 11 additions & 7 deletions zerocopy-derive/src/repr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,23 +106,24 @@ pub trait KindRepr: 'static + Sized + Ord {
// etc), and provide implementations of `KindRepr`, `Ord`, and `Display`, and
// those traits' super-traits.
macro_rules! define_kind_specific_repr {
($type_name:expr, $repr_name:ident, $($repr_variant:ident),*) => {
($type_name:expr, $repr_name:ident, [ $($repr_variant:ident),* ] , [ $($repr_variant_aligned:ident),* ]) => {
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum $repr_name {
$($repr_variant,)*
Align(u64),
$($repr_variant_aligned(u64),)*
}

impl KindRepr for $repr_name {
fn is_align(&self) -> bool {
match self {
$repr_name::Align(_) => true,
$($repr_name::$repr_variant_aligned(_) => true,)*
_ => false,
}
}

fn is_align_gt_one(&self) -> bool {
match self {
// `packed(n)` only lowers alignment
$repr_name::Align(n) => n > &1,
_ => false,
}
Expand All @@ -131,7 +132,7 @@ macro_rules! define_kind_specific_repr {
fn parse(meta: &Meta) -> syn::Result<$repr_name> {
match Repr::from_meta(meta)? {
$(Repr::$repr_variant => Ok($repr_name::$repr_variant),)*
Repr::Align(u) => Ok($repr_name::Align(u)),
$(Repr::$repr_variant_aligned(u) => Ok($repr_name::$repr_variant_aligned(u)),)*
_ => Err(Error::new_spanned(meta, concat!("unsupported representation for deriving FromBytes, AsBytes, or Unaligned on ", $type_name)))
}
}
Expand All @@ -155,16 +156,19 @@ macro_rules! define_kind_specific_repr {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
$($repr_name::$repr_variant => Repr::$repr_variant,)*
$repr_name::Align(u) => Repr::Align(*u),
$($repr_name::$repr_variant_aligned(u) => Repr::$repr_variant_aligned(*u),)*
}.fmt(f)
}
}
}
}

define_kind_specific_repr!("a struct", StructRepr, C, Transparent, Packed);
define_kind_specific_repr!("a struct", StructRepr, [C, Transparent, Packed], [Align, PackedN]);
define_kind_specific_repr!(
"an enum", EnumRepr, C, U8, U16, U32, U64, Usize, I8, I16, I32, I64, Isize
"an enum",
EnumRepr,
[C, U8, U16, U32, U64, Usize, I8, I16, I32, I64, Isize],
[Align]
);

// All representations known to Rust.
Expand Down
12 changes: 12 additions & 0 deletions zerocopy-derive/tests/struct_as_bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,18 @@ struct CPacked {

assert_impl_all!(CPacked: AsBytes);

#[derive(AsBytes)]
#[repr(C, packed(2))]
// The same caveats as for CPacked apply - we're assuming u64 is at least
// 4-byte aligned by default. Without packed(2), this should fail, as there
// would be padding between a/b assuming u64 is 4+ byte aligned.
struct CPacked2 {
a: u16,
b: u64,
}

assert_impl_all!(CPacked2: AsBytes);

#[derive(AsBytes)]
#[repr(C, packed)]
struct CPackedGeneric<T, U: ?Sized> {
Expand Down
35 changes: 23 additions & 12 deletions zerocopy-derive/tests/ui-msrv/struct.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,39 @@ error: unsupported on generic structs that are not repr(transparent) or repr(pac
= note: this error originates in the derive macro `AsBytes` (in Nightly builds, run with -Z macro-backtrace for more info)

error: cannot derive Unaligned with repr(align(N > 1))
--> tests/ui-msrv/struct.rs:71:11
--> tests/ui-msrv/struct.rs:80:11
|
71 | #[repr(C, align(2))]
80 | #[repr(C, align(2))]
| ^^^^^^^^

error: cannot derive Unaligned with repr(align(N > 1))
--> tests/ui-msrv/struct.rs:75:21
--> tests/ui-msrv/struct.rs:84:21
|
75 | #[repr(transparent, align(2))]
84 | #[repr(transparent, align(2))]
| ^^^^^^^^

error: cannot derive Unaligned with repr(align(N > 1))
--> tests/ui-msrv/struct.rs:81:16
--> tests/ui-msrv/struct.rs:90:16
|
81 | #[repr(packed, align(2))]
90 | #[repr(packed, align(2))]
| ^^^^^^^^

error: cannot derive Unaligned with repr(align(N > 1))
--> tests/ui-msrv/struct.rs:85:18
--> tests/ui-msrv/struct.rs:94:18
|
85 | #[repr(align(1), align(2))]
94 | #[repr(align(1), align(2))]
| ^^^^^^^^

error: cannot derive Unaligned with repr(align(N > 1))
--> tests/ui-msrv/struct.rs:89:8
--> tests/ui-msrv/struct.rs:98:8
|
89 | #[repr(align(2), align(4))]
98 | #[repr(align(2), align(4))]
| ^^^^^^^^

error[E0692]: transparent struct cannot have other repr hints
--> tests/ui-msrv/struct.rs:75:8
--> tests/ui-msrv/struct.rs:84:8
|
75 | #[repr(transparent, align(2))]
84 | #[repr(transparent, align(2))]
| ^^^^^^^^^^^ ^^^^^^^^

error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
Expand Down Expand Up @@ -100,3 +100,14 @@ error[E0277]: the trait bound `HasPadding<AsBytes2, true>: ShouldBe<false>` is n
<HasPadding<T, VALUE> as ShouldBe<VALUE>>
= help: see issue #48214
= note: this error originates in the derive macro `AsBytes` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `HasPadding<AsBytes3, true>: ShouldBe<false>` is not satisfied
--> tests/ui-msrv/struct.rs:66:10
|
66 | #[derive(AsBytes)]
| ^^^^^^^ the trait `ShouldBe<false>` is not implemented for `HasPadding<AsBytes3, true>`
|
= help: the following implementations were found:
<HasPadding<T, VALUE> as ShouldBe<VALUE>>
= help: see issue #48214
= note: this error originates in the derive macro `AsBytes` (in Nightly builds, run with -Z macro-backtrace for more info)
9 changes: 9 additions & 0 deletions zerocopy-derive/tests/ui-nightly/struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@ struct AsBytes2 {
bar: AU16,
}

#[derive(AsBytes)]
#[repr(C, packed(2))]
struct AsBytes3 {
foo: u8,
// We'd prefer to use AU64 here, but you can't use aligned types in
// packed structs.
bar: u64,
}

//
// Unaligned errors
//
Expand Down
39 changes: 25 additions & 14 deletions zerocopy-derive/tests/ui-nightly/struct.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,39 @@ error: unsupported on generic structs that are not repr(transparent) or repr(pac
= note: this error originates in the derive macro `AsBytes` (in Nightly builds, run with -Z macro-backtrace for more info)

error: cannot derive Unaligned with repr(align(N > 1))
--> tests/ui-nightly/struct.rs:71:11
--> tests/ui-nightly/struct.rs:80:11
|
71 | #[repr(C, align(2))]
80 | #[repr(C, align(2))]
| ^^^^^^^^

error: cannot derive Unaligned with repr(align(N > 1))
--> tests/ui-nightly/struct.rs:75:21
--> tests/ui-nightly/struct.rs:84:21
|
75 | #[repr(transparent, align(2))]
84 | #[repr(transparent, align(2))]
| ^^^^^^^^

error: cannot derive Unaligned with repr(align(N > 1))
--> tests/ui-nightly/struct.rs:81:16
--> tests/ui-nightly/struct.rs:90:16
|
81 | #[repr(packed, align(2))]
90 | #[repr(packed, align(2))]
| ^^^^^^^^

error: cannot derive Unaligned with repr(align(N > 1))
--> tests/ui-nightly/struct.rs:85:18
--> tests/ui-nightly/struct.rs:94:18
|
85 | #[repr(align(1), align(2))]
94 | #[repr(align(1), align(2))]
| ^^^^^^^^

error: cannot derive Unaligned with repr(align(N > 1))
--> tests/ui-nightly/struct.rs:89:8
--> tests/ui-nightly/struct.rs:98:8
|
89 | #[repr(align(2), align(4))]
98 | #[repr(align(2), align(4))]
| ^^^^^^^^

error[E0692]: transparent struct cannot have other repr hints
--> tests/ui-nightly/struct.rs:75:8
--> tests/ui-nightly/struct.rs:84:8
|
75 | #[repr(transparent, align(2))]
84 | #[repr(transparent, align(2))]
| ^^^^^^^^^^^ ^^^^^^^^

error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
Expand Down Expand Up @@ -125,8 +125,19 @@ error[E0277]: the trait bound `HasPadding<AsBytes2, true>: ShouldBe<false>` is n
= help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
= note: this error originates in the derive macro `AsBytes` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `HasPadding<AsBytes3, true>: ShouldBe<false>` is not satisfied
--> tests/ui-nightly/struct.rs:66:10
|
66 | #[derive(AsBytes)]
| ^^^^^^^ the trait `ShouldBe<false>` is not implemented for `HasPadding<AsBytes3, true>`
|
= help: the trait `ShouldBe<true>` is implemented for `HasPadding<AsBytes3, true>`
= help: see issue #48214
= help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
= note: this error originates in the derive macro `AsBytes` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0587]: type has conflicting packed and align representation hints
--> tests/ui-nightly/struct.rs:82:1
--> tests/ui-nightly/struct.rs:91:1
|
82 | struct Unaligned3;
91 | struct Unaligned3;
| ^^^^^^^^^^^^^^^^^
34 changes: 22 additions & 12 deletions zerocopy-derive/tests/ui-stable/struct.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,39 @@ error: unsupported on generic structs that are not repr(transparent) or repr(pac
= note: this error originates in the derive macro `AsBytes` (in Nightly builds, run with -Z macro-backtrace for more info)

error: cannot derive Unaligned with repr(align(N > 1))
--> tests/ui-stable/struct.rs:71:11
--> tests/ui-stable/struct.rs:80:11
|
71 | #[repr(C, align(2))]
80 | #[repr(C, align(2))]
| ^^^^^^^^

error: cannot derive Unaligned with repr(align(N > 1))
--> tests/ui-stable/struct.rs:75:21
--> tests/ui-stable/struct.rs:84:21
|
75 | #[repr(transparent, align(2))]
84 | #[repr(transparent, align(2))]
| ^^^^^^^^

error: cannot derive Unaligned with repr(align(N > 1))
--> tests/ui-stable/struct.rs:81:16
--> tests/ui-stable/struct.rs:90:16
|
81 | #[repr(packed, align(2))]
90 | #[repr(packed, align(2))]
| ^^^^^^^^

error: cannot derive Unaligned with repr(align(N > 1))
--> tests/ui-stable/struct.rs:85:18
--> tests/ui-stable/struct.rs:94:18
|
85 | #[repr(align(1), align(2))]
94 | #[repr(align(1), align(2))]
| ^^^^^^^^

error: cannot derive Unaligned with repr(align(N > 1))
--> tests/ui-stable/struct.rs:89:8
--> tests/ui-stable/struct.rs:98:8
|
89 | #[repr(align(2), align(4))]
98 | #[repr(align(2), align(4))]
| ^^^^^^^^

error[E0692]: transparent struct cannot have other repr hints
--> tests/ui-stable/struct.rs:75:8
--> tests/ui-stable/struct.rs:84:8
|
75 | #[repr(transparent, align(2))]
84 | #[repr(transparent, align(2))]
| ^^^^^^^^^^^ ^^^^^^^^

error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
Expand Down Expand Up @@ -119,3 +119,13 @@ error[E0277]: the trait bound `HasPadding<AsBytes2, true>: ShouldBe<false>` is n
= help: the trait `ShouldBe<VALUE>` is implemented for `HasPadding<T, VALUE>`
= help: see issue #48214
= note: this error originates in the derive macro `AsBytes` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `HasPadding<AsBytes3, true>: ShouldBe<false>` is not satisfied
--> tests/ui-stable/struct.rs:66:10
|
66 | #[derive(AsBytes)]
| ^^^^^^^ the trait `ShouldBe<false>` is not implemented for `HasPadding<AsBytes3, true>`
|
= help: the trait `ShouldBe<VALUE>` is implemented for `HasPadding<T, VALUE>`
= help: see issue #48214
= note: this error originates in the derive macro `AsBytes` (in Nightly builds, run with -Z macro-backtrace for more info)

0 comments on commit 941c83f

Please sign in to comment.