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::uninlined_format_args)]
27#![deny(
28 rustdoc::bare_urls,
29 rustdoc::broken_intra_doc_links,
30 rustdoc::invalid_codeblock_attributes,
31 rustdoc::invalid_html_tags,
32 rustdoc::invalid_rust_codeblocks,
33 rustdoc::missing_crate_level_docs,
34 rustdoc::private_intra_doc_links
35)]
36#![recursion_limit = "128"]
37
38mod r#enum;
39mod ext;
40#[cfg(test)]
41mod output_tests;
42mod repr;
43
44use proc_macro2::{Span, TokenStream, TokenTree};
45use quote::{quote, ToTokens};
46use syn::{
47 parse_quote, Attribute, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Error, Expr,
48 ExprLit, ExprUnary, GenericParam, Ident, Lit, Meta, Path, Type, UnOp, WherePredicate,
49};
50
51use crate::{ext::*, 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)]
248 fn raw_from_ptr_len(
249 bytes: #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<u8>,
250 meta: Self::PointerMetadata,
251 ) -> #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<Self> {
252 use #zerocopy_crate::KnownLayout;
253 let trailing = <#trailing_field_ty as KnownLayout>::raw_from_ptr_len(bytes, meta);
254 let slf = trailing.as_ptr() as *mut Self;
255 unsafe { #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull::new_unchecked(slf) }
257 }
258
259 #[inline(always)]
260 fn pointer_to_metadata(ptr: *mut Self) -> Self::PointerMetadata {
261 <#trailing_field_ty>::pointer_to_metadata(ptr as *mut _)
262 }
263 }
264 };
265
266 let inner_extras = {
267 let leading_fields_tys = leading_fields_tys.clone();
268 let methods = make_methods(*trailing_field_ty);
269 let (_, ty_generics, _) = ast.generics.split_for_impl();
270
271 quote!(
272 type PointerMetadata = <#trailing_field_ty as #zerocopy_crate::KnownLayout>::PointerMetadata;
273
274 type MaybeUninit = __ZerocopyKnownLayoutMaybeUninit #ty_generics;
275
276 const LAYOUT: #zerocopy_crate::DstLayout = {
291 use #zerocopy_crate::util::macro_util::core_reexport::num::NonZeroUsize;
292 use #zerocopy_crate::{DstLayout, KnownLayout};
293
294 DstLayout::for_repr_c_struct(
295 #repr_align,
296 #repr_packed,
297 &[
298 #(DstLayout::for_type::<#leading_fields_tys>(),)*
299 <#trailing_field_ty as KnownLayout>::LAYOUT
300 ],
301 )
302 };
303
304 #methods
305 )
306 };
307
308 let outer_extras = {
309 let ident = &ast.ident;
310 let vis = &ast.vis;
311 let params = &ast.generics.params;
312 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
313
314 let predicates = if let Some(where_clause) = where_clause {
315 where_clause.predicates.clone()
316 } else {
317 Default::default()
318 };
319
320 let field_index = |name: &TokenStream| {
323 let name = to_ident_str(name);
324 Ident::new(&format!("__Zerocopy_Field_{}", name), ident.span())
325 };
326
327 let field_indices: Vec<_> =
328 fields.iter().map(|(_vis, name, _ty)| field_index(name)).collect();
329
330 let field_defs = field_indices.iter().zip(&fields).map(|(idx, (vis, _, _))| {
332 quote! {
333 #[allow(non_camel_case_types)]
334 #vis struct #idx;
335 }
336 });
337
338 let field_impls = field_indices.iter().zip(&fields).map(|(idx, (_, _, ty))| quote! {
339 #[allow(deprecated)]
341 unsafe impl #impl_generics #zerocopy_crate::util::macro_util::Field<#idx> for #ident #ty_generics
342 where
343 #predicates
344 {
345 type Type = #ty;
346 }
347 });
348
349 let trailing_field_index = field_index(trailing_field_name);
350 let leading_field_indices =
351 leading_fields.iter().map(|(_vis, name, _ty)| field_index(name));
352
353 let trailing_field_ty = quote! {
354 <#ident #ty_generics as
355 #zerocopy_crate::util::macro_util::Field<#trailing_field_index>
356 >::Type
357 };
358
359 let methods = make_methods(&parse_quote! {
360 <#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit
361 });
362
363 quote! {
364 #(#field_defs)*
365
366 #(#field_impls)*
367
368 #repr
376 #[doc(hidden)]
377 #[allow(private_bounds)]
381 #[allow(deprecated)]
382 #vis struct __ZerocopyKnownLayoutMaybeUninit<#params> (
383 #(#zerocopy_crate::util::macro_util::core_reexport::mem::MaybeUninit<
384 <#ident #ty_generics as
385 #zerocopy_crate::util::macro_util::Field<#leading_field_indices>
386 >::Type
387 >,)*
388 #zerocopy_crate::util::macro_util::core_reexport::mem::ManuallyDrop<
395 <#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit
396 >
397 )
398 where
399 #trailing_field_ty: #zerocopy_crate::KnownLayout,
400 #predicates;
401
402 #[allow(deprecated)]
409 unsafe impl #impl_generics #zerocopy_crate::KnownLayout for __ZerocopyKnownLayoutMaybeUninit #ty_generics
410 where
411 #trailing_field_ty: #zerocopy_crate::KnownLayout,
412 #predicates
413 {
414 #[allow(clippy::missing_inline_in_public_items)]
415 fn only_derive_is_allowed_to_implement_this_trait() {}
416
417 type PointerMetadata = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::PointerMetadata;
418
419 type MaybeUninit = Self;
420
421 const LAYOUT: #zerocopy_crate::DstLayout = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::LAYOUT;
422
423 #methods
424 }
425 }
426 };
427
428 (SelfBounds::None, inner_extras, Some(outer_extras))
429 } else {
430 (
434 SelfBounds::SIZED,
435 quote!(
436 type PointerMetadata = ();
437 type MaybeUninit =
438 #zerocopy_crate::util::macro_util::core_reexport::mem::MaybeUninit<Self>;
439
440 const LAYOUT: #zerocopy_crate::DstLayout = #zerocopy_crate::DstLayout::for_type::<Self>();
444
445 #[inline(always)]
450 fn raw_from_ptr_len(
451 bytes: #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<u8>,
452 _meta: (),
453 ) -> #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<Self>
454 {
455 bytes.cast::<Self>()
456 }
457
458 #[inline(always)]
459 fn pointer_to_metadata(_ptr: *mut Self) -> () {}
460 ),
461 None,
462 )
463 };
464
465 Ok(match &ast.data {
466 Data::Struct(strct) => {
467 let require_trait_bound_on_field_types = if self_bounds == SelfBounds::SIZED {
468 FieldBounds::None
469 } else {
470 FieldBounds::TRAILING_SELF
471 };
472
473 ImplBlockBuilder::new(
478 ast,
479 strct,
480 Trait::KnownLayout,
481 require_trait_bound_on_field_types,
482 zerocopy_crate,
483 )
484 .self_type_trait_bounds(self_bounds)
485 .inner_extras(inner_extras)
486 .outer_extras(outer_extras)
487 .build()
488 }
489 Data::Enum(enm) => {
490 ImplBlockBuilder::new(ast, enm, Trait::KnownLayout, FieldBounds::None, zerocopy_crate)
493 .self_type_trait_bounds(SelfBounds::SIZED)
494 .inner_extras(inner_extras)
495 .outer_extras(outer_extras)
496 .build()
497 }
498 Data::Union(unn) => {
499 ImplBlockBuilder::new(ast, unn, Trait::KnownLayout, FieldBounds::None, zerocopy_crate)
502 .self_type_trait_bounds(SelfBounds::SIZED)
503 .inner_extras(inner_extras)
504 .outer_extras(outer_extras)
505 .build()
506 }
507 })
508}
509
510fn derive_no_cell_inner(
511 ast: &DeriveInput,
512 _top_level: Trait,
513 zerocopy_crate: &Path,
514) -> TokenStream {
515 match &ast.data {
516 Data::Struct(strct) => ImplBlockBuilder::new(
517 ast,
518 strct,
519 Trait::Immutable,
520 FieldBounds::ALL_SELF,
521 zerocopy_crate,
522 )
523 .build(),
524 Data::Enum(enm) => {
525 ImplBlockBuilder::new(ast, enm, Trait::Immutable, FieldBounds::ALL_SELF, zerocopy_crate)
526 .build()
527 }
528 Data::Union(unn) => {
529 ImplBlockBuilder::new(ast, unn, Trait::Immutable, FieldBounds::ALL_SELF, zerocopy_crate)
530 .build()
531 }
532 }
533}
534
535fn derive_try_from_bytes_inner(
536 ast: &DeriveInput,
537 top_level: Trait,
538 zerocopy_crate: &Path,
539) -> Result<TokenStream, Error> {
540 match &ast.data {
541 Data::Struct(strct) => derive_try_from_bytes_struct(ast, strct, top_level, zerocopy_crate),
542 Data::Enum(enm) => derive_try_from_bytes_enum(ast, enm, top_level, zerocopy_crate),
543 Data::Union(unn) => Ok(derive_try_from_bytes_union(ast, unn, top_level, zerocopy_crate)),
544 }
545}
546
547fn derive_from_zeros_inner(
548 ast: &DeriveInput,
549 top_level: Trait,
550 zerocopy_crate: &Path,
551) -> Result<TokenStream, Error> {
552 let try_from_bytes = derive_try_from_bytes_inner(ast, top_level, zerocopy_crate)?;
553 let from_zeros = match &ast.data {
554 Data::Struct(strct) => derive_from_zeros_struct(ast, strct, zerocopy_crate),
555 Data::Enum(enm) => derive_from_zeros_enum(ast, enm, zerocopy_crate)?,
556 Data::Union(unn) => derive_from_zeros_union(ast, unn, zerocopy_crate),
557 };
558 Ok(IntoIterator::into_iter([try_from_bytes, from_zeros]).collect())
559}
560
561fn derive_from_bytes_inner(
562 ast: &DeriveInput,
563 top_level: Trait,
564 zerocopy_crate: &Path,
565) -> Result<TokenStream, Error> {
566 let from_zeros = derive_from_zeros_inner(ast, top_level, zerocopy_crate)?;
567 let from_bytes = match &ast.data {
568 Data::Struct(strct) => derive_from_bytes_struct(ast, strct, zerocopy_crate),
569 Data::Enum(enm) => derive_from_bytes_enum(ast, enm, zerocopy_crate)?,
570 Data::Union(unn) => derive_from_bytes_union(ast, unn, zerocopy_crate),
571 };
572
573 Ok(IntoIterator::into_iter([from_zeros, from_bytes]).collect())
574}
575
576fn derive_into_bytes_inner(
577 ast: &DeriveInput,
578 _top_level: Trait,
579 zerocopy_crate: &Path,
580) -> Result<TokenStream, Error> {
581 match &ast.data {
582 Data::Struct(strct) => derive_into_bytes_struct(ast, strct, zerocopy_crate),
583 Data::Enum(enm) => derive_into_bytes_enum(ast, enm, zerocopy_crate),
584 Data::Union(unn) => derive_into_bytes_union(ast, unn, zerocopy_crate),
585 }
586}
587
588fn derive_unaligned_inner(
589 ast: &DeriveInput,
590 _top_level: Trait,
591 zerocopy_crate: &Path,
592) -> Result<TokenStream, Error> {
593 match &ast.data {
594 Data::Struct(strct) => derive_unaligned_struct(ast, strct, zerocopy_crate),
595 Data::Enum(enm) => derive_unaligned_enum(ast, enm, zerocopy_crate),
596 Data::Union(unn) => derive_unaligned_union(ast, unn, zerocopy_crate),
597 }
598}
599
600fn derive_hash_inner(
601 ast: &DeriveInput,
602 _top_level: Trait,
603 zerocopy_crate: &Path,
604) -> Result<TokenStream, Error> {
605 let type_ident = &ast.ident;
611 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
612 let where_predicates = where_clause.map(|clause| &clause.predicates);
613 Ok(quote! {
614 #[allow(deprecated)]
615 #[automatically_derived]
618 impl #impl_generics #zerocopy_crate::util::macro_util::core_reexport::hash::Hash for #type_ident #ty_generics
619 where
620 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
621 #where_predicates
622 {
623 fn hash<H>(&self, state: &mut H)
624 where
625 H: #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher,
626 {
627 #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher::write(
628 state,
629 #zerocopy_crate::IntoBytes::as_bytes(self)
630 )
631 }
632
633 fn hash_slice<H>(data: &[Self], state: &mut H)
634 where
635 H: #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher,
636 {
637 #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher::write(
638 state,
639 #zerocopy_crate::IntoBytes::as_bytes(data)
640 )
641 }
642 }
643 })
644}
645
646fn derive_eq_inner(
647 ast: &DeriveInput,
648 _top_level: Trait,
649 zerocopy_crate: &Path,
650) -> Result<TokenStream, Error> {
651 let type_ident = &ast.ident;
657 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
658 let where_predicates = where_clause.map(|clause| &clause.predicates);
659 Ok(quote! {
660 #[allow(deprecated)]
663 #[automatically_derived]
666 impl #impl_generics #zerocopy_crate::util::macro_util::core_reexport::cmp::PartialEq for #type_ident #ty_generics
667 where
668 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
669 #where_predicates
670 {
671 fn eq(&self, other: &Self) -> bool {
672 #zerocopy_crate::util::macro_util::core_reexport::cmp::PartialEq::eq(
673 #zerocopy_crate::IntoBytes::as_bytes(self),
674 #zerocopy_crate::IntoBytes::as_bytes(other),
675 )
676 }
677 }
678
679 #[allow(deprecated)]
682 #[automatically_derived]
685 impl #impl_generics #zerocopy_crate::util::macro_util::core_reexport::cmp::Eq for #type_ident #ty_generics
686 where
687 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
688 #where_predicates
689 {
690 }
691 })
692}
693
694fn derive_split_at_inner(
695 ast: &DeriveInput,
696 _top_level: Trait,
697 zerocopy_crate: &Path,
698) -> Result<TokenStream, Error> {
699 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
700
701 match &ast.data {
702 Data::Struct(_) => {}
703 Data::Enum(_) | Data::Union(_) => {
704 return Err(Error::new(Span::call_site(), "can only be applied to structs"));
705 }
706 };
707
708 if repr.get_packed().is_some() {
709 return Err(Error::new(Span::call_site(), "must not have #[repr(packed)] attribute"));
710 }
711
712 if !(repr.is_c() || repr.is_transparent()) {
713 return Err(Error::new(Span::call_site(), "must have #[repr(C)] or #[repr(transparent)] in order to guarantee this type's layout is splitable"));
714 }
715
716 let fields = ast.data.fields();
717 let trailing_field = if let Some(((_, _, trailing_field), _)) = fields.split_last() {
718 trailing_field
719 } else {
720 return Err(Error::new(Span::call_site(), "must at least one field"));
721 };
722
723 Ok(ImplBlockBuilder::new(
728 ast,
729 &ast.data,
730 Trait::SplitAt,
731 FieldBounds::TRAILING_SELF,
732 zerocopy_crate,
733 )
734 .inner_extras(quote! {
735 type Elem = <#trailing_field as ::zerocopy::SplitAt>::Elem;
736 })
737 .build())
738}
739
740fn derive_try_from_bytes_struct(
743 ast: &DeriveInput,
744 strct: &DataStruct,
745 top_level: Trait,
746 zerocopy_crate: &Path,
747) -> Result<TokenStream, Error> {
748 let extras =
749 try_gen_trivial_is_bit_valid(ast, top_level, zerocopy_crate).unwrap_or_else(|| {
750 let fields = strct.fields();
751 let field_names = fields.iter().map(|(_vis, name, _ty)| name);
752 let field_tys = fields.iter().map(|(_vis, _name, ty)| ty);
753 quote!(
754 fn is_bit_valid<___ZerocopyAliasing>(
760 mut candidate: #zerocopy_crate::Maybe<Self, ___ZerocopyAliasing>,
761 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
762 where
763 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
764 {
765 use #zerocopy_crate::util::macro_util::core_reexport;
766 use #zerocopy_crate::pointer::PtrInner;
767
768 true #(&& {
769 let field_candidate = unsafe {
777 let project = |slf: PtrInner<'_, Self>| {
778 let slf = slf.as_non_null().as_ptr();
779 let field = core_reexport::ptr::addr_of_mut!((*slf).#field_names);
780 let ptr = unsafe { core_reexport::ptr::NonNull::new_unchecked(field) };
789 unsafe { PtrInner::new(ptr) }
800 };
801
802 candidate.reborrow().cast_unsized_unchecked(project)
803 };
804
805 <#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate)
806 })*
807 }
808 )
809 });
810 Ok(ImplBlockBuilder::new(
811 ast,
812 strct,
813 Trait::TryFromBytes,
814 FieldBounds::ALL_SELF,
815 zerocopy_crate,
816 )
817 .inner_extras(extras)
818 .build())
819}
820
821fn derive_try_from_bytes_union(
824 ast: &DeriveInput,
825 unn: &DataUnion,
826 top_level: Trait,
827 zerocopy_crate: &Path,
828) -> TokenStream {
829 let field_type_trait_bounds =
831 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
832 let extras =
833 try_gen_trivial_is_bit_valid(ast, top_level, zerocopy_crate).unwrap_or_else(|| {
834 let fields = unn.fields();
835 let field_names = fields.iter().map(|(_vis, name, _ty)| name);
836 let field_tys = fields.iter().map(|(_vis, _name, ty)| ty);
837 quote!(
838 fn is_bit_valid<___ZerocopyAliasing>(
844 mut candidate: #zerocopy_crate::Maybe<'_, Self,___ZerocopyAliasing>
845 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
846 where
847 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
848 {
849 use #zerocopy_crate::util::macro_util::core_reexport;
850 use #zerocopy_crate::pointer::PtrInner;
851
852 false #(|| {
853 let field_candidate = unsafe {
862 let project = |slf: PtrInner<'_, Self>| {
863 let slf = slf.as_non_null().as_ptr();
864 let field = core_reexport::ptr::addr_of_mut!((*slf).#field_names);
865 let ptr = unsafe { core_reexport::ptr::NonNull::new_unchecked(field) };
874 unsafe { PtrInner::new(ptr) }
885 };
886
887 candidate.reborrow().cast_unsized_unchecked(project)
888 };
889
890 <#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate)
891 })*
892 }
893 )
894 });
895 ImplBlockBuilder::new(ast, unn, Trait::TryFromBytes, field_type_trait_bounds, zerocopy_crate)
896 .inner_extras(extras)
897 .build()
898}
899
900fn derive_try_from_bytes_enum(
901 ast: &DeriveInput,
902 enm: &DataEnum,
903 top_level: Trait,
904 zerocopy_crate: &Path,
905) -> Result<TokenStream, Error> {
906 let repr = EnumRepr::from_attrs(&ast.attrs)?;
907
908 let could_be_from_bytes = enum_size_from_repr(&repr)
914 .map(|size| enm.fields().is_empty() && enm.variants.len() == 1usize << size)
915 .unwrap_or(false);
916
917 let trivial_is_bit_valid = try_gen_trivial_is_bit_valid(ast, top_level, zerocopy_crate);
918 let extra = match (trivial_is_bit_valid, could_be_from_bytes) {
919 (Some(is_bit_valid), _) => is_bit_valid,
920 (None, true) => unsafe { gen_trivial_is_bit_valid_unchecked(zerocopy_crate) },
923 (None, false) => {
924 r#enum::derive_is_bit_valid(&ast.ident, &repr, &ast.generics, enm, zerocopy_crate)?
925 }
926 };
927
928 Ok(ImplBlockBuilder::new(ast, enm, Trait::TryFromBytes, FieldBounds::ALL_SELF, zerocopy_crate)
929 .inner_extras(extra)
930 .build())
931}
932
933fn try_gen_trivial_is_bit_valid(
955 ast: &DeriveInput,
956 top_level: Trait,
957 zerocopy_crate: &Path,
958) -> Option<proc_macro2::TokenStream> {
959 if top_level == Trait::FromBytes && ast.generics.params.is_empty() {
968 Some(quote!(
969 fn is_bit_valid<___ZerocopyAliasing>(
971 _candidate: #zerocopy_crate::Maybe<Self, ___ZerocopyAliasing>,
972 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
973 where
974 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
975 {
976 if false {
977 fn assert_is_from_bytes<T>()
978 where
979 T: #zerocopy_crate::FromBytes,
980 T: ?#zerocopy_crate::util::macro_util::core_reexport::marker::Sized,
981 {
982 }
983
984 assert_is_from_bytes::<Self>();
985 }
986
987 true
991 }
992 ))
993 } else {
994 None
995 }
996}
997
998unsafe fn gen_trivial_is_bit_valid_unchecked(zerocopy_crate: &Path) -> proc_macro2::TokenStream {
1010 quote!(
1011 fn is_bit_valid<___ZerocopyAliasing>(
1014 _candidate: #zerocopy_crate::Maybe<Self, ___ZerocopyAliasing>,
1015 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
1016 where
1017 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
1018 {
1019 true
1020 }
1021 )
1022}
1023
1024fn derive_from_zeros_struct(
1027 ast: &DeriveInput,
1028 strct: &DataStruct,
1029 zerocopy_crate: &Path,
1030) -> TokenStream {
1031 ImplBlockBuilder::new(ast, strct, Trait::FromZeros, FieldBounds::ALL_SELF, zerocopy_crate)
1032 .build()
1033}
1034
1035fn find_zero_variant(enm: &DataEnum) -> Result<usize, bool> {
1041 let mut next_negative_discriminant = Some(0);
1049
1050 let mut has_unknown_discriminants = false;
1057
1058 for (i, v) in enm.variants.iter().enumerate() {
1059 match v.discriminant.as_ref() {
1060 None => {
1062 match next_negative_discriminant.as_mut() {
1063 Some(0) => return Ok(i),
1064 Some(n) => *n -= 1,
1066 None => (),
1067 }
1068 }
1069 Some((_, Expr::Lit(ExprLit { lit: Lit::Int(int), .. }))) => {
1071 match int.base10_parse::<u128>().ok() {
1072 Some(0) => return Ok(i),
1073 Some(_) => next_negative_discriminant = None,
1074 None => {
1075 has_unknown_discriminants = true;
1077 next_negative_discriminant = None;
1078 }
1079 }
1080 }
1081 Some((_, Expr::Unary(ExprUnary { op: UnOp::Neg(_), expr, .. }))) => match &**expr {
1083 Expr::Lit(ExprLit { lit: Lit::Int(int), .. }) => {
1084 match int.base10_parse::<u128>().ok() {
1085 Some(0) => return Ok(i),
1086 Some(x) => next_negative_discriminant = Some(x - 1),
1088 None => {
1089 has_unknown_discriminants = true;
1092 next_negative_discriminant = None;
1093 }
1094 }
1095 }
1096 _ => {
1098 has_unknown_discriminants = true;
1099 next_negative_discriminant = None;
1100 }
1101 },
1102 _ => {
1104 has_unknown_discriminants = true;
1105 next_negative_discriminant = None;
1106 }
1107 }
1108 }
1109
1110 Err(has_unknown_discriminants)
1111}
1112
1113fn derive_from_zeros_enum(
1117 ast: &DeriveInput,
1118 enm: &DataEnum,
1119 zerocopy_crate: &Path,
1120) -> Result<TokenStream, Error> {
1121 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1122
1123 match repr {
1126 Repr::Compound(
1127 Spanned { t: CompoundRepr::C | CompoundRepr::Primitive(_), span: _ },
1128 _,
1129 ) => {}
1130 Repr::Transparent(_)
1131 | 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")),
1132 }
1133
1134 let zero_variant = match find_zero_variant(enm) {
1135 Ok(index) => enm.variants.iter().nth(index).unwrap(),
1136 Err(true) => {
1138 return Err(Error::new_spanned(
1139 ast,
1140 "FromZeros only supported on enums with a variant that has a discriminant of `0`\n\
1141 help: This enum has discriminants which are not literal integers. One of those may \
1142 define or imply which variant has a discriminant of zero. Use a literal integer to \
1143 define or imply the variant with a discriminant of zero.",
1144 ));
1145 }
1146 Err(false) => {
1148 return Err(Error::new_spanned(
1149 ast,
1150 "FromZeros only supported on enums with a variant that has a discriminant of `0`",
1151 ));
1152 }
1153 };
1154
1155 let explicit_bounds = zero_variant
1156 .fields
1157 .iter()
1158 .map(|field| {
1159 let ty = &field.ty;
1160 parse_quote! { #ty: #zerocopy_crate::FromZeros }
1161 })
1162 .collect::<Vec<WherePredicate>>();
1163
1164 Ok(ImplBlockBuilder::new(
1165 ast,
1166 enm,
1167 Trait::FromZeros,
1168 FieldBounds::Explicit(explicit_bounds),
1169 zerocopy_crate,
1170 )
1171 .build())
1172}
1173
1174fn derive_from_zeros_union(
1177 ast: &DeriveInput,
1178 unn: &DataUnion,
1179 zerocopy_crate: &Path,
1180) -> TokenStream {
1181 let field_type_trait_bounds =
1184 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
1185 ImplBlockBuilder::new(ast, unn, Trait::FromZeros, field_type_trait_bounds, zerocopy_crate)
1186 .build()
1187}
1188
1189fn derive_from_bytes_struct(
1192 ast: &DeriveInput,
1193 strct: &DataStruct,
1194 zerocopy_crate: &Path,
1195) -> TokenStream {
1196 ImplBlockBuilder::new(ast, strct, Trait::FromBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1197 .build()
1198}
1199
1200fn derive_from_bytes_enum(
1216 ast: &DeriveInput,
1217 enm: &DataEnum,
1218 zerocopy_crate: &Path,
1219) -> Result<TokenStream, Error> {
1220 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1221
1222 let variants_required = 1usize << enum_size_from_repr(&repr)?;
1223 if enm.variants.len() != variants_required {
1224 return Err(Error::new_spanned(
1225 ast,
1226 format!(
1227 "FromBytes only supported on {} enum with {} variants",
1228 repr.repr_type_name(),
1229 variants_required
1230 ),
1231 ));
1232 }
1233
1234 Ok(ImplBlockBuilder::new(ast, enm, Trait::FromBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1235 .build())
1236}
1237
1238fn enum_size_from_repr(repr: &EnumRepr) -> Result<usize, Error> {
1240 use CompoundRepr::*;
1241 use PrimitiveRepr::*;
1242 use Repr::*;
1243 match repr {
1244 Transparent(span)
1245 | Compound(
1246 Spanned { t: C | Rust | Primitive(U32 | I32 | U64 | I64 | U128 | I128 | Usize | Isize), span },
1247 _,
1248 ) => Err(Error::new(*span, "`FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16`")),
1249 Compound(Spanned { t: Primitive(U8 | I8), span: _ }, _align) => Ok(8),
1250 Compound(Spanned { t: Primitive(U16 | I16), span: _ }, _align) => Ok(16),
1251 }
1252}
1253
1254fn derive_from_bytes_union(
1257 ast: &DeriveInput,
1258 unn: &DataUnion,
1259 zerocopy_crate: &Path,
1260) -> TokenStream {
1261 let field_type_trait_bounds =
1264 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
1265 ImplBlockBuilder::new(ast, unn, Trait::FromBytes, field_type_trait_bounds, zerocopy_crate)
1266 .build()
1267}
1268
1269fn derive_into_bytes_struct(
1270 ast: &DeriveInput,
1271 strct: &DataStruct,
1272 zerocopy_crate: &Path,
1273) -> Result<TokenStream, Error> {
1274 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1275
1276 let is_transparent = repr.is_transparent();
1277 let is_c = repr.is_c();
1278 let is_packed_1 = repr.is_packed_1();
1279 let num_fields = strct.fields().len();
1280
1281 let (padding_check, require_unaligned_fields) = if is_transparent || is_packed_1 {
1282 (None, false)
1299 } else if is_c && !repr.is_align_gt_1() && num_fields <= 1 {
1300 (None, false)
1304 } else if ast.generics.params.is_empty() {
1305 let is_syntactic_dst =
1307 strct.fields().last().map(|(_, _, ty)| matches!(ty, Type::Slice(_))).unwrap_or(false);
1308 if is_c && is_syntactic_dst {
1321 (Some(PaddingCheck::ReprCStruct), false)
1322 } else {
1323 (Some(PaddingCheck::Struct), false)
1324 }
1325 } else if is_c && !repr.is_align_gt_1() {
1326 (None, true)
1335 } else {
1336 return Err(Error::new(Span::call_site(), "must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout"));
1337 };
1338
1339 let field_bounds = if require_unaligned_fields {
1340 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Unaligned)])
1341 } else {
1342 FieldBounds::ALL_SELF
1343 };
1344
1345 Ok(ImplBlockBuilder::new(ast, strct, Trait::IntoBytes, field_bounds, zerocopy_crate)
1346 .padding_check(padding_check)
1347 .build())
1348}
1349
1350fn derive_into_bytes_enum(
1356 ast: &DeriveInput,
1357 enm: &DataEnum,
1358 zerocopy_crate: &Path,
1359) -> Result<TokenStream, Error> {
1360 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1361 if !repr.is_c() && !repr.is_primitive() {
1362 return Err(Error::new(Span::call_site(), "must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout"));
1363 }
1364
1365 let tag_type_definition = r#enum::generate_tag_enum(&repr, enm);
1366 Ok(ImplBlockBuilder::new(ast, enm, Trait::IntoBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1367 .padding_check(PaddingCheck::Enum { tag_type_definition })
1368 .build())
1369}
1370
1371fn derive_into_bytes_union(
1376 ast: &DeriveInput,
1377 unn: &DataUnion,
1378 zerocopy_crate: &Path,
1379) -> Result<TokenStream, Error> {
1380 let cfg_compile_error = if cfg!(zerocopy_derive_union_into_bytes) {
1389 quote!()
1390 } else {
1391 let error_message = "requires --cfg zerocopy_derive_union_into_bytes;
1392please let us know you use this feature: https://github.com/google/zerocopy/discussions/1802";
1393 quote!(
1394 const _: () = {
1395 #[cfg(not(zerocopy_derive_union_into_bytes))]
1396 #zerocopy_crate::util::macro_util::core_reexport::compile_error!(#error_message);
1397 };
1398 )
1399 };
1400
1401 if !ast.generics.params.is_empty() {
1403 return Err(Error::new(Span::call_site(), "unsupported on types with type parameters"));
1404 }
1405
1406 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1411 if !repr.is_c() && !repr.is_transparent() && !repr.is_packed_1() {
1412 return Err(Error::new(
1413 Span::call_site(),
1414 "must be #[repr(C)], #[repr(packed)], or #[repr(transparent)]",
1415 ));
1416 }
1417
1418 let impl_block =
1419 ImplBlockBuilder::new(ast, unn, Trait::IntoBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1420 .padding_check(PaddingCheck::Union)
1421 .build();
1422 Ok(quote!(#cfg_compile_error #impl_block))
1423}
1424
1425fn derive_unaligned_struct(
1431 ast: &DeriveInput,
1432 strct: &DataStruct,
1433 zerocopy_crate: &Path,
1434) -> Result<TokenStream, Error> {
1435 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1436 repr.unaligned_validate_no_align_gt_1()?;
1437
1438 let field_bounds = if repr.is_packed_1() {
1439 FieldBounds::None
1440 } else if repr.is_c() || repr.is_transparent() {
1441 FieldBounds::ALL_SELF
1442 } else {
1443 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"));
1444 };
1445
1446 Ok(ImplBlockBuilder::new(ast, strct, Trait::Unaligned, field_bounds, zerocopy_crate).build())
1447}
1448
1449fn derive_unaligned_enum(
1453 ast: &DeriveInput,
1454 enm: &DataEnum,
1455 zerocopy_crate: &Path,
1456) -> Result<TokenStream, Error> {
1457 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1458 repr.unaligned_validate_no_align_gt_1()?;
1459
1460 if !repr.is_u8() && !repr.is_i8() {
1461 return Err(Error::new(Span::call_site(), "must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment"));
1462 }
1463
1464 Ok(ImplBlockBuilder::new(ast, enm, Trait::Unaligned, FieldBounds::ALL_SELF, zerocopy_crate)
1465 .build())
1466}
1467
1468fn derive_unaligned_union(
1474 ast: &DeriveInput,
1475 unn: &DataUnion,
1476 zerocopy_crate: &Path,
1477) -> Result<TokenStream, Error> {
1478 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1479 repr.unaligned_validate_no_align_gt_1()?;
1480
1481 let field_type_trait_bounds = if repr.is_packed_1() {
1482 FieldBounds::None
1483 } else if repr.is_c() || repr.is_transparent() {
1484 FieldBounds::ALL_SELF
1485 } else {
1486 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"));
1487 };
1488
1489 Ok(ImplBlockBuilder::new(ast, unn, Trait::Unaligned, field_type_trait_bounds, zerocopy_crate)
1490 .build())
1491}
1492
1493enum PaddingCheck {
1496 Struct,
1499 ReprCStruct,
1501 Union,
1503 Enum { tag_type_definition: TokenStream },
1508}
1509
1510impl PaddingCheck {
1511 fn validator_trait_and_macro_idents(&self) -> (Ident, Ident) {
1514 let (trt, mcro) = match self {
1515 PaddingCheck::Struct => ("PaddingFree", "struct_padding"),
1516 PaddingCheck::ReprCStruct => ("DynamicPaddingFree", "repr_c_struct_has_padding"),
1517 PaddingCheck::Union => ("PaddingFree", "union_padding"),
1518 PaddingCheck::Enum { .. } => ("PaddingFree", "enum_padding"),
1519 };
1520
1521 let trt = Ident::new(trt, Span::call_site());
1522 let mcro = Ident::new(mcro, Span::call_site());
1523 (trt, mcro)
1524 }
1525
1526 fn validator_macro_context(&self) -> Option<&TokenStream> {
1529 match self {
1530 PaddingCheck::Struct | PaddingCheck::ReprCStruct | PaddingCheck::Union => None,
1531 PaddingCheck::Enum { tag_type_definition } => Some(tag_type_definition),
1532 }
1533 }
1534}
1535
1536#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1537enum Trait {
1538 KnownLayout,
1539 Immutable,
1540 TryFromBytes,
1541 FromZeros,
1542 FromBytes,
1543 IntoBytes,
1544 Unaligned,
1545 Sized,
1546 ByteHash,
1547 ByteEq,
1548 SplitAt,
1549}
1550
1551impl ToTokens for Trait {
1552 fn to_tokens(&self, tokens: &mut TokenStream) {
1553 let s = match self {
1563 Trait::KnownLayout => "KnownLayout",
1564 Trait::Immutable => "Immutable",
1565 Trait::TryFromBytes => "TryFromBytes",
1566 Trait::FromZeros => "FromZeros",
1567 Trait::FromBytes => "FromBytes",
1568 Trait::IntoBytes => "IntoBytes",
1569 Trait::Unaligned => "Unaligned",
1570 Trait::Sized => "Sized",
1571 Trait::ByteHash => "ByteHash",
1572 Trait::ByteEq => "ByteEq",
1573 Trait::SplitAt => "SplitAt",
1574 };
1575 let ident = Ident::new(s, Span::call_site());
1576 tokens.extend(core::iter::once(TokenTree::Ident(ident)));
1577 }
1578}
1579
1580impl Trait {
1581 fn crate_path(&self, zerocopy_crate: &Path) -> Path {
1582 match self {
1583 Self::Sized => {
1584 parse_quote!(#zerocopy_crate::util::macro_util::core_reexport::marker::#self)
1585 }
1586 _ => parse_quote!(#zerocopy_crate::#self),
1587 }
1588 }
1589}
1590
1591#[derive(Debug, Eq, PartialEq)]
1592enum TraitBound {
1593 Slf,
1594 Other(Trait),
1595}
1596
1597enum FieldBounds<'a> {
1598 None,
1599 All(&'a [TraitBound]),
1600 Trailing(&'a [TraitBound]),
1601 Explicit(Vec<WherePredicate>),
1602}
1603
1604impl<'a> FieldBounds<'a> {
1605 const ALL_SELF: FieldBounds<'a> = FieldBounds::All(&[TraitBound::Slf]);
1606 const TRAILING_SELF: FieldBounds<'a> = FieldBounds::Trailing(&[TraitBound::Slf]);
1607}
1608
1609#[derive(Debug, Eq, PartialEq)]
1610enum SelfBounds<'a> {
1611 None,
1612 All(&'a [Trait]),
1613}
1614
1615#[allow(clippy::needless_lifetimes)]
1618impl<'a> SelfBounds<'a> {
1619 const SIZED: Self = Self::All(&[Trait::Sized]);
1620}
1621
1622fn normalize_bounds(slf: Trait, bounds: &[TraitBound]) -> impl '_ + Iterator<Item = Trait> {
1624 bounds.iter().map(move |bound| match bound {
1625 TraitBound::Slf => slf,
1626 TraitBound::Other(trt) => *trt,
1627 })
1628}
1629
1630struct ImplBlockBuilder<'a, D: DataExt> {
1631 input: &'a DeriveInput,
1632 data: &'a D,
1633 trt: Trait,
1634 field_type_trait_bounds: FieldBounds<'a>,
1635 zerocopy_crate: &'a Path,
1636 self_type_trait_bounds: SelfBounds<'a>,
1637 padding_check: Option<PaddingCheck>,
1638 inner_extras: Option<TokenStream>,
1639 outer_extras: Option<TokenStream>,
1640}
1641
1642impl<'a, D: DataExt> ImplBlockBuilder<'a, D> {
1643 fn new(
1644 input: &'a DeriveInput,
1645 data: &'a D,
1646 trt: Trait,
1647 field_type_trait_bounds: FieldBounds<'a>,
1648 zerocopy_crate: &'a Path,
1649 ) -> Self {
1650 Self {
1651 input,
1652 data,
1653 trt,
1654 field_type_trait_bounds,
1655 zerocopy_crate,
1656 self_type_trait_bounds: SelfBounds::None,
1657 padding_check: None,
1658 inner_extras: None,
1659 outer_extras: None,
1660 }
1661 }
1662
1663 fn self_type_trait_bounds(mut self, self_type_trait_bounds: SelfBounds<'a>) -> Self {
1664 self.self_type_trait_bounds = self_type_trait_bounds;
1665 self
1666 }
1667
1668 fn padding_check<P: Into<Option<PaddingCheck>>>(mut self, padding_check: P) -> Self {
1669 self.padding_check = padding_check.into();
1670 self
1671 }
1672
1673 fn inner_extras(mut self, inner_extras: TokenStream) -> Self {
1674 self.inner_extras = Some(inner_extras);
1675 self
1676 }
1677
1678 fn outer_extras<T: Into<Option<TokenStream>>>(mut self, outer_extras: T) -> Self {
1679 self.outer_extras = outer_extras.into();
1680 self
1681 }
1682
1683 fn build(self) -> TokenStream {
1684 let type_ident = &self.input.ident;
1744 let trait_path = self.trt.crate_path(self.zerocopy_crate);
1745 let fields = self.data.fields();
1746 let variants = self.data.variants();
1747 let tag = self.data.tag();
1748 let zerocopy_crate = self.zerocopy_crate;
1749
1750 fn bound_tt(
1751 ty: &Type,
1752 traits: impl Iterator<Item = Trait>,
1753 zerocopy_crate: &Path,
1754 ) -> WherePredicate {
1755 let traits = traits.map(|t| t.crate_path(zerocopy_crate));
1756 parse_quote!(#ty: #(#traits)+*)
1757 }
1758 let field_type_bounds: Vec<_> = match (self.field_type_trait_bounds, &fields[..]) {
1759 (FieldBounds::All(traits), _) => fields
1760 .iter()
1761 .map(|(_vis, _name, ty)| {
1762 bound_tt(ty, normalize_bounds(self.trt, traits), zerocopy_crate)
1763 })
1764 .collect(),
1765 (FieldBounds::None, _) | (FieldBounds::Trailing(..), []) => vec![],
1766 (FieldBounds::Trailing(traits), [.., last]) => {
1767 vec![bound_tt(last.2, normalize_bounds(self.trt, traits), zerocopy_crate)]
1768 }
1769 (FieldBounds::Explicit(bounds), _) => bounds,
1770 };
1771
1772 #[allow(unstable_name_collisions)] let padding_check_bound = self
1775 .padding_check
1776 .and_then(|check| (!fields.is_empty()).then_some(check))
1777 .map(|check| {
1778 let variant_types = variants.iter().map(|var| {
1779 let types = var.iter().map(|(_vis, _name, ty)| ty);
1780 quote!([#((#types)),*])
1781 });
1782 let validator_context = check.validator_macro_context();
1783 let (trt, validator_macro) = check.validator_trait_and_macro_idents();
1784 let t = tag.iter();
1785 parse_quote! {
1786 (): #zerocopy_crate::util::macro_util::#trt<
1787 Self,
1788 {
1789 #validator_context
1790 #zerocopy_crate::#validator_macro!(Self, #(#t,)* #(#variant_types),*)
1791 }
1792 >
1793 }
1794 });
1795
1796 let self_bounds: Option<WherePredicate> = match self.self_type_trait_bounds {
1797 SelfBounds::None => None,
1798 SelfBounds::All(traits) => {
1799 Some(bound_tt(&parse_quote!(Self), traits.iter().copied(), zerocopy_crate))
1800 }
1801 };
1802
1803 let bounds = self
1804 .input
1805 .generics
1806 .where_clause
1807 .as_ref()
1808 .map(|where_clause| where_clause.predicates.iter())
1809 .into_iter()
1810 .flatten()
1811 .chain(field_type_bounds.iter())
1812 .chain(padding_check_bound.iter())
1813 .chain(self_bounds.iter());
1814
1815 let params = self.input.generics.params.clone().into_iter().map(|mut param| {
1817 match &mut param {
1818 GenericParam::Type(ty) => ty.default = None,
1819 GenericParam::Const(cnst) => cnst.default = None,
1820 GenericParam::Lifetime(_) => {}
1821 }
1822 quote!(#param)
1823 });
1824
1825 let param_idents = self.input.generics.params.iter().map(|param| match param {
1828 GenericParam::Type(ty) => {
1829 let ident = &ty.ident;
1830 quote!(#ident)
1831 }
1832 GenericParam::Lifetime(l) => {
1833 let ident = &l.lifetime;
1834 quote!(#ident)
1835 }
1836 GenericParam::Const(cnst) => {
1837 let ident = &cnst.ident;
1838 quote!({#ident})
1839 }
1840 });
1841
1842 let inner_extras = self.inner_extras;
1843 let impl_tokens = quote! {
1844 #[allow(deprecated)]
1845 #[automatically_derived]
1848 unsafe impl < #(#params),* > #trait_path for #type_ident < #(#param_idents),* >
1849 where
1850 #(#bounds,)*
1851 {
1852 fn only_derive_is_allowed_to_implement_this_trait() {}
1853
1854 #inner_extras
1855 }
1856 };
1857
1858 if let Some(outer_extras) = self.outer_extras {
1859 quote! {
1862 const _: () = {
1863 #impl_tokens
1864
1865 #outer_extras
1866 };
1867 }
1868 } else {
1869 impl_tokens
1870 }
1871 }
1872}
1873
1874#[allow(unused)]
1882trait BoolExt {
1883 fn then_some<T>(self, t: T) -> Option<T>;
1884}
1885
1886impl BoolExt for bool {
1887 fn then_some<T>(self, t: T) -> Option<T> {
1888 if self {
1889 Some(t)
1890 } else {
1891 None
1892 }
1893 }
1894}