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