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::{Span, TokenStream, TokenTree};
40use quote::{quote, ToTokens};
41use syn::{
42 parse_quote, Attribute, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Error, Expr,
43 ExprLit, ExprUnary, GenericParam, Ident, Lit, Meta, Path, Type, UnOp, WherePredicate,
44};
45
46use crate::{ext::*, repr::*};
47
48macro_rules! derive {
72 ($trait:ident => $outer:ident => $inner:ident) => {
73 #[proc_macro_derive($trait, attributes(zerocopy))]
74 pub fn $outer(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
75 let ast = syn::parse_macro_input!(ts as DeriveInput);
76 let zerocopy_crate = match extract_zerocopy_crate(&ast.attrs) {
77 Ok(zerocopy_crate) => zerocopy_crate,
78 Err(e) => return e.into_compile_error().into(),
79 };
80 $inner(&ast, Trait::$trait, &zerocopy_crate).into_ts().into()
81 }
82 };
83}
84
85trait IntoTokenStream {
86 fn into_ts(self) -> TokenStream;
87}
88
89impl IntoTokenStream for TokenStream {
90 fn into_ts(self) -> TokenStream {
91 self
92 }
93}
94
95impl IntoTokenStream for Result<TokenStream, Error> {
96 fn into_ts(self) -> TokenStream {
97 match self {
98 Ok(ts) => ts,
99 Err(err) => err.to_compile_error(),
100 }
101 }
102}
103
104fn extract_zerocopy_crate(attrs: &[Attribute]) -> Result<Path, Error> {
107 let mut path = parse_quote!(::zerocopy);
108
109 for attr in attrs {
110 if let Meta::List(ref meta_list) = attr.meta {
111 if meta_list.path.is_ident("zerocopy") {
112 attr.parse_nested_meta(|meta| {
113 if meta.path.is_ident("crate") {
114 let expr = meta.value().and_then(|value| value.parse());
115 if let Ok(Expr::Lit(ExprLit { lit: Lit::Str(lit), .. })) = expr {
116 if let Ok(path_lit) = lit.parse() {
117 path = path_lit;
118 return Ok(());
119 }
120 }
121
122 return Err(Error::new(
123 Span::call_site(),
124 "`crate` attribute requires a path as the value",
125 ));
126 }
127
128 Err(Error::new(
129 Span::call_site(),
130 format!("unknown attribute encountered: {}", meta.path.into_token_stream()),
131 ))
132 })?;
133 }
134 }
135 }
136
137 Ok(path)
138}
139
140derive!(KnownLayout => derive_known_layout => derive_known_layout_inner);
141derive!(Immutable => derive_no_cell => derive_no_cell_inner);
142derive!(TryFromBytes => derive_try_from_bytes => derive_try_from_bytes_inner);
143derive!(FromZeros => derive_from_zeros => derive_from_zeros_inner);
144derive!(FromBytes => derive_from_bytes => derive_from_bytes_inner);
145derive!(IntoBytes => derive_into_bytes => derive_into_bytes_inner);
146derive!(Unaligned => derive_unaligned => derive_unaligned_inner);
147derive!(ByteHash => derive_hash => derive_hash_inner);
148derive!(ByteEq => derive_eq => derive_eq_inner);
149derive!(SplitAt => derive_split_at => derive_split_at_inner);
150
151#[deprecated(since = "0.8.0", note = "`FromZeroes` was renamed to `FromZeros`")]
153#[doc(hidden)]
154#[proc_macro_derive(FromZeroes)]
155pub fn derive_from_zeroes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
156 derive_from_zeros(ts)
157}
158
159#[deprecated(since = "0.8.0", note = "`AsBytes` was renamed to `IntoBytes`")]
161#[doc(hidden)]
162#[proc_macro_derive(AsBytes)]
163pub fn derive_as_bytes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
164 derive_into_bytes(ts)
165}
166
167fn derive_known_layout_inner(
168 ast: &DeriveInput,
169 _top_level: Trait,
170 zerocopy_crate: &Path,
171) -> Result<TokenStream, Error> {
172 let is_repr_c_struct = match &ast.data {
173 Data::Struct(..) => {
174 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
175 if repr.is_c() {
176 Some(repr)
177 } else {
178 None
179 }
180 }
181 Data::Enum(..) | Data::Union(..) => None,
182 };
183
184 let fields = ast.data.fields();
185
186 let (self_bounds, inner_extras, outer_extras) = if let (
187 Some(repr),
188 Some((trailing_field, leading_fields)),
189 ) = (is_repr_c_struct, fields.split_last())
190 {
191 let (_vis, trailing_field_name, trailing_field_ty) = trailing_field;
192 let leading_fields_tys = leading_fields.iter().map(|(_vis, _name, ty)| ty);
193
194 let core_path = quote!(#zerocopy_crate::util::macro_util::core_reexport);
195 let repr_align = repr
196 .get_align()
197 .map(|align| {
198 let align = align.t.get();
199 quote!(#core_path::num::NonZeroUsize::new(#align as usize))
200 })
201 .unwrap_or_else(|| quote!(#core_path::option::Option::None));
202 let repr_packed = repr
203 .get_packed()
204 .map(|packed| {
205 let packed = packed.get();
206 quote!(#core_path::num::NonZeroUsize::new(#packed as usize))
207 })
208 .unwrap_or_else(|| quote!(#core_path::option::Option::None));
209
210 let make_methods = |trailing_field_ty| {
211 quote! {
212 #[inline(always)]
242 fn raw_from_ptr_len(
243 bytes: #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<u8>,
244 meta: Self::PointerMetadata,
245 ) -> #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<Self> {
246 use #zerocopy_crate::KnownLayout;
247 let trailing = <#trailing_field_ty as KnownLayout>::raw_from_ptr_len(bytes, meta);
248 let slf = trailing.as_ptr() as *mut Self;
249 unsafe { #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull::new_unchecked(slf) }
251 }
252
253 #[inline(always)]
254 fn pointer_to_metadata(ptr: *mut Self) -> Self::PointerMetadata {
255 <#trailing_field_ty>::pointer_to_metadata(ptr as *mut _)
256 }
257 }
258 };
259
260 let inner_extras = {
261 let leading_fields_tys = leading_fields_tys.clone();
262 let methods = make_methods(*trailing_field_ty);
263 let (_, ty_generics, _) = ast.generics.split_for_impl();
264
265 quote!(
266 type PointerMetadata = <#trailing_field_ty as #zerocopy_crate::KnownLayout>::PointerMetadata;
267
268 type MaybeUninit = __ZerocopyKnownLayoutMaybeUninit #ty_generics;
269
270 const LAYOUT: #zerocopy_crate::DstLayout = {
285 use #zerocopy_crate::util::macro_util::core_reexport::num::NonZeroUsize;
286 use #zerocopy_crate::{DstLayout, KnownLayout};
287
288 DstLayout::for_repr_c_struct(
289 #repr_align,
290 #repr_packed,
291 &[
292 #(DstLayout::for_type::<#leading_fields_tys>(),)*
293 <#trailing_field_ty as KnownLayout>::LAYOUT
294 ],
295 )
296 };
297
298 #methods
299 )
300 };
301
302 let outer_extras = {
303 let ident = &ast.ident;
304 let vis = &ast.vis;
305 let params = &ast.generics.params;
306 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
307
308 let predicates = if let Some(where_clause) = where_clause {
309 where_clause.predicates.clone()
310 } else {
311 Default::default()
312 };
313
314 let field_index =
317 |name| Ident::new(&format!("__Zerocopy_Field_{}", name), ident.span());
318
319 let field_indices: Vec<_> =
320 fields.iter().map(|(_vis, name, _ty)| field_index(name)).collect();
321
322 let field_defs = field_indices.iter().zip(&fields).map(|(idx, (vis, _, _))| {
324 quote! {
325 #[allow(non_camel_case_types)]
326 #vis struct #idx;
327 }
328 });
329
330 let field_impls = field_indices.iter().zip(&fields).map(|(idx, (_, _, ty))| quote! {
331 unsafe impl #impl_generics #zerocopy_crate::util::macro_util::Field<#idx> for #ident #ty_generics
333 where
334 #predicates
335 {
336 type Type = #ty;
337 }
338 });
339
340 let trailing_field_index = field_index(trailing_field_name);
341 let leading_field_indices =
342 leading_fields.iter().map(|(_vis, name, _ty)| field_index(name));
343
344 let trailing_field_ty = quote! {
345 <#ident #ty_generics as
346 #zerocopy_crate::util::macro_util::Field<#trailing_field_index>
347 >::Type
348 };
349
350 let methods = make_methods(&parse_quote! {
351 <#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit
352 });
353
354 quote! {
355 #(#field_defs)*
356
357 #(#field_impls)*
358
359 #repr
367 #[doc(hidden)]
368 #[allow(private_bounds)]
372 #vis struct __ZerocopyKnownLayoutMaybeUninit<#params> (
373 #(#zerocopy_crate::util::macro_util::core_reexport::mem::MaybeUninit<
374 <#ident #ty_generics as
375 #zerocopy_crate::util::macro_util::Field<#leading_field_indices>
376 >::Type
377 >,)*
378 #zerocopy_crate::util::macro_util::core_reexport::mem::ManuallyDrop<
385 <#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit
386 >
387 )
388 where
389 #trailing_field_ty: #zerocopy_crate::KnownLayout,
390 #predicates;
391
392 unsafe impl #impl_generics #zerocopy_crate::KnownLayout for __ZerocopyKnownLayoutMaybeUninit #ty_generics
399 where
400 #trailing_field_ty: #zerocopy_crate::KnownLayout,
401 #predicates
402 {
403 #[allow(clippy::missing_inline_in_public_items)]
404 fn only_derive_is_allowed_to_implement_this_trait() {}
405
406 type PointerMetadata = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::PointerMetadata;
407
408 type MaybeUninit = Self;
409
410 const LAYOUT: #zerocopy_crate::DstLayout = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::LAYOUT;
411
412 #methods
413 }
414 }
415 };
416
417 (SelfBounds::None, inner_extras, Some(outer_extras))
418 } else {
419 (
423 SelfBounds::SIZED,
424 quote!(
425 type PointerMetadata = ();
426 type MaybeUninit =
427 #zerocopy_crate::util::macro_util::core_reexport::mem::MaybeUninit<Self>;
428
429 const LAYOUT: #zerocopy_crate::DstLayout = #zerocopy_crate::DstLayout::for_type::<Self>();
433
434 #[inline(always)]
439 fn raw_from_ptr_len(
440 bytes: #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<u8>,
441 _meta: (),
442 ) -> #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<Self>
443 {
444 bytes.cast::<Self>()
445 }
446
447 #[inline(always)]
448 fn pointer_to_metadata(_ptr: *mut Self) -> () {}
449 ),
450 None,
451 )
452 };
453
454 Ok(match &ast.data {
455 Data::Struct(strct) => {
456 let require_trait_bound_on_field_types = if self_bounds == SelfBounds::SIZED {
457 FieldBounds::None
458 } else {
459 FieldBounds::TRAILING_SELF
460 };
461
462 ImplBlockBuilder::new(
467 ast,
468 strct,
469 Trait::KnownLayout,
470 require_trait_bound_on_field_types,
471 zerocopy_crate,
472 )
473 .self_type_trait_bounds(self_bounds)
474 .inner_extras(inner_extras)
475 .outer_extras(outer_extras)
476 .build()
477 }
478 Data::Enum(enm) => {
479 ImplBlockBuilder::new(ast, enm, Trait::KnownLayout, FieldBounds::None, zerocopy_crate)
482 .self_type_trait_bounds(SelfBounds::SIZED)
483 .inner_extras(inner_extras)
484 .outer_extras(outer_extras)
485 .build()
486 }
487 Data::Union(unn) => {
488 ImplBlockBuilder::new(ast, unn, Trait::KnownLayout, FieldBounds::None, zerocopy_crate)
491 .self_type_trait_bounds(SelfBounds::SIZED)
492 .inner_extras(inner_extras)
493 .outer_extras(outer_extras)
494 .build()
495 }
496 })
497}
498
499fn derive_no_cell_inner(
500 ast: &DeriveInput,
501 _top_level: Trait,
502 zerocopy_crate: &Path,
503) -> TokenStream {
504 match &ast.data {
505 Data::Struct(strct) => ImplBlockBuilder::new(
506 ast,
507 strct,
508 Trait::Immutable,
509 FieldBounds::ALL_SELF,
510 zerocopy_crate,
511 )
512 .build(),
513 Data::Enum(enm) => {
514 ImplBlockBuilder::new(ast, enm, Trait::Immutable, FieldBounds::ALL_SELF, zerocopy_crate)
515 .build()
516 }
517 Data::Union(unn) => {
518 ImplBlockBuilder::new(ast, unn, Trait::Immutable, FieldBounds::ALL_SELF, zerocopy_crate)
519 .build()
520 }
521 }
522}
523
524fn derive_try_from_bytes_inner(
525 ast: &DeriveInput,
526 top_level: Trait,
527 zerocopy_crate: &Path,
528) -> Result<TokenStream, Error> {
529 match &ast.data {
530 Data::Struct(strct) => derive_try_from_bytes_struct(ast, strct, top_level, zerocopy_crate),
531 Data::Enum(enm) => derive_try_from_bytes_enum(ast, enm, top_level, zerocopy_crate),
532 Data::Union(unn) => Ok(derive_try_from_bytes_union(ast, unn, top_level, zerocopy_crate)),
533 }
534}
535
536fn derive_from_zeros_inner(
537 ast: &DeriveInput,
538 top_level: Trait,
539 zerocopy_crate: &Path,
540) -> Result<TokenStream, Error> {
541 let try_from_bytes = derive_try_from_bytes_inner(ast, top_level, zerocopy_crate)?;
542 let from_zeros = match &ast.data {
543 Data::Struct(strct) => derive_from_zeros_struct(ast, strct, zerocopy_crate),
544 Data::Enum(enm) => derive_from_zeros_enum(ast, enm, zerocopy_crate)?,
545 Data::Union(unn) => derive_from_zeros_union(ast, unn, zerocopy_crate),
546 };
547 Ok(IntoIterator::into_iter([try_from_bytes, from_zeros]).collect())
548}
549
550fn derive_from_bytes_inner(
551 ast: &DeriveInput,
552 top_level: Trait,
553 zerocopy_crate: &Path,
554) -> Result<TokenStream, Error> {
555 let from_zeros = derive_from_zeros_inner(ast, top_level, zerocopy_crate)?;
556 let from_bytes = match &ast.data {
557 Data::Struct(strct) => derive_from_bytes_struct(ast, strct, zerocopy_crate),
558 Data::Enum(enm) => derive_from_bytes_enum(ast, enm, zerocopy_crate)?,
559 Data::Union(unn) => derive_from_bytes_union(ast, unn, zerocopy_crate),
560 };
561
562 Ok(IntoIterator::into_iter([from_zeros, from_bytes]).collect())
563}
564
565fn derive_into_bytes_inner(
566 ast: &DeriveInput,
567 _top_level: Trait,
568 zerocopy_crate: &Path,
569) -> Result<TokenStream, Error> {
570 match &ast.data {
571 Data::Struct(strct) => derive_into_bytes_struct(ast, strct, zerocopy_crate),
572 Data::Enum(enm) => derive_into_bytes_enum(ast, enm, zerocopy_crate),
573 Data::Union(unn) => derive_into_bytes_union(ast, unn, zerocopy_crate),
574 }
575}
576
577fn derive_unaligned_inner(
578 ast: &DeriveInput,
579 _top_level: Trait,
580 zerocopy_crate: &Path,
581) -> Result<TokenStream, Error> {
582 match &ast.data {
583 Data::Struct(strct) => derive_unaligned_struct(ast, strct, zerocopy_crate),
584 Data::Enum(enm) => derive_unaligned_enum(ast, enm, zerocopy_crate),
585 Data::Union(unn) => derive_unaligned_union(ast, unn, zerocopy_crate),
586 }
587}
588
589fn derive_hash_inner(
590 ast: &DeriveInput,
591 _top_level: Trait,
592 zerocopy_crate: &Path,
593) -> Result<TokenStream, Error> {
594 let type_ident = &ast.ident;
600 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
601 let where_predicates = where_clause.map(|clause| &clause.predicates);
602 Ok(quote! {
603 #[allow(deprecated)]
606 #[automatically_derived]
609 impl #impl_generics #zerocopy_crate::util::macro_util::core_reexport::hash::Hash for #type_ident #ty_generics
610 where
611 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
612 #where_predicates
613 {
614 fn hash<H>(&self, state: &mut H)
615 where
616 H: #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher,
617 {
618 #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher::write(
619 state,
620 #zerocopy_crate::IntoBytes::as_bytes(self)
621 )
622 }
623
624 fn hash_slice<H>(data: &[Self], state: &mut H)
625 where
626 H: #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher,
627 {
628 #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher::write(
629 state,
630 #zerocopy_crate::IntoBytes::as_bytes(data)
631 )
632 }
633 }
634 })
635}
636
637fn derive_eq_inner(
638 ast: &DeriveInput,
639 _top_level: Trait,
640 zerocopy_crate: &Path,
641) -> Result<TokenStream, Error> {
642 let type_ident = &ast.ident;
648 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
649 let where_predicates = where_clause.map(|clause| &clause.predicates);
650 Ok(quote! {
651 #[allow(deprecated)]
654 #[automatically_derived]
657 impl #impl_generics #zerocopy_crate::util::macro_util::core_reexport::cmp::PartialEq for #type_ident #ty_generics
658 where
659 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
660 #where_predicates
661 {
662 fn eq(&self, other: &Self) -> bool {
663 #zerocopy_crate::util::macro_util::core_reexport::cmp::PartialEq::eq(
664 #zerocopy_crate::IntoBytes::as_bytes(self),
665 #zerocopy_crate::IntoBytes::as_bytes(other),
666 )
667 }
668 }
669
670 #[allow(deprecated)]
673 #[automatically_derived]
676 impl #impl_generics #zerocopy_crate::util::macro_util::core_reexport::cmp::Eq for #type_ident #ty_generics
677 where
678 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
679 #where_predicates
680 {
681 }
682 })
683}
684
685fn derive_split_at_inner(
686 ast: &DeriveInput,
687 _top_level: Trait,
688 zerocopy_crate: &Path,
689) -> Result<TokenStream, Error> {
690 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
691
692 match &ast.data {
693 Data::Struct(_) => {}
694 Data::Enum(_) | Data::Union(_) => {
695 return Err(Error::new(Span::call_site(), "can only be applied to structs"));
696 }
697 };
698
699 if repr.get_packed().is_some() {
700 return Err(Error::new(Span::call_site(), "must not have #[repr(packed)] attribute"));
701 }
702
703 if !(repr.is_c() || repr.is_transparent()) {
704 return Err(Error::new(Span::call_site(), "must have #[repr(C)] or #[repr(transparent)] in order to guarantee this type's layout is splitable"));
705 }
706
707 let fields = ast.data.fields();
708 let trailing_field = if let Some(((_, _, trailing_field), _)) = fields.split_last() {
709 trailing_field
710 } else {
711 return Err(Error::new(Span::call_site(), "must at least one field"));
712 };
713
714 Ok(ImplBlockBuilder::new(
719 ast,
720 &ast.data,
721 Trait::SplitAt,
722 FieldBounds::TRAILING_SELF,
723 zerocopy_crate,
724 )
725 .inner_extras(quote! {
726 type Elem = <#trailing_field as ::zerocopy::SplitAt>::Elem;
727 })
728 .build())
729}
730
731fn derive_try_from_bytes_struct(
734 ast: &DeriveInput,
735 strct: &DataStruct,
736 top_level: Trait,
737 zerocopy_crate: &Path,
738) -> Result<TokenStream, Error> {
739 let extras =
740 try_gen_trivial_is_bit_valid(ast, top_level, zerocopy_crate).unwrap_or_else(|| {
741 let fields = strct.fields();
742 let field_names = fields.iter().map(|(_vis, name, _ty)| name);
743 let field_tys = fields.iter().map(|(_vis, _name, ty)| ty);
744 quote!(
745 fn is_bit_valid<___ZerocopyAliasing>(
751 mut candidate: #zerocopy_crate::Maybe<Self, ___ZerocopyAliasing>,
752 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
753 where
754 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
755 {
756 use #zerocopy_crate::util::macro_util::core_reexport;
757 use #zerocopy_crate::pointer::PtrInner;
758
759 true #(&& {
760 let field_candidate = unsafe {
768 let project = |slf: PtrInner<'_, Self>| {
769 let slf = slf.as_non_null().as_ptr();
770 let field = core_reexport::ptr::addr_of_mut!((*slf).#field_names);
771 let ptr = unsafe { core_reexport::ptr::NonNull::new_unchecked(field) };
779 unsafe { PtrInner::new(ptr) }
790 };
791
792 candidate.reborrow().cast_unsized_unchecked(project)
793 };
794
795 <#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate)
796 })*
797 }
798 )
799 });
800 Ok(ImplBlockBuilder::new(
801 ast,
802 strct,
803 Trait::TryFromBytes,
804 FieldBounds::ALL_SELF,
805 zerocopy_crate,
806 )
807 .inner_extras(extras)
808 .build())
809}
810
811fn derive_try_from_bytes_union(
814 ast: &DeriveInput,
815 unn: &DataUnion,
816 top_level: Trait,
817 zerocopy_crate: &Path,
818) -> TokenStream {
819 let field_type_trait_bounds =
821 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
822 let extras =
823 try_gen_trivial_is_bit_valid(ast, top_level, zerocopy_crate).unwrap_or_else(|| {
824 let fields = unn.fields();
825 let field_names = fields.iter().map(|(_vis, name, _ty)| name);
826 let field_tys = fields.iter().map(|(_vis, _name, ty)| ty);
827 quote!(
828 fn is_bit_valid<___ZerocopyAliasing>(
834 mut candidate: #zerocopy_crate::Maybe<'_, Self,___ZerocopyAliasing>
835 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
836 where
837 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
838 {
839 use #zerocopy_crate::util::macro_util::core_reexport;
840 use #zerocopy_crate::pointer::PtrInner;
841
842 false #(|| {
843 let field_candidate = unsafe {
851 let project = |slf: PtrInner<'_, Self>| {
852 let slf = slf.as_non_null().as_ptr();
853 let field = core_reexport::ptr::addr_of_mut!((*slf).#field_names);
854 let ptr = unsafe { core_reexport::ptr::NonNull::new_unchecked(field) };
862 unsafe { PtrInner::new(ptr) }
873 };
874
875 candidate.reborrow().cast_unsized_unchecked(project)
876 };
877
878 <#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate)
879 })*
880 }
881 )
882 });
883 ImplBlockBuilder::new(ast, unn, Trait::TryFromBytes, field_type_trait_bounds, zerocopy_crate)
884 .inner_extras(extras)
885 .build()
886}
887
888fn derive_try_from_bytes_enum(
889 ast: &DeriveInput,
890 enm: &DataEnum,
891 top_level: Trait,
892 zerocopy_crate: &Path,
893) -> Result<TokenStream, Error> {
894 let repr = EnumRepr::from_attrs(&ast.attrs)?;
895
896 let could_be_from_bytes = enum_size_from_repr(&repr)
902 .map(|size| enm.fields().is_empty() && enm.variants.len() == 1usize << size)
903 .unwrap_or(false);
904
905 let trivial_is_bit_valid = try_gen_trivial_is_bit_valid(ast, top_level, zerocopy_crate);
906 let extra = match (trivial_is_bit_valid, could_be_from_bytes) {
907 (Some(is_bit_valid), _) => is_bit_valid,
908 (None, true) => unsafe { gen_trivial_is_bit_valid_unchecked(zerocopy_crate) },
911 (None, false) => {
912 r#enum::derive_is_bit_valid(&ast.ident, &repr, &ast.generics, enm, zerocopy_crate)?
913 }
914 };
915
916 Ok(ImplBlockBuilder::new(ast, enm, Trait::TryFromBytes, FieldBounds::ALL_SELF, zerocopy_crate)
917 .inner_extras(extra)
918 .build())
919}
920
921fn try_gen_trivial_is_bit_valid(
943 ast: &DeriveInput,
944 top_level: Trait,
945 zerocopy_crate: &Path,
946) -> Option<proc_macro2::TokenStream> {
947 if top_level == Trait::FromBytes && ast.generics.params.is_empty() {
956 Some(quote!(
957 fn is_bit_valid<___ZerocopyAliasing>(
959 _candidate: #zerocopy_crate::Maybe<Self, ___ZerocopyAliasing>,
960 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
961 where
962 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
963 {
964 if false {
965 fn assert_is_from_bytes<T>()
966 where
967 T: #zerocopy_crate::FromBytes,
968 T: ?#zerocopy_crate::util::macro_util::core_reexport::marker::Sized,
969 {
970 }
971
972 assert_is_from_bytes::<Self>();
973 }
974
975 true
979 }
980 ))
981 } else {
982 None
983 }
984}
985
986unsafe fn gen_trivial_is_bit_valid_unchecked(zerocopy_crate: &Path) -> proc_macro2::TokenStream {
998 quote!(
999 fn is_bit_valid<___ZerocopyAliasing>(
1002 _candidate: #zerocopy_crate::Maybe<Self, ___ZerocopyAliasing>,
1003 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
1004 where
1005 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
1006 {
1007 true
1008 }
1009 )
1010}
1011
1012fn derive_from_zeros_struct(
1015 ast: &DeriveInput,
1016 strct: &DataStruct,
1017 zerocopy_crate: &Path,
1018) -> TokenStream {
1019 ImplBlockBuilder::new(ast, strct, Trait::FromZeros, FieldBounds::ALL_SELF, zerocopy_crate)
1020 .build()
1021}
1022
1023fn find_zero_variant(enm: &DataEnum) -> Result<usize, bool> {
1029 let mut next_negative_discriminant = Some(0);
1037
1038 let mut has_unknown_discriminants = false;
1045
1046 for (i, v) in enm.variants.iter().enumerate() {
1047 match v.discriminant.as_ref() {
1048 None => {
1050 match next_negative_discriminant.as_mut() {
1051 Some(0) => return Ok(i),
1052 Some(n) => *n -= 1,
1054 None => (),
1055 }
1056 }
1057 Some((_, Expr::Lit(ExprLit { lit: Lit::Int(int), .. }))) => {
1059 match int.base10_parse::<u128>().ok() {
1060 Some(0) => return Ok(i),
1061 Some(_) => next_negative_discriminant = None,
1062 None => {
1063 has_unknown_discriminants = true;
1065 next_negative_discriminant = None;
1066 }
1067 }
1068 }
1069 Some((_, Expr::Unary(ExprUnary { op: UnOp::Neg(_), expr, .. }))) => match &**expr {
1071 Expr::Lit(ExprLit { lit: Lit::Int(int), .. }) => {
1072 match int.base10_parse::<u128>().ok() {
1073 Some(0) => return Ok(i),
1074 Some(x) => next_negative_discriminant = Some(x - 1),
1076 None => {
1077 has_unknown_discriminants = true;
1080 next_negative_discriminant = None;
1081 }
1082 }
1083 }
1084 _ => {
1086 has_unknown_discriminants = true;
1087 next_negative_discriminant = None;
1088 }
1089 },
1090 _ => {
1092 has_unknown_discriminants = true;
1093 next_negative_discriminant = None;
1094 }
1095 }
1096 }
1097
1098 Err(has_unknown_discriminants)
1099}
1100
1101fn derive_from_zeros_enum(
1105 ast: &DeriveInput,
1106 enm: &DataEnum,
1107 zerocopy_crate: &Path,
1108) -> Result<TokenStream, Error> {
1109 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1110
1111 match repr {
1114 Repr::Compound(
1115 Spanned { t: CompoundRepr::C | CompoundRepr::Primitive(_), span: _ },
1116 _,
1117 ) => {}
1118 Repr::Transparent(_)
1119 | 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")),
1120 }
1121
1122 let zero_variant = match find_zero_variant(enm) {
1123 Ok(index) => enm.variants.iter().nth(index).unwrap(),
1124 Err(true) => {
1126 return Err(Error::new_spanned(
1127 ast,
1128 "FromZeros only supported on enums with a variant that has a discriminant of `0`\n\
1129 help: This enum has discriminants which are not literal integers. One of those may \
1130 define or imply which variant has a discriminant of zero. Use a literal integer to \
1131 define or imply the variant with a discriminant of zero.",
1132 ));
1133 }
1134 Err(false) => {
1136 return Err(Error::new_spanned(
1137 ast,
1138 "FromZeros only supported on enums with a variant that has a discriminant of `0`",
1139 ));
1140 }
1141 };
1142
1143 let explicit_bounds = zero_variant
1144 .fields
1145 .iter()
1146 .map(|field| {
1147 let ty = &field.ty;
1148 parse_quote! { #ty: #zerocopy_crate::FromZeros }
1149 })
1150 .collect::<Vec<WherePredicate>>();
1151
1152 Ok(ImplBlockBuilder::new(
1153 ast,
1154 enm,
1155 Trait::FromZeros,
1156 FieldBounds::Explicit(explicit_bounds),
1157 zerocopy_crate,
1158 )
1159 .build())
1160}
1161
1162fn derive_from_zeros_union(
1165 ast: &DeriveInput,
1166 unn: &DataUnion,
1167 zerocopy_crate: &Path,
1168) -> TokenStream {
1169 let field_type_trait_bounds =
1172 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
1173 ImplBlockBuilder::new(ast, unn, Trait::FromZeros, field_type_trait_bounds, zerocopy_crate)
1174 .build()
1175}
1176
1177fn derive_from_bytes_struct(
1180 ast: &DeriveInput,
1181 strct: &DataStruct,
1182 zerocopy_crate: &Path,
1183) -> TokenStream {
1184 ImplBlockBuilder::new(ast, strct, Trait::FromBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1185 .build()
1186}
1187
1188fn derive_from_bytes_enum(
1203 ast: &DeriveInput,
1204 enm: &DataEnum,
1205 zerocopy_crate: &Path,
1206) -> Result<TokenStream, Error> {
1207 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1208
1209 let variants_required = 1usize << enum_size_from_repr(&repr)?;
1210 if enm.variants.len() != variants_required {
1211 return Err(Error::new_spanned(
1212 ast,
1213 format!(
1214 "FromBytes only supported on {} enum with {} variants",
1215 repr.repr_type_name(),
1216 variants_required
1217 ),
1218 ));
1219 }
1220
1221 Ok(ImplBlockBuilder::new(ast, enm, Trait::FromBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1222 .build())
1223}
1224
1225fn enum_size_from_repr(repr: &EnumRepr) -> Result<usize, Error> {
1227 use CompoundRepr::*;
1228 use PrimitiveRepr::*;
1229 use Repr::*;
1230 match repr {
1231 Transparent(span)
1232 | Compound(
1233 Spanned { t: C | Rust | Primitive(U32 | I32 | U64 | I64 | U128 | I128 | Usize | Isize), span },
1234 _,
1235 ) => Err(Error::new(*span, "`FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16`")),
1236 Compound(Spanned { t: Primitive(U8 | I8), span: _ }, _align) => Ok(8),
1237 Compound(Spanned { t: Primitive(U16 | I16), span: _ }, _align) => Ok(16),
1238 }
1239}
1240
1241fn derive_from_bytes_union(
1244 ast: &DeriveInput,
1245 unn: &DataUnion,
1246 zerocopy_crate: &Path,
1247) -> TokenStream {
1248 let field_type_trait_bounds =
1251 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
1252 ImplBlockBuilder::new(ast, unn, Trait::FromBytes, field_type_trait_bounds, zerocopy_crate)
1253 .build()
1254}
1255
1256fn derive_into_bytes_struct(
1257 ast: &DeriveInput,
1258 strct: &DataStruct,
1259 zerocopy_crate: &Path,
1260) -> Result<TokenStream, Error> {
1261 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1262
1263 let is_transparent = repr.is_transparent();
1264 let is_c = repr.is_c();
1265 let is_packed_1 = repr.is_packed_1();
1266 let num_fields = strct.fields().len();
1267
1268 let (padding_check, require_unaligned_fields) = if is_transparent || is_packed_1 {
1269 (None, false)
1286 } else if is_c && !repr.is_align_gt_1() && num_fields <= 1 {
1287 (None, false)
1291 } else if ast.generics.params.is_empty() {
1292 let is_syntactic_dst =
1294 strct.fields().last().map(|(_, _, ty)| matches!(ty, Type::Slice(_))).unwrap_or(false);
1295 if is_c && is_syntactic_dst {
1308 (Some(PaddingCheck::ReprCStruct), false)
1309 } else {
1310 (Some(PaddingCheck::Struct), false)
1311 }
1312 } else if is_c && !repr.is_align_gt_1() {
1313 (None, true)
1322 } else {
1323 return Err(Error::new(Span::call_site(), "must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout"));
1324 };
1325
1326 let field_bounds = if require_unaligned_fields {
1327 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Unaligned)])
1328 } else {
1329 FieldBounds::ALL_SELF
1330 };
1331
1332 Ok(ImplBlockBuilder::new(ast, strct, Trait::IntoBytes, field_bounds, zerocopy_crate)
1333 .padding_check(padding_check)
1334 .build())
1335}
1336
1337fn derive_into_bytes_enum(
1343 ast: &DeriveInput,
1344 enm: &DataEnum,
1345 zerocopy_crate: &Path,
1346) -> Result<TokenStream, Error> {
1347 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1348 if !repr.is_c() && !repr.is_primitive() {
1349 return Err(Error::new(Span::call_site(), "must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout"));
1350 }
1351
1352 let tag_type_definition = r#enum::generate_tag_enum(&repr, enm);
1353 Ok(ImplBlockBuilder::new(ast, enm, Trait::IntoBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1354 .padding_check(PaddingCheck::Enum { tag_type_definition })
1355 .build())
1356}
1357
1358fn derive_into_bytes_union(
1363 ast: &DeriveInput,
1364 unn: &DataUnion,
1365 zerocopy_crate: &Path,
1366) -> Result<TokenStream, Error> {
1367 let cfg_compile_error = if cfg!(zerocopy_derive_union_into_bytes) {
1376 quote!()
1377 } else {
1378 let error_message = "requires --cfg zerocopy_derive_union_into_bytes;
1379please let us know you use this feature: https://github.com/google/zerocopy/discussions/1802";
1380 quote!(
1381 const _: () = {
1382 #[cfg(not(zerocopy_derive_union_into_bytes))]
1383 #zerocopy_crate::util::macro_util::core_reexport::compile_error!(#error_message);
1384 };
1385 )
1386 };
1387
1388 if !ast.generics.params.is_empty() {
1390 return Err(Error::new(Span::call_site(), "unsupported on types with type parameters"));
1391 }
1392
1393 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1398 if !repr.is_c() && !repr.is_transparent() && !repr.is_packed_1() {
1399 return Err(Error::new(
1400 Span::call_site(),
1401 "must be #[repr(C)], #[repr(packed)], or #[repr(transparent)]",
1402 ));
1403 }
1404
1405 let impl_block =
1406 ImplBlockBuilder::new(ast, unn, Trait::IntoBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1407 .padding_check(PaddingCheck::Union)
1408 .build();
1409 Ok(quote!(#cfg_compile_error #impl_block))
1410}
1411
1412fn derive_unaligned_struct(
1418 ast: &DeriveInput,
1419 strct: &DataStruct,
1420 zerocopy_crate: &Path,
1421) -> Result<TokenStream, Error> {
1422 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1423 repr.unaligned_validate_no_align_gt_1()?;
1424
1425 let field_bounds = if repr.is_packed_1() {
1426 FieldBounds::None
1427 } else if repr.is_c() || repr.is_transparent() {
1428 FieldBounds::ALL_SELF
1429 } else {
1430 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"));
1431 };
1432
1433 Ok(ImplBlockBuilder::new(ast, strct, Trait::Unaligned, field_bounds, zerocopy_crate).build())
1434}
1435
1436fn derive_unaligned_enum(
1440 ast: &DeriveInput,
1441 enm: &DataEnum,
1442 zerocopy_crate: &Path,
1443) -> Result<TokenStream, Error> {
1444 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1445 repr.unaligned_validate_no_align_gt_1()?;
1446
1447 if !repr.is_u8() && !repr.is_i8() {
1448 return Err(Error::new(Span::call_site(), "must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment"));
1449 }
1450
1451 Ok(ImplBlockBuilder::new(ast, enm, Trait::Unaligned, FieldBounds::ALL_SELF, zerocopy_crate)
1452 .build())
1453}
1454
1455fn derive_unaligned_union(
1461 ast: &DeriveInput,
1462 unn: &DataUnion,
1463 zerocopy_crate: &Path,
1464) -> Result<TokenStream, Error> {
1465 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1466 repr.unaligned_validate_no_align_gt_1()?;
1467
1468 let field_type_trait_bounds = if repr.is_packed_1() {
1469 FieldBounds::None
1470 } else if repr.is_c() || repr.is_transparent() {
1471 FieldBounds::ALL_SELF
1472 } else {
1473 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"));
1474 };
1475
1476 Ok(ImplBlockBuilder::new(ast, unn, Trait::Unaligned, field_type_trait_bounds, zerocopy_crate)
1477 .build())
1478}
1479
1480enum PaddingCheck {
1483 Struct,
1486 ReprCStruct,
1488 Union,
1490 Enum { tag_type_definition: TokenStream },
1495}
1496
1497impl PaddingCheck {
1498 fn validator_trait_and_macro_idents(&self) -> (Ident, Ident) {
1501 let (trt, mcro) = match self {
1502 PaddingCheck::Struct => ("PaddingFree", "struct_padding"),
1503 PaddingCheck::ReprCStruct => ("DynamicPaddingFree", "repr_c_struct_has_padding"),
1504 PaddingCheck::Union => ("PaddingFree", "union_padding"),
1505 PaddingCheck::Enum { .. } => ("PaddingFree", "enum_padding"),
1506 };
1507
1508 let trt = Ident::new(trt, Span::call_site());
1509 let mcro = Ident::new(mcro, Span::call_site());
1510 (trt, mcro)
1511 }
1512
1513 fn validator_macro_context(&self) -> Option<&TokenStream> {
1516 match self {
1517 PaddingCheck::Struct | PaddingCheck::ReprCStruct | PaddingCheck::Union => None,
1518 PaddingCheck::Enum { tag_type_definition } => Some(tag_type_definition),
1519 }
1520 }
1521}
1522
1523#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1524enum Trait {
1525 KnownLayout,
1526 Immutable,
1527 TryFromBytes,
1528 FromZeros,
1529 FromBytes,
1530 IntoBytes,
1531 Unaligned,
1532 Sized,
1533 ByteHash,
1534 ByteEq,
1535 SplitAt,
1536}
1537
1538impl ToTokens for Trait {
1539 fn to_tokens(&self, tokens: &mut TokenStream) {
1540 let s = match self {
1550 Trait::KnownLayout => "KnownLayout",
1551 Trait::Immutable => "Immutable",
1552 Trait::TryFromBytes => "TryFromBytes",
1553 Trait::FromZeros => "FromZeros",
1554 Trait::FromBytes => "FromBytes",
1555 Trait::IntoBytes => "IntoBytes",
1556 Trait::Unaligned => "Unaligned",
1557 Trait::Sized => "Sized",
1558 Trait::ByteHash => "ByteHash",
1559 Trait::ByteEq => "ByteEq",
1560 Trait::SplitAt => "SplitAt",
1561 };
1562 let ident = Ident::new(s, Span::call_site());
1563 tokens.extend(core::iter::once(TokenTree::Ident(ident)));
1564 }
1565}
1566
1567impl Trait {
1568 fn crate_path(&self, zerocopy_crate: &Path) -> Path {
1569 match self {
1570 Self::Sized => {
1571 parse_quote!(#zerocopy_crate::util::macro_util::core_reexport::marker::#self)
1572 }
1573 _ => parse_quote!(#zerocopy_crate::#self),
1574 }
1575 }
1576}
1577
1578#[derive(Debug, Eq, PartialEq)]
1579enum TraitBound {
1580 Slf,
1581 Other(Trait),
1582}
1583
1584enum FieldBounds<'a> {
1585 None,
1586 All(&'a [TraitBound]),
1587 Trailing(&'a [TraitBound]),
1588 Explicit(Vec<WherePredicate>),
1589}
1590
1591impl<'a> FieldBounds<'a> {
1592 const ALL_SELF: FieldBounds<'a> = FieldBounds::All(&[TraitBound::Slf]);
1593 const TRAILING_SELF: FieldBounds<'a> = FieldBounds::Trailing(&[TraitBound::Slf]);
1594}
1595
1596#[derive(Debug, Eq, PartialEq)]
1597enum SelfBounds<'a> {
1598 None,
1599 All(&'a [Trait]),
1600}
1601
1602#[allow(clippy::needless_lifetimes)]
1605impl<'a> SelfBounds<'a> {
1606 const SIZED: Self = Self::All(&[Trait::Sized]);
1607}
1608
1609fn normalize_bounds(slf: Trait, bounds: &[TraitBound]) -> impl '_ + Iterator<Item = Trait> {
1611 bounds.iter().map(move |bound| match bound {
1612 TraitBound::Slf => slf,
1613 TraitBound::Other(trt) => *trt,
1614 })
1615}
1616
1617struct ImplBlockBuilder<'a, D: DataExt> {
1618 input: &'a DeriveInput,
1619 data: &'a D,
1620 trt: Trait,
1621 field_type_trait_bounds: FieldBounds<'a>,
1622 zerocopy_crate: &'a Path,
1623 self_type_trait_bounds: SelfBounds<'a>,
1624 padding_check: Option<PaddingCheck>,
1625 inner_extras: Option<TokenStream>,
1626 outer_extras: Option<TokenStream>,
1627}
1628
1629impl<'a, D: DataExt> ImplBlockBuilder<'a, D> {
1630 fn new(
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 {
1637 Self {
1638 input,
1639 data,
1640 trt,
1641 field_type_trait_bounds,
1642 zerocopy_crate,
1643 self_type_trait_bounds: SelfBounds::None,
1644 padding_check: None,
1645 inner_extras: None,
1646 outer_extras: None,
1647 }
1648 }
1649
1650 fn self_type_trait_bounds(mut self, self_type_trait_bounds: SelfBounds<'a>) -> Self {
1651 self.self_type_trait_bounds = self_type_trait_bounds;
1652 self
1653 }
1654
1655 fn padding_check<P: Into<Option<PaddingCheck>>>(mut self, padding_check: P) -> Self {
1656 self.padding_check = padding_check.into();
1657 self
1658 }
1659
1660 fn inner_extras(mut self, inner_extras: TokenStream) -> Self {
1661 self.inner_extras = Some(inner_extras);
1662 self
1663 }
1664
1665 fn outer_extras<T: Into<Option<TokenStream>>>(mut self, outer_extras: T) -> Self {
1666 self.outer_extras = outer_extras.into();
1667 self
1668 }
1669
1670 fn build(self) -> TokenStream {
1671 let type_ident = &self.input.ident;
1731 let trait_path = self.trt.crate_path(self.zerocopy_crate);
1732 let fields = self.data.fields();
1733 let variants = self.data.variants();
1734 let tag = self.data.tag();
1735 let zerocopy_crate = self.zerocopy_crate;
1736
1737 fn bound_tt(
1738 ty: &Type,
1739 traits: impl Iterator<Item = Trait>,
1740 zerocopy_crate: &Path,
1741 ) -> WherePredicate {
1742 let traits = traits.map(|t| t.crate_path(zerocopy_crate));
1743 parse_quote!(#ty: #(#traits)+*)
1744 }
1745 let field_type_bounds: Vec<_> = match (self.field_type_trait_bounds, &fields[..]) {
1746 (FieldBounds::All(traits), _) => fields
1747 .iter()
1748 .map(|(_vis, _name, ty)| {
1749 bound_tt(ty, normalize_bounds(self.trt, traits), zerocopy_crate)
1750 })
1751 .collect(),
1752 (FieldBounds::None, _) | (FieldBounds::Trailing(..), []) => vec![],
1753 (FieldBounds::Trailing(traits), [.., last]) => {
1754 vec![bound_tt(last.2, normalize_bounds(self.trt, traits), zerocopy_crate)]
1755 }
1756 (FieldBounds::Explicit(bounds), _) => bounds,
1757 };
1758
1759 #[allow(unstable_name_collisions)] let padding_check_bound = self
1762 .padding_check
1763 .and_then(|check| (!fields.is_empty()).then_some(check))
1764 .map(|check| {
1765 let variant_types = variants.iter().map(|var| {
1766 let types = var.iter().map(|(_vis, _name, ty)| ty);
1767 quote!([#(#types),*])
1768 });
1769 let validator_context = check.validator_macro_context();
1770 let (trt, validator_macro) = check.validator_trait_and_macro_idents();
1771 let t = tag.iter();
1772 parse_quote! {
1773 (): #zerocopy_crate::util::macro_util::#trt<
1774 Self,
1775 {
1776 #validator_context
1777 #zerocopy_crate::#validator_macro!(Self, #(#t,)* #(#variant_types),*)
1778 }
1779 >
1780 }
1781 });
1782
1783 let self_bounds: Option<WherePredicate> = match self.self_type_trait_bounds {
1784 SelfBounds::None => None,
1785 SelfBounds::All(traits) => {
1786 Some(bound_tt(&parse_quote!(Self), traits.iter().copied(), zerocopy_crate))
1787 }
1788 };
1789
1790 let bounds = self
1791 .input
1792 .generics
1793 .where_clause
1794 .as_ref()
1795 .map(|where_clause| where_clause.predicates.iter())
1796 .into_iter()
1797 .flatten()
1798 .chain(field_type_bounds.iter())
1799 .chain(padding_check_bound.iter())
1800 .chain(self_bounds.iter());
1801
1802 let params = self.input.generics.params.clone().into_iter().map(|mut param| {
1804 match &mut param {
1805 GenericParam::Type(ty) => ty.default = None,
1806 GenericParam::Const(cnst) => cnst.default = None,
1807 GenericParam::Lifetime(_) => {}
1808 }
1809 quote!(#param)
1810 });
1811
1812 let param_idents = self.input.generics.params.iter().map(|param| match param {
1815 GenericParam::Type(ty) => {
1816 let ident = &ty.ident;
1817 quote!(#ident)
1818 }
1819 GenericParam::Lifetime(l) => {
1820 let ident = &l.lifetime;
1821 quote!(#ident)
1822 }
1823 GenericParam::Const(cnst) => {
1824 let ident = &cnst.ident;
1825 quote!({#ident})
1826 }
1827 });
1828
1829 let inner_extras = self.inner_extras;
1830 let impl_tokens = quote! {
1831 #[allow(deprecated)]
1834 #[automatically_derived]
1837 unsafe impl < #(#params),* > #trait_path for #type_ident < #(#param_idents),* >
1838 where
1839 #(#bounds,)*
1840 {
1841 fn only_derive_is_allowed_to_implement_this_trait() {}
1842
1843 #inner_extras
1844 }
1845 };
1846
1847 if let Some(outer_extras) = self.outer_extras {
1848 quote! {
1851 const _: () = {
1852 #impl_tokens
1853
1854 #outer_extras
1855 };
1856 }
1857 } else {
1858 impl_tokens
1859 }
1860 }
1861}
1862
1863#[allow(unused)]
1871trait BoolExt {
1872 fn then_some<T>(self, t: T) -> Option<T>;
1873}
1874
1875impl BoolExt for bool {
1876 fn then_some<T>(self, t: T) -> Option<T> {
1877 if self {
1878 Some(t)
1879 } else {
1880 None
1881 }
1882 }
1883}