zerocopy_derive/derive/
into_bytes.rs1use proc_macro2::{Span, TokenStream};
2use quote::quote;
3use syn::{Data, DataEnum, DataStruct, DataUnion, Error, Type};
4
5use crate::{
6 repr::{EnumRepr, StructUnionRepr},
7 util::{
8 generate_tag_enum, Ctx, DataExt, FieldBounds, ImplBlockBuilder, PaddingCheck, Trait,
9 TraitBound,
10 },
11};
12pub(crate) fn derive_into_bytes(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
13 match &ctx.ast.data {
14 Data::Struct(strct) => derive_into_bytes_struct(ctx, strct),
15 Data::Enum(enm) => derive_into_bytes_enum(ctx, enm),
16 Data::Union(unn) => derive_into_bytes_union(ctx, unn),
17 }
18}
19fn derive_into_bytes_struct(ctx: &Ctx, strct: &DataStruct) -> Result<TokenStream, Error> {
20 let repr = StructUnionRepr::from_attrs(&ctx.ast.attrs)?;
21
22 let is_transparent = repr.is_transparent();
23 let is_c = repr.is_c();
24 let is_packed_1 = repr.is_packed_1();
25 let num_fields = strct.fields().len();
26
27 let (padding_check, require_unaligned_fields) = if is_transparent || is_packed_1 {
28 (None, false)
45 } else if is_c && !repr.is_align_gt_1() && num_fields <= 1 {
46 (None, false)
50 } else if ctx.ast.generics.params.is_empty() {
51 let is_syntactic_dst =
53 strct.fields().last().map(|(_, _, ty)| matches!(ty, Type::Slice(_))).unwrap_or(false);
54 if is_c && is_syntactic_dst {
67 (Some(PaddingCheck::ReprCStruct), false)
68 } else {
69 (Some(PaddingCheck::Struct), false)
70 }
71 } else if is_c && !repr.is_align_gt_1() {
72 (None, true)
81 } else {
82 return ctx.error_or_skip(Error::new(
83 Span::call_site(),
84 "must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout",
85 ));
86 };
87
88 let field_bounds = if require_unaligned_fields {
89 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Unaligned)])
90 } else {
91 FieldBounds::ALL_SELF
92 };
93
94 Ok(ImplBlockBuilder::new(ctx, strct, Trait::IntoBytes, field_bounds)
95 .padding_check(padding_check)
96 .build())
97}
98
99fn derive_into_bytes_enum(ctx: &Ctx, enm: &DataEnum) -> Result<TokenStream, Error> {
100 let repr = EnumRepr::from_attrs(&ctx.ast.attrs)?;
101 if !repr.is_c() && !repr.is_primitive() {
102 return ctx.error_or_skip(Error::new(
103 Span::call_site(),
104 "must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout",
105 ));
106 }
107
108 let tag_type_definition = generate_tag_enum(ctx, &repr, enm);
109 Ok(ImplBlockBuilder::new(ctx, enm, Trait::IntoBytes, FieldBounds::ALL_SELF)
110 .padding_check(PaddingCheck::Enum { tag_type_definition })
111 .build())
112}
113
114fn derive_into_bytes_union(ctx: &Ctx, unn: &DataUnion) -> Result<TokenStream, Error> {
115 let cfg_compile_error = if cfg!(zerocopy_derive_union_into_bytes) {
124 quote!()
125 } else {
126 let core = ctx.core_path();
127 let error_message = "requires --cfg zerocopy_derive_union_into_bytes;
128please let us know you use this feature: https://github.com/google/zerocopy/discussions/1802";
129 quote!(
130 #[allow(unused_attributes, unexpected_cfgs)]
131 const _: () = {
132 #[cfg(not(zerocopy_derive_union_into_bytes))]
133 #core::compile_error!(#error_message);
134 };
135 )
136 };
137
138 if !ctx.ast.generics.params.is_empty() {
140 return ctx.error_or_skip(Error::new(
141 Span::call_site(),
142 "unsupported on types with type parameters",
143 ));
144 }
145
146 let repr = StructUnionRepr::from_attrs(&ctx.ast.attrs)?;
151 if !repr.is_c() && !repr.is_transparent() && !repr.is_packed_1() {
152 return ctx.error_or_skip(Error::new(
153 Span::call_site(),
154 "must be #[repr(C)], #[repr(packed)], or #[repr(transparent)]",
155 ));
156 }
157
158 let impl_block = ImplBlockBuilder::new(ctx, unn, Trait::IntoBytes, FieldBounds::ALL_SELF)
159 .padding_check(PaddingCheck::Union)
160 .build();
161 Ok(quote!(#cfg_compile_error #impl_block))
162}