Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3251,4 +3251,22 @@ mod tests {
assert_eq!(<()>::new_zeroed(), ());
}
}

#[test]
fn test_transparent_generic_struct() {
#[derive(AsBytes, FromBytes, Unaligned)]
#[repr(transparent)]
struct Foo<T> {
_bar: T,
_phantom: PhantomData<()>,
}

fn assert_impls_asbytes<T: AsBytes>() {}
fn assert_impls_frombytes<T: FromBytes>() {}
fn assert_impls_unaligned<T: Unaligned>() {}

assert_impls_asbytes::<Foo<f32>>();
assert_impls_frombytes::<Foo<u32>>();
assert_impls_unaligned::<Foo<u8>>();
}
}
26 changes: 18 additions & 8 deletions zerocopy-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,16 +195,26 @@ fn derive_from_bytes_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::T
// - `repr(packed)`

fn derive_as_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream {
// TODO(#10): Support type parameters.
if !ast.generics.params.is_empty() {
return Error::new(Span::call_site(), "unsupported on types with type parameters")
.to_compile_error();
}

let reprs = try_or_print!(STRUCT_UNION_AS_BYTES_CFG.validate_reprs(ast));
let padding_check =
if reprs.contains(&StructRepr::Packed) { PaddingCheck::None } else { PaddingCheck::Struct };
let is_transparent = reprs.contains(&StructRepr::Transparent);

// TODO(#10): Support type parameters for non-transparent structs.
if !ast.generics.params.is_empty() && !is_transparent {
return Error::new(
Span::call_site(),
"unsupported on generic structs that are not repr(transparent)",
)
.to_compile_error();
}

// We don't need a padding check if the struct is repr(transparent) since
// the layout and ABI of the whole struct is the same as its only non-ZST
// field, and we require that field to be `AsBytes`.
let padding_check = if is_transparent || reprs.contains(&StructRepr::Packed) {
PaddingCheck::None
} else {
PaddingCheck::Struct
};
impl_block(ast, strct, "AsBytes", true, padding_check)
}

Expand Down
36 changes: 14 additions & 22 deletions zerocopy-derive/tests/enum_as_bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,10 @@

#![allow(warnings)]

use zerocopy::AsBytes;

struct IsAsBytes<T: AsBytes>(T);
#[macro_use]
mod util;

// Fail compilation if `$ty: !AsBytes`.
macro_rules! is_as_bytes {
($ty:ty) => {
const _: () = {
let _: IsAsBytes<$ty>;
};
};
}
use zerocopy::AsBytes;

// An enum is `AsBytes` if if has a defined repr.

Expand All @@ -25,84 +17,84 @@ enum C {
A,
}

is_as_bytes!(C);
assert_is_as_bytes!(C);

#[derive(AsBytes)]
#[repr(u8)]
enum U8 {
A,
}

is_as_bytes!(U8);
assert_is_as_bytes!(U8);

#[derive(AsBytes)]
#[repr(u16)]
enum U16 {
A,
}

is_as_bytes!(U16);
assert_is_as_bytes!(U16);

#[derive(AsBytes)]
#[repr(u32)]
enum U32 {
A,
}

is_as_bytes!(U32);
assert_is_as_bytes!(U32);

#[derive(AsBytes)]
#[repr(u64)]
enum U64 {
A,
}

is_as_bytes!(U64);
assert_is_as_bytes!(U64);

#[derive(AsBytes)]
#[repr(usize)]
enum Usize {
A,
}

is_as_bytes!(Usize);
assert_is_as_bytes!(Usize);

#[derive(AsBytes)]
#[repr(i8)]
enum I8 {
A,
}

is_as_bytes!(I8);
assert_is_as_bytes!(I8);

#[derive(AsBytes)]
#[repr(i16)]
enum I16 {
A,
}

is_as_bytes!(I16);
assert_is_as_bytes!(I16);

#[derive(AsBytes)]
#[repr(i32)]
enum I32 {
A,
}

is_as_bytes!(I32);
assert_is_as_bytes!(I32);

#[derive(AsBytes)]
#[repr(i64)]
enum I64 {
A,
}

is_as_bytes!(I64);
assert_is_as_bytes!(I64);

#[derive(AsBytes)]
#[repr(isize)]
enum Isize {
A,
}

is_as_bytes!(Isize);
assert_is_as_bytes!(Isize);
26 changes: 9 additions & 17 deletions zerocopy-derive/tests/enum_from_bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

#![allow(warnings)]

#[macro_use]
mod util;

use zerocopy::FromBytes;

// An enum is `FromBytes` if:
Expand All @@ -23,17 +26,6 @@ use zerocopy::FromBytes;
// `Variant128` has a discriminant of -128) since Rust won't automatically wrap
// a signed discriminant around without you explicitly telling it to.

