xref: /linux/rust/zerocopy-derive/derive/mod.rs (revision b079329b8691768962aa514b8f8c9077ca352459)
1*5f85604cSMiguel Ojeda // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT
2*5f85604cSMiguel Ojeda 
3b437b383SMiguel Ojeda pub mod from_bytes;
4b437b383SMiguel Ojeda pub mod into_bytes;
5b437b383SMiguel Ojeda pub mod known_layout;
6b437b383SMiguel Ojeda pub mod try_from_bytes;
7b437b383SMiguel Ojeda pub mod unaligned;
8b437b383SMiguel Ojeda 
9b437b383SMiguel Ojeda use proc_macro2::{Span, TokenStream};
10b437b383SMiguel Ojeda use quote::quote;
11b437b383SMiguel Ojeda use syn::{Data, Error};
12b437b383SMiguel Ojeda 
13b437b383SMiguel Ojeda use crate::{
14b437b383SMiguel Ojeda     repr::StructUnionRepr,
15b437b383SMiguel Ojeda     util::{Ctx, DataExt, FieldBounds, ImplBlockBuilder, Trait},
16b437b383SMiguel Ojeda };
17b437b383SMiguel Ojeda 
18b437b383SMiguel Ojeda pub(crate) fn derive_immutable(ctx: &Ctx, _top_level: Trait) -> TokenStream {
19b437b383SMiguel Ojeda     match &ctx.ast.data {
20b437b383SMiguel Ojeda         Data::Struct(strct) => {
21b437b383SMiguel Ojeda             ImplBlockBuilder::new(ctx, strct, Trait::Immutable, FieldBounds::ALL_SELF).build()
22b437b383SMiguel Ojeda         }
23b437b383SMiguel Ojeda         Data::Enum(enm) => {
24b437b383SMiguel Ojeda             ImplBlockBuilder::new(ctx, enm, Trait::Immutable, FieldBounds::ALL_SELF).build()
25b437b383SMiguel Ojeda         }
26b437b383SMiguel Ojeda         Data::Union(unn) => {
27b437b383SMiguel Ojeda             ImplBlockBuilder::new(ctx, unn, Trait::Immutable, FieldBounds::ALL_SELF).build()
28b437b383SMiguel Ojeda         }
29b437b383SMiguel Ojeda     }
30b437b383SMiguel Ojeda }
31b437b383SMiguel Ojeda 
32b437b383SMiguel Ojeda pub(crate) fn derive_hash(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
33b437b383SMiguel Ojeda     // This doesn't delegate to `impl_block` because `impl_block` assumes it is
34b437b383SMiguel Ojeda     // deriving a `zerocopy`-defined trait, and these trait impls share a common
35b437b383SMiguel Ojeda     // shape that `Hash` does not. In particular, `zerocopy` traits contain a
36b437b383SMiguel Ojeda     // method that only `zerocopy_derive` macros are supposed to implement, and
37b437b383SMiguel Ojeda     // `impl_block` generating this trait method is incompatible with `Hash`.
38b437b383SMiguel Ojeda     let type_ident = &ctx.ast.ident;
39b437b383SMiguel Ojeda     let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl();
40b437b383SMiguel Ojeda     let where_predicates = where_clause.map(|clause| &clause.predicates);
41b437b383SMiguel Ojeda     let zerocopy_crate = &ctx.zerocopy_crate;
42b437b383SMiguel Ojeda     let core = ctx.core_path();
43b437b383SMiguel Ojeda     Ok(quote! {
44b437b383SMiguel Ojeda         impl #impl_generics #core::hash::Hash for #type_ident #ty_generics
45b437b383SMiguel Ojeda         where
46b437b383SMiguel Ojeda             Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
47b437b383SMiguel Ojeda             #where_predicates
48b437b383SMiguel Ojeda         {
49b437b383SMiguel Ojeda             fn hash<H: #core::hash::Hasher>(&self, state: &mut H) {
50b437b383SMiguel Ojeda                 #core::hash::Hasher::write(state, #zerocopy_crate::IntoBytes::as_bytes(self))
51b437b383SMiguel Ojeda             }
52b437b383SMiguel Ojeda 
53b437b383SMiguel Ojeda             fn hash_slice<H: #core::hash::Hasher>(data: &[Self], state: &mut H) {
54b437b383SMiguel Ojeda                 #core::hash::Hasher::write(state, #zerocopy_crate::IntoBytes::as_bytes(data))
55b437b383SMiguel Ojeda             }
56b437b383SMiguel Ojeda         }
57b437b383SMiguel Ojeda     })
58b437b383SMiguel Ojeda }
59b437b383SMiguel Ojeda 
60b437b383SMiguel Ojeda pub(crate) fn derive_eq(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
61b437b383SMiguel Ojeda     // This doesn't delegate to `impl_block` because `impl_block` assumes it is
62b437b383SMiguel Ojeda     // deriving a `zerocopy`-defined trait, and these trait impls share a common
63b437b383SMiguel Ojeda     // shape that `Eq` does not. In particular, `zerocopy` traits contain a
64b437b383SMiguel Ojeda     // method that only `zerocopy_derive` macros are supposed to implement, and
65b437b383SMiguel Ojeda     // `impl_block` generating this trait method is incompatible with `Eq`.
66b437b383SMiguel Ojeda     let type_ident = &ctx.ast.ident;
67b437b383SMiguel Ojeda     let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl();
68b437b383SMiguel Ojeda     let where_predicates = where_clause.map(|clause| &clause.predicates);
69b437b383SMiguel Ojeda     let zerocopy_crate = &ctx.zerocopy_crate;
70b437b383SMiguel Ojeda     let core = ctx.core_path();
71b437b383SMiguel Ojeda     Ok(quote! {
72b437b383SMiguel Ojeda         impl #impl_generics #core::cmp::PartialEq for #type_ident #ty_generics
73b437b383SMiguel Ojeda         where
74b437b383SMiguel Ojeda             Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
75b437b383SMiguel Ojeda             #where_predicates
76b437b383SMiguel Ojeda         {
77b437b383SMiguel Ojeda             fn eq(&self, other: &Self) -> bool {
78b437b383SMiguel Ojeda                 #core::cmp::PartialEq::eq(
79b437b383SMiguel Ojeda                     #zerocopy_crate::IntoBytes::as_bytes(self),
80b437b383SMiguel Ojeda                     #zerocopy_crate::IntoBytes::as_bytes(other),
81b437b383SMiguel Ojeda                 )
82b437b383SMiguel Ojeda             }
83b437b383SMiguel Ojeda         }
84b437b383SMiguel Ojeda 
85b437b383SMiguel Ojeda         impl #impl_generics #core::cmp::Eq for #type_ident #ty_generics
86b437b383SMiguel Ojeda         where
87b437b383SMiguel Ojeda             Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
88b437b383SMiguel Ojeda             #where_predicates
89b437b383SMiguel Ojeda         {
90b437b383SMiguel Ojeda         }
91b437b383SMiguel Ojeda     })
92b437b383SMiguel Ojeda }
93b437b383SMiguel Ojeda 
94b437b383SMiguel Ojeda pub(crate) fn derive_split_at(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
95b437b383SMiguel Ojeda     let repr = StructUnionRepr::from_attrs(&ctx.ast.attrs)?;
96b437b383SMiguel Ojeda 
97b437b383SMiguel Ojeda     match &ctx.ast.data {
98b437b383SMiguel Ojeda         Data::Struct(_) => {}
99b437b383SMiguel Ojeda         Data::Enum(_) | Data::Union(_) => {
100b437b383SMiguel Ojeda             return Err(Error::new(Span::call_site(), "can only be applied to structs"));
101b437b383SMiguel Ojeda         }
102b437b383SMiguel Ojeda     };
103b437b383SMiguel Ojeda 
104b437b383SMiguel Ojeda     if repr.get_packed().is_some() {
105b437b383SMiguel Ojeda         return Err(Error::new(Span::call_site(), "must not have #[repr(packed)] attribute"));
106b437b383SMiguel Ojeda     }
107b437b383SMiguel Ojeda 
108b437b383SMiguel Ojeda     if !(repr.is_c() || repr.is_transparent()) {
109b437b383SMiguel Ojeda         return Err(Error::new(
110b437b383SMiguel Ojeda             Span::call_site(),
111b437b383SMiguel Ojeda             "must have #[repr(C)] or #[repr(transparent)] in order to guarantee this type's layout is splitable",
112b437b383SMiguel Ojeda         ));
113b437b383SMiguel Ojeda     }
114b437b383SMiguel Ojeda 
115b437b383SMiguel Ojeda     let fields = ctx.ast.data.fields();
116b437b383SMiguel Ojeda     let trailing_field = if let Some(((_, _, trailing_field), _)) = fields.split_last() {
117b437b383SMiguel Ojeda         trailing_field
118b437b383SMiguel Ojeda     } else {
119b437b383SMiguel Ojeda         return Err(Error::new(Span::call_site(), "must at least one field"));
120b437b383SMiguel Ojeda     };
121b437b383SMiguel Ojeda 
122b437b383SMiguel Ojeda     let zerocopy_crate = &ctx.zerocopy_crate;
123b437b383SMiguel Ojeda     // SAFETY: `#ty`, per the above checks, is `repr(C)` or `repr(transparent)`
124b437b383SMiguel Ojeda     // and is not packed; its trailing field is guaranteed to be well-aligned
125b437b383SMiguel Ojeda     // for its type. By invariant on `FieldBounds::TRAILING_SELF`, the trailing
126b437b383SMiguel Ojeda     // slice of the trailing field is also well-aligned for its type.
127b437b383SMiguel Ojeda     Ok(ImplBlockBuilder::new(ctx, &ctx.ast.data, Trait::SplitAt, FieldBounds::TRAILING_SELF)
128b437b383SMiguel Ojeda         .inner_extras(quote! {
129b437b383SMiguel Ojeda             type Elem = <#trailing_field as #zerocopy_crate::SplitAt>::Elem;
130b437b383SMiguel Ojeda         })
131b437b383SMiguel Ojeda         .build())
132b437b383SMiguel Ojeda }
133