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