1#![allow(unknown_lints)]
18#![deny(renamed_and_removed_lints)]
19#![deny(
20 clippy::all,
21 clippy::missing_safety_doc,
22 clippy::multiple_unsafe_ops_per_block,
23 clippy::undocumented_unsafe_blocks
24)]
25#![allow(clippy::type_complexity)]
27#![allow(clippy::uninlined_format_args)]
29#![deny(
30 rustdoc::bare_urls,
31 rustdoc::broken_intra_doc_links,
32 rustdoc::invalid_codeblock_attributes,
33 rustdoc::invalid_html_tags,
34 rustdoc::invalid_rust_codeblocks,
35 rustdoc::missing_crate_level_docs,
36 rustdoc::private_intra_doc_links
37)]
38#![recursion_limit = "128"]
39
40macro_rules! ident {
41 (($fmt:literal $(, $arg:expr)*), $span:expr) => {
42 syn::Ident::new(&format!($fmt $(, crate::ext::to_ident_str($arg))*), $span)
43 };
44}
45
46mod r#enum;
47mod ext;
48#[cfg(test)]
49mod output_tests;
50mod repr;
51
52use proc_macro2::{Span, TokenStream};
53use quote::{quote, ToTokens};
54use syn::{
55 parse_quote, spanned::Spanned as _, Attribute, Data, DataEnum, DataStruct, DataUnion,
56 DeriveInput, Error, Expr, ExprLit, ExprUnary, GenericParam, Ident, Lit, Meta, Path, Type, UnOp,
57 WherePredicate,
58};
59
60use crate::{ext::*, repr::*};
61
62macro_rules! derive {
86 ($trait:ident => $outer:ident => $inner:ident) => {
87 #[proc_macro_derive($trait, attributes(zerocopy))]
88 pub fn $outer(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
89 let ast = syn::parse_macro_input!(ts as DeriveInput);
90 let zerocopy_crate = match extract_zerocopy_crate(&ast.attrs) {
91 Ok(zerocopy_crate) => zerocopy_crate,
92 Err(e) => return e.into_compile_error().into(),
93 };
94 $inner(&ast, Trait::$trait, &zerocopy_crate).into_ts().into()
95 }
96 };
97}
98
99trait IntoTokenStream {
100 fn into_ts(self) -> TokenStream;
101}
102
103impl IntoTokenStream for TokenStream {
104 fn into_ts(self) -> TokenStream {
105 self
106 }
107}
108
109impl IntoTokenStream for Result<TokenStream, Error> {
110 fn into_ts(self) -> TokenStream {
111 match self {
112 Ok(ts) => ts,
113 Err(err) => err.to_compile_error(),
114 }
115 }
116}
117
118fn extract_zerocopy_crate(attrs: &[Attribute]) -> Result<Path, Error> {
121 let mut path = parse_quote!(::zerocopy);
122
123 for attr in attrs {
124 if let Meta::List(ref meta_list) = attr.meta {
125 if meta_list.path.is_ident("zerocopy") {
126 attr.parse_nested_meta(|meta| {
127 if meta.path.is_ident("crate") {
128 let expr = meta.value().and_then(|value| value.parse());
129 if let Ok(Expr::Lit(ExprLit { lit: Lit::Str(lit), .. })) = expr {
130 if let Ok(path_lit) = lit.parse() {
131 path = path_lit;
132 return Ok(());
133 }
134 }
135
136 return Err(Error::new(
137 Span::call_site(),
138 "`crate` attribute requires a path as the value",
139 ));
140 }
141
142 Err(Error::new(
143 Span::call_site(),
144 format!("unknown attribute encountered: {}", meta.path.into_token_stream()),
145 ))
146 })?;
147 }
148 }
149 }
150
151 Ok(path)
152}
153
154derive!(KnownLayout => derive_known_layout => derive_known_layout_inner);
155derive!(Immutable => derive_no_cell => derive_no_cell_inner);
156derive!(TryFromBytes => derive_try_from_bytes => derive_try_from_bytes_inner);
157derive!(FromZeros => derive_from_zeros => derive_from_zeros_inner);
158derive!(FromBytes => derive_from_bytes => derive_from_bytes_inner);
159derive!(IntoBytes => derive_into_bytes => derive_into_bytes_inner);
160derive!(Unaligned => derive_unaligned => derive_unaligned_inner);
161derive!(ByteHash => derive_hash => derive_hash_inner);
162derive!(ByteEq => derive_eq => derive_eq_inner);
163derive!(SplitAt => derive_split_at => derive_split_at_inner);
164
165#[deprecated(since = "0.8.0", note = "`FromZeroes` was renamed to `FromZeros`")]
167#[doc(hidden)]
168#[proc_macro_derive(FromZeroes)]
169pub fn derive_from_zeroes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
170 derive_from_zeros(ts)
171}
172
173#[deprecated(since = "0.8.0", note = "`AsBytes` was renamed to `IntoBytes`")]
175#[doc(hidden)]
176#[proc_macro_derive(AsBytes)]
177pub fn derive_as_bytes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
178 derive_into_bytes(ts)
179}
180
181fn derive_known_layout_inner(
182 ast: &DeriveInput,
183 _top_level: Trait,
184 zerocopy_crate: &Path,
185) -> Result<TokenStream, Error> {
186 let is_repr_c_struct = match &ast.data {
187 Data::Struct(..) => {
188 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
189 if repr.is_c() {
190 Some(repr)
191 } else {
192 None
193 }
194 }
195 Data::Enum(..) | Data::Union(..) => None,
196 };
197
198 let fields = ast.data.fields();
199
200 let (self_bounds, inner_extras, outer_extras) = if let (
201 Some(repr),
202 Some((trailing_field, leading_fields)),
203 ) = (is_repr_c_struct, fields.split_last())
204 {
205 let (_vis, trailing_field_name, trailing_field_ty) = trailing_field;
206 let leading_fields_tys = leading_fields.iter().map(|(_vis, _name, ty)| ty);
207
208 let core_path = quote!(#zerocopy_crate::util::macro_util::core_reexport);
209 let repr_align = repr
210 .get_align()
211 .map(|align| {
212 let align = align.t.get();
213 quote!(#core_path::num::NonZeroUsize::new(#align as usize))
214 })
215 .unwrap_or_else(|| quote!(#core_path::option::Option::None));
216 let repr_packed = repr
217 .get_packed()
218 .map(|packed| {
219 let packed = packed.get();
220 quote!(#core_path::num::NonZeroUsize::new(#packed as usize))
221 })
222 .unwrap_or_else(|| quote!(#core_path::option::Option::None));
223
224 let make_methods = |trailing_field_ty| {
225 quote! {
226 #[inline(always)]
257 fn raw_from_ptr_len(
258 bytes: #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<u8>,
259 meta: Self::PointerMetadata,
260 ) -> #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<Self> {
261 use #zerocopy_crate::KnownLayout;
262 let trailing = <#trailing_field_ty as KnownLayout>::raw_from_ptr_len(bytes, meta);
263 let slf = trailing.as_ptr() as *mut Self;
264 unsafe { #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull::new_unchecked(slf) }
266 }
267
268 #[inline(always)]
269 fn pointer_to_metadata(ptr: *mut Self) -> Self::PointerMetadata {
270 <#trailing_field_ty>::pointer_to_metadata(ptr as *mut _)
271 }
272 }
273 };
274
275 let inner_extras = {
276 let leading_fields_tys = leading_fields_tys.clone();
277 let methods = make_methods(*trailing_field_ty);
278 let (_, ty_generics, _) = ast.generics.split_for_impl();
279
280 quote!(
281 type PointerMetadata = <#trailing_field_ty as #zerocopy_crate::KnownLayout>::PointerMetadata;
282
283 type MaybeUninit = __ZerocopyKnownLayoutMaybeUninit #ty_generics;
284
285 const LAYOUT: #zerocopy_crate::DstLayout = {
300 use #zerocopy_crate::util::macro_util::core_reexport::num::NonZeroUsize;
301 use #zerocopy_crate::{DstLayout, KnownLayout};
302
303 DstLayout::for_repr_c_struct(
304 #repr_align,
305 #repr_packed,
306 &[
307 #(DstLayout::for_type::<#leading_fields_tys>(),)*
308 <#trailing_field_ty as KnownLayout>::LAYOUT
309 ],
310 )
311 };
312
313 #methods
314 )
315 };
316
317 let outer_extras = {
318 let ident = &ast.ident;
319 let vis = &ast.vis;
320 let params = &ast.generics.params;
321 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
322
323 let predicates = if let Some(where_clause) = where_clause {
324 where_clause.predicates.clone()
325 } else {
326 Default::default()
327 };
328
329 let field_index =
332 |name: &TokenStream| ident!(("__Zerocopy_Field_{}", name), ident.span());
333
334 let field_indices: Vec<_> =
335 fields.iter().map(|(_vis, name, _ty)| field_index(name)).collect();
336
337 let field_defs = field_indices.iter().zip(&fields).map(|(idx, (vis, _, _))| {
339 quote! {
340 #[allow(non_camel_case_types)]
341 #vis struct #idx;
342 }
343 });
344
345 let field_impls = field_indices.iter().zip(&fields).map(|(idx, (_, _, ty))| quote! {
346 #[allow(deprecated)]
356 unsafe impl #impl_generics #zerocopy_crate::util::macro_util::Field<#idx> for #ident #ty_generics
357 where
358 #predicates
359 {
360 type Type = #ty;
361 }
362 });
363
364 let trailing_field_index = field_index(trailing_field_name);
365 let leading_field_indices =
366 leading_fields.iter().map(|(_vis, name, _ty)| field_index(name));
367
368 let trailing_field_ty = quote! {
373 <#ident #ty_generics as
374 #zerocopy_crate::util::macro_util::Field<#trailing_field_index>
375 >::Type
376 };
377
378 let methods = make_methods(&parse_quote! {
379 <#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit
380 });
381
382 quote! {
383 #(#field_defs)*
384
385 #(#field_impls)*
386
387 #repr
395 #[doc(hidden)]
396 #[allow(private_bounds)]
400 #[allow(deprecated)]
401 #vis struct __ZerocopyKnownLayoutMaybeUninit<#params> (
402 #(#zerocopy_crate::util::macro_util::core_reexport::mem::MaybeUninit<
403 <#ident #ty_generics as
404 #zerocopy_crate::util::macro_util::Field<#leading_field_indices>
405 >::Type
406 >,)*
407 #zerocopy_crate::util::macro_util::core_reexport::mem::ManuallyDrop<
414 <#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit
415 >
416 )
417 where
418 #trailing_field_ty: #zerocopy_crate::KnownLayout,
419 #predicates;
420
421 #[allow(deprecated)]
428 unsafe impl #impl_generics #zerocopy_crate::KnownLayout for __ZerocopyKnownLayoutMaybeUninit #ty_generics
429 where
430 #trailing_field_ty: #zerocopy_crate::KnownLayout,
431 #predicates
432 {
433 #[allow(clippy::missing_inline_in_public_items)]
434 fn only_derive_is_allowed_to_implement_this_trait() {}
435
436 type PointerMetadata = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::PointerMetadata;
437
438 type MaybeUninit = Self;
439
440 const LAYOUT: #zerocopy_crate::DstLayout = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::LAYOUT;
441
442 #methods
443 }
444 }
445 };
446
447 (SelfBounds::None, inner_extras, Some(outer_extras))
448 } else {
449 (
453 SelfBounds::SIZED,
454 quote!(
455 type PointerMetadata = ();
456 type MaybeUninit =
457 #zerocopy_crate::util::macro_util::core_reexport::mem::MaybeUninit<Self>;
458
459 const LAYOUT: #zerocopy_crate::DstLayout = #zerocopy_crate::DstLayout::for_type::<Self>();
463
464 #[inline(always)]
469 fn raw_from_ptr_len(
470 bytes: #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<u8>,
471 _meta: (),
472 ) -> #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<Self>
473 {
474 bytes.cast::<Self>()
475 }
476
477 #[inline(always)]
478 fn pointer_to_metadata(_ptr: *mut Self) -> () {}
479 ),
480 None,
481 )
482 };
483
484 Ok(match &ast.data {
485 Data::Struct(strct) => {
486 let require_trait_bound_on_field_types =
487 if matches!(self_bounds, SelfBounds::All(&[Trait::Sized])) {
488 FieldBounds::None
489 } else {
490 FieldBounds::TRAILING_SELF
491 };
492
493 ImplBlockBuilder::new(
498 ast,
499 strct,
500 Trait::KnownLayout,
501 require_trait_bound_on_field_types,
502 zerocopy_crate,
503 )
504 .self_type_trait_bounds(self_bounds)
505 .inner_extras(inner_extras)
506 .outer_extras(outer_extras)
507 .build()
508 }
509 Data::Enum(enm) => {
510 ImplBlockBuilder::new(ast, enm, Trait::KnownLayout, FieldBounds::None, zerocopy_crate)
513 .self_type_trait_bounds(SelfBounds::SIZED)
514 .inner_extras(inner_extras)
515 .outer_extras(outer_extras)
516 .build()
517 }
518 Data::Union(unn) => {
519 ImplBlockBuilder::new(ast, unn, Trait::KnownLayout, FieldBounds::None, zerocopy_crate)
522 .self_type_trait_bounds(SelfBounds::SIZED)
523 .inner_extras(inner_extras)
524 .outer_extras(outer_extras)
525 .build()
526 }
527 })
528}
529
530fn derive_no_cell_inner(
531 ast: &DeriveInput,
532 _top_level: Trait,
533 zerocopy_crate: &Path,
534) -> TokenStream {
535 match &ast.data {
536 Data::Struct(strct) => ImplBlockBuilder::new(
537 ast,
538 strct,
539 Trait::Immutable,
540 FieldBounds::ALL_SELF,
541 zerocopy_crate,
542 )
543 .build(),
544 Data::Enum(enm) => {
545 ImplBlockBuilder::new(ast, enm, Trait::Immutable, FieldBounds::ALL_SELF, zerocopy_crate)
546 .build()
547 }
548 Data::Union(unn) => {
549 ImplBlockBuilder::new(ast, unn, Trait::Immutable, FieldBounds::ALL_SELF, zerocopy_crate)
550 .build()
551 }
552 }
553}
554
555fn derive_try_from_bytes_inner(
556 ast: &DeriveInput,
557 top_level: Trait,
558 zerocopy_crate: &Path,
559) -> Result<TokenStream, Error> {
560 match &ast.data {
561 Data::Struct(strct) => derive_try_from_bytes_struct(ast, strct, top_level, zerocopy_crate),
562 Data::Enum(enm) => derive_try_from_bytes_enum(ast, enm, top_level, zerocopy_crate),
563 Data::Union(unn) => Ok(derive_try_from_bytes_union(ast, unn, top_level, zerocopy_crate)),
564 }
565}
566
567fn derive_from_zeros_inner(
568 ast: &DeriveInput,
569 top_level: Trait,
570 zerocopy_crate: &Path,
571) -> Result<TokenStream, Error> {
572 let try_from_bytes = derive_try_from_bytes_inner(ast, top_level, zerocopy_crate)?;
573 let from_zeros = match &ast.data {
574 Data::Struct(strct) => derive_from_zeros_struct(ast, strct, zerocopy_crate),
575 Data::Enum(enm) => derive_from_zeros_enum(ast, enm, zerocopy_crate)?,
576 Data::Union(unn) => derive_from_zeros_union(ast, unn, zerocopy_crate),
577 };
578 Ok(IntoIterator::into_iter([try_from_bytes, from_zeros]).collect())
579}
580
581fn derive_from_bytes_inner(
582 ast: &DeriveInput,
583 top_level: Trait,
584 zerocopy_crate: &Path,
585) -> Result<TokenStream, Error> {
586 let from_zeros = derive_from_zeros_inner(ast, top_level, zerocopy_crate)?;
587 let from_bytes = match &ast.data {
588 Data::Struct(strct) => derive_from_bytes_struct(ast, strct, zerocopy_crate),
589 Data::Enum(enm) => derive_from_bytes_enum(ast, enm, zerocopy_crate)?,
590 Data::Union(unn) => derive_from_bytes_union(ast, unn, zerocopy_crate),
591 };
592
593 Ok(IntoIterator::into_iter([from_zeros, from_bytes]).collect())
594}
595
596fn derive_into_bytes_inner(
597 ast: &DeriveInput,
598 _top_level: Trait,
599 zerocopy_crate: &Path,
600) -> Result<TokenStream, Error> {
601 match &ast.data {
602 Data::Struct(strct) => derive_into_bytes_struct(ast, strct, zerocopy_crate),
603 Data::Enum(enm) => derive_into_bytes_enum(ast, enm, zerocopy_crate),
604 Data::Union(unn) => derive_into_bytes_union(ast, unn, zerocopy_crate),
605 }
606}
607
608fn derive_unaligned_inner(
609 ast: &DeriveInput,
610 _top_level: Trait,
611 zerocopy_crate: &Path,
612) -> Result<TokenStream, Error> {
613 match &ast.data {
614 Data::Struct(strct) => derive_unaligned_struct(ast, strct, zerocopy_crate),
615 Data::Enum(enm) => derive_unaligned_enum(ast, enm, zerocopy_crate),
616 Data::Union(unn) => derive_unaligned_union(ast, unn, zerocopy_crate),
617 }
618}
619
620fn derive_hash_inner(
621 ast: &DeriveInput,
622 _top_level: Trait,
623 zerocopy_crate: &Path,
624) -> Result<TokenStream, Error> {
625 let type_ident = &ast.ident;
631 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
632 let where_predicates = where_clause.map(|clause| &clause.predicates);
633 Ok(quote! {
634 #[allow(deprecated)]
635 #[automatically_derived]
638 impl #impl_generics #zerocopy_crate::util::macro_util::core_reexport::hash::Hash for #type_ident #ty_generics
639 where
640 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
641 #where_predicates
642 {
643 fn hash<H>(&self, state: &mut H)
644 where
645 H: #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher,
646 {
647 #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher::write(
648 state,
649 #zerocopy_crate::IntoBytes::as_bytes(self)
650 )
651 }
652
653 fn hash_slice<H>(data: &[Self], state: &mut H)
654 where
655 H: #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher,
656 {
657 #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher::write(
658 state,
659 #zerocopy_crate::IntoBytes::as_bytes(data)
660 )
661 }
662 }
663 })
664}
665
666fn derive_eq_inner(
667 ast: &DeriveInput,
668 _top_level: Trait,
669 zerocopy_crate: &Path,
670) -> Result<TokenStream, Error> {
671 let type_ident = &ast.ident;
677 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
678 let where_predicates = where_clause.map(|clause| &clause.predicates);
679 Ok(quote! {
680 #[allow(deprecated)]
683 #[automatically_derived]
686 impl #impl_generics #zerocopy_crate::util::macro_util::core_reexport::cmp::PartialEq for #type_ident #ty_generics
687 where
688 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
689 #where_predicates
690 {
691 fn eq(&self, other: &Self) -> bool {
692 #zerocopy_crate::util::macro_util::core_reexport::cmp::PartialEq::eq(
693 #zerocopy_crate::IntoBytes::as_bytes(self),
694 #zerocopy_crate::IntoBytes::as_bytes(other),
695 )
696 }
697 }
698
699 #[allow(deprecated)]
702 #[automatically_derived]
705 impl #impl_generics #zerocopy_crate::util::macro_util::core_reexport::cmp::Eq for #type_ident #ty_generics
706 where
707 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
708 #where_predicates
709 {
710 }
711 })
712}
713
714fn derive_split_at_inner(
715 ast: &DeriveInput,
716 _top_level: Trait,
717 zerocopy_crate: &Path,
718) -> Result<TokenStream, Error> {
719 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
720
721 match &ast.data {
722 Data::Struct(_) => {}
723 Data::Enum(_) | Data::Union(_) => {
724 return Err(Error::new(Span::call_site(), "can only be applied to structs"));
725 }
726 };
727
728 if repr.get_packed().is_some() {
729 return Err(Error::new(Span::call_site(), "must not have #[repr(packed)] attribute"));
730 }
731
732 if !(repr.is_c() || repr.is_transparent()) {
733 return Err(Error::new(Span::call_site(), "must have #[repr(C)] or #[repr(transparent)] in order to guarantee this type's layout is splitable"));
734 }
735
736 let fields = ast.data.fields();
737 let trailing_field = if let Some(((_, _, trailing_field), _)) = fields.split_last() {
738 trailing_field
739 } else {
740 return Err(Error::new(Span::call_site(), "must at least one field"));
741 };
742
743 Ok(ImplBlockBuilder::new(
748 ast,
749 &ast.data,
750 Trait::SplitAt,
751 FieldBounds::TRAILING_SELF,
752 zerocopy_crate,
753 )
754 .inner_extras(quote! {
755 type Elem = <#trailing_field as ::zerocopy::SplitAt>::Elem;
756 })
757 .build())
758}
759
760fn derive_has_field_struct_union(
761 ast: &DeriveInput,
762 data: &dyn DataExt,
763 zerocopy_crate: &Path,
764) -> TokenStream {
765 let fields = ast.data.fields();
766 if fields.is_empty() {
767 return quote! {};
768 }
769
770 let field_tokens = fields.iter().map(|(vis, ident, _)| {
771 let ident = ident!(("ẕ{}", ident), ident.span());
772 quote!(
773 #vis enum #ident {}
774 )
775 });
776
777 let variant_id: Box<Expr> = match &ast.data {
778 Data::Struct(_) => parse_quote!({ #zerocopy_crate::STRUCT_VARIANT_ID }),
779 Data::Union(_) => parse_quote!({ #zerocopy_crate::UNION_VARIANT_ID }),
780 _ => unreachable!(),
781 };
782
783 let is_repr_c_union = match &ast.data {
784 Data::Union(..) => {
785 StructUnionRepr::from_attrs(&ast.attrs).map(|repr| repr.is_c()).unwrap_or(false)
786 }
787 Data::Enum(..) | Data::Struct(..) => false,
788 };
789 let has_fields = fields.iter().map(move |(_, ident, ty)| {
790 let field_token = ident!(("ẕ{}", ident), ident.span());
791 let field: Box<Type> = parse_quote!(#field_token);
792 let field_id: Box<Expr> = parse_quote!({ #zerocopy_crate::ident_id!(#ident) });
793 ImplBlockBuilder::new(
794 ast,
795 data,
796 Trait::HasField {
797 variant_id: variant_id.clone(),
798 field: field.clone(),
799 field_id: field_id.clone(),
800 },
801 FieldBounds::None,
802 zerocopy_crate,
803 )
804 .inner_extras(quote! {
805 type Type = #ty;
806
807 #[inline(always)]
808 fn project(slf: #zerocopy_crate::pointer::PtrInner<'_, Self>) -> *mut Self::Type {
809 let slf = slf.as_ptr();
810 unsafe { #zerocopy_crate::util::macro_util::core_reexport::ptr::addr_of_mut!((*slf).#ident) }
817 }
818 }).outer_extras(if is_repr_c_union {
819 let ident = &ast.ident;
820 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
821 quote! {
822 unsafe impl #impl_generics #zerocopy_crate::pointer::cast::Cast<#ident #ty_generics, #ty>
859 for #zerocopy_crate::pointer::cast::Projection<#field, { #zerocopy_crate::UNION_VARIANT_ID }, #field_id>
860 #where_clause
861 {
862 }
863 }
864 } else {
865 quote! {}
866 })
867 .build()
868 });
869
870 quote! {
871 #[allow(non_camel_case_types)]
872 const _: () = {
873 #(#field_tokens)*
874 #(#has_fields)*
875 };
876 }
877}
878
879fn derive_try_from_bytes_struct(
882 ast: &DeriveInput,
883 strct: &DataStruct,
884 top_level: Trait,
885 zerocopy_crate: &Path,
886) -> Result<TokenStream, Error> {
887 let extras =
888 try_gen_trivial_is_bit_valid(ast, top_level, zerocopy_crate).unwrap_or_else(|| {
889 let fields = strct.fields();
890 let field_names = fields.iter().map(|(_vis, name, _ty)| name);
891 let field_tys = fields.iter().map(|(_vis, _name, ty)| ty);
892 quote!(
893 fn is_bit_valid<___ZerocopyAliasing>(
899 mut candidate: #zerocopy_crate::Maybe<Self, ___ZerocopyAliasing>,
900 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
901 where
902 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
903 {
904 use #zerocopy_crate::util::macro_util::core_reexport;
905 use #zerocopy_crate::pointer::PtrInner;
906
907 true #(&& {
908 let field_candidate = candidate.reborrow().project::<
909 _,
910 { #zerocopy_crate::ident_id!(#field_names) }
911 >();
912
913 <#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate)
914 })*
915 }
916 )
917 });
918 Ok(ImplBlockBuilder::new(
919 ast,
920 strct,
921 Trait::TryFromBytes,
922 FieldBounds::ALL_SELF,
923 zerocopy_crate,
924 )
925 .inner_extras(extras)
926 .outer_extras(derive_has_field_struct_union(ast, strct, zerocopy_crate))
927 .build())
928}
929
930fn derive_try_from_bytes_union(
933 ast: &DeriveInput,
934 unn: &DataUnion,
935 top_level: Trait,
936 zerocopy_crate: &Path,
937) -> TokenStream {
938 let field_type_trait_bounds =
940 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
941 let extras =
942 try_gen_trivial_is_bit_valid(ast, top_level, zerocopy_crate).unwrap_or_else(|| {
943 let fields = unn.fields();
944 let field_names = fields.iter().map(|(_vis, name, _ty)| name);
945 let field_tys = fields.iter().map(|(_vis, _name, ty)| ty);
946 quote!(
947 fn is_bit_valid<___ZerocopyAliasing>(
953 mut candidate: #zerocopy_crate::Maybe<'_, Self,___ZerocopyAliasing>
954 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
955 where
956 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
957 {
958 use #zerocopy_crate::util::macro_util::core_reexport;
959 use #zerocopy_crate::pointer::PtrInner;
960
961 false #(|| {
962 let field_candidate = unsafe {
971 candidate.reborrow().project_transmute_unchecked::<
972 _,
973 _,
974 #zerocopy_crate::pointer::cast::Projection<_, { #zerocopy_crate::UNION_VARIANT_ID }, { #zerocopy_crate::ident_id!(#field_names) }>
975 >()
976 };
977
978 <#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate)
979 })*
980 }
981 )
982 });
983 ImplBlockBuilder::new(ast, unn, Trait::TryFromBytes, field_type_trait_bounds, zerocopy_crate)
984 .inner_extras(extras)
985 .outer_extras(derive_has_field_struct_union(ast, unn, zerocopy_crate))
986 .build()
987}
988
989fn derive_try_from_bytes_enum(
990 ast: &DeriveInput,
991 enm: &DataEnum,
992 top_level: Trait,
993 zerocopy_crate: &Path,
994) -> Result<TokenStream, Error> {
995 let repr = EnumRepr::from_attrs(&ast.attrs)?;
996
997 let could_be_from_bytes = enum_size_from_repr(&repr)
1003 .map(|size| enm.fields().is_empty() && enm.variants.len() == 1usize << size)
1004 .unwrap_or(false);
1005
1006 let trivial_is_bit_valid = try_gen_trivial_is_bit_valid(ast, top_level, zerocopy_crate);
1007 let extra = match (trivial_is_bit_valid, could_be_from_bytes) {
1008 (Some(is_bit_valid), _) => is_bit_valid,
1009 (None, true) => unsafe { gen_trivial_is_bit_valid_unchecked(zerocopy_crate) },
1012 (None, false) => {
1013 r#enum::derive_is_bit_valid(ast, &ast.ident, &repr, &ast.generics, enm, zerocopy_crate)?
1014 }
1015 };
1016
1017 Ok(ImplBlockBuilder::new(ast, enm, Trait::TryFromBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1018 .inner_extras(extra)
1019 .build())
1020}
1021
1022fn try_gen_trivial_is_bit_valid(
1044 ast: &DeriveInput,
1045 top_level: Trait,
1046 zerocopy_crate: &Path,
1047) -> Option<proc_macro2::TokenStream> {
1048 if matches!(top_level, Trait::FromBytes) && ast.generics.params.is_empty() {
1057 Some(quote!(
1058 fn is_bit_valid<___ZerocopyAliasing>(
1060 _candidate: #zerocopy_crate::Maybe<Self, ___ZerocopyAliasing>,
1061 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
1062 where
1063 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
1064 {
1065 if false {
1066 fn assert_is_from_bytes<T>()
1067 where
1068 T: #zerocopy_crate::FromBytes,
1069 T: ?#zerocopy_crate::util::macro_util::core_reexport::marker::Sized,
1070 {
1071 }
1072
1073 assert_is_from_bytes::<Self>();
1074 }
1075
1076 true
1080 }
1081 ))
1082 } else {
1083 None
1084 }
1085}
1086
1087unsafe fn gen_trivial_is_bit_valid_unchecked(zerocopy_crate: &Path) -> proc_macro2::TokenStream {
1099 quote!(
1100 fn is_bit_valid<___ZerocopyAliasing>(
1103 _candidate: #zerocopy_crate::Maybe<Self, ___ZerocopyAliasing>,
1104 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
1105 where
1106 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
1107 {
1108 true
1109 }
1110 )
1111}
1112
1113fn derive_from_zeros_struct(
1116 ast: &DeriveInput,
1117 strct: &DataStruct,
1118 zerocopy_crate: &Path,
1119) -> TokenStream {
1120 ImplBlockBuilder::new(ast, strct, Trait::FromZeros, FieldBounds::ALL_SELF, zerocopy_crate)
1121 .build()
1122}
1123
1124fn find_zero_variant(enm: &DataEnum) -> Result<usize, bool> {
1130 let mut next_negative_discriminant = Some(0);
1138
1139 let mut has_unknown_discriminants = false;
1146
1147 for (i, v) in enm.variants.iter().enumerate() {
1148 match v.discriminant.as_ref() {
1149 None => {
1151 match next_negative_discriminant.as_mut() {
1152 Some(0) => return Ok(i),
1153 Some(n) => *n -= 1,
1155 None => (),
1156 }
1157 }
1158 Some((_, Expr::Lit(ExprLit { lit: Lit::Int(int), .. }))) => {
1160 match int.base10_parse::<u128>().ok() {
1161 Some(0) => return Ok(i),
1162 Some(_) => next_negative_discriminant = None,
1163 None => {
1164 has_unknown_discriminants = true;
1166 next_negative_discriminant = None;
1167 }
1168 }
1169 }
1170 Some((_, Expr::Unary(ExprUnary { op: UnOp::Neg(_), expr, .. }))) => match &**expr {
1172 Expr::Lit(ExprLit { lit: Lit::Int(int), .. }) => {
1173 match int.base10_parse::<u128>().ok() {
1174 Some(0) => return Ok(i),
1175 Some(x) => next_negative_discriminant = Some(x - 1),
1177 None => {
1178 has_unknown_discriminants = true;
1181 next_negative_discriminant = None;
1182 }
1183 }
1184 }
1185 _ => {
1187 has_unknown_discriminants = true;
1188 next_negative_discriminant = None;
1189 }
1190 },
1191 _ => {
1193 has_unknown_discriminants = true;
1194 next_negative_discriminant = None;
1195 }
1196 }
1197 }
1198
1199 Err(has_unknown_discriminants)
1200}
1201
1202fn derive_from_zeros_enum(
1206 ast: &DeriveInput,
1207 enm: &DataEnum,
1208 zerocopy_crate: &Path,
1209) -> Result<TokenStream, Error> {
1210 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1211
1212 match repr {
1215 Repr::Compound(
1216 Spanned { t: CompoundRepr::C | CompoundRepr::Primitive(_), span: _ },
1217 _,
1218 ) => {}
1219 Repr::Transparent(_)
1220 | Repr::Compound(Spanned { t: CompoundRepr::Rust, span: _ }, _) => return Err(Error::new(Span::call_site(), "must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout")),
1221 }
1222
1223 let zero_variant = match find_zero_variant(enm) {
1224 Ok(index) => enm.variants.iter().nth(index).unwrap(),
1225 Err(true) => {
1227 return Err(Error::new_spanned(
1228 ast,
1229 "FromZeros only supported on enums with a variant that has a discriminant of `0`\n\
1230 help: This enum has discriminants which are not literal integers. One of those may \
1231 define or imply which variant has a discriminant of zero. Use a literal integer to \
1232 define or imply the variant with a discriminant of zero.",
1233 ));
1234 }
1235 Err(false) => {
1237 return Err(Error::new_spanned(
1238 ast,
1239 "FromZeros only supported on enums with a variant that has a discriminant of `0`",
1240 ));
1241 }
1242 };
1243
1244 let explicit_bounds = zero_variant
1245 .fields
1246 .iter()
1247 .map(|field| {
1248 let ty = &field.ty;
1249 parse_quote! { #ty: #zerocopy_crate::FromZeros }
1250 })
1251 .collect::<Vec<WherePredicate>>();
1252
1253 Ok(ImplBlockBuilder::new(
1254 ast,
1255 enm,
1256 Trait::FromZeros,
1257 FieldBounds::Explicit(explicit_bounds),
1258 zerocopy_crate,
1259 )
1260 .build())
1261}
1262
1263fn derive_from_zeros_union(
1266 ast: &DeriveInput,
1267 unn: &DataUnion,
1268 zerocopy_crate: &Path,
1269) -> TokenStream {
1270 let field_type_trait_bounds =
1273 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
1274 ImplBlockBuilder::new(ast, unn, Trait::FromZeros, field_type_trait_bounds, zerocopy_crate)
1275 .build()
1276}
1277
1278fn derive_from_bytes_struct(
1281 ast: &DeriveInput,
1282 strct: &DataStruct,
1283 zerocopy_crate: &Path,
1284) -> TokenStream {
1285 ImplBlockBuilder::new(ast, strct, Trait::FromBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1286 .build()
1287}
1288
1289fn derive_from_bytes_enum(
1305 ast: &DeriveInput,
1306 enm: &DataEnum,
1307 zerocopy_crate: &Path,
1308) -> Result<TokenStream, Error> {
1309 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1310
1311 let variants_required = 1usize << enum_size_from_repr(&repr)?;
1312 if enm.variants.len() != variants_required {
1313 return Err(Error::new_spanned(
1314 ast,
1315 format!(
1316 "FromBytes only supported on {} enum with {} variants",
1317 repr.repr_type_name(),
1318 variants_required
1319 ),
1320 ));
1321 }
1322
1323 Ok(ImplBlockBuilder::new(ast, enm, Trait::FromBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1324 .build())
1325}
1326
1327fn enum_size_from_repr(repr: &EnumRepr) -> Result<usize, Error> {
1329 use CompoundRepr::*;
1330 use PrimitiveRepr::*;
1331 use Repr::*;
1332 match repr {
1333 Transparent(span)
1334 | Compound(
1335 Spanned { t: C | Rust | Primitive(U32 | I32 | U64 | I64 | U128 | I128 | Usize | Isize), span },
1336 _,
1337 ) => Err(Error::new(*span, "`FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16`")),
1338 Compound(Spanned { t: Primitive(U8 | I8), span: _ }, _align) => Ok(8),
1339 Compound(Spanned { t: Primitive(U16 | I16), span: _ }, _align) => Ok(16),
1340 }
1341}
1342
1343fn derive_from_bytes_union(
1346 ast: &DeriveInput,
1347 unn: &DataUnion,
1348 zerocopy_crate: &Path,
1349) -> TokenStream {
1350 let field_type_trait_bounds =
1353 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
1354 ImplBlockBuilder::new(ast, unn, Trait::FromBytes, field_type_trait_bounds, zerocopy_crate)
1355 .build()
1356}
1357
1358fn derive_into_bytes_struct(
1359 ast: &DeriveInput,
1360 strct: &DataStruct,
1361 zerocopy_crate: &Path,
1362) -> Result<TokenStream, Error> {
1363 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1364
1365 let is_transparent = repr.is_transparent();
1366 let is_c = repr.is_c();
1367 let is_packed_1 = repr.is_packed_1();
1368 let num_fields = strct.fields().len();
1369
1370 let (padding_check, require_unaligned_fields) = if is_transparent || is_packed_1 {
1371 (None, false)
1388 } else if is_c && !repr.is_align_gt_1() && num_fields <= 1 {
1389 (None, false)
1393 } else if ast.generics.params.is_empty() {
1394 let is_syntactic_dst =
1396 strct.fields().last().map(|(_, _, ty)| matches!(ty, Type::Slice(_))).unwrap_or(false);
1397 if is_c && is_syntactic_dst {
1410 (Some(PaddingCheck::ReprCStruct), false)
1411 } else {
1412 (Some(PaddingCheck::Struct), false)
1413 }
1414 } else if is_c && !repr.is_align_gt_1() {
1415 (None, true)
1424 } else {
1425 return Err(Error::new(Span::call_site(), "must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout"));
1426 };
1427
1428 let field_bounds = if require_unaligned_fields {
1429 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Unaligned)])
1430 } else {
1431 FieldBounds::ALL_SELF
1432 };
1433
1434 Ok(ImplBlockBuilder::new(ast, strct, Trait::IntoBytes, field_bounds, zerocopy_crate)
1435 .padding_check(padding_check)
1436 .build())
1437}
1438
1439fn derive_into_bytes_enum(
1445 ast: &DeriveInput,
1446 enm: &DataEnum,
1447 zerocopy_crate: &Path,
1448) -> Result<TokenStream, Error> {
1449 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1450 if !repr.is_c() && !repr.is_primitive() {
1451 return Err(Error::new(Span::call_site(), "must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout"));
1452 }
1453
1454 let tag_type_definition = r#enum::generate_tag_enum(&repr, enm);
1455 Ok(ImplBlockBuilder::new(ast, enm, Trait::IntoBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1456 .padding_check(PaddingCheck::Enum { tag_type_definition })
1457 .build())
1458}
1459
1460fn derive_into_bytes_union(
1465 ast: &DeriveInput,
1466 unn: &DataUnion,
1467 zerocopy_crate: &Path,
1468) -> Result<TokenStream, Error> {
1469 let cfg_compile_error = if cfg!(zerocopy_derive_union_into_bytes) {
1478 quote!()
1479 } else {
1480 let error_message = "requires --cfg zerocopy_derive_union_into_bytes;
1481please let us know you use this feature: https://github.com/google/zerocopy/discussions/1802";
1482 quote!(
1483 const _: () = {
1484 #[cfg(not(zerocopy_derive_union_into_bytes))]
1485 #zerocopy_crate::util::macro_util::core_reexport::compile_error!(#error_message);
1486 };
1487 )
1488 };
1489
1490 if !ast.generics.params.is_empty() {
1492 return Err(Error::new(Span::call_site(), "unsupported on types with type parameters"));
1493 }
1494
1495 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1500 if !repr.is_c() && !repr.is_transparent() && !repr.is_packed_1() {
1501 return Err(Error::new(
1502 Span::call_site(),
1503 "must be #[repr(C)], #[repr(packed)], or #[repr(transparent)]",
1504 ));
1505 }
1506
1507 let impl_block =
1508 ImplBlockBuilder::new(ast, unn, Trait::IntoBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1509 .padding_check(PaddingCheck::Union)
1510 .build();
1511 Ok(quote!(#cfg_compile_error #impl_block))
1512}
1513
1514fn derive_unaligned_struct(
1520 ast: &DeriveInput,
1521 strct: &DataStruct,
1522 zerocopy_crate: &Path,
1523) -> Result<TokenStream, Error> {
1524 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1525 repr.unaligned_validate_no_align_gt_1()?;
1526
1527 let field_bounds = if repr.is_packed_1() {
1528 FieldBounds::None
1529 } else if repr.is_c() || repr.is_transparent() {
1530 FieldBounds::ALL_SELF
1531 } else {
1532 return Err(Error::new(Span::call_site(), "must have #[repr(C)], #[repr(transparent)], or #[repr(packed)] attribute in order to guarantee this type's alignment"));
1533 };
1534
1535 Ok(ImplBlockBuilder::new(ast, strct, Trait::Unaligned, field_bounds, zerocopy_crate).build())
1536}
1537
1538fn derive_unaligned_enum(
1542 ast: &DeriveInput,
1543 enm: &DataEnum,
1544 zerocopy_crate: &Path,
1545) -> Result<TokenStream, Error> {
1546 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1547 repr.unaligned_validate_no_align_gt_1()?;
1548
1549 if !repr.is_u8() && !repr.is_i8() {
1550 return Err(Error::new(Span::call_site(), "must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment"));
1551 }
1552
1553 Ok(ImplBlockBuilder::new(ast, enm, Trait::Unaligned, FieldBounds::ALL_SELF, zerocopy_crate)
1554 .build())
1555}
1556
1557fn derive_unaligned_union(
1563 ast: &DeriveInput,
1564 unn: &DataUnion,
1565 zerocopy_crate: &Path,
1566) -> Result<TokenStream, Error> {
1567 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1568 repr.unaligned_validate_no_align_gt_1()?;
1569
1570 let field_type_trait_bounds = if repr.is_packed_1() {
1571 FieldBounds::None
1572 } else if repr.is_c() || repr.is_transparent() {
1573 FieldBounds::ALL_SELF
1574 } else {
1575 return Err(Error::new(Span::call_site(), "must have #[repr(C)], #[repr(transparent)], or #[repr(packed)] attribute in order to guarantee this type's alignment"));
1576 };
1577
1578 Ok(ImplBlockBuilder::new(ast, unn, Trait::Unaligned, field_type_trait_bounds, zerocopy_crate)
1579 .build())
1580}
1581
1582enum PaddingCheck {
1585 Struct,
1588 ReprCStruct,
1590 Union,
1592 Enum { tag_type_definition: TokenStream },
1597}
1598
1599impl PaddingCheck {
1600 fn validator_trait_and_macro_idents(&self) -> (Ident, Ident) {
1603 let (trt, mcro) = match self {
1604 PaddingCheck::Struct => ("PaddingFree", "struct_padding"),
1605 PaddingCheck::ReprCStruct => ("DynamicPaddingFree", "repr_c_struct_has_padding"),
1606 PaddingCheck::Union => ("PaddingFree", "union_padding"),
1607 PaddingCheck::Enum { .. } => ("PaddingFree", "enum_padding"),
1608 };
1609
1610 let trt = Ident::new(trt, Span::call_site());
1611 let mcro = Ident::new(mcro, Span::call_site());
1612 (trt, mcro)
1613 }
1614
1615 fn validator_macro_context(&self) -> Option<&TokenStream> {
1618 match self {
1619 PaddingCheck::Struct | PaddingCheck::ReprCStruct | PaddingCheck::Union => None,
1620 PaddingCheck::Enum { tag_type_definition } => Some(tag_type_definition),
1621 }
1622 }
1623}
1624
1625#[derive(Clone)]
1626enum Trait {
1627 KnownLayout,
1628 HasField { variant_id: Box<Expr>, field: Box<Type>, field_id: Box<Expr> },
1629 Immutable,
1630 TryFromBytes,
1631 FromZeros,
1632 FromBytes,
1633 IntoBytes,
1634 Unaligned,
1635 Sized,
1636 ByteHash,
1637 ByteEq,
1638 SplitAt,
1639}
1640
1641impl ToTokens for Trait {
1642 fn to_tokens(&self, tokens: &mut TokenStream) {
1643 let s = match self {
1653 Trait::HasField { .. } => "HasField",
1654 Trait::KnownLayout => "KnownLayout",
1655 Trait::Immutable => "Immutable",
1656 Trait::TryFromBytes => "TryFromBytes",
1657 Trait::FromZeros => "FromZeros",
1658 Trait::FromBytes => "FromBytes",
1659 Trait::IntoBytes => "IntoBytes",
1660 Trait::Unaligned => "Unaligned",
1661 Trait::Sized => "Sized",
1662 Trait::ByteHash => "ByteHash",
1663 Trait::ByteEq => "ByteEq",
1664 Trait::SplitAt => "SplitAt",
1665 };
1666 let ident = Ident::new(s, Span::call_site());
1667 let arguments: Option<syn::AngleBracketedGenericArguments> = match self {
1668 Trait::HasField { variant_id, field, field_id } => {
1669 Some(parse_quote!(<#field, #variant_id, #field_id>))
1670 }
1671 Trait::KnownLayout
1672 | Trait::Immutable
1673 | Trait::TryFromBytes
1674 | Trait::FromZeros
1675 | Trait::FromBytes
1676 | Trait::IntoBytes
1677 | Trait::Unaligned
1678 | Trait::Sized
1679 | Trait::ByteHash
1680 | Trait::ByteEq
1681 | Trait::SplitAt => None,
1682 };
1683 tokens.extend(quote!(#ident #arguments));
1684 }
1685}
1686
1687impl Trait {
1688 fn crate_path(&self, zerocopy_crate: &Path) -> Path {
1689 match self {
1690 Self::Sized => {
1691 parse_quote!(#zerocopy_crate::util::macro_util::core_reexport::marker::#self)
1692 }
1693 _ => parse_quote!(#zerocopy_crate::#self),
1694 }
1695 }
1696}
1697
1698enum TraitBound {
1699 Slf,
1700 Other(Trait),
1701}
1702
1703enum FieldBounds<'a> {
1704 None,
1705 All(&'a [TraitBound]),
1706 Trailing(&'a [TraitBound]),
1707 Explicit(Vec<WherePredicate>),
1708}
1709
1710impl<'a> FieldBounds<'a> {
1711 const ALL_SELF: FieldBounds<'a> = FieldBounds::All(&[TraitBound::Slf]);
1712 const TRAILING_SELF: FieldBounds<'a> = FieldBounds::Trailing(&[TraitBound::Slf]);
1713}
1714
1715enum SelfBounds<'a> {
1716 None,
1717 All(&'a [Trait]),
1718}
1719
1720#[allow(clippy::needless_lifetimes)]
1723impl<'a> SelfBounds<'a> {
1724 const SIZED: Self = Self::All(&[Trait::Sized]);
1725}
1726
1727fn normalize_bounds<'a>(
1729 slf: &'a Trait,
1730 bounds: &'a [TraitBound],
1731) -> impl 'a + Iterator<Item = Trait> {
1732 bounds.iter().map(move |bound| match bound {
1733 TraitBound::Slf => slf.clone(),
1734 TraitBound::Other(trt) => trt.clone(),
1735 })
1736}
1737
1738struct ImplBlockBuilder<'a> {
1739 input: &'a DeriveInput,
1740 data: &'a dyn DataExt,
1741 trt: Trait,
1742 field_type_trait_bounds: FieldBounds<'a>,
1743 zerocopy_crate: &'a Path,
1744 self_type_trait_bounds: SelfBounds<'a>,
1745 padding_check: Option<PaddingCheck>,
1746 inner_extras: Option<TokenStream>,
1747 outer_extras: Option<TokenStream>,
1748}
1749
1750impl<'a> ImplBlockBuilder<'a> {
1751 fn new(
1752 input: &'a DeriveInput,
1753 data: &'a dyn DataExt,
1754 trt: Trait,
1755 field_type_trait_bounds: FieldBounds<'a>,
1756 zerocopy_crate: &'a Path,
1757 ) -> Self {
1758 Self {
1759 input,
1760 data,
1761 trt,
1762 field_type_trait_bounds,
1763 zerocopy_crate,
1764 self_type_trait_bounds: SelfBounds::None,
1765 padding_check: None,
1766 inner_extras: None,
1767 outer_extras: None,
1768 }
1769 }
1770
1771 fn self_type_trait_bounds(mut self, self_type_trait_bounds: SelfBounds<'a>) -> Self {
1772 self.self_type_trait_bounds = self_type_trait_bounds;
1773 self
1774 }
1775
1776 fn padding_check<P: Into<Option<PaddingCheck>>>(mut self, padding_check: P) -> Self {
1777 self.padding_check = padding_check.into();
1778 self
1779 }
1780
1781 fn inner_extras(mut self, inner_extras: TokenStream) -> Self {
1782 self.inner_extras = Some(inner_extras);
1783 self
1784 }
1785
1786 fn outer_extras<T: Into<Option<TokenStream>>>(mut self, outer_extras: T) -> Self {
1787 self.outer_extras = outer_extras.into();
1788 self
1789 }
1790
1791 fn build(self) -> TokenStream {
1792 let type_ident = &self.input.ident;
1852 let trait_path = self.trt.crate_path(self.zerocopy_crate);
1853 let fields = self.data.fields();
1854 let variants = self.data.variants();
1855 let tag = self.data.tag();
1856 let zerocopy_crate = self.zerocopy_crate;
1857
1858 fn bound_tt(
1859 ty: &Type,
1860 traits: impl Iterator<Item = Trait>,
1861 zerocopy_crate: &Path,
1862 ) -> WherePredicate {
1863 let traits = traits.map(|t| t.crate_path(zerocopy_crate));
1864 parse_quote!(#ty: #(#traits)+*)
1865 }
1866 let field_type_bounds: Vec<_> = match (self.field_type_trait_bounds, &fields[..]) {
1867 (FieldBounds::All(traits), _) => fields
1868 .iter()
1869 .map(|(_vis, _name, ty)| {
1870 bound_tt(ty, normalize_bounds(&self.trt, traits), zerocopy_crate)
1871 })
1872 .collect(),
1873 (FieldBounds::None, _) | (FieldBounds::Trailing(..), []) => vec![],
1874 (FieldBounds::Trailing(traits), [.., last]) => {
1875 vec![bound_tt(last.2, normalize_bounds(&self.trt, traits), zerocopy_crate)]
1876 }
1877 (FieldBounds::Explicit(bounds), _) => bounds,
1878 };
1879
1880 #[allow(unstable_name_collisions)] let padding_check_bound = self
1883 .padding_check
1884 .and_then(|check| (!fields.is_empty()).then_some(check))
1885 .map(|check| {
1886 let variant_types = variants.iter().map(|(_, fields)| {
1887 let types = fields.iter().map(|(_vis, _name, ty)| ty);
1888 quote!([#((#types)),*])
1889 });
1890 let validator_context = check.validator_macro_context();
1891 let (trt, validator_macro) = check.validator_trait_and_macro_idents();
1892 let t = tag.iter();
1893 parse_quote! {
1894 (): #zerocopy_crate::util::macro_util::#trt<
1895 Self,
1896 {
1897 #validator_context
1898 #zerocopy_crate::#validator_macro!(Self, #(#t,)* #(#variant_types),*)
1899 }
1900 >
1901 }
1902 });
1903
1904 let self_bounds: Option<WherePredicate> = match self.self_type_trait_bounds {
1905 SelfBounds::None => None,
1906 SelfBounds::All(traits) => {
1907 Some(bound_tt(&parse_quote!(Self), traits.iter().cloned(), zerocopy_crate))
1908 }
1909 };
1910
1911 let bounds = self
1912 .input
1913 .generics
1914 .where_clause
1915 .as_ref()
1916 .map(|where_clause| where_clause.predicates.iter())
1917 .into_iter()
1918 .flatten()
1919 .chain(field_type_bounds.iter())
1920 .chain(padding_check_bound.iter())
1921 .chain(self_bounds.iter());
1922
1923 let params = self.input.generics.params.clone().into_iter().map(|mut param| {
1925 match &mut param {
1926 GenericParam::Type(ty) => ty.default = None,
1927 GenericParam::Const(cnst) => cnst.default = None,
1928 GenericParam::Lifetime(_) => {}
1929 }
1930 quote!(#param)
1931 });
1932
1933 let param_idents = self.input.generics.params.iter().map(|param| match param {
1936 GenericParam::Type(ty) => {
1937 let ident = &ty.ident;
1938 quote!(#ident)
1939 }
1940 GenericParam::Lifetime(l) => {
1941 let ident = &l.lifetime;
1942 quote!(#ident)
1943 }
1944 GenericParam::Const(cnst) => {
1945 let ident = &cnst.ident;
1946 quote!({#ident})
1947 }
1948 });
1949
1950 let inner_extras = self.inner_extras;
1951 let impl_tokens = quote! {
1952 #[allow(deprecated, non_local_definitions)]
1953 #[automatically_derived]
1956 unsafe impl < #(#params),* > #trait_path for #type_ident < #(#param_idents),* >
1957 where
1958 #(#bounds,)*
1959 {
1960 fn only_derive_is_allowed_to_implement_this_trait() {}
1961
1962 #inner_extras
1963 }
1964 };
1965
1966 if let Some(outer_extras) = self.outer_extras.filter(|e| !e.is_empty()) {
1967 quote! {
1970 #[allow(deprecated, non_local_definitions)]
1971 #[automatically_derived]
1974 const _: () = {
1975 #impl_tokens
1976
1977 #outer_extras
1978 };
1979 }
1980 } else {
1981 impl_tokens
1982 }
1983 }
1984}
1985
1986#[allow(unused)]
1994trait BoolExt {
1995 fn then_some<T>(self, t: T) -> Option<T>;
1996}
1997
1998impl BoolExt for bool {
1999 fn then_some<T>(self, t: T) -> Option<T> {
2000 if self {
2001 Some(t)
2002 } else {
2003 None
2004 }
2005 }
2006}