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 let repr_align = #repr_align;
289 let repr_packed = #repr_packed;
290
291 DstLayout::new_zst(repr_align)
292 #(.extend(DstLayout::for_type::<#leading_fields_tys>(), repr_packed))*
293 .extend(<#trailing_field_ty as KnownLayout>::LAYOUT, repr_packed)
294 .pad_to_align()
295 };
296
297 #methods
298 )
299 };
300
301 let outer_extras = {
302 let ident = &ast.ident;
303 let vis = &ast.vis;
304 let params = &ast.generics.params;
305 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
306
307 let predicates = if let Some(where_clause) = where_clause {
308 where_clause.predicates.clone()
309 } else {
310 Default::default()
311 };
312
313 let field_index =
316 |name| Ident::new(&format!("__Zerocopy_Field_{}", name), ident.span());
317
318 let field_indices: Vec<_> =
319 fields.iter().map(|(_vis, name, _ty)| field_index(name)).collect();
320
321 let field_defs = field_indices.iter().zip(&fields).map(|(idx, (vis, _, _))| {
323 quote! {
324 #[allow(non_camel_case_types)]
325 #vis struct #idx;
326 }
327 });
328
329 let field_impls = field_indices.iter().zip(&fields).map(|(idx, (_, _, ty))| quote! {
330 unsafe impl #impl_generics #zerocopy_crate::util::macro_util::Field<#idx> for #ident #ty_generics
332 where
333 #predicates
334 {
335 type Type = #ty;
336 }
337 });
338
339 let trailing_field_index = field_index(trailing_field_name);
340 let leading_field_indices =
341 leading_fields.iter().map(|(_vis, name, _ty)| field_index(name));
342
343 let trailing_field_ty = quote! {
344 <#ident #ty_generics as
345 #zerocopy_crate::util::macro_util::Field<#trailing_field_index>
346 >::Type
347 };
348
349 let methods = make_methods(&parse_quote! {
350 <#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit
351 });
352
353 quote! {
354 #(#field_defs)*
355
356 #(#field_impls)*
357
358 #repr
366 #[doc(hidden)]
367 #[allow(private_bounds)]
371 #vis struct __ZerocopyKnownLayoutMaybeUninit<#params> (
372 #(#zerocopy_crate::util::macro_util::core_reexport::mem::MaybeUninit<
373 <#ident #ty_generics as
374 #zerocopy_crate::util::macro_util::Field<#leading_field_indices>
375 >::Type
376 >,)*
377 #zerocopy_crate::util::macro_util::core_reexport::mem::ManuallyDrop<
384 <#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit
385 >
386 )
387 where
388 #trailing_field_ty: #zerocopy_crate::KnownLayout,
389 #predicates;
390
391 unsafe impl #impl_generics #zerocopy_crate::KnownLayout for __ZerocopyKnownLayoutMaybeUninit #ty_generics
398 where
399 #trailing_field_ty: #zerocopy_crate::KnownLayout,
400 #predicates
401 {
402 #[allow(clippy::missing_inline_in_public_items)]
403 fn only_derive_is_allowed_to_implement_this_trait() {}
404
405 type PointerMetadata = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::PointerMetadata;
406
407 type MaybeUninit = Self;
408
409 const LAYOUT: #zerocopy_crate::DstLayout = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::LAYOUT;
410
411 #methods
412 }
413 }
414 };
415
416 (SelfBounds::None, inner_extras, Some(outer_extras))
417 } else {
418 (
422 SelfBounds::SIZED,
423 quote!(
424 type PointerMetadata = ();
425 type MaybeUninit =
426 #zerocopy_crate::util::macro_util::core_reexport::mem::MaybeUninit<Self>;
427
428 const LAYOUT: #zerocopy_crate::DstLayout = #zerocopy_crate::DstLayout::for_type::<Self>();
432
433 #[inline(always)]
438 fn raw_from_ptr_len(
439 bytes: #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<u8>,
440 _meta: (),
441 ) -> #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<Self>
442 {
443 bytes.cast::<Self>()
444 }
445
446 #[inline(always)]
447 fn pointer_to_metadata(_ptr: *mut Self) -> () {}
448 ),
449 None,
450 )
451 };
452
453 Ok(match &ast.data {
454 Data::Struct(strct) => {
455 let require_trait_bound_on_field_types = if self_bounds == SelfBounds::SIZED {
456 FieldBounds::None
457 } else {
458 FieldBounds::TRAILING_SELF
459 };
460
461 ImplBlockBuilder::new(
466 ast,
467 strct,
468 Trait::KnownLayout,
469 require_trait_bound_on_field_types,
470 zerocopy_crate,
471 )
472 .self_type_trait_bounds(self_bounds)
473 .inner_extras(inner_extras)
474 .outer_extras(outer_extras)
475 .build()
476 }
477 Data::Enum(enm) => {
478 ImplBlockBuilder::new(ast, enm, Trait::KnownLayout, FieldBounds::None, zerocopy_crate)
481 .self_type_trait_bounds(SelfBounds::SIZED)
482 .inner_extras(inner_extras)
483 .outer_extras(outer_extras)
484 .build()
485 }
486 Data::Union(unn) => {
487 ImplBlockBuilder::new(ast, unn, Trait::KnownLayout, FieldBounds::None, zerocopy_crate)
490 .self_type_trait_bounds(SelfBounds::SIZED)
491 .inner_extras(inner_extras)
492 .outer_extras(outer_extras)
493 .build()
494 }
495 })
496}
497
498fn derive_no_cell_inner(
499 ast: &DeriveInput,
500 _top_level: Trait,
501 zerocopy_crate: &Path,
502) -> TokenStream {
503 match &ast.data {
504 Data::Struct(strct) => ImplBlockBuilder::new(
505 ast,
506 strct,
507 Trait::Immutable,
508 FieldBounds::ALL_SELF,
509 zerocopy_crate,
510 )
511 .build(),
512 Data::Enum(enm) => {
513 ImplBlockBuilder::new(ast, enm, Trait::Immutable, FieldBounds::ALL_SELF, zerocopy_crate)
514 .build()
515 }
516 Data::Union(unn) => {
517 ImplBlockBuilder::new(ast, unn, Trait::Immutable, FieldBounds::ALL_SELF, zerocopy_crate)
518 .build()
519 }
520 }
521}
522
523fn derive_try_from_bytes_inner(
524 ast: &DeriveInput,
525 top_level: Trait,
526 zerocopy_crate: &Path,
527) -> Result<TokenStream, Error> {
528 match &ast.data {
529 Data::Struct(strct) => derive_try_from_bytes_struct(ast, strct, top_level, zerocopy_crate),
530 Data::Enum(enm) => derive_try_from_bytes_enum(ast, enm, top_level, zerocopy_crate),
531 Data::Union(unn) => Ok(derive_try_from_bytes_union(ast, unn, top_level, zerocopy_crate)),
532 }
533}
534
535fn derive_from_zeros_inner(
536 ast: &DeriveInput,
537 top_level: Trait,
538 zerocopy_crate: &Path,
539) -> Result<TokenStream, Error> {
540 let try_from_bytes = derive_try_from_bytes_inner(ast, top_level, zerocopy_crate)?;
541 let from_zeros = match &ast.data {
542 Data::Struct(strct) => derive_from_zeros_struct(ast, strct, zerocopy_crate),
543 Data::Enum(enm) => derive_from_zeros_enum(ast, enm, zerocopy_crate)?,
544 Data::Union(unn) => derive_from_zeros_union(ast, unn, zerocopy_crate),
545 };
546 Ok(IntoIterator::into_iter([try_from_bytes, from_zeros]).collect())
547}
548
549fn derive_from_bytes_inner(
550 ast: &DeriveInput,
551 top_level: Trait,
552 zerocopy_crate: &Path,
553) -> Result<TokenStream, Error> {
554 let from_zeros = derive_from_zeros_inner(ast, top_level, zerocopy_crate)?;
555 let from_bytes = match &ast.data {
556 Data::Struct(strct) => derive_from_bytes_struct(ast, strct, zerocopy_crate),
557 Data::Enum(enm) => derive_from_bytes_enum(ast, enm, zerocopy_crate)?,
558 Data::Union(unn) => derive_from_bytes_union(ast, unn, zerocopy_crate),
559 };
560
561 Ok(IntoIterator::into_iter([from_zeros, from_bytes]).collect())
562}
563
564fn derive_into_bytes_inner(
565 ast: &DeriveInput,
566 _top_level: Trait,
567 zerocopy_crate: &Path,
568) -> Result<TokenStream, Error> {
569 match &ast.data {
570 Data::Struct(strct) => derive_into_bytes_struct(ast, strct, zerocopy_crate),
571 Data::Enum(enm) => derive_into_bytes_enum(ast, enm, zerocopy_crate),
572 Data::Union(unn) => derive_into_bytes_union(ast, unn, zerocopy_crate),
573 }
574}
575
576fn derive_unaligned_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_unaligned_struct(ast, strct, zerocopy_crate),
583 Data::Enum(enm) => derive_unaligned_enum(ast, enm, zerocopy_crate),
584 Data::Union(unn) => derive_unaligned_union(ast, unn, zerocopy_crate),
585 }
586}
587
588fn derive_hash_inner(
589 ast: &DeriveInput,
590 _top_level: Trait,
591 zerocopy_crate: &Path,
592) -> Result<TokenStream, Error> {
593 let type_ident = &ast.ident;
599 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
600 let where_predicates = where_clause.map(|clause| &clause.predicates);
601 Ok(quote! {
602 #[allow(deprecated)]
605 #[automatically_derived]
608 impl #impl_generics #zerocopy_crate::util::macro_util::core_reexport::hash::Hash for #type_ident #ty_generics
609 where
610 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
611 #where_predicates
612 {
613 fn hash<H>(&self, state: &mut H)
614 where
615 H: #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher,
616 {
617 #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher::write(
618 state,
619 #zerocopy_crate::IntoBytes::as_bytes(self)
620 )
621 }
622
623 fn hash_slice<H>(data: &[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(data)
630 )
631 }
632 }
633 })
634}
635
636fn derive_eq_inner(
637 ast: &DeriveInput,
638 _top_level: Trait,
639 zerocopy_crate: &Path,
640) -> Result<TokenStream, Error> {
641 let type_ident = &ast.ident;
647 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
648 let where_predicates = where_clause.map(|clause| &clause.predicates);
649 Ok(quote! {
650 #[allow(deprecated)]
653 #[automatically_derived]
656 impl #impl_generics #zerocopy_crate::util::macro_util::core_reexport::cmp::PartialEq for #type_ident #ty_generics
657 where
658 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
659 #where_predicates
660 {
661 fn eq(&self, other: &Self) -> bool {
662 #zerocopy_crate::util::macro_util::core_reexport::cmp::PartialEq::eq(
663 #zerocopy_crate::IntoBytes::as_bytes(self),
664 #zerocopy_crate::IntoBytes::as_bytes(other),
665 )
666 }
667 }
668
669 #[allow(deprecated)]
672 #[automatically_derived]
675 impl #impl_generics #zerocopy_crate::util::macro_util::core_reexport::cmp::Eq for #type_ident #ty_generics
676 where
677 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
678 #where_predicates
679 {
680 }
681 })
682}
683
684fn derive_split_at_inner(
685 ast: &DeriveInput,
686 _top_level: Trait,
687 zerocopy_crate: &Path,
688) -> Result<TokenStream, Error> {
689 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
690
691 match &ast.data {
692 Data::Struct(_) => {}
693 Data::Enum(_) | Data::Union(_) => {
694 return Err(Error::new(Span::call_site(), "can only be applied to structs"));
695 }
696 };
697
698 if repr.get_packed().is_some() {
699 return Err(Error::new(Span::call_site(), "must not have #[repr(packed)] attribute"));
700 }
701
702 if !(repr.is_c() || repr.is_transparent()) {
703 return Err(Error::new(Span::call_site(), "must have #[repr(C)] or #[repr(transparent)] in order to guarantee this type's layout is splitable"));
704 }
705
706 let fields = ast.data.fields();
707 let trailing_field = if let Some(((_, _, trailing_field), _)) = fields.split_last() {
708 trailing_field
709 } else {
710 return Err(Error::new(Span::call_site(), "must at least one field"));
711 };
712
713 Ok(ImplBlockBuilder::new(
718 ast,
719 &ast.data,
720 Trait::SplitAt,
721 FieldBounds::TRAILING_SELF,
722 zerocopy_crate,
723 )
724 .inner_extras(quote! {
725 type Elem = <#trailing_field as ::zerocopy::SplitAt>::Elem;
726 })
727 .build())
728}
729
730fn derive_try_from_bytes_struct(
733 ast: &DeriveInput,
734 strct: &DataStruct,
735 top_level: Trait,
736 zerocopy_crate: &Path,
737) -> Result<TokenStream, Error> {
738 let extras =
739 try_gen_trivial_is_bit_valid(ast, top_level, zerocopy_crate).unwrap_or_else(|| {
740 let fields = strct.fields();
741 let field_names = fields.iter().map(|(_vis, name, _ty)| name);
742 let field_tys = fields.iter().map(|(_vis, _name, ty)| ty);
743 quote!(
744 fn is_bit_valid<___ZerocopyAliasing>(
750 mut candidate: #zerocopy_crate::Maybe<Self, ___ZerocopyAliasing>,
751 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
752 where
753 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
754 {
755 use #zerocopy_crate::util::macro_util::core_reexport;
756 use #zerocopy_crate::pointer::PtrInner;
757
758 true #(&& {
759 let field_candidate = unsafe {
767 let project = |slf: PtrInner<'_, Self>| {
768 let slf = slf.as_non_null().as_ptr();
769 let field = core_reexport::ptr::addr_of_mut!((*slf).#field_names);
770 let ptr = unsafe { core_reexport::ptr::NonNull::new_unchecked(field) };
778 unsafe { PtrInner::new(ptr) }
789 };
790
791 candidate.reborrow().cast_unsized_unchecked(project)
792 };
793
794 <#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate)
795 })*
796 }
797 )
798 });
799 Ok(ImplBlockBuilder::new(
800 ast,
801 strct,
802 Trait::TryFromBytes,
803 FieldBounds::ALL_SELF,
804 zerocopy_crate,
805 )
806 .inner_extras(extras)
807 .build())
808}
809
810fn derive_try_from_bytes_union(
813 ast: &DeriveInput,
814 unn: &DataUnion,
815 top_level: Trait,
816 zerocopy_crate: &Path,
817) -> TokenStream {
818 let field_type_trait_bounds =
820 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
821 let extras =
822 try_gen_trivial_is_bit_valid(ast, top_level, zerocopy_crate).unwrap_or_else(|| {
823 let fields = unn.fields();
824 let field_names = fields.iter().map(|(_vis, name, _ty)| name);
825 let field_tys = fields.iter().map(|(_vis, _name, ty)| ty);
826 quote!(
827 fn is_bit_valid<___ZerocopyAliasing>(
833 mut candidate: #zerocopy_crate::Maybe<'_, Self,___ZerocopyAliasing>
834 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
835 where
836 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
837 {
838 use #zerocopy_crate::util::macro_util::core_reexport;
839 use #zerocopy_crate::pointer::PtrInner;
840
841 false #(|| {
842 let field_candidate = unsafe {
850 let project = |slf: PtrInner<'_, Self>| {
851 let slf = slf.as_non_null().as_ptr();
852 let field = core_reexport::ptr::addr_of_mut!((*slf).#field_names);
853 let ptr = unsafe { core_reexport::ptr::NonNull::new_unchecked(field) };
861 unsafe { PtrInner::new(ptr) }
872 };
873
874 candidate.reborrow().cast_unsized_unchecked(project)
875 };
876
877 <#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate)
878 })*
879 }
880 )
881 });
882 ImplBlockBuilder::new(ast, unn, Trait::TryFromBytes, field_type_trait_bounds, zerocopy_crate)
883 .inner_extras(extras)
884 .build()
885}
886
887fn derive_try_from_bytes_enum(
888 ast: &DeriveInput,
889 enm: &DataEnum,
890 top_level: Trait,
891 zerocopy_crate: &Path,
892) -> Result<TokenStream, Error> {
893 let repr = EnumRepr::from_attrs(&ast.attrs)?;
894
895 let could_be_from_bytes = enum_size_from_repr(&repr)
901 .map(|size| enm.fields().is_empty() && enm.variants.len() == 1usize << size)
902 .unwrap_or(false);
903
904 let trivial_is_bit_valid = try_gen_trivial_is_bit_valid(ast, top_level, zerocopy_crate);
905 let extra = match (trivial_is_bit_valid, could_be_from_bytes) {
906 (Some(is_bit_valid), _) => is_bit_valid,
907 (None, true) => unsafe { gen_trivial_is_bit_valid_unchecked(zerocopy_crate) },
910 (None, false) => {
911 r#enum::derive_is_bit_valid(&ast.ident, &repr, &ast.generics, enm, zerocopy_crate)?
912 }
913 };
914
915 Ok(ImplBlockBuilder::new(ast, enm, Trait::TryFromBytes, FieldBounds::ALL_SELF, zerocopy_crate)
916 .inner_extras(extra)
917 .build())
918}
919
920fn try_gen_trivial_is_bit_valid(
942 ast: &DeriveInput,
943 top_level: Trait,
944 zerocopy_crate: &Path,
945) -> Option<proc_macro2::TokenStream> {
946 if top_level == Trait::FromBytes && ast.generics.params.is_empty() {
955 Some(quote!(
956 fn is_bit_valid<___ZerocopyAliasing>(
958 _candidate: #zerocopy_crate::Maybe<Self, ___ZerocopyAliasing>,
959 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
960 where
961 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
962 {
963 if false {
964 fn assert_is_from_bytes<T>()
965 where
966 T: #zerocopy_crate::FromBytes,
967 T: ?#zerocopy_crate::util::macro_util::core_reexport::marker::Sized,
968 {
969 }
970
971 assert_is_from_bytes::<Self>();
972 }
973
974 true
978 }
979 ))
980 } else {
981 None
982 }
983}
984
985unsafe fn gen_trivial_is_bit_valid_unchecked(zerocopy_crate: &Path) -> proc_macro2::TokenStream {
997 quote!(
998 fn is_bit_valid<___ZerocopyAliasing>(
1001 _candidate: #zerocopy_crate::Maybe<Self, ___ZerocopyAliasing>,
1002 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
1003 where
1004 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
1005 {
1006 true
1007 }
1008 )
1009}
1010
1011fn derive_from_zeros_struct(
1014 ast: &DeriveInput,
1015 strct: &DataStruct,
1016 zerocopy_crate: &Path,
1017) -> TokenStream {
1018 ImplBlockBuilder::new(ast, strct, Trait::FromZeros, FieldBounds::ALL_SELF, zerocopy_crate)
1019 .build()
1020}
1021
1022fn find_zero_variant(enm: &DataEnum) -> Result<usize, bool> {
1028 let mut next_negative_discriminant = Some(0);
1036
1037 let mut has_unknown_discriminants = false;
1044
1045 for (i, v) in enm.variants.iter().enumerate() {
1046 match v.discriminant.as_ref() {
1047 None => {
1049 match next_negative_discriminant.as_mut() {
1050 Some(0) => return Ok(i),
1051 Some(n) => *n -= 1,
1053 None => (),
1054 }
1055 }
1056 Some((_, Expr::Lit(ExprLit { lit: Lit::Int(int), .. }))) => {
1058 match int.base10_parse::<u128>().ok() {
1059 Some(0) => return Ok(i),
1060 Some(_) => next_negative_discriminant = None,
1061 None => {
1062 has_unknown_discriminants = true;
1064 next_negative_discriminant = None;
1065 }
1066 }
1067 }
1068 Some((_, Expr::Unary(ExprUnary { op: UnOp::Neg(_), expr, .. }))) => match &**expr {
1070 Expr::Lit(ExprLit { lit: Lit::Int(int), .. }) => {
1071 match int.base10_parse::<u128>().ok() {
1072 Some(0) => return Ok(i),
1073 Some(x) => next_negative_discriminant = Some(x - 1),
1075 None => {
1076 has_unknown_discriminants = true;
1079 next_negative_discriminant = None;
1080 }
1081 }
1082 }
1083 _ => {
1085 has_unknown_discriminants = true;
1086 next_negative_discriminant = None;
1087 }
1088 },
1089 _ => {
1091 has_unknown_discriminants = true;
1092 next_negative_discriminant = None;
1093 }
1094 }
1095 }
1096
1097 Err(has_unknown_discriminants)
1098}
1099
1100fn derive_from_zeros_enum(
1104 ast: &DeriveInput,
1105 enm: &DataEnum,
1106 zerocopy_crate: &Path,
1107) -> Result<TokenStream, Error> {
1108 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1109
1110 match repr {
1113 Repr::Compound(
1114 Spanned { t: CompoundRepr::C | CompoundRepr::Primitive(_), span: _ },
1115 _,
1116 ) => {}
1117 Repr::Transparent(_)
1118 | 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")),
1119 }
1120
1121 let zero_variant = match find_zero_variant(enm) {
1122 Ok(index) => enm.variants.iter().nth(index).unwrap(),
1123 Err(true) => {
1125 return Err(Error::new_spanned(
1126 ast,
1127 "FromZeros only supported on enums with a variant that has a discriminant of `0`\n\
1128 help: This enum has discriminants which are not literal integers. One of those may \
1129 define or imply which variant has a discriminant of zero. Use a literal integer to \
1130 define or imply the variant with a discriminant of zero.",
1131 ));
1132 }
1133 Err(false) => {
1135 return Err(Error::new_spanned(
1136 ast,
1137 "FromZeros only supported on enums with a variant that has a discriminant of `0`",
1138 ));
1139 }
1140 };
1141
1142 let explicit_bounds = zero_variant
1143 .fields
1144 .iter()
1145 .map(|field| {
1146 let ty = &field.ty;
1147 parse_quote! { #ty: #zerocopy_crate::FromZeros }
1148 })
1149 .collect::<Vec<WherePredicate>>();
1150
1151 Ok(ImplBlockBuilder::new(
1152 ast,
1153 enm,
1154 Trait::FromZeros,
1155 FieldBounds::Explicit(explicit_bounds),
1156 zerocopy_crate,
1157 )
1158 .build())
1159}
1160
1161fn derive_from_zeros_union(
1164 ast: &DeriveInput,
1165 unn: &DataUnion,
1166 zerocopy_crate: &Path,
1167) -> TokenStream {
1168 let field_type_trait_bounds =
1171 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
1172 ImplBlockBuilder::new(ast, unn, Trait::FromZeros, field_type_trait_bounds, zerocopy_crate)
1173 .build()
1174}
1175
1176fn derive_from_bytes_struct(
1179 ast: &DeriveInput,
1180 strct: &DataStruct,
1181 zerocopy_crate: &Path,
1182) -> TokenStream {
1183 ImplBlockBuilder::new(ast, strct, Trait::FromBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1184 .build()
1185}
1186
1187fn derive_from_bytes_enum(
1202 ast: &DeriveInput,
1203 enm: &DataEnum,
1204 zerocopy_crate: &Path,
1205) -> Result<TokenStream, Error> {
1206 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1207
1208 let variants_required = 1usize << enum_size_from_repr(&repr)?;
1209 if enm.variants.len() != variants_required {
1210 return Err(Error::new_spanned(
1211 ast,
1212 format!(
1213 "FromBytes only supported on {} enum with {} variants",
1214 repr.repr_type_name(),
1215 variants_required
1216 ),
1217 ));
1218 }
1219
1220 Ok(ImplBlockBuilder::new(ast, enm, Trait::FromBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1221 .build())
1222}
1223
1224fn enum_size_from_repr(repr: &EnumRepr) -> Result<usize, Error> {
1226 use CompoundRepr::*;
1227 use PrimitiveRepr::*;
1228 use Repr::*;
1229 match repr {
1230 Transparent(span)
1231 | Compound(
1232 Spanned { t: C | Rust | Primitive(U32 | I32 | U64 | I64 | Usize | Isize), span },
1233 _,
1234 ) => Err(Error::new(*span, "`FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16`")),
1235 Compound(Spanned { t: Primitive(U8 | I8), span: _ }, _align) => Ok(8),
1236 Compound(Spanned { t: Primitive(U16 | I16), span: _ }, _align) => Ok(16),
1237 }
1238}
1239
1240fn derive_from_bytes_union(
1243 ast: &DeriveInput,
1244 unn: &DataUnion,
1245 zerocopy_crate: &Path,
1246) -> TokenStream {
1247 let field_type_trait_bounds =
1250 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
1251 ImplBlockBuilder::new(ast, unn, Trait::FromBytes, field_type_trait_bounds, zerocopy_crate)
1252 .build()
1253}
1254
1255fn derive_into_bytes_struct(
1256 ast: &DeriveInput,
1257 strct: &DataStruct,
1258 zerocopy_crate: &Path,
1259) -> Result<TokenStream, Error> {
1260 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1261
1262 let is_transparent = repr.is_transparent();
1263 let is_c = repr.is_c();
1264 let is_packed_1 = repr.is_packed_1();
1265 let num_fields = strct.fields().len();
1266
1267 let (padding_check, require_unaligned_fields) = if is_transparent || is_packed_1 {
1268 (None, false)
1285 } else if is_c && !repr.is_align_gt_1() && num_fields <= 1 {
1286 (None, false)
1290 } else if ast.generics.params.is_empty() {
1291 (Some(PaddingCheck::Struct), false)
1304 } else if is_c && !repr.is_align_gt_1() {
1305 (None, true)
1314 } else {
1315 return Err(Error::new(Span::call_site(), "must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout"));
1316 };
1317
1318 let field_bounds = if require_unaligned_fields {
1319 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Unaligned)])
1320 } else {
1321 FieldBounds::ALL_SELF
1322 };
1323
1324 Ok(ImplBlockBuilder::new(ast, strct, Trait::IntoBytes, field_bounds, zerocopy_crate)
1325 .padding_check(padding_check)
1326 .build())
1327}
1328
1329fn derive_into_bytes_enum(
1335 ast: &DeriveInput,
1336 enm: &DataEnum,
1337 zerocopy_crate: &Path,
1338) -> Result<TokenStream, Error> {
1339 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1340 if !repr.is_c() && !repr.is_primitive() {
1341 return Err(Error::new(Span::call_site(), "must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout"));
1342 }
1343
1344 let tag_type_definition = r#enum::generate_tag_enum(&repr, enm);
1345 Ok(ImplBlockBuilder::new(ast, enm, Trait::IntoBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1346 .padding_check(PaddingCheck::Enum { tag_type_definition })
1347 .build())
1348}
1349
1350fn derive_into_bytes_union(
1355 ast: &DeriveInput,
1356 unn: &DataUnion,
1357 zerocopy_crate: &Path,
1358) -> Result<TokenStream, Error> {
1359 let cfg_compile_error = if cfg!(zerocopy_derive_union_into_bytes) {
1368 quote!()
1369 } else {
1370 let error_message = "requires --cfg zerocopy_derive_union_into_bytes;
1371please let us know you use this feature: https://github.com/google/zerocopy/discussions/1802";
1372 quote!(
1373 const _: () = {
1374 #[cfg(not(zerocopy_derive_union_into_bytes))]
1375 #zerocopy_crate::util::macro_util::core_reexport::compile_error!(#error_message);
1376 };
1377 )
1378 };
1379
1380 if !ast.generics.params.is_empty() {
1382 return Err(Error::new(Span::call_site(), "unsupported on types with type parameters"));
1383 }
1384
1385 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1390 if !repr.is_c() && !repr.is_transparent() && !repr.is_packed_1() {
1391 return Err(Error::new(
1392 Span::call_site(),
1393 "must be #[repr(C)], #[repr(packed)], or #[repr(transparent)]",
1394 ));
1395 }
1396
1397 let impl_block =
1398 ImplBlockBuilder::new(ast, unn, Trait::IntoBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1399 .padding_check(PaddingCheck::Union)
1400 .build();
1401 Ok(quote!(#cfg_compile_error #impl_block))
1402}
1403
1404fn derive_unaligned_struct(
1410 ast: &DeriveInput,
1411 strct: &DataStruct,
1412 zerocopy_crate: &Path,
1413) -> Result<TokenStream, Error> {
1414 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1415 repr.unaligned_validate_no_align_gt_1()?;
1416
1417 let field_bounds = if repr.is_packed_1() {
1418 FieldBounds::None
1419 } else if repr.is_c() || repr.is_transparent() {
1420 FieldBounds::ALL_SELF
1421 } else {
1422 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"));
1423 };
1424
1425 Ok(ImplBlockBuilder::new(ast, strct, Trait::Unaligned, field_bounds, zerocopy_crate).build())
1426}
1427
1428fn derive_unaligned_enum(
1432 ast: &DeriveInput,
1433 enm: &DataEnum,
1434 zerocopy_crate: &Path,
1435) -> Result<TokenStream, Error> {
1436 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1437 repr.unaligned_validate_no_align_gt_1()?;
1438
1439 if !repr.is_u8() && !repr.is_i8() {
1440 return Err(Error::new(Span::call_site(), "must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment"));
1441 }
1442
1443 Ok(ImplBlockBuilder::new(ast, enm, Trait::Unaligned, FieldBounds::ALL_SELF, zerocopy_crate)
1444 .build())
1445}
1446
1447fn derive_unaligned_union(
1453 ast: &DeriveInput,
1454 unn: &DataUnion,
1455 zerocopy_crate: &Path,
1456) -> Result<TokenStream, Error> {
1457 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1458 repr.unaligned_validate_no_align_gt_1()?;
1459
1460 let field_type_trait_bounds = if repr.is_packed_1() {
1461 FieldBounds::None
1462 } else if repr.is_c() || repr.is_transparent() {
1463 FieldBounds::ALL_SELF
1464 } else {
1465 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"));
1466 };
1467
1468 Ok(ImplBlockBuilder::new(ast, unn, Trait::Unaligned, field_type_trait_bounds, zerocopy_crate)
1469 .build())
1470}
1471
1472enum PaddingCheck {
1475 Struct,
1478 Union,
1480 Enum { tag_type_definition: TokenStream },
1485}
1486
1487impl PaddingCheck {
1488 fn validator_macro_ident(&self) -> Ident {
1491 let s = match self {
1492 PaddingCheck::Struct => "struct_has_padding",
1493 PaddingCheck::Union => "union_has_padding",
1494 PaddingCheck::Enum { .. } => "enum_has_padding",
1495 };
1496
1497 Ident::new(s, Span::call_site())
1498 }
1499
1500 fn validator_macro_context(&self) -> Option<&TokenStream> {
1503 match self {
1504 PaddingCheck::Struct | PaddingCheck::Union => None,
1505 PaddingCheck::Enum { tag_type_definition } => Some(tag_type_definition),
1506 }
1507 }
1508}
1509
1510#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1511enum Trait {
1512 KnownLayout,
1513 Immutable,
1514 TryFromBytes,
1515 FromZeros,
1516 FromBytes,
1517 IntoBytes,
1518 Unaligned,
1519 Sized,
1520 ByteHash,
1521 ByteEq,
1522 SplitAt,
1523}
1524
1525impl ToTokens for Trait {
1526 fn to_tokens(&self, tokens: &mut TokenStream) {
1527 let s = match self {
1537 Trait::KnownLayout => "KnownLayout",
1538 Trait::Immutable => "Immutable",
1539 Trait::TryFromBytes => "TryFromBytes",
1540 Trait::FromZeros => "FromZeros",
1541 Trait::FromBytes => "FromBytes",
1542 Trait::IntoBytes => "IntoBytes",
1543 Trait::Unaligned => "Unaligned",
1544 Trait::Sized => "Sized",
1545 Trait::ByteHash => "ByteHash",
1546 Trait::ByteEq => "ByteEq",
1547 Trait::SplitAt => "SplitAt",
1548 };
1549 let ident = Ident::new(s, Span::call_site());
1550 tokens.extend(core::iter::once(TokenTree::Ident(ident)));
1551 }
1552}
1553
1554impl Trait {
1555 fn crate_path(&self, zerocopy_crate: &Path) -> Path {
1556 match self {
1557 Self::Sized => {
1558 parse_quote!(#zerocopy_crate::util::macro_util::core_reexport::marker::#self)
1559 }
1560 _ => parse_quote!(#zerocopy_crate::#self),
1561 }
1562 }
1563}
1564
1565#[derive(Debug, Eq, PartialEq)]
1566enum TraitBound {
1567 Slf,
1568 Other(Trait),
1569}
1570
1571enum FieldBounds<'a> {
1572 None,
1573 All(&'a [TraitBound]),
1574 Trailing(&'a [TraitBound]),
1575 Explicit(Vec<WherePredicate>),
1576}
1577
1578impl<'a> FieldBounds<'a> {
1579 const ALL_SELF: FieldBounds<'a> = FieldBounds::All(&[TraitBound::Slf]);
1580 const TRAILING_SELF: FieldBounds<'a> = FieldBounds::Trailing(&[TraitBound::Slf]);
1581}
1582
1583#[derive(Debug, Eq, PartialEq)]
1584enum SelfBounds<'a> {
1585 None,
1586 All(&'a [Trait]),
1587}
1588
1589#[allow(clippy::needless_lifetimes)]
1592impl<'a> SelfBounds<'a> {
1593 const SIZED: Self = Self::All(&[Trait::Sized]);
1594}
1595
1596fn normalize_bounds(slf: Trait, bounds: &[TraitBound]) -> impl '_ + Iterator<Item = Trait> {
1598 bounds.iter().map(move |bound| match bound {
1599 TraitBound::Slf => slf,
1600 TraitBound::Other(trt) => *trt,
1601 })
1602}
1603
1604struct ImplBlockBuilder<'a, D: DataExt> {
1605 input: &'a DeriveInput,
1606 data: &'a D,
1607 trt: Trait,
1608 field_type_trait_bounds: FieldBounds<'a>,
1609 zerocopy_crate: &'a Path,
1610 self_type_trait_bounds: SelfBounds<'a>,
1611 padding_check: Option<PaddingCheck>,
1612 inner_extras: Option<TokenStream>,
1613 outer_extras: Option<TokenStream>,
1614}
1615
1616impl<'a, D: DataExt> ImplBlockBuilder<'a, D> {
1617 fn new(
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 {
1624 Self {
1625 input,
1626 data,
1627 trt,
1628 field_type_trait_bounds,
1629 zerocopy_crate,
1630 self_type_trait_bounds: SelfBounds::None,
1631 padding_check: None,
1632 inner_extras: None,
1633 outer_extras: None,
1634 }
1635 }
1636
1637 fn self_type_trait_bounds(mut self, self_type_trait_bounds: SelfBounds<'a>) -> Self {
1638 self.self_type_trait_bounds = self_type_trait_bounds;
1639 self
1640 }
1641
1642 fn padding_check<P: Into<Option<PaddingCheck>>>(mut self, padding_check: P) -> Self {
1643 self.padding_check = padding_check.into();
1644 self
1645 }
1646
1647 fn inner_extras(mut self, inner_extras: TokenStream) -> Self {
1648 self.inner_extras = Some(inner_extras);
1649 self
1650 }
1651
1652 fn outer_extras<T: Into<Option<TokenStream>>>(mut self, outer_extras: T) -> Self {
1653 self.outer_extras = outer_extras.into();
1654 self
1655 }
1656
1657 fn build(self) -> TokenStream {
1658 let type_ident = &self.input.ident;
1718 let trait_path = self.trt.crate_path(self.zerocopy_crate);
1719 let fields = self.data.fields();
1720 let variants = self.data.variants();
1721 let tag = self.data.tag();
1722 let zerocopy_crate = self.zerocopy_crate;
1723
1724 fn bound_tt(
1725 ty: &Type,
1726 traits: impl Iterator<Item = Trait>,
1727 zerocopy_crate: &Path,
1728 ) -> WherePredicate {
1729 let traits = traits.map(|t| t.crate_path(zerocopy_crate));
1730 parse_quote!(#ty: #(#traits)+*)
1731 }
1732 let field_type_bounds: Vec<_> = match (self.field_type_trait_bounds, &fields[..]) {
1733 (FieldBounds::All(traits), _) => fields
1734 .iter()
1735 .map(|(_vis, _name, ty)| {
1736 bound_tt(ty, normalize_bounds(self.trt, traits), zerocopy_crate)
1737 })
1738 .collect(),
1739 (FieldBounds::None, _) | (FieldBounds::Trailing(..), []) => vec![],
1740 (FieldBounds::Trailing(traits), [.., last]) => {
1741 vec![bound_tt(last.2, normalize_bounds(self.trt, traits), zerocopy_crate)]
1742 }
1743 (FieldBounds::Explicit(bounds), _) => bounds,
1744 };
1745
1746 #[allow(unstable_name_collisions)] let padding_check_bound = self
1749 .padding_check
1750 .and_then(|check| (!fields.is_empty()).then_some(check))
1751 .map(|check| {
1752 let variant_types = variants.iter().map(|var| {
1753 let types = var.iter().map(|(_vis, _name, ty)| ty);
1754 quote!([#(#types),*])
1755 });
1756 let validator_context = check.validator_macro_context();
1757 let validator_macro = check.validator_macro_ident();
1758 let t = tag.iter();
1759 parse_quote! {
1760 (): #zerocopy_crate::util::macro_util::PaddingFree<
1761 Self,
1762 {
1763 #validator_context
1764 #zerocopy_crate::#validator_macro!(Self, #(#t,)* #(#variant_types),*)
1765 }
1766 >
1767 }
1768 });
1769
1770 let self_bounds: Option<WherePredicate> = match self.self_type_trait_bounds {
1771 SelfBounds::None => None,
1772 SelfBounds::All(traits) => {
1773 Some(bound_tt(&parse_quote!(Self), traits.iter().copied(), zerocopy_crate))
1774 }
1775 };
1776
1777 let bounds = self
1778 .input
1779 .generics
1780 .where_clause
1781 .as_ref()
1782 .map(|where_clause| where_clause.predicates.iter())
1783 .into_iter()
1784 .flatten()
1785 .chain(field_type_bounds.iter())
1786 .chain(padding_check_bound.iter())
1787 .chain(self_bounds.iter());
1788
1789 let params = self.input.generics.params.clone().into_iter().map(|mut param| {
1791 match &mut param {
1792 GenericParam::Type(ty) => ty.default = None,
1793 GenericParam::Const(cnst) => cnst.default = None,
1794 GenericParam::Lifetime(_) => {}
1795 }
1796 quote!(#param)
1797 });
1798
1799 let param_idents = self.input.generics.params.iter().map(|param| match param {
1802 GenericParam::Type(ty) => {
1803 let ident = &ty.ident;
1804 quote!(#ident)
1805 }
1806 GenericParam::Lifetime(l) => {
1807 let ident = &l.lifetime;
1808 quote!(#ident)
1809 }
1810 GenericParam::Const(cnst) => {
1811 let ident = &cnst.ident;
1812 quote!({#ident})
1813 }
1814 });
1815
1816 let inner_extras = self.inner_extras;
1817 let impl_tokens = quote! {
1818 #[allow(deprecated)]
1821 #[automatically_derived]
1824 unsafe impl < #(#params),* > #trait_path for #type_ident < #(#param_idents),* >
1825 where
1826 #(#bounds,)*
1827 {
1828 fn only_derive_is_allowed_to_implement_this_trait() {}
1829
1830 #inner_extras
1831 }
1832 };
1833
1834 if let Some(outer_extras) = self.outer_extras {
1835 quote! {
1838 const _: () = {
1839 #impl_tokens
1840
1841 #outer_extras
1842 };
1843 }
1844 } else {
1845 impl_tokens
1846 }
1847 }
1848}
1849
1850#[allow(unused)]
1858trait BoolExt {
1859 fn then_some<T>(self, t: T) -> Option<T>;
1860}
1861
1862impl BoolExt for bool {
1863 fn then_some<T>(self, t: T) -> Option<T> {
1864 if self {
1865 Some(t)
1866 } else {
1867 None
1868 }
1869 }
1870}