1#![allow(unknown_lints)]
18#![deny(renamed_and_removed_lints)]
19#![deny(clippy::all, clippy::missing_safety_doc, clippy::undocumented_unsafe_blocks)]
20#![allow(clippy::uninlined_format_args)]
22#![deny(
23 rustdoc::bare_urls,
24 rustdoc::broken_intra_doc_links,
25 rustdoc::invalid_codeblock_attributes,
26 rustdoc::invalid_html_tags,
27 rustdoc::invalid_rust_codeblocks,
28 rustdoc::missing_crate_level_docs,
29 rustdoc::private_intra_doc_links
30)]
31#![recursion_limit = "128"]
32
33mod r#enum;
34mod ext;
35#[cfg(test)]
36mod output_tests;
37mod repr;
38
39use proc_macro2::{TokenStream, TokenTree};
40use quote::ToTokens;
41
42use {
43 proc_macro2::Span,
44 quote::quote,
45 syn::{
46 parse_quote, Attribute, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Error, Expr,
47 ExprLit, ExprUnary, GenericParam, Ident, Lit, Meta, Path, Type, UnOp, WherePredicate,
48 },
49};
50
51use {crate::ext::*, crate::repr::*};
52
53macro_rules! derive {
77 ($trait:ident => $outer:ident => $inner:ident) => {
78 #[proc_macro_derive($trait, attributes(zerocopy))]
79 pub fn $outer(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
80 let ast = syn::parse_macro_input!(ts as DeriveInput);
81 let zerocopy_crate = match extract_zerocopy_crate(&ast.attrs) {
82 Ok(zerocopy_crate) => zerocopy_crate,
83 Err(e) => return e.into_compile_error().into(),
84 };
85 $inner(&ast, Trait::$trait, &zerocopy_crate).into_ts().into()
86 }
87 };
88}
89
90trait IntoTokenStream {
91 fn into_ts(self) -> TokenStream;
92}
93
94impl IntoTokenStream for TokenStream {
95 fn into_ts(self) -> TokenStream {
96 self
97 }
98}
99
100impl IntoTokenStream for Result<TokenStream, Error> {
101 fn into_ts(self) -> TokenStream {
102 match self {
103 Ok(ts) => ts,
104 Err(err) => err.to_compile_error(),
105 }
106 }
107}
108
109fn extract_zerocopy_crate(attrs: &[Attribute]) -> Result<Path, Error> {
112 let mut path = parse_quote!(::zerocopy);
113
114 for attr in attrs {
115 if let Meta::List(ref meta_list) = attr.meta {
116 if meta_list.path.is_ident("zerocopy") {
117 attr.parse_nested_meta(|meta| {
118 if meta.path.is_ident("crate") {
119 let expr = meta.value().and_then(|value| value.parse());
120 if let Ok(Expr::Lit(ExprLit { lit: Lit::Str(lit), .. })) = expr {
121 if let Ok(path_lit) = lit.parse() {
122 path = path_lit;
123 return Ok(());
124 }
125 }
126
127 return Err(Error::new(
128 Span::call_site(),
129 "`crate` attribute requires a path as the value",
130 ));
131 }
132
133 Err(Error::new(
134 Span::call_site(),
135 format!("unknown attribute encountered: {}", meta.path.into_token_stream()),
136 ))
137 })?;
138 }
139 }
140 }
141
142 Ok(path)
143}
144
145derive!(KnownLayout => derive_known_layout => derive_known_layout_inner);
146derive!(Immutable => derive_no_cell => derive_no_cell_inner);
147derive!(TryFromBytes => derive_try_from_bytes => derive_try_from_bytes_inner);
148derive!(FromZeros => derive_from_zeros => derive_from_zeros_inner);
149derive!(FromBytes => derive_from_bytes => derive_from_bytes_inner);
150derive!(IntoBytes => derive_into_bytes => derive_into_bytes_inner);
151derive!(Unaligned => derive_unaligned => derive_unaligned_inner);
152derive!(ByteHash => derive_hash => derive_hash_inner);
153derive!(ByteEq => derive_eq => derive_eq_inner);
154derive!(SplitAt => derive_split_at => derive_split_at_inner);
155
156#[deprecated(since = "0.8.0", note = "`FromZeroes` was renamed to `FromZeros`")]
158#[doc(hidden)]
159#[proc_macro_derive(FromZeroes)]
160pub fn derive_from_zeroes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
161 derive_from_zeros(ts)
162}
163
164#[deprecated(since = "0.8.0", note = "`AsBytes` was renamed to `IntoBytes`")]
166#[doc(hidden)]
167#[proc_macro_derive(AsBytes)]
168pub fn derive_as_bytes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
169 derive_into_bytes(ts)
170}
171
172fn derive_known_layout_inner(
173 ast: &DeriveInput,
174 _top_level: Trait,
175 zerocopy_crate: &Path,
176) -> Result<TokenStream, Error> {
177 let is_repr_c_struct = match &ast.data {
178 Data::Struct(..) => {
179 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
180 if repr.is_c() {
181 Some(repr)
182 } else {
183 None
184 }
185 }
186 Data::Enum(..) | Data::Union(..) => None,
187 };
188
189 let fields = ast.data.fields();
190
191 let (self_bounds, inner_extras, outer_extras) = if let (
192 Some(repr),
193 Some((trailing_field, leading_fields)),
194 ) = (is_repr_c_struct, fields.split_last())
195 {
196 let (_vis, trailing_field_name, trailing_field_ty) = trailing_field;
197 let leading_fields_tys = leading_fields.iter().map(|(_vis, _name, ty)| ty);
198
199 let core_path = quote!(#zerocopy_crate::util::macro_util::core_reexport);
200 let repr_align = repr
201 .get_align()
202 .map(|align| {
203 let align = align.t.get();
204 quote!(#core_path::num::NonZeroUsize::new(#align as usize))
205 })
206 .unwrap_or_else(|| quote!(#core_path::option::Option::None));
207 let repr_packed = repr
208 .get_packed()
209 .map(|packed| {
210 let packed = packed.get();
211 quote!(#core_path::num::NonZeroUsize::new(#packed as usize))
212 })
213 .unwrap_or_else(|| quote!(#core_path::option::Option::None));
214
215 let make_methods = |trailing_field_ty| {
216 quote! {
217 #[inline(always)]
247 fn raw_from_ptr_len(
248 bytes: #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<u8>,
249 meta: Self::PointerMetadata,
250 ) -> #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<Self> {
251 use #zerocopy_crate::KnownLayout;
252 let trailing = <#trailing_field_ty as KnownLayout>::raw_from_ptr_len(bytes, meta);
253 let slf = trailing.as_ptr() as *mut Self;
254 unsafe { #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull::new_unchecked(slf) }
256 }
257
258 #[inline(always)]
259 fn pointer_to_metadata(ptr: *mut Self) -> Self::PointerMetadata {
260 <#trailing_field_ty>::pointer_to_metadata(ptr as *mut _)
261 }
262 }
263 };
264
265 let inner_extras = {
266 let leading_fields_tys = leading_fields_tys.clone();
267 let methods = make_methods(*trailing_field_ty);
268 let (_, ty_generics, _) = ast.generics.split_for_impl();
269
270 quote!(
271 type PointerMetadata = <#trailing_field_ty as #zerocopy_crate::KnownLayout>::PointerMetadata;
272
273 type MaybeUninit = __ZerocopyKnownLayoutMaybeUninit #ty_generics;
274
275 const LAYOUT: #zerocopy_crate::DstLayout = {
290 use #zerocopy_crate::util::macro_util::core_reexport::num::NonZeroUsize;
291 use #zerocopy_crate::{DstLayout, KnownLayout};
292
293 let repr_align = #repr_align;
294 let repr_packed = #repr_packed;
295
296 DstLayout::new_zst(repr_align)
297 #(.extend(DstLayout::for_type::<#leading_fields_tys>(), repr_packed))*
298 .extend(<#trailing_field_ty as KnownLayout>::LAYOUT, repr_packed)
299 .pad_to_align()
300 };
301
302 #methods
303 )
304 };
305
306 let outer_extras = {
307 let ident = &ast.ident;
308 let vis = &ast.vis;
309 let params = &ast.generics.params;
310 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
311
312 let predicates = if let Some(where_clause) = where_clause {
313 where_clause.predicates.clone()
314 } else {
315 Default::default()
316 };
317
318 let field_index =
321 |name| Ident::new(&format!("__Zerocopy_Field_{}", name), ident.span());
322
323 let field_indices: Vec<_> =
324 fields.iter().map(|(_vis, name, _ty)| field_index(name)).collect();
325
326 let field_defs = field_indices.iter().zip(&fields).map(|(idx, (vis, _, _))| {
328 quote! {
329 #[allow(non_camel_case_types)]
330 #vis struct #idx;
331 }
332 });
333
334 let field_impls = field_indices.iter().zip(&fields).map(|(idx, (_, _, ty))| quote! {
335 unsafe impl #impl_generics #zerocopy_crate::util::macro_util::Field<#idx> for #ident #ty_generics
337 where
338 #predicates
339 {
340 type Type = #ty;
341 }
342 });
343
344 let trailing_field_index = field_index(trailing_field_name);
345 let leading_field_indices =
346 leading_fields.iter().map(|(_vis, name, _ty)| field_index(name));
347
348 let trailing_field_ty = quote! {
349 <#ident #ty_generics as
350 #zerocopy_crate::util::macro_util::Field<#trailing_field_index>
351 >::Type
352 };
353
354 let methods = make_methods(&parse_quote! {
355 <#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit
356 });
357
358 quote! {
359 #(#field_defs)*
360
361 #(#field_impls)*
362
363 #repr
371 #[doc(hidden)]
372 #[allow(private_bounds)]
376 #vis struct __ZerocopyKnownLayoutMaybeUninit<#params> (
377 #(#zerocopy_crate::util::macro_util::core_reexport::mem::MaybeUninit<
378 <#ident #ty_generics as
379 #zerocopy_crate::util::macro_util::Field<#leading_field_indices>
380 >::Type
381 >,)*
382 #zerocopy_crate::util::macro_util::core_reexport::mem::ManuallyDrop<
389 <#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit
390 >
391 )
392 where
393 #trailing_field_ty: #zerocopy_crate::KnownLayout,
394 #predicates;
395
396 unsafe impl #impl_generics #zerocopy_crate::KnownLayout for __ZerocopyKnownLayoutMaybeUninit #ty_generics
403 where
404 #trailing_field_ty: #zerocopy_crate::KnownLayout,
405 #predicates
406 {
407 #[allow(clippy::missing_inline_in_public_items)]
408 fn only_derive_is_allowed_to_implement_this_trait() {}
409
410 type PointerMetadata = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::PointerMetadata;
411
412 type MaybeUninit = Self;
413
414 const LAYOUT: #zerocopy_crate::DstLayout = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::LAYOUT;
415
416 #methods
417 }
418 }
419 };
420
421 (SelfBounds::None, inner_extras, Some(outer_extras))
422 } else {
423 (
427 SelfBounds::SIZED,
428 quote!(
429 type PointerMetadata = ();
430 type MaybeUninit =
431 #zerocopy_crate::util::macro_util::core_reexport::mem::MaybeUninit<Self>;
432
433 const LAYOUT: #zerocopy_crate::DstLayout = #zerocopy_crate::DstLayout::for_type::<Self>();
437
438 #[inline(always)]
443 fn raw_from_ptr_len(
444 bytes: #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<u8>,
445 _meta: (),
446 ) -> #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<Self>
447 {
448 bytes.cast::<Self>()
449 }
450
451 #[inline(always)]
452 fn pointer_to_metadata(_ptr: *mut Self) -> () {}
453 ),
454 None,
455 )
456 };
457
458 Ok(match &ast.data {
459 Data::Struct(strct) => {
460 let require_trait_bound_on_field_types = if self_bounds == SelfBounds::SIZED {
461 FieldBounds::None
462 } else {
463 FieldBounds::TRAILING_SELF
464 };
465
466 ImplBlockBuilder::new(
471 ast,
472 strct,
473 Trait::KnownLayout,
474 require_trait_bound_on_field_types,
475 zerocopy_crate,
476 )
477 .self_type_trait_bounds(self_bounds)
478 .inner_extras(inner_extras)
479 .outer_extras(outer_extras)
480 .build()
481 }
482 Data::Enum(enm) => {
483 ImplBlockBuilder::new(ast, enm, Trait::KnownLayout, FieldBounds::None, zerocopy_crate)
486 .self_type_trait_bounds(SelfBounds::SIZED)
487 .inner_extras(inner_extras)
488 .outer_extras(outer_extras)
489 .build()
490 }
491 Data::Union(unn) => {
492 ImplBlockBuilder::new(ast, unn, Trait::KnownLayout, FieldBounds::None, zerocopy_crate)
495 .self_type_trait_bounds(SelfBounds::SIZED)
496 .inner_extras(inner_extras)
497 .outer_extras(outer_extras)
498 .build()
499 }
500 })
501}
502
503fn derive_no_cell_inner(
504 ast: &DeriveInput,
505 _top_level: Trait,
506 zerocopy_crate: &Path,
507) -> TokenStream {
508 match &ast.data {
509 Data::Struct(strct) => ImplBlockBuilder::new(
510 ast,
511 strct,
512 Trait::Immutable,
513 FieldBounds::ALL_SELF,
514 zerocopy_crate,
515 )
516 .build(),
517 Data::Enum(enm) => {
518 ImplBlockBuilder::new(ast, enm, Trait::Immutable, FieldBounds::ALL_SELF, zerocopy_crate)
519 .build()
520 }
521 Data::Union(unn) => {
522 ImplBlockBuilder::new(ast, unn, Trait::Immutable, FieldBounds::ALL_SELF, zerocopy_crate)
523 .build()
524 }
525 }
526}
527
528fn derive_try_from_bytes_inner(
529 ast: &DeriveInput,
530 top_level: Trait,
531 zerocopy_crate: &Path,
532) -> Result<TokenStream, Error> {
533 match &ast.data {
534 Data::Struct(strct) => derive_try_from_bytes_struct(ast, strct, top_level, zerocopy_crate),
535 Data::Enum(enm) => derive_try_from_bytes_enum(ast, enm, top_level, zerocopy_crate),
536 Data::Union(unn) => Ok(derive_try_from_bytes_union(ast, unn, top_level, zerocopy_crate)),
537 }
538}
539
540fn derive_from_zeros_inner(
541 ast: &DeriveInput,
542 top_level: Trait,
543 zerocopy_crate: &Path,
544) -> Result<TokenStream, Error> {
545 let try_from_bytes = derive_try_from_bytes_inner(ast, top_level, zerocopy_crate)?;
546 let from_zeros = match &ast.data {
547 Data::Struct(strct) => derive_from_zeros_struct(ast, strct, zerocopy_crate),
548 Data::Enum(enm) => derive_from_zeros_enum(ast, enm, zerocopy_crate)?,
549 Data::Union(unn) => derive_from_zeros_union(ast, unn, zerocopy_crate),
550 };
551 Ok(IntoIterator::into_iter([try_from_bytes, from_zeros]).collect())
552}
553
554fn derive_from_bytes_inner(
555 ast: &DeriveInput,
556 top_level: Trait,
557 zerocopy_crate: &Path,
558) -> Result<TokenStream, Error> {
559 let from_zeros = derive_from_zeros_inner(ast, top_level, zerocopy_crate)?;
560 let from_bytes = match &ast.data {
561 Data::Struct(strct) => derive_from_bytes_struct(ast, strct, zerocopy_crate),
562 Data::Enum(enm) => derive_from_bytes_enum(ast, enm, zerocopy_crate)?,
563 Data::Union(unn) => derive_from_bytes_union(ast, unn, zerocopy_crate),
564 };
565
566 Ok(IntoIterator::into_iter([from_zeros, from_bytes]).collect())
567}
568
569fn derive_into_bytes_inner(
570 ast: &DeriveInput,
571 _top_level: Trait,
572 zerocopy_crate: &Path,
573) -> Result<TokenStream, Error> {
574 match &ast.data {
575 Data::Struct(strct) => derive_into_bytes_struct(ast, strct, zerocopy_crate),
576 Data::Enum(enm) => derive_into_bytes_enum(ast, enm, zerocopy_crate),
577 Data::Union(unn) => derive_into_bytes_union(ast, unn, zerocopy_crate),
578 }
579}
580
581fn derive_unaligned_inner(
582 ast: &DeriveInput,
583 _top_level: Trait,
584 zerocopy_crate: &Path,
585) -> Result<TokenStream, Error> {
586 match &ast.data {
587 Data::Struct(strct) => derive_unaligned_struct(ast, strct, zerocopy_crate),
588 Data::Enum(enm) => derive_unaligned_enum(ast, enm, zerocopy_crate),
589 Data::Union(unn) => derive_unaligned_union(ast, unn, zerocopy_crate),
590 }
591}
592
593fn derive_hash_inner(
594 ast: &DeriveInput,
595 _top_level: Trait,
596 zerocopy_crate: &Path,
597) -> Result<TokenStream, Error> {
598 let type_ident = &ast.ident;
604 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
605 let where_predicates = where_clause.map(|clause| &clause.predicates);
606 Ok(quote! {
607 #[allow(deprecated)]
610 #[automatically_derived]
613 impl #impl_generics #zerocopy_crate::util::macro_util::core_reexport::hash::Hash for #type_ident #ty_generics
614 where
615 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
616 #where_predicates
617 {
618 fn hash<H>(&self, state: &mut H)
619 where
620 H: #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher,
621 {
622 #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher::write(
623 state,
624 #zerocopy_crate::IntoBytes::as_bytes(self)
625 )
626 }
627
628 fn hash_slice<H>(data: &[Self], state: &mut H)
629 where
630 H: #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher,
631 {
632 #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher::write(
633 state,
634 #zerocopy_crate::IntoBytes::as_bytes(data)
635 )
636 }
637 }
638 })
639}
640
641fn derive_eq_inner(
642 ast: &DeriveInput,
643 _top_level: Trait,
644 zerocopy_crate: &Path,
645) -> Result<TokenStream, Error> {
646 let type_ident = &ast.ident;
652 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
653 let where_predicates = where_clause.map(|clause| &clause.predicates);
654 Ok(quote! {
655 #[allow(deprecated)]
658 #[automatically_derived]
661 impl #impl_generics #zerocopy_crate::util::macro_util::core_reexport::cmp::PartialEq for #type_ident #ty_generics
662 where
663 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
664 #where_predicates
665 {
666 fn eq(&self, other: &Self) -> bool {
667 #zerocopy_crate::util::macro_util::core_reexport::cmp::PartialEq::eq(
668 #zerocopy_crate::IntoBytes::as_bytes(self),
669 #zerocopy_crate::IntoBytes::as_bytes(other),
670 )
671 }
672 }
673
674 #[allow(deprecated)]
677 #[automatically_derived]
680 impl #impl_generics #zerocopy_crate::util::macro_util::core_reexport::cmp::Eq for #type_ident #ty_generics
681 where
682 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
683 #where_predicates
684 {
685 }
686 })
687}
688
689fn derive_split_at_inner(
690 ast: &DeriveInput,
691 _top_level: Trait,
692 zerocopy_crate: &Path,
693) -> Result<TokenStream, Error> {
694 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
695
696 match &ast.data {
697 Data::Struct(_) => {}
698 Data::Enum(_) | Data::Union(_) => {
699 return Err(Error::new(Span::call_site(), "can only be applied to structs"));
700 }
701 };
702
703 if repr.get_packed().is_some() {
704 return Err(Error::new(Span::call_site(), "must not have #[repr(packed)] attribute"));
705 }
706
707 if !(repr.is_c() || repr.is_transparent()) {
708 return Err(Error::new(Span::call_site(), "must have #[repr(C)] or #[repr(transparent)] in order to guarantee this type's layout is splitable"));
709 }
710
711 let fields = ast.data.fields();
712 let trailing_field = if let Some(((_, _, trailing_field), _)) = fields.split_last() {
713 trailing_field
714 } else {
715 return Err(Error::new(Span::call_site(), "must at least one field"));
716 };
717
718 Ok(ImplBlockBuilder::new(
723 ast,
724 &ast.data,
725 Trait::SplitAt,
726 FieldBounds::TRAILING_SELF,
727 zerocopy_crate,
728 )
729 .inner_extras(quote! {
730 type Elem = <#trailing_field as ::zerocopy::SplitAt>::Elem;
731 })
732 .build())
733}
734
735fn derive_try_from_bytes_struct(
738 ast: &DeriveInput,
739 strct: &DataStruct,
740 top_level: Trait,
741 zerocopy_crate: &Path,
742) -> Result<TokenStream, Error> {
743 let extras =
744 try_gen_trivial_is_bit_valid(ast, top_level, zerocopy_crate).unwrap_or_else(|| {
745 let fields = strct.fields();
746 let field_names = fields.iter().map(|(_vis, name, _ty)| name);
747 let field_tys = fields.iter().map(|(_vis, _name, ty)| ty);
748 quote!(
749 fn is_bit_valid<___ZerocopyAliasing>(
755 mut candidate: #zerocopy_crate::Maybe<Self, ___ZerocopyAliasing>,
756 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
757 where
758 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
759 {
760 use #zerocopy_crate::util::macro_util::core_reexport;
761
762 true #(&& {
763 let field_candidate = unsafe {
771 let project = |slf: #zerocopy_crate::pointer::PtrInner<'_, Self>| {
772 let slf = slf.as_non_null().as_ptr();
773 let field = core_reexport::ptr::addr_of_mut!((*slf).#field_names);
774 unsafe { core_reexport::ptr::NonNull::new_unchecked(field) }
782 };
783
784 candidate.reborrow().cast_unsized_unchecked(project)
785 };
786
787 <#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate)
788 })*
789 }
790 )
791 });
792 Ok(ImplBlockBuilder::new(
793 ast,
794 strct,
795 Trait::TryFromBytes,
796 FieldBounds::ALL_SELF,
797 zerocopy_crate,
798 )
799 .inner_extras(extras)
800 .build())
801}
802
803fn derive_try_from_bytes_union(
806 ast: &DeriveInput,
807 unn: &DataUnion,
808 top_level: Trait,
809 zerocopy_crate: &Path,
810) -> TokenStream {
811 let field_type_trait_bounds =
813 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
814 let extras =
815 try_gen_trivial_is_bit_valid(ast, top_level, zerocopy_crate).unwrap_or_else(|| {
816 let fields = unn.fields();
817 let field_names = fields.iter().map(|(_vis, name, _ty)| name);
818 let field_tys = fields.iter().map(|(_vis, _name, ty)| ty);
819 quote!(
820 fn is_bit_valid<___ZerocopyAliasing>(
826 mut candidate: #zerocopy_crate::Maybe<'_, Self,___ZerocopyAliasing>
827 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
828 where
829 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
830 {
831 use #zerocopy_crate::util::macro_util::core_reexport;
832
833 false #(|| {
834 let field_candidate = unsafe {
842 let project = |slf: #zerocopy_crate::pointer::PtrInner<'_, Self>| {
843 let slf = slf.as_non_null().as_ptr();
844 let field = core_reexport::ptr::addr_of_mut!((*slf).#field_names);
845 unsafe { core_reexport::ptr::NonNull::new_unchecked(field) }
853 };
854
855 candidate.reborrow().cast_unsized_unchecked(project)
856 };
857
858 <#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate)
859 })*
860 }
861 )
862 });
863 ImplBlockBuilder::new(ast, unn, Trait::TryFromBytes, field_type_trait_bounds, zerocopy_crate)
864 .inner_extras(extras)
865 .build()
866}
867
868fn derive_try_from_bytes_enum(
869 ast: &DeriveInput,
870 enm: &DataEnum,
871 top_level: Trait,
872 zerocopy_crate: &Path,
873) -> Result<TokenStream, Error> {
874 let repr = EnumRepr::from_attrs(&ast.attrs)?;
875
876 let could_be_from_bytes = enum_size_from_repr(&repr)
882 .map(|size| enm.fields().is_empty() && enm.variants.len() == 1usize << size)
883 .unwrap_or(false);
884
885 let trivial_is_bit_valid = try_gen_trivial_is_bit_valid(ast, top_level, zerocopy_crate);
886 let extra = match (trivial_is_bit_valid, could_be_from_bytes) {
887 (Some(is_bit_valid), _) => is_bit_valid,
888 (None, true) => unsafe { gen_trivial_is_bit_valid_unchecked(zerocopy_crate) },
891 (None, false) => {
892 r#enum::derive_is_bit_valid(&ast.ident, &repr, &ast.generics, enm, zerocopy_crate)?
893 }
894 };
895
896 Ok(ImplBlockBuilder::new(ast, enm, Trait::TryFromBytes, FieldBounds::ALL_SELF, zerocopy_crate)
897 .inner_extras(extra)
898 .build())
899}
900
901fn try_gen_trivial_is_bit_valid(
923 ast: &DeriveInput,
924 top_level: Trait,
925 zerocopy_crate: &Path,
926) -> Option<proc_macro2::TokenStream> {
927 if top_level == Trait::FromBytes && ast.generics.params.is_empty() {
936 Some(quote!(
937 fn is_bit_valid<___ZerocopyAliasing>(
939 _candidate: #zerocopy_crate::Maybe<Self, ___ZerocopyAliasing>,
940 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
941 where
942 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
943 {
944 if false {
945 fn assert_is_from_bytes<T>()
946 where
947 T: #zerocopy_crate::FromBytes,
948 T: ?#zerocopy_crate::util::macro_util::core_reexport::marker::Sized,
949 {
950 }
951
952 assert_is_from_bytes::<Self>();
953 }
954
955 true
959 }
960 ))
961 } else {
962 None
963 }
964}
965
966unsafe fn gen_trivial_is_bit_valid_unchecked(zerocopy_crate: &Path) -> proc_macro2::TokenStream {
978 quote!(
979 fn is_bit_valid<___ZerocopyAliasing>(
982 _candidate: #zerocopy_crate::Maybe<Self, ___ZerocopyAliasing>,
983 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
984 where
985 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
986 {
987 true
988 }
989 )
990}
991
992fn derive_from_zeros_struct(
995 ast: &DeriveInput,
996 strct: &DataStruct,
997 zerocopy_crate: &Path,
998) -> TokenStream {
999 ImplBlockBuilder::new(ast, strct, Trait::FromZeros, FieldBounds::ALL_SELF, zerocopy_crate)
1000 .build()
1001}
1002
1003fn find_zero_variant(enm: &DataEnum) -> Result<usize, bool> {
1009 let mut next_negative_discriminant = Some(0);
1017
1018 let mut has_unknown_discriminants = false;
1025
1026 for (i, v) in enm.variants.iter().enumerate() {
1027 match v.discriminant.as_ref() {
1028 None => {
1030 match next_negative_discriminant.as_mut() {
1031 Some(0) => return Ok(i),
1032 Some(n) => *n -= 1,
1034 None => (),
1035 }
1036 }
1037 Some((_, Expr::Lit(ExprLit { lit: Lit::Int(int), .. }))) => {
1039 match int.base10_parse::<u128>().ok() {
1040 Some(0) => return Ok(i),
1041 Some(_) => next_negative_discriminant = None,
1042 None => {
1043 has_unknown_discriminants = true;
1045 next_negative_discriminant = None;
1046 }
1047 }
1048 }
1049 Some((_, Expr::Unary(ExprUnary { op: UnOp::Neg(_), expr, .. }))) => match &**expr {
1051 Expr::Lit(ExprLit { lit: Lit::Int(int), .. }) => {
1052 match int.base10_parse::<u128>().ok() {
1053 Some(0) => return Ok(i),
1054 Some(x) => next_negative_discriminant = Some(x - 1),
1056 None => {
1057 has_unknown_discriminants = true;
1060 next_negative_discriminant = None;
1061 }
1062 }
1063 }
1064 _ => {
1066 has_unknown_discriminants = true;
1067 next_negative_discriminant = None;
1068 }
1069 },
1070 _ => {
1072 has_unknown_discriminants = true;
1073 next_negative_discriminant = None;
1074 }
1075 }
1076 }
1077
1078 Err(has_unknown_discriminants)
1079}
1080
1081fn derive_from_zeros_enum(
1085 ast: &DeriveInput,
1086 enm: &DataEnum,
1087 zerocopy_crate: &Path,
1088) -> Result<TokenStream, Error> {
1089 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1090
1091 match repr {
1094 Repr::Compound(
1095 Spanned { t: CompoundRepr::C | CompoundRepr::Primitive(_), span: _ },
1096 _,
1097 ) => {}
1098 Repr::Transparent(_)
1099 | 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")),
1100 }
1101
1102 let zero_variant = match find_zero_variant(enm) {
1103 Ok(index) => enm.variants.iter().nth(index).unwrap(),
1104 Err(true) => {
1106 return Err(Error::new_spanned(
1107 ast,
1108 "FromZeros only supported on enums with a variant that has a discriminant of `0`\n\
1109 help: This enum has discriminants which are not literal integers. One of those may \
1110 define or imply which variant has a discriminant of zero. Use a literal integer to \
1111 define or imply the variant with a discriminant of zero.",
1112 ));
1113 }
1114 Err(false) => {
1116 return Err(Error::new_spanned(
1117 ast,
1118 "FromZeros only supported on enums with a variant that has a discriminant of `0`",
1119 ));
1120 }
1121 };
1122
1123 let explicit_bounds = zero_variant
1124 .fields
1125 .iter()
1126 .map(|field| {
1127 let ty = &field.ty;
1128 parse_quote! { #ty: #zerocopy_crate::FromZeros }
1129 })
1130 .collect::<Vec<WherePredicate>>();
1131
1132 Ok(ImplBlockBuilder::new(
1133 ast,
1134 enm,
1135 Trait::FromZeros,
1136 FieldBounds::Explicit(explicit_bounds),
1137 zerocopy_crate,
1138 )
1139 .build())
1140}
1141
1142fn derive_from_zeros_union(
1145 ast: &DeriveInput,
1146 unn: &DataUnion,
1147 zerocopy_crate: &Path,
1148) -> TokenStream {
1149 let field_type_trait_bounds =
1152 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
1153 ImplBlockBuilder::new(ast, unn, Trait::FromZeros, field_type_trait_bounds, zerocopy_crate)
1154 .build()
1155}
1156
1157fn derive_from_bytes_struct(
1160 ast: &DeriveInput,
1161 strct: &DataStruct,
1162 zerocopy_crate: &Path,
1163) -> TokenStream {
1164 ImplBlockBuilder::new(ast, strct, Trait::FromBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1165 .build()
1166}
1167
1168fn derive_from_bytes_enum(
1183 ast: &DeriveInput,
1184 enm: &DataEnum,
1185 zerocopy_crate: &Path,
1186) -> Result<TokenStream, Error> {
1187 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1188
1189 let variants_required = 1usize << enum_size_from_repr(&repr)?;
1190 if enm.variants.len() != variants_required {
1191 return Err(Error::new_spanned(
1192 ast,
1193 format!(
1194 "FromBytes only supported on {} enum with {} variants",
1195 repr.repr_type_name(),
1196 variants_required
1197 ),
1198 ));
1199 }
1200
1201 Ok(ImplBlockBuilder::new(ast, enm, Trait::FromBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1202 .build())
1203}
1204
1205fn enum_size_from_repr(repr: &EnumRepr) -> Result<usize, Error> {
1207 use {CompoundRepr::*, PrimitiveRepr::*, Repr::*};
1208 match repr {
1209 Transparent(span)
1210 | Compound(
1211 Spanned { t: C | Rust | Primitive(U32 | I32 | U64 | I64 | Usize | Isize), span },
1212 _,
1213 ) => Err(Error::new(*span, "`FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16`")),
1214 Compound(Spanned { t: Primitive(U8 | I8), span: _ }, _align) => Ok(8),
1215 Compound(Spanned { t: Primitive(U16 | I16), span: _ }, _align) => Ok(16),
1216 }
1217}
1218
1219fn derive_from_bytes_union(
1222 ast: &DeriveInput,
1223 unn: &DataUnion,
1224 zerocopy_crate: &Path,
1225) -> TokenStream {
1226 let field_type_trait_bounds =
1229 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
1230 ImplBlockBuilder::new(ast, unn, Trait::FromBytes, field_type_trait_bounds, zerocopy_crate)
1231 .build()
1232}
1233
1234fn derive_into_bytes_struct(
1235 ast: &DeriveInput,
1236 strct: &DataStruct,
1237 zerocopy_crate: &Path,
1238) -> Result<TokenStream, Error> {
1239 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1240
1241 let is_transparent = repr.is_transparent();
1242 let is_c = repr.is_c();
1243 let is_packed_1 = repr.is_packed_1();
1244 let num_fields = strct.fields().len();
1245
1246 let (padding_check, require_unaligned_fields) = if is_transparent || is_packed_1 {
1247 (None, false)
1264 } else if is_c && !repr.is_align_gt_1() && num_fields <= 1 {
1265 (None, false)
1269 } else if ast.generics.params.is_empty() {
1270 (Some(PaddingCheck::Struct), false)
1283 } else if is_c && !repr.is_align_gt_1() {
1284 (None, true)
1293 } else {
1294 return Err(Error::new(Span::call_site(), "must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout"));
1295 };
1296
1297 let field_bounds = if require_unaligned_fields {
1298 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Unaligned)])
1299 } else {
1300 FieldBounds::ALL_SELF
1301 };
1302
1303 Ok(ImplBlockBuilder::new(ast, strct, Trait::IntoBytes, field_bounds, zerocopy_crate)
1304 .padding_check(padding_check)
1305 .build())
1306}
1307
1308fn derive_into_bytes_enum(
1314 ast: &DeriveInput,
1315 enm: &DataEnum,
1316 zerocopy_crate: &Path,
1317) -> Result<TokenStream, Error> {
1318 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1319 if !repr.is_c() && !repr.is_primitive() {
1320 return Err(Error::new(Span::call_site(), "must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout"));
1321 }
1322
1323 let tag_type_definition = r#enum::generate_tag_enum(&repr, enm);
1324 Ok(ImplBlockBuilder::new(ast, enm, Trait::IntoBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1325 .padding_check(PaddingCheck::Enum { tag_type_definition })
1326 .build())
1327}
1328
1329fn derive_into_bytes_union(
1334 ast: &DeriveInput,
1335 unn: &DataUnion,
1336 zerocopy_crate: &Path,
1337) -> Result<TokenStream, Error> {
1338 let cfg_compile_error = if cfg!(zerocopy_derive_union_into_bytes) {
1347 quote!()
1348 } else {
1349 let error_message = "requires --cfg zerocopy_derive_union_into_bytes;
1350please let us know you use this feature: https://github.com/google/zerocopy/discussions/1802";
1351 quote!(
1352 const _: () = {
1353 #[cfg(not(zerocopy_derive_union_into_bytes))]
1354 #zerocopy_crate::util::macro_util::core_reexport::compile_error!(#error_message);
1355 };
1356 )
1357 };
1358
1359 if !ast.generics.params.is_empty() {
1361 return Err(Error::new(Span::call_site(), "unsupported on types with type parameters"));
1362 }
1363
1364 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1369 if !repr.is_c() && !repr.is_transparent() && !repr.is_packed_1() {
1370 return Err(Error::new(
1371 Span::call_site(),
1372 "must be #[repr(C)], #[repr(packed)], or #[repr(transparent)]",
1373 ));
1374 }
1375
1376 let impl_block =
1377 ImplBlockBuilder::new(ast, unn, Trait::IntoBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1378 .padding_check(PaddingCheck::Union)
1379 .build();
1380 Ok(quote!(#cfg_compile_error #impl_block))
1381}
1382
1383fn derive_unaligned_struct(
1389 ast: &DeriveInput,
1390 strct: &DataStruct,
1391 zerocopy_crate: &Path,
1392) -> Result<TokenStream, Error> {
1393 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1394 repr.unaligned_validate_no_align_gt_1()?;
1395
1396 let field_bounds = if repr.is_packed_1() {
1397 FieldBounds::None
1398 } else if repr.is_c() || repr.is_transparent() {
1399 FieldBounds::ALL_SELF
1400 } else {
1401 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"));
1402 };
1403
1404 Ok(ImplBlockBuilder::new(ast, strct, Trait::Unaligned, field_bounds, zerocopy_crate).build())
1405}
1406
1407fn derive_unaligned_enum(
1411 ast: &DeriveInput,
1412 enm: &DataEnum,
1413 zerocopy_crate: &Path,
1414) -> Result<TokenStream, Error> {
1415 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1416 repr.unaligned_validate_no_align_gt_1()?;
1417
1418 if !repr.is_u8() && !repr.is_i8() {
1419 return Err(Error::new(Span::call_site(), "must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment"));
1420 }
1421
1422 Ok(ImplBlockBuilder::new(ast, enm, Trait::Unaligned, FieldBounds::ALL_SELF, zerocopy_crate)
1423 .build())
1424}
1425
1426fn derive_unaligned_union(
1432 ast: &DeriveInput,
1433 unn: &DataUnion,
1434 zerocopy_crate: &Path,
1435) -> Result<TokenStream, Error> {
1436 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1437 repr.unaligned_validate_no_align_gt_1()?;
1438
1439 let field_type_trait_bounds = if repr.is_packed_1() {
1440 FieldBounds::None
1441 } else if repr.is_c() || repr.is_transparent() {
1442 FieldBounds::ALL_SELF
1443 } else {
1444 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"));
1445 };
1446
1447 Ok(ImplBlockBuilder::new(ast, unn, Trait::Unaligned, field_type_trait_bounds, zerocopy_crate)
1448 .build())
1449}
1450
1451enum PaddingCheck {
1454 Struct,
1457 Union,
1459 Enum { tag_type_definition: TokenStream },
1464}
1465
1466impl PaddingCheck {
1467 fn validator_macro_ident(&self) -> Ident {
1470 let s = match self {
1471 PaddingCheck::Struct => "struct_has_padding",
1472 PaddingCheck::Union => "union_has_padding",
1473 PaddingCheck::Enum { .. } => "enum_has_padding",
1474 };
1475
1476 Ident::new(s, Span::call_site())
1477 }
1478
1479 fn validator_macro_context(&self) -> Option<&TokenStream> {
1482 match self {
1483 PaddingCheck::Struct | PaddingCheck::Union => None,
1484 PaddingCheck::Enum { tag_type_definition } => Some(tag_type_definition),
1485 }
1486 }
1487}
1488
1489#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1490enum Trait {
1491 KnownLayout,
1492 Immutable,
1493 TryFromBytes,
1494 FromZeros,
1495 FromBytes,
1496 IntoBytes,
1497 Unaligned,
1498 Sized,
1499 ByteHash,
1500 ByteEq,
1501 SplitAt,
1502}
1503
1504impl ToTokens for Trait {
1505 fn to_tokens(&self, tokens: &mut TokenStream) {
1506 let s = match self {
1516 Trait::KnownLayout => "KnownLayout",
1517 Trait::Immutable => "Immutable",
1518 Trait::TryFromBytes => "TryFromBytes",
1519 Trait::FromZeros => "FromZeros",
1520 Trait::FromBytes => "FromBytes",
1521 Trait::IntoBytes => "IntoBytes",
1522 Trait::Unaligned => "Unaligned",
1523 Trait::Sized => "Sized",
1524 Trait::ByteHash => "ByteHash",
1525 Trait::ByteEq => "ByteEq",
1526 Trait::SplitAt => "SplitAt",
1527 };
1528 let ident = Ident::new(s, Span::call_site());
1529 tokens.extend(core::iter::once(TokenTree::Ident(ident)));
1530 }
1531}
1532
1533impl Trait {
1534 fn crate_path(&self, zerocopy_crate: &Path) -> Path {
1535 match self {
1536 Self::Sized => {
1537 parse_quote!(#zerocopy_crate::util::macro_util::core_reexport::marker::#self)
1538 }
1539 _ => parse_quote!(#zerocopy_crate::#self),
1540 }
1541 }
1542}
1543
1544#[derive(Debug, Eq, PartialEq)]
1545enum TraitBound {
1546 Slf,
1547 Other(Trait),
1548}
1549
1550enum FieldBounds<'a> {
1551 None,
1552 All(&'a [TraitBound]),
1553 Trailing(&'a [TraitBound]),
1554 Explicit(Vec<WherePredicate>),
1555}
1556
1557impl<'a> FieldBounds<'a> {
1558 const ALL_SELF: FieldBounds<'a> = FieldBounds::All(&[TraitBound::Slf]);
1559 const TRAILING_SELF: FieldBounds<'a> = FieldBounds::Trailing(&[TraitBound::Slf]);
1560}
1561
1562#[derive(Debug, Eq, PartialEq)]
1563enum SelfBounds<'a> {
1564 None,
1565 All(&'a [Trait]),
1566}
1567
1568#[allow(clippy::needless_lifetimes)]
1571impl<'a> SelfBounds<'a> {
1572 const SIZED: Self = Self::All(&[Trait::Sized]);
1573}
1574
1575fn normalize_bounds(slf: Trait, bounds: &[TraitBound]) -> impl '_ + Iterator<Item = Trait> {
1577 bounds.iter().map(move |bound| match bound {
1578 TraitBound::Slf => slf,
1579 TraitBound::Other(trt) => *trt,
1580 })
1581}
1582
1583struct ImplBlockBuilder<'a, D: DataExt> {
1584 input: &'a DeriveInput,
1585 data: &'a D,
1586 trt: Trait,
1587 field_type_trait_bounds: FieldBounds<'a>,
1588 zerocopy_crate: &'a Path,
1589 self_type_trait_bounds: SelfBounds<'a>,
1590 padding_check: Option<PaddingCheck>,
1591 inner_extras: Option<TokenStream>,
1592 outer_extras: Option<TokenStream>,
1593}
1594
1595impl<'a, D: DataExt> ImplBlockBuilder<'a, D> {
1596 fn new(
1597 input: &'a DeriveInput,
1598 data: &'a D,
1599 trt: Trait,
1600 field_type_trait_bounds: FieldBounds<'a>,
1601 zerocopy_crate: &'a Path,
1602 ) -> Self {
1603 Self {
1604 input,
1605 data,
1606 trt,
1607 field_type_trait_bounds,
1608 zerocopy_crate,
1609 self_type_trait_bounds: SelfBounds::None,
1610 padding_check: None,
1611 inner_extras: None,
1612 outer_extras: None,
1613 }
1614 }
1615
1616 fn self_type_trait_bounds(mut self, self_type_trait_bounds: SelfBounds<'a>) -> Self {
1617 self.self_type_trait_bounds = self_type_trait_bounds;
1618 self
1619 }
1620
1621 fn padding_check<P: Into<Option<PaddingCheck>>>(mut self, padding_check: P) -> Self {
1622 self.padding_check = padding_check.into();
1623 self
1624 }
1625
1626 fn inner_extras(mut self, inner_extras: TokenStream) -> Self {
1627 self.inner_extras = Some(inner_extras);
1628 self
1629 }
1630
1631 fn outer_extras<T: Into<Option<TokenStream>>>(mut self, outer_extras: T) -> Self {
1632 self.outer_extras = outer_extras.into();
1633 self
1634 }
1635
1636 fn build(self) -> TokenStream {
1637 let type_ident = &self.input.ident;
1697 let trait_path = self.trt.crate_path(self.zerocopy_crate);
1698 let fields = self.data.fields();
1699 let variants = self.data.variants();
1700 let tag = self.data.tag();
1701 let zerocopy_crate = self.zerocopy_crate;
1702
1703 fn bound_tt(
1704 ty: &Type,
1705 traits: impl Iterator<Item = Trait>,
1706 zerocopy_crate: &Path,
1707 ) -> WherePredicate {
1708 let traits = traits.map(|t| t.crate_path(zerocopy_crate));
1709 parse_quote!(#ty: #(#traits)+*)
1710 }
1711 let field_type_bounds: Vec<_> = match (self.field_type_trait_bounds, &fields[..]) {
1712 (FieldBounds::All(traits), _) => fields
1713 .iter()
1714 .map(|(_vis, _name, ty)| {
1715 bound_tt(ty, normalize_bounds(self.trt, traits), zerocopy_crate)
1716 })
1717 .collect(),
1718 (FieldBounds::None, _) | (FieldBounds::Trailing(..), []) => vec![],
1719 (FieldBounds::Trailing(traits), [.., last]) => {
1720 vec![bound_tt(last.2, normalize_bounds(self.trt, traits), zerocopy_crate)]
1721 }
1722 (FieldBounds::Explicit(bounds), _) => bounds,
1723 };
1724
1725 #[allow(unstable_name_collisions)] let padding_check_bound = self
1728 .padding_check
1729 .and_then(|check| (!fields.is_empty()).then_some(check))
1730 .map(|check| {
1731 let variant_types = variants.iter().map(|var| {
1732 let types = var.iter().map(|(_vis, _name, ty)| ty);
1733 quote!([#(#types),*])
1734 });
1735 let validator_context = check.validator_macro_context();
1736 let validator_macro = check.validator_macro_ident();
1737 let t = tag.iter();
1738 parse_quote! {
1739 (): #zerocopy_crate::util::macro_util::PaddingFree<
1740 Self,
1741 {
1742 #validator_context
1743 #zerocopy_crate::#validator_macro!(Self, #(#t,)* #(#variant_types),*)
1744 }
1745 >
1746 }
1747 });
1748
1749 let self_bounds: Option<WherePredicate> = match self.self_type_trait_bounds {
1750 SelfBounds::None => None,
1751 SelfBounds::All(traits) => {
1752 Some(bound_tt(&parse_quote!(Self), traits.iter().copied(), zerocopy_crate))
1753 }
1754 };
1755
1756 let bounds = self
1757 .input
1758 .generics
1759 .where_clause
1760 .as_ref()
1761 .map(|where_clause| where_clause.predicates.iter())
1762 .into_iter()
1763 .flatten()
1764 .chain(field_type_bounds.iter())
1765 .chain(padding_check_bound.iter())
1766 .chain(self_bounds.iter());
1767
1768 let params = self.input.generics.params.clone().into_iter().map(|mut param| {
1770 match &mut param {
1771 GenericParam::Type(ty) => ty.default = None,
1772 GenericParam::Const(cnst) => cnst.default = None,
1773 GenericParam::Lifetime(_) => {}
1774 }
1775 quote!(#param)
1776 });
1777
1778 let param_idents = self.input.generics.params.iter().map(|param| match param {
1781 GenericParam::Type(ty) => {
1782 let ident = &ty.ident;
1783 quote!(#ident)
1784 }
1785 GenericParam::Lifetime(l) => {
1786 let ident = &l.lifetime;
1787 quote!(#ident)
1788 }
1789 GenericParam::Const(cnst) => {
1790 let ident = &cnst.ident;
1791 quote!({#ident})
1792 }
1793 });
1794
1795 let inner_extras = self.inner_extras;
1796 let impl_tokens = quote! {
1797 #[allow(deprecated)]
1800 #[automatically_derived]
1803 unsafe impl < #(#params),* > #trait_path for #type_ident < #(#param_idents),* >
1804 where
1805 #(#bounds,)*
1806 {
1807 fn only_derive_is_allowed_to_implement_this_trait() {}
1808
1809 #inner_extras
1810 }
1811 };
1812
1813 if let Some(outer_extras) = self.outer_extras {
1814 quote! {
1817 const _: () = {
1818 #impl_tokens
1819
1820 #outer_extras
1821 };
1822 }
1823 } else {
1824 impl_tokens
1825 }
1826 }
1827}
1828
1829#[allow(unused)]
1837trait BoolExt {
1838 fn then_some<T>(self, t: T) -> Option<T>;
1839}
1840
1841impl BoolExt for bool {
1842 fn then_some<T>(self, t: T) -> Option<T> {
1843 if self {
1844 Some(t)
1845 } else {
1846 None
1847 }
1848 }
1849}