struct IsFromBytes<T: FromBytes>(T);

// Fail compilation if `$ty: !FromBytes`.
macro_rules! is_from_bytes {
($ty:ty) => {
const _: () = {
let _: IsFromBytes<$ty>;
};
};
}

#[derive(FromBytes)]
#[repr(u8)]
enum FooU8 {
Expand Down Expand Up @@ -295,7 +287,7 @@ enum FooU8 {
Variant255,
}

is_from_bytes!(FooU8);
assert_is_from_bytes!(FooU8);

#[derive(FromBytes)]
#[repr(i8)]
Expand Down Expand Up @@ -558,7 +550,7 @@ enum FooI8 {
Variant255,
}

is_from_bytes!(FooI8);
assert_is_from_bytes!(FooI8);

#[derive(FromBytes)]
#[repr(u8, align(2))]
Expand Down Expand Up @@ -821,7 +813,7 @@ enum FooU8Align {
Variant255,
}

is_from_bytes!(FooU8Align);
assert_is_from_bytes!(FooU8Align);

#[derive(FromBytes)]
#[repr(i8, align(2))]
Expand Down Expand Up @@ -1084,7 +1076,7 @@ enum FooI8Align {
Variant255,
}

is_from_bytes!(FooI8Align);
assert_is_from_bytes!(FooI8Align);

#[derive(FromBytes)]
#[repr(u16)]
Expand Down Expand Up @@ -66627,7 +66619,7 @@ enum FooU16 {
Variant65535,
}

is_from_bytes!(FooU16);
assert_is_from_bytes!(FooU16);

#[derive(FromBytes)]
#[repr(i16)]
Expand Down Expand Up @@ -132170,4 +132162,4 @@ enum FooI16 {
Variant65535,
}

is_from_bytes!(FooI16);
assert_is_from_bytes!(FooI16);
22 changes: 7 additions & 15 deletions zerocopy-derive/tests/enum_unaligned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,10 @@

#![allow(warnings)]

use zerocopy::Unaligned;

struct IsUnaligned<T: Unaligned>(T);
#[macro_use]
mod util;

// Fail compilation if `$ty: !Unaligned`.
macro_rules! is_unaligned {
($ty:ty) => {
const _: () = {
let _: IsUnaligned<$ty>;
};
};
}
use zerocopy::Unaligned;

// An enum is `Unaligned` if:
// - No `repr(align(N > 1))`
Expand All @@ -27,28 +19,28 @@ enum Foo {
A,
}

is_unaligned!(Foo);
assert_is_unaligned!(Foo);

#[derive(Unaligned)]
#[repr(i8)]
enum Bar {
A,
}

is_unaligned!(Bar);
assert_is_unaligned!(Bar);

#[derive(Unaligned)]
#[repr(u8, align(1))]
enum Baz {
A,
}

is_unaligned!(Baz);
assert_is_unaligned!(Baz);

#[derive(Unaligned)]
#[repr(i8, align(1))]
enum Blah {
B,
}

is_unaligned!(Blah);
assert_is_unaligned!(Blah);
26 changes: 7 additions & 19 deletions zerocopy-derive/tests/struct_as_bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,13 @@

#![allow(warnings)]

#[macro_use]
mod util;

use self::util::AU16;
use std::{marker::PhantomData, option::IntoIter};

use zerocopy::AsBytes;

use crate::util::AU16;

struct IsAsBytes<T: AsBytes>(T);

// Fail compilation if `$ty: !AsBytes`.
macro_rules! is_as_bytes {
($ty:ty) => {
const _: () = {
let _: IsAsBytes<$ty>;
};
};
}

// A struct is `AsBytes` if:
// - all fields are `AsBytes`
// - `repr(C)` or `repr(transparent)` and
Expand All @@ -33,7 +21,7 @@ macro_rules! is_as_bytes {
#[repr(C)]
struct CZst;

is_as_bytes!(CZst);
assert_is_as_bytes!(CZst);

#[derive(AsBytes)]
#[repr(C)]
Expand All @@ -43,7 +31,7 @@ struct C {
c: AU16,
}

is_as_bytes!(C);
assert_is_as_bytes!(C);

#[derive(AsBytes)]
#[repr(transparent)]
Expand All @@ -52,13 +40,13 @@ struct Transparent {
b: CZst,
}

is_as_bytes!(Transparent);
assert_is_as_bytes!(Transparent);

#[derive(AsBytes)]
#[repr(C, packed)]
struct CZstPacked;

is_as_bytes!(CZstPacked);
assert_is_as_bytes!(CZstPacked);

#[derive(AsBytes)]
#[repr(C, packed)]
Expand All @@ -74,4 +62,4 @@ struct CPacked {
b: u16,
}

is_as_bytes!(CPacked);
assert_is_as_bytes!(CPacked);
Loading