1#![allow(unknown_lints)]
18#![deny(renamed_and_removed_lints)]
19#![deny(
20 clippy::all,
21 clippy::missing_safety_doc,
22 clippy::multiple_unsafe_ops_per_block,
23 clippy::undocumented_unsafe_blocks
24)]
25#![allow(clippy::type_complexity)]
27#![allow(clippy::uninlined_format_args)]
29#![deny(
30 rustdoc::bare_urls,
31 rustdoc::broken_intra_doc_links,
32 rustdoc::invalid_codeblock_attributes,
33 rustdoc::invalid_html_tags,
34 rustdoc::invalid_rust_codeblocks,
35 rustdoc::missing_crate_level_docs,
36 rustdoc::private_intra_doc_links
37)]
38#![recursion_limit = "128"]
39
40mod r#enum;
41mod ext;
42#[cfg(test)]
43mod output_tests;
44mod repr;
45
46use proc_macro2::{Span, TokenStream};
47use quote::{quote, ToTokens};
48use syn::{
49 parse_quote, spanned::Spanned as _, Attribute, Data, DataEnum, DataStruct, DataUnion,
50 DeriveInput, Error, Expr, ExprLit, ExprUnary, GenericParam, Ident, Lit, Meta, Path, Type, UnOp,
51 WherePredicate,
52};
53
54use crate::{ext::*, repr::*};
55
56macro_rules! derive {
80 ($trait:ident => $outer:ident => $inner:ident) => {
81 #[proc_macro_derive($trait, attributes(zerocopy))]
82 pub fn $outer(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
83 let ast = syn::parse_macro_input!(ts as DeriveInput);
84 let zerocopy_crate = match extract_zerocopy_crate(&ast.attrs) {
85 Ok(zerocopy_crate) => zerocopy_crate,
86 Err(e) => return e.into_compile_error().into(),
87 };
88 $inner(&ast, Trait::$trait, &zerocopy_crate).into_ts().into()
89 }
90 };
91}
92
93trait IntoTokenStream {
94 fn into_ts(self) -> TokenStream;
95}
96
97impl IntoTokenStream for TokenStream {
98 fn into_ts(self) -> TokenStream {
99 self
100 }
101}
102
103impl IntoTokenStream for Result<TokenStream, Error> {
104 fn into_ts(self) -> TokenStream {
105 match self {
106 Ok(ts) => ts,
107 Err(err) => err.to_compile_error(),
108 }
109 }
110}
111
112fn extract_zerocopy_crate(attrs: &[Attribute]) -> Result<Path, Error> {
115 let mut path = parse_quote!(::zerocopy);
116
117 for attr in attrs {
118 if let Meta::List(ref meta_list) = attr.meta {
119 if meta_list.path.is_ident("zerocopy") {
120 attr.parse_nested_meta(|meta| {
121 if meta.path.is_ident("crate") {
122 let expr = meta.value().and_then(|value| value.parse());
123 if let Ok(Expr::Lit(ExprLit { lit: Lit::Str(lit), .. })) = expr {
124 if let Ok(path_lit) = lit.parse() {
125 path = path_lit;
126 return Ok(());
127 }
128 }
129
130 return Err(Error::new(
131 Span::call_site(),
132 "`crate` attribute requires a path as the value",
133 ));
134 }
135
136 Err(Error::new(
137 Span::call_site(),
138 format!("unknown attribute encountered: {}", meta.path.into_token_stream()),
139 ))
140 })?;
141 }
142 }
143 }
144
145 Ok(path)
146}
147
148derive!(KnownLayout => derive_known_layout => derive_known_layout_inner);
149derive!(Immutable => derive_no_cell => derive_no_cell_inner);
150derive!(TryFromBytes => derive_try_from_bytes => derive_try_from_bytes_inner);
151derive!(FromZeros => derive_from_zeros => derive_from_zeros_inner);
152derive!(FromBytes => derive_from_bytes => derive_from_bytes_inner);
153derive!(IntoBytes => derive_into_bytes => derive_into_bytes_inner);
154derive!(Unaligned => derive_unaligned => derive_unaligned_inner);
155derive!(ByteHash => derive_hash => derive_hash_inner);
156derive!(ByteEq => derive_eq => derive_eq_inner);
157derive!(SplitAt => derive_split_at => derive_split_at_inner);
158
159#[deprecated(since = "0.8.0", note = "`FromZeroes` was renamed to `FromZeros`")]
161#[doc(hidden)]
162#[proc_macro_derive(FromZeroes)]
163pub fn derive_from_zeroes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
164 derive_from_zeros(ts)
165}
166
167#[deprecated(since = "0.8.0", note = "`AsBytes` was renamed to `IntoBytes`")]
169#[doc(hidden)]
170#[proc_macro_derive(AsBytes)]
171pub fn derive_as_bytes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
172 derive_into_bytes(ts)
173}
174
175fn derive_known_layout_inner(
176 ast: &DeriveInput,
177 _top_level: Trait,
178 zerocopy_crate: &Path,
179) -> Result<TokenStream, Error> {
180 let is_repr_c_struct = match &ast.data {
181 Data::Struct(..) => {
182 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
183 if repr.is_c() {
184 Some(repr)
185 } else {
186 None
187 }
188 }
189 Data::Enum(..) | Data::Union(..) => None,
190 };
191
192 let fields = ast.data.fields();
193
194 let (self_bounds, inner_extras, outer_extras) = if let (
195 Some(repr),
196 Some((trailing_field, leading_fields)),
197 ) = (is_repr_c_struct, fields.split_last())
198 {
199 let (_vis, trailing_field_name, trailing_field_ty) = trailing_field;
200 let leading_fields_tys = leading_fields.iter().map(|(_vis, _name, ty)| ty);
201
202 let core_path = quote!(#zerocopy_crate::util::macro_util::core_reexport);
203 let repr_align = repr
204 .get_align()
205 .map(|align| {
206 let align = align.t.get();
207 quote!(#core_path::num::NonZeroUsize::new(#align as usize))
208 })
209 .unwrap_or_else(|| quote!(#core_path::option::Option::None));
210 let repr_packed = repr
211 .get_packed()
212 .map(|packed| {
213 let packed = packed.get();
214 quote!(#core_path::num::NonZeroUsize::new(#packed as usize))
215 })
216 .unwrap_or_else(|| quote!(#core_path::option::Option::None));
217
218 let make_methods = |trailing_field_ty| {
219 quote! {
220 #[inline(always)]
251 fn raw_from_ptr_len(
252 bytes: #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<u8>,
253 meta: Self::PointerMetadata,
254 ) -> #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<Self> {
255 use #zerocopy_crate::KnownLayout;
256 let trailing = <#trailing_field_ty as KnownLayout>::raw_from_ptr_len(bytes, meta);
257 let slf = trailing.as_ptr() as *mut Self;
258 unsafe { #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull::new_unchecked(slf) }
260 }
261
262 #[inline(always)]
263 fn pointer_to_metadata(ptr: *mut Self) -> Self::PointerMetadata {
264 <#trailing_field_ty>::pointer_to_metadata(ptr as *mut _)
265 }
266 }
267 };
268
269 let inner_extras = {
270 let leading_fields_tys = leading_fields_tys.clone();
271 let methods = make_methods(*trailing_field_ty);
272 let (_, ty_generics, _) = ast.generics.split_for_impl();
273
274 quote!(
275 type PointerMetadata = <#trailing_field_ty as #zerocopy_crate::KnownLayout>::PointerMetadata;
276
277 type MaybeUninit = __ZerocopyKnownLayoutMaybeUninit #ty_generics;
278
279 const LAYOUT: #zerocopy_crate::DstLayout = {
294 use #zerocopy_crate::util::macro_util::core_reexport::num::NonZeroUsize;
295 use #zerocopy_crate::{DstLayout, KnownLayout};
296
297 DstLayout::for_repr_c_struct(
298 #repr_align,
299 #repr_packed,
300 &[
301 #(DstLayout::for_type::<#leading_fields_tys>(),)*
302 <#trailing_field_ty as KnownLayout>::LAYOUT
303 ],
304 )
305 };
306
307 #methods
308 )
309 };
310
311 let outer_extras = {
312 let ident = &ast.ident;
313 let vis = &ast.vis;
314 let params = &ast.generics.params;
315 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
316
317 let predicates = if let Some(where_clause) = where_clause {
318 where_clause.predicates.clone()
319 } else {
320 Default::default()
321 };
322
323 let field_index = |name: &TokenStream| {
326 let name = to_ident_str(name);
327 Ident::new(&format!("__Zerocopy_Field_{}", name), ident.span())
328 };
329
330 let field_indices: Vec<_> =
331 fields.iter().map(|(_vis, name, _ty)| field_index(name)).collect();
332
333 let field_defs = field_indices.iter().zip(&fields).map(|(idx, (vis, _, _))| {
335 quote! {
336 #[allow(non_camel_case_types)]
337 #vis struct #idx;
338 }
339 });
340
341 let field_impls = field_indices.iter().zip(&fields).map(|(idx, (_, _, ty))| quote! {
342 #[allow(deprecated)]
352 unsafe impl #impl_generics #zerocopy_crate::util::macro_util::Field<#idx> for #ident #ty_generics
353 where
354 #predicates
355 {
356 type Type = #ty;
357 }
358 });
359
360 let trailing_field_index = field_index(trailing_field_name);
361 let leading_field_indices =
362 leading_fields.iter().map(|(_vis, name, _ty)| field_index(name));
363
364 let trailing_field_ty = quote! {
369 <#ident #ty_generics as
370 #zerocopy_crate::util::macro_util::Field<#trailing_field_index>
371 >::Type
372 };
373
374 let methods = make_methods(&parse_quote! {
375 <#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit
376 });
377
378 quote! {
379 #(#field_defs)*
380
381 #(#field_impls)*
382
383 #repr
391 #[doc(hidden)]
392 #[allow(private_bounds)]
396 #[allow(deprecated)]
397 #vis struct __ZerocopyKnownLayoutMaybeUninit<#params> (
398 #(#zerocopy_crate::util::macro_util::core_reexport::mem::MaybeUninit<
399 <#ident #ty_generics as
400 #zerocopy_crate::util::macro_util::Field<#leading_field_indices>
401 >::Type
402 >,)*
403 #zerocopy_crate::util::macro_util::core_reexport::mem::ManuallyDrop<
410 <#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit
411 >
412 )
413 where
414 #trailing_field_ty: #zerocopy_crate::KnownLayout,
415 #predicates;
416
417 #[allow(deprecated)]
424 unsafe impl #impl_generics #zerocopy_crate::KnownLayout for __ZerocopyKnownLayoutMaybeUninit #ty_generics
425 where
426 #trailing_field_ty: #zerocopy_crate::KnownLayout,
427 #predicates
428 {
429 #[allow(clippy::missing_inline_in_public_items)]
430 fn only_derive_is_allowed_to_implement_this_trait() {}
431
432 type PointerMetadata = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::PointerMetadata;
433
434 type MaybeUninit = Self;
435
436 const LAYOUT: #zerocopy_crate::DstLayout = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::LAYOUT;
437
438 #methods
439 }
440 }
441 };
442
443 (SelfBounds::None, inner_extras, Some(outer_extras))
444 } else {
445 (
449 SelfBounds::SIZED,
450 quote!(
451 type PointerMetadata = ();
452 type MaybeUninit =
453 #zerocopy_crate::util::macro_util::core_reexport::mem::MaybeUninit<Self>;
454
455 const LAYOUT: #zerocopy_crate::DstLayout = #zerocopy_crate::DstLayout::for_type::<Self>();
459
460 #[inline(always)]
465 fn raw_from_ptr_len(
466 bytes: #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<u8>,
467 _meta: (),
468 ) -> #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<Self>
469 {
470 bytes.cast::<Self>()
471 }
472
473 #[inline(always)]
474 fn pointer_to_metadata(_ptr: *mut Self) -> () {}
475 ),
476 None,
477 )
478 };
479
480 Ok(match &ast.data {
481 Data::Struct(strct) => {
482 let require_trait_bound_on_field_types =
483 if matches!(self_bounds, SelfBounds::All(&[Trait::Sized])) {
484 FieldBounds::None
485 } else {
486 FieldBounds::TRAILING_SELF
487 };
488
489 ImplBlockBuilder::new(
494 ast,
495 strct,
496 Trait::KnownLayout,
497 require_trait_bound_on_field_types,
498 zerocopy_crate,
499 )
500 .self_type_trait_bounds(self_bounds)
501 .inner_extras(inner_extras)
502 .outer_extras(outer_extras)
503 .build()
504 }
505 Data::Enum(enm) => {
506 ImplBlockBuilder::new(ast, enm, Trait::KnownLayout, FieldBounds::None, zerocopy_crate)
509 .self_type_trait_bounds(SelfBounds::SIZED)
510 .inner_extras(inner_extras)
511 .outer_extras(outer_extras)
512 .build()
513 }
514 Data::Union(unn) => {
515 ImplBlockBuilder::new(ast, unn, Trait::KnownLayout, FieldBounds::None, zerocopy_crate)
518 .self_type_trait_bounds(SelfBounds::SIZED)
519 .inner_extras(inner_extras)
520 .outer_extras(outer_extras)
521 .build()
522 }
523 })
524}
525
526fn derive_no_cell_inner(
527 ast: &DeriveInput,
528 _top_level: Trait,
529 zerocopy_crate: &Path,
530) -> TokenStream {
531 match &ast.data {
532 Data::Struct(strct) => ImplBlockBuilder::new(
533 ast,
534 strct,
535 Trait::Immutable,
536 FieldBounds::ALL_SELF,
537 zerocopy_crate,
538 )
539 .build(),
540 Data::Enum(enm) => {
541 ImplBlockBuilder::new(ast, enm, Trait::Immutable, FieldBounds::ALL_SELF, zerocopy_crate)
542 .build()
543 }
544 Data::Union(unn) => {
545 ImplBlockBuilder::new(ast, unn, Trait::Immutable, FieldBounds::ALL_SELF, zerocopy_crate)
546 .build()
547 }
548 }
549}
550
551fn derive_try_from_bytes_inner(
552 ast: &DeriveInput,
553 top_level: Trait,
554 zerocopy_crate: &Path,
555) -> Result<TokenStream, Error> {
556 match &ast.data {
557 Data::Struct(strct) => derive_try_from_bytes_struct(ast, strct, top_level, zerocopy_crate),
558 Data::Enum(enm) => derive_try_from_bytes_enum(ast, enm, top_level, zerocopy_crate),
559 Data::Union(unn) => Ok(derive_try_from_bytes_union(ast, unn, top_level, zerocopy_crate)),
560 }
561}
562
563fn derive_from_zeros_inner(
564 ast: &DeriveInput,
565 top_level: Trait,
566 zerocopy_crate: &Path,
567) -> Result<TokenStream, Error> {
568 let try_from_bytes = derive_try_from_bytes_inner(ast, top_level, zerocopy_crate)?;
569 let from_zeros = match &ast.data {
570 Data::Struct(strct) => derive_from_zeros_struct(ast, strct, zerocopy_crate),
571 Data::Enum(enm) => derive_from_zeros_enum(ast, enm, zerocopy_crate)?,
572 Data::Union(unn) => derive_from_zeros_union(ast, unn, zerocopy_crate),
573 };
574 Ok(IntoIterator::into_iter([try_from_bytes, from_zeros]).collect())
575}
576
577fn derive_from_bytes_inner(
578 ast: &DeriveInput,
579 top_level: Trait,
580 zerocopy_crate: &Path,
581) -> Result<TokenStream, Error> {
582 let from_zeros = derive_from_zeros_inner(ast, top_level, zerocopy_crate)?;
583 let from_bytes = match &ast.data {
584 Data::Struct(strct) => derive_from_bytes_struct(ast, strct, zerocopy_crate),
585 Data::Enum(enm) => derive_from_bytes_enum(ast, enm, zerocopy_crate)?,
586 Data::Union(unn) => derive_from_bytes_union(ast, unn, zerocopy_crate),
587 };
588
589 Ok(IntoIterator::into_iter([from_zeros, from_bytes]).collect())
590}
591
592fn derive_into_bytes_inner(
593 ast: &DeriveInput,
594 _top_level: Trait,
595 zerocopy_crate: &Path,
596) -> Result<TokenStream, Error> {
597 match &ast.data {
598 Data::Struct(strct) => derive_into_bytes_struct(ast, strct, zerocopy_crate),
599 Data::Enum(enm) => derive_into_bytes_enum(ast, enm, zerocopy_crate),
600 Data::Union(unn) => derive_into_bytes_union(ast, unn, zerocopy_crate),
601 }
602}
603
604fn derive_unaligned_inner(
605 ast: &DeriveInput,
606 _top_level: Trait,
607 zerocopy_crate: &Path,
608) -> Result<TokenStream, Error> {
609 match &ast.data {
610 Data::Struct(strct) => derive_unaligned_struct(ast, strct, zerocopy_crate),
611 Data::Enum(enm) => derive_unaligned_enum(ast, enm, zerocopy_crate),
612 Data::Union(unn) => derive_unaligned_union(ast, unn, zerocopy_crate),
613 }
614}
615
616fn derive_hash_inner(
617 ast: &DeriveInput,
618 _top_level: Trait,
619 zerocopy_crate: &Path,
620) -> Result<TokenStream, Error> {
621 let type_ident = &ast.ident;
627 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
628 let where_predicates = where_clause.map(|clause| &clause.predicates);
629 Ok(quote! {
630 #[allow(deprecated)]
631 #[automatically_derived]
634 impl #impl_generics #zerocopy_crate::util::macro_util::core_reexport::hash::Hash for #type_ident #ty_generics
635 where
636 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
637 #where_predicates
638 {
639 fn hash<H>(&self, state: &mut H)
640 where
641 H: #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher,
642 {
643 #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher::write(
644 state,
645 #zerocopy_crate::IntoBytes::as_bytes(self)
646 )
647 }
648
649 fn hash_slice<H>(data: &[Self], state: &mut H)
650 where
651 H: #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher,
652 {
653 #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher::write(
654 state,
655 #zerocopy_crate::IntoBytes::as_bytes(data)
656 )
657 }
658 }
659 })
660}
661
662fn derive_eq_inner(
663 ast: &DeriveInput,
664 _top_level: Trait,
665 zerocopy_crate: &Path,
666) -> Result<TokenStream, Error> {
667 let type_ident = &ast.ident;
673 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
674 let where_predicates = where_clause.map(|clause| &clause.predicates);
675 Ok(quote! {
676 #[allow(deprecated)]
679 #[automatically_derived]
682 impl #impl_generics #zerocopy_crate::util::macro_util::core_reexport::cmp::PartialEq for #type_ident #ty_generics
683 where
684 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
685 #where_predicates
686 {
687 fn eq(&self, other: &Self) -> bool {
688 #zerocopy_crate::util::macro_util::core_reexport::cmp::PartialEq::eq(
689 #zerocopy_crate::IntoBytes::as_bytes(self),
690 #zerocopy_crate::IntoBytes::as_bytes(other),
691 )
692 }
693 }
694
695 #[allow(deprecated)]
698 #[automatically_derived]
701 impl #impl_generics #zerocopy_crate::util::macro_util::core_reexport::cmp::Eq for #type_ident #ty_generics
702 where
703 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
704 #where_predicates
705 {
706 }
707 })
708}
709
710fn derive_split_at_inner(
711 ast: &DeriveInput,
712 _top_level: Trait,
713 zerocopy_crate: &Path,
714) -> Result<TokenStream, Error> {
715 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
716
717 match &ast.data {
718 Data::Struct(_) => {}
719 Data::Enum(_) | Data::Union(_) => {
720 return Err(Error::new(Span::call_site(), "can only be applied to structs"));
721 }
722 };
723
724 if repr.get_packed().is_some() {
725 return Err(Error::new(Span::call_site(), "must not have #[repr(packed)] attribute"));
726 }
727
728 if !(repr.is_c() || repr.is_transparent()) {
729 return Err(Error::new(Span::call_site(), "must have #[repr(C)] or #[repr(transparent)] in order to guarantee this type's layout is splitable"));
730 }
731
732 let fields = ast.data.fields();
733 let trailing_field = if let Some(((_, _, trailing_field), _)) = fields.split_last() {
734 trailing_field
735 } else {
736 return Err(Error::new(Span::call_site(), "must at least one field"));
737 };
738
739 Ok(ImplBlockBuilder::new(
744 ast,
745 &ast.data,
746 Trait::SplitAt,
747 FieldBounds::TRAILING_SELF,
748 zerocopy_crate,
749 )
750 .inner_extras(quote! {
751 type Elem = <#trailing_field as ::zerocopy::SplitAt>::Elem;
752 })
753 .build())
754}
755
756fn derive_has_field_struct_union(
757 ast: &DeriveInput,
758 data: &dyn DataExt,
759 zerocopy_crate: &Path,
760) -> TokenStream {
761 let fields = ast.data.fields();
762 if fields.is_empty() {
763 return quote! {};
764 }
765
766 let field_tokens = fields.iter().map(|(vis, ident, _)| {
767 let ident = Ident::new(&format!("ẕ{}", ident), ident.span());
768 quote!(
769 #vis enum #ident {}
770 )
771 });
772
773 let variant_id: Box<Expr> = match &ast.data {
774 Data::Struct(_) => parse_quote!({ #zerocopy_crate::STRUCT_VARIANT_ID }),
775 Data::Union(_) => parse_quote!({ #zerocopy_crate::UNION_VARIANT_ID }),
776 _ => unreachable!(),
777 };
778
779 let is_repr_c_union = match &ast.data {
780 Data::Union(..) => {
781 StructUnionRepr::from_attrs(&ast.attrs).map(|repr| repr.is_c()).unwrap_or(false)
782 }
783 Data::Enum(..) | Data::Struct(..) => false,
784 };
785 let has_fields = fields.iter().map(move |(_, ident, ty)| {
786 let field_token = Ident::new(&format!("ẕ{}", ident), ident.span());
787 let field: Box<Type> = parse_quote!(#field_token);
788 let field_id: Box<Expr> = parse_quote!({ #zerocopy_crate::ident_id!(#ident) });
789 ImplBlockBuilder::new(
790 ast,
791 data,
792 Trait::HasField {
793 variant_id: variant_id.clone(),
794 field: field.clone(),
795 field_id: field_id.clone(),
796 },
797 FieldBounds::None,
798 zerocopy_crate,
799 )
800 .inner_extras(quote! {
801 type Type = #ty;
802
803 #[inline(always)]
804 fn project(slf: #zerocopy_crate::pointer::PtrInner<'_, Self>) -> *mut Self::Type {
805 let slf = slf.as_ptr();
806 unsafe { #zerocopy_crate::util::macro_util::core_reexport::ptr::addr_of_mut!((*slf).#ident) }
813 }
814 }).outer_extras(if is_repr_c_union {
815 let ident = &ast.ident;
816 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
817 quote! {
818 unsafe impl #impl_generics #zerocopy_crate::pointer::cast::Cast<#ident #ty_generics, #ty>
855 for #zerocopy_crate::pointer::cast::Projection<#field, { #zerocopy_crate::UNION_VARIANT_ID }, #field_id>
856 #where_clause
857 {
858 }
859 }
860 } else {
861 quote! {}
862 })
863 .build()
864 });
865
866 quote! {
867 #[allow(non_camel_case_types)]
868 const _: () = {
869 #(#field_tokens)*
870 #(#has_fields)*
871 };
872 }
873}
874
875fn derive_try_from_bytes_struct(
878 ast: &DeriveInput,
879 strct: &DataStruct,
880 top_level: Trait,
881 zerocopy_crate: &Path,
882) -> Result<TokenStream, Error> {
883 let extras =
884 try_gen_trivial_is_bit_valid(ast, top_level, zerocopy_crate).unwrap_or_else(|| {
885 let fields = strct.fields();
886 let field_names = fields.iter().map(|(_vis, name, _ty)| name);
887 let field_tys = fields.iter().map(|(_vis, _name, ty)| ty);
888 quote!(
889 fn is_bit_valid<___ZerocopyAliasing>(
895 mut candidate: #zerocopy_crate::Maybe<Self, ___ZerocopyAliasing>,
896 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
897 where
898 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
899 {
900 use #zerocopy_crate::util::macro_util::core_reexport;
901 use #zerocopy_crate::pointer::PtrInner;
902
903 true #(&& {
904 let field_candidate = candidate.reborrow().project::<
905 _,
906 { #zerocopy_crate::ident_id!(#field_names) }
907 >();
908
909 <#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate)
910 })*
911 }
912 )
913 });
914 Ok(ImplBlockBuilder::new(
915 ast,
916 strct,
917 Trait::TryFromBytes,
918 FieldBounds::ALL_SELF,
919 zerocopy_crate,
920 )
921 .inner_extras(extras)
922 .outer_extras(derive_has_field_struct_union(ast, strct, zerocopy_crate))
923 .build())
924}
925
926fn derive_try_from_bytes_union(
929 ast: &DeriveInput,
930 unn: &DataUnion,
931 top_level: Trait,
932 zerocopy_crate: &Path,
933) -> TokenStream {
934 let field_type_trait_bounds =
936 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
937 let extras =
938 try_gen_trivial_is_bit_valid(ast, top_level, zerocopy_crate).unwrap_or_else(|| {
939 let fields = unn.fields();
940 let field_names = fields.iter().map(|(_vis, name, _ty)| name);
941 let field_tys = fields.iter().map(|(_vis, _name, ty)| ty);
942 quote!(
943 fn is_bit_valid<___ZerocopyAliasing>(
949 mut candidate: #zerocopy_crate::Maybe<'_, Self,___ZerocopyAliasing>
950 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
951 where
952 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
953 {
954 use #zerocopy_crate::util::macro_util::core_reexport;
955 use #zerocopy_crate::pointer::PtrInner;
956
957 false #(|| {
958 let field_candidate = unsafe {
967 candidate.reborrow().project_transmute_unchecked::<
968 _,
969 _,
970 #zerocopy_crate::pointer::cast::Projection<_, { #zerocopy_crate::UNION_VARIANT_ID }, { #zerocopy_crate::ident_id!(#field_names) }>
971 >()
972 };
973
974 <#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate)
975 })*
976 }
977 )
978 });
979 ImplBlockBuilder::new(ast, unn, Trait::TryFromBytes, field_type_trait_bounds, zerocopy_crate)
980 .inner_extras(extras)
981 .outer_extras(derive_has_field_struct_union(ast, unn, zerocopy_crate))
982 .build()
983}
984
985fn derive_try_from_bytes_enum(
986 ast: &DeriveInput,
987 enm: &DataEnum,
988 top_level: Trait,
989 zerocopy_crate: &Path,
990) -> Result<TokenStream, Error> {
991 let repr = EnumRepr::from_attrs(&ast.attrs)?;
992
993 let could_be_from_bytes = enum_size_from_repr(&repr)
999 .map(|size| enm.fields().is_empty() && enm.variants.len() == 1usize << size)
1000 .unwrap_or(false);
1001
1002 let trivial_is_bit_valid = try_gen_trivial_is_bit_valid(ast, top_level, zerocopy_crate);
1003 let extra = match (trivial_is_bit_valid, could_be_from_bytes) {
1004 (Some(is_bit_valid), _) => is_bit_valid,
1005 (None, true) => unsafe { gen_trivial_is_bit_valid_unchecked(zerocopy_crate) },
1008 (None, false) => {
1009 r#enum::derive_is_bit_valid(ast, &ast.ident, &repr, &ast.generics, enm, zerocopy_crate)?
1010 }
1011 };
1012
1013 Ok(ImplBlockBuilder::new(ast, enm, Trait::TryFromBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1014 .inner_extras(extra)
1015 .build())
1016}
1017
1018fn try_gen_trivial_is_bit_valid(
1040 ast: &DeriveInput,
1041 top_level: Trait,
1042 zerocopy_crate: &Path,
1043) -> Option<proc_macro2::TokenStream> {
1044 if matches!(top_level, Trait::FromBytes) && ast.generics.params.is_empty() {
1053 Some(quote!(
1054 fn is_bit_valid<___ZerocopyAliasing>(
1056 _candidate: #zerocopy_crate::Maybe<Self, ___ZerocopyAliasing>,
1057 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
1058 where
1059 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
1060 {
1061 if false {
1062 fn assert_is_from_bytes<T>()
1063 where
1064 T: #zerocopy_crate::FromBytes,
1065 T: ?#zerocopy_crate::util::macro_util::core_reexport::marker::Sized,
1066 {
1067 }
1068
1069 assert_is_from_bytes::<Self>();
1070 }
1071
1072 true
1076 }
1077 ))
1078 } else {
1079 None
1080 }
1081}
1082
1083unsafe fn gen_trivial_is_bit_valid_unchecked(zerocopy_crate: &Path) -> proc_macro2::TokenStream {
1095 quote!(
1096 fn is_bit_valid<___ZerocopyAliasing>(
1099 _candidate: #zerocopy_crate::Maybe<Self, ___ZerocopyAliasing>,
1100 ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
1101 where
1102 ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
1103 {
1104 true
1105 }
1106 )
1107}
1108
1109fn derive_from_zeros_struct(
1112 ast: &DeriveInput,
1113 strct: &DataStruct,
1114 zerocopy_crate: &Path,
1115) -> TokenStream {
1116 ImplBlockBuilder::new(ast, strct, Trait::FromZeros, FieldBounds::ALL_SELF, zerocopy_crate)
1117 .build()
1118}
1119
1120fn find_zero_variant(enm: &DataEnum) -> Result<usize, bool> {
1126 let mut next_negative_discriminant = Some(0);
1134
1135 let mut has_unknown_discriminants = false;
1142
1143 for (i, v) in enm.variants.iter().enumerate() {
1144 match v.discriminant.as_ref() {
1145 None => {
1147 match next_negative_discriminant.as_mut() {
1148 Some(0) => return Ok(i),
1149 Some(n) => *n -= 1,
1151 None => (),
1152 }
1153 }
1154 Some((_, Expr::Lit(ExprLit { lit: Lit::Int(int), .. }))) => {
1156 match int.base10_parse::<u128>().ok() {
1157 Some(0) => return Ok(i),
1158 Some(_) => next_negative_discriminant = None,
1159 None => {
1160 has_unknown_discriminants = true;
1162 next_negative_discriminant = None;
1163 }
1164 }
1165 }
1166 Some((_, Expr::Unary(ExprUnary { op: UnOp::Neg(_), expr, .. }))) => match &**expr {
1168 Expr::Lit(ExprLit { lit: Lit::Int(int), .. }) => {
1169 match int.base10_parse::<u128>().ok() {
1170 Some(0) => return Ok(i),
1171 Some(x) => next_negative_discriminant = Some(x - 1),
1173 None => {
1174 has_unknown_discriminants = true;
1177 next_negative_discriminant = None;
1178 }
1179 }
1180 }
1181 _ => {
1183 has_unknown_discriminants = true;
1184 next_negative_discriminant = None;
1185 }
1186 },
1187 _ => {
1189 has_unknown_discriminants = true;
1190 next_negative_discriminant = None;
1191 }
1192 }
1193 }
1194
1195 Err(has_unknown_discriminants)
1196}
1197
1198fn derive_from_zeros_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 match repr {
1211 Repr::Compound(
1212 Spanned { t: CompoundRepr::C | CompoundRepr::Primitive(_), span: _ },
1213 _,
1214 ) => {}
1215 Repr::Transparent(_)
1216 | 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")),
1217 }
1218
1219 let zero_variant = match find_zero_variant(enm) {
1220 Ok(index) => enm.variants.iter().nth(index).unwrap(),
1221 Err(true) => {
1223 return Err(Error::new_spanned(
1224 ast,
1225 "FromZeros only supported on enums with a variant that has a discriminant of `0`\n\
1226 help: This enum has discriminants which are not literal integers. One of those may \
1227 define or imply which variant has a discriminant of zero. Use a literal integer to \
1228 define or imply the variant with a discriminant of zero.",
1229 ));
1230 }
1231 Err(false) => {
1233 return Err(Error::new_spanned(
1234 ast,
1235 "FromZeros only supported on enums with a variant that has a discriminant of `0`",
1236 ));
1237 }
1238 };
1239
1240 let explicit_bounds = zero_variant
1241 .fields
1242 .iter()
1243 .map(|field| {
1244 let ty = &field.ty;
1245 parse_quote! { #ty: #zerocopy_crate::FromZeros }
1246 })
1247 .collect::<Vec<WherePredicate>>();
1248
1249 Ok(ImplBlockBuilder::new(
1250 ast,
1251 enm,
1252 Trait::FromZeros,
1253 FieldBounds::Explicit(explicit_bounds),
1254 zerocopy_crate,
1255 )
1256 .build())
1257}
1258
1259fn derive_from_zeros_union(
1262 ast: &DeriveInput,
1263 unn: &DataUnion,
1264 zerocopy_crate: &Path,
1265) -> TokenStream {
1266 let field_type_trait_bounds =
1269 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
1270 ImplBlockBuilder::new(ast, unn, Trait::FromZeros, field_type_trait_bounds, zerocopy_crate)
1271 .build()
1272}
1273
1274fn derive_from_bytes_struct(
1277 ast: &DeriveInput,
1278 strct: &DataStruct,
1279 zerocopy_crate: &Path,
1280) -> TokenStream {
1281 ImplBlockBuilder::new(ast, strct, Trait::FromBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1282 .build()
1283}
1284
1285fn derive_from_bytes_enum(
1301 ast: &DeriveInput,
1302 enm: &DataEnum,
1303 zerocopy_crate: &Path,
1304) -> Result<TokenStream, Error> {
1305 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1306
1307 let variants_required = 1usize << enum_size_from_repr(&repr)?;
1308 if enm.variants.len() != variants_required {
1309 return Err(Error::new_spanned(
1310 ast,
1311 format!(
1312 "FromBytes only supported on {} enum with {} variants",
1313 repr.repr_type_name(),
1314 variants_required
1315 ),
1316 ));
1317 }
1318
1319 Ok(ImplBlockBuilder::new(ast, enm, Trait::FromBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1320 .build())
1321}
1322
1323fn enum_size_from_repr(repr: &EnumRepr) -> Result<usize, Error> {
1325 use CompoundRepr::*;
1326 use PrimitiveRepr::*;
1327 use Repr::*;
1328 match repr {
1329 Transparent(span)
1330 | Compound(
1331 Spanned { t: C | Rust | Primitive(U32 | I32 | U64 | I64 | U128 | I128 | Usize | Isize), span },
1332 _,
1333 ) => Err(Error::new(*span, "`FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16`")),
1334 Compound(Spanned { t: Primitive(U8 | I8), span: _ }, _align) => Ok(8),
1335 Compound(Spanned { t: Primitive(U16 | I16), span: _ }, _align) => Ok(16),
1336 }
1337}
1338
1339fn derive_from_bytes_union(
1342 ast: &DeriveInput,
1343 unn: &DataUnion,
1344 zerocopy_crate: &Path,
1345) -> TokenStream {
1346 let field_type_trait_bounds =
1349 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
1350 ImplBlockBuilder::new(ast, unn, Trait::FromBytes, field_type_trait_bounds, zerocopy_crate)
1351 .build()
1352}
1353
1354fn derive_into_bytes_struct(
1355 ast: &DeriveInput,
1356 strct: &DataStruct,
1357 zerocopy_crate: &Path,
1358) -> Result<TokenStream, Error> {
1359 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1360
1361 let is_transparent = repr.is_transparent();
1362 let is_c = repr.is_c();
1363 let is_packed_1 = repr.is_packed_1();
1364 let num_fields = strct.fields().len();
1365
1366 let (padding_check, require_unaligned_fields) = if is_transparent || is_packed_1 {
1367 (None, false)
1384 } else if is_c && !repr.is_align_gt_1() && num_fields <= 1 {
1385 (None, false)
1389 } else if ast.generics.params.is_empty() {
1390 let is_syntactic_dst =
1392 strct.fields().last().map(|(_, _, ty)| matches!(ty, Type::Slice(_))).unwrap_or(false);
1393 if is_c && is_syntactic_dst {
1406 (Some(PaddingCheck::ReprCStruct), false)
1407 } else {
1408 (Some(PaddingCheck::Struct), false)
1409 }
1410 } else if is_c && !repr.is_align_gt_1() {
1411 (None, true)
1420 } else {
1421 return Err(Error::new(Span::call_site(), "must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout"));
1422 };
1423
1424 let field_bounds = if require_unaligned_fields {
1425 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Unaligned)])
1426 } else {
1427 FieldBounds::ALL_SELF
1428 };
1429
1430 Ok(ImplBlockBuilder::new(ast, strct, Trait::IntoBytes, field_bounds, zerocopy_crate)
1431 .padding_check(padding_check)
1432 .build())
1433}
1434
1435fn derive_into_bytes_enum(
1441 ast: &DeriveInput,
1442 enm: &DataEnum,
1443 zerocopy_crate: &Path,
1444) -> Result<TokenStream, Error> {
1445 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1446 if !repr.is_c() && !repr.is_primitive() {
1447 return Err(Error::new(Span::call_site(), "must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout"));
1448 }
1449
1450 let tag_type_definition = r#enum::generate_tag_enum(&repr, enm);
1451 Ok(ImplBlockBuilder::new(ast, enm, Trait::IntoBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1452 .padding_check(PaddingCheck::Enum { tag_type_definition })
1453 .build())
1454}
1455
1456fn derive_into_bytes_union(
1461 ast: &DeriveInput,
1462 unn: &DataUnion,
1463 zerocopy_crate: &Path,
1464) -> Result<TokenStream, Error> {
1465 let cfg_compile_error = if cfg!(zerocopy_derive_union_into_bytes) {
1474 quote!()
1475 } else {
1476 let error_message = "requires --cfg zerocopy_derive_union_into_bytes;
1477please let us know you use this feature: https://github.com/google/zerocopy/discussions/1802";
1478 quote!(
1479 const _: () = {
1480 #[cfg(not(zerocopy_derive_union_into_bytes))]
1481 #zerocopy_crate::util::macro_util::core_reexport::compile_error!(#error_message);
1482 };
1483 )
1484 };
1485
1486 if !ast.generics.params.is_empty() {
1488 return Err(Error::new(Span::call_site(), "unsupported on types with type parameters"));
1489 }
1490
1491 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1496 if !repr.is_c() && !repr.is_transparent() && !repr.is_packed_1() {
1497 return Err(Error::new(
1498 Span::call_site(),
1499 "must be #[repr(C)], #[repr(packed)], or #[repr(transparent)]",
1500 ));
1501 }
1502
1503 let impl_block =
1504 ImplBlockBuilder::new(ast, unn, Trait::IntoBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1505 .padding_check(PaddingCheck::Union)
1506 .build();
1507 Ok(quote!(#cfg_compile_error #impl_block))
1508}
1509
1510fn derive_unaligned_struct(
1516 ast: &DeriveInput,
1517 strct: &DataStruct,
1518 zerocopy_crate: &Path,
1519) -> Result<TokenStream, Error> {
1520 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1521 repr.unaligned_validate_no_align_gt_1()?;
1522
1523 let field_bounds = if repr.is_packed_1() {
1524 FieldBounds::None
1525 } else if repr.is_c() || repr.is_transparent() {
1526 FieldBounds::ALL_SELF
1527 } else {
1528 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"));
1529 };
1530
1531 Ok(ImplBlockBuilder::new(ast, strct, Trait::Unaligned, field_bounds, zerocopy_crate).build())
1532}
1533
1534fn derive_unaligned_enum(
1538 ast: &DeriveInput,
1539 enm: &DataEnum,
1540 zerocopy_crate: &Path,
1541) -> Result<TokenStream, Error> {
1542 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1543 repr.unaligned_validate_no_align_gt_1()?;
1544
1545 if !repr.is_u8() && !repr.is_i8() {
1546 return Err(Error::new(Span::call_site(), "must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment"));
1547 }
1548
1549 Ok(ImplBlockBuilder::new(ast, enm, Trait::Unaligned, FieldBounds::ALL_SELF, zerocopy_crate)
1550 .build())
1551}
1552
1553fn derive_unaligned_union(
1559 ast: &DeriveInput,
1560 unn: &DataUnion,
1561 zerocopy_crate: &Path,
1562) -> Result<TokenStream, Error> {
1563 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1564 repr.unaligned_validate_no_align_gt_1()?;
1565
1566 let field_type_trait_bounds = if repr.is_packed_1() {
1567 FieldBounds::None
1568 } else if repr.is_c() || repr.is_transparent() {
1569 FieldBounds::ALL_SELF
1570 } else {
1571 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"));
1572 };
1573
1574 Ok(ImplBlockBuilder::new(ast, unn, Trait::Unaligned, field_type_trait_bounds, zerocopy_crate)
1575 .build())
1576}
1577
1578enum PaddingCheck {
1581 Struct,
1584 ReprCStruct,
1586 Union,
1588 Enum { tag_type_definition: TokenStream },
1593}
1594
1595impl PaddingCheck {
1596 fn validator_trait_and_macro_idents(&self) -> (Ident, Ident) {
1599 let (trt, mcro) = match self {
1600 PaddingCheck::Struct => ("PaddingFree", "struct_padding"),
1601 PaddingCheck::ReprCStruct => ("DynamicPaddingFree", "repr_c_struct_has_padding"),
1602 PaddingCheck::Union => ("PaddingFree", "union_padding"),
1603 PaddingCheck::Enum { .. } => ("PaddingFree", "enum_padding"),
1604 };
1605
1606 let trt = Ident::new(trt, Span::call_site());
1607 let mcro = Ident::new(mcro, Span::call_site());
1608 (trt, mcro)
1609 }
1610
1611 fn validator_macro_context(&self) -> Option<&TokenStream> {
1614 match self {
1615 PaddingCheck::Struct | PaddingCheck::ReprCStruct | PaddingCheck::Union => None,
1616 PaddingCheck::Enum { tag_type_definition } => Some(tag_type_definition),
1617 }
1618 }
1619}
1620
1621#[derive(Clone)]
1622enum Trait {
1623 KnownLayout,
1624 HasField { variant_id: Box<Expr>, field: Box<Type>, field_id: Box<Expr> },
1625 Immutable,
1626 TryFromBytes,
1627 FromZeros,
1628 FromBytes,
1629 IntoBytes,
1630 Unaligned,
1631 Sized,
1632 ByteHash,
1633 ByteEq,
1634 SplitAt,
1635}
1636
1637impl ToTokens for Trait {
1638 fn to_tokens(&self, tokens: &mut TokenStream) {
1639 let s = match self {
1649 Trait::HasField { .. } => "HasField",
1650 Trait::KnownLayout => "KnownLayout",
1651 Trait::Immutable => "Immutable",
1652 Trait::TryFromBytes => "TryFromBytes",
1653 Trait::FromZeros => "FromZeros",
1654 Trait::FromBytes => "FromBytes",
1655 Trait::IntoBytes => "IntoBytes",
1656 Trait::Unaligned => "Unaligned",
1657 Trait::Sized => "Sized",
1658 Trait::ByteHash => "ByteHash",
1659 Trait::ByteEq => "ByteEq",
1660 Trait::SplitAt => "SplitAt",
1661 };
1662 let ident = Ident::new(s, Span::call_site());
1663 let arguments: Option<syn::AngleBracketedGenericArguments> = match self {
1664 Trait::HasField { variant_id, field, field_id } => {
1665 Some(parse_quote!(<#field, #variant_id, #field_id>))
1666 }
1667 Trait::KnownLayout
1668 | Trait::Immutable
1669 | Trait::TryFromBytes
1670 | Trait::FromZeros
1671 | Trait::FromBytes
1672 | Trait::IntoBytes
1673 | Trait::Unaligned
1674 | Trait::Sized
1675 | Trait::ByteHash
1676 | Trait::ByteEq
1677 | Trait::SplitAt => None,
1678 };
1679 tokens.extend(quote!(#ident #arguments));
1680 }
1681}
1682
1683impl Trait {
1684 fn crate_path(&self, zerocopy_crate: &Path) -> Path {
1685 match self {
1686 Self::Sized => {
1687 parse_quote!(#zerocopy_crate::util::macro_util::core_reexport::marker::#self)
1688 }
1689 _ => parse_quote!(#zerocopy_crate::#self),
1690 }
1691 }
1692}
1693
1694enum TraitBound {
1695 Slf,
1696 Other(Trait),
1697}
1698
1699enum FieldBounds<'a> {
1700 None,
1701 All(&'a [TraitBound]),
1702 Trailing(&'a [TraitBound]),
1703 Explicit(Vec<WherePredicate>),
1704}
1705
1706impl<'a> FieldBounds<'a> {
1707 const ALL_SELF: FieldBounds<'a> = FieldBounds::All(&[TraitBound::Slf]);
1708 const TRAILING_SELF: FieldBounds<'a> = FieldBounds::Trailing(&[TraitBound::Slf]);
1709}
1710
1711enum SelfBounds<'a> {
1712 None,
1713 All(&'a [Trait]),
1714}
1715
1716#[allow(clippy::needless_lifetimes)]
1719impl<'a> SelfBounds<'a> {
1720 const SIZED: Self = Self::All(&[Trait::Sized]);
1721}
1722
1723fn normalize_bounds<'a>(
1725 slf: &'a Trait,
1726 bounds: &'a [TraitBound],
1727) -> impl 'a + Iterator<Item = Trait> {
1728 bounds.iter().map(move |bound| match bound {
1729 TraitBound::Slf => slf.clone(),
1730 TraitBound::Other(trt) => trt.clone(),
1731 })
1732}
1733
1734struct ImplBlockBuilder<'a> {
1735 input: &'a DeriveInput,
1736 data: &'a dyn DataExt,
1737 trt: Trait,
1738 field_type_trait_bounds: FieldBounds<'a>,
1739 zerocopy_crate: &'a Path,
1740 self_type_trait_bounds: SelfBounds<'a>,
1741 padding_check: Option<PaddingCheck>,
1742 inner_extras: Option<TokenStream>,
1743 outer_extras: Option<TokenStream>,
1744}
1745
1746impl<'a> ImplBlockBuilder<'a> {
1747 fn new(
1748 input: &'a DeriveInput,
1749 data: &'a dyn DataExt,
1750 trt: Trait,
1751 field_type_trait_bounds: FieldBounds<'a>,
1752 zerocopy_crate: &'a Path,
1753 ) -> Self {
1754 Self {
1755 input,
1756 data,
1757 trt,
1758 field_type_trait_bounds,
1759 zerocopy_crate,
1760 self_type_trait_bounds: SelfBounds::None,
1761 padding_check: None,
1762 inner_extras: None,
1763 outer_extras: None,
1764 }
1765 }
1766
1767 fn self_type_trait_bounds(mut self, self_type_trait_bounds: SelfBounds<'a>) -> Self {
1768 self.self_type_trait_bounds = self_type_trait_bounds;
1769 self
1770 }
1771
1772 fn padding_check<P: Into<Option<PaddingCheck>>>(mut self, padding_check: P) -> Self {
1773 self.padding_check = padding_check.into();
1774 self
1775 }
1776
1777 fn inner_extras(mut self, inner_extras: TokenStream) -> Self {
1778 self.inner_extras = Some(inner_extras);
1779 self
1780 }
1781
1782 fn outer_extras<T: Into<Option<TokenStream>>>(mut self, outer_extras: T) -> Self {
1783 self.outer_extras = outer_extras.into();
1784 self
1785 }
1786
1787 fn build(self) -> TokenStream {
1788 let type_ident = &self.input.ident;
1848 let trait_path = self.trt.crate_path(self.zerocopy_crate);
1849 let fields = self.data.fields();
1850 let variants = self.data.variants();
1851 let tag = self.data.tag();
1852 let zerocopy_crate = self.zerocopy_crate;
1853
1854 fn bound_tt(
1855 ty: &Type,
1856 traits: impl Iterator<Item = Trait>,
1857 zerocopy_crate: &Path,
1858 ) -> WherePredicate {
1859 let traits = traits.map(|t| t.crate_path(zerocopy_crate));
1860 parse_quote!(#ty: #(#traits)+*)
1861 }
1862 let field_type_bounds: Vec<_> = match (self.field_type_trait_bounds, &fields[..]) {
1863 (FieldBounds::All(traits), _) => fields
1864 .iter()
1865 .map(|(_vis, _name, ty)| {
1866 bound_tt(ty, normalize_bounds(&self.trt, traits), zerocopy_crate)
1867 })
1868 .collect(),
1869 (FieldBounds::None, _) | (FieldBounds::Trailing(..), []) => vec![],
1870 (FieldBounds::Trailing(traits), [.., last]) => {
1871 vec![bound_tt(last.2, normalize_bounds(&self.trt, traits), zerocopy_crate)]
1872 }
1873 (FieldBounds::Explicit(bounds), _) => bounds,
1874 };
1875
1876 #[allow(unstable_name_collisions)] let padding_check_bound = self
1879 .padding_check
1880 .and_then(|check| (!fields.is_empty()).then_some(check))
1881 .map(|check| {
1882 let variant_types = variants.iter().map(|(_, fields)| {
1883 let types = fields.iter().map(|(_vis, _name, ty)| ty);
1884 quote!([#((#types)),*])
1885 });
1886 let validator_context = check.validator_macro_context();
1887 let (trt, validator_macro) = check.validator_trait_and_macro_idents();
1888 let t = tag.iter();
1889 parse_quote! {
1890 (): #zerocopy_crate::util::macro_util::#trt<
1891 Self,
1892 {
1893 #validator_context
1894 #zerocopy_crate::#validator_macro!(Self, #(#t,)* #(#variant_types),*)
1895 }
1896 >
1897 }
1898 });
1899
1900 let self_bounds: Option<WherePredicate> = match self.self_type_trait_bounds {
1901 SelfBounds::None => None,
1902 SelfBounds::All(traits) => {
1903 Some(bound_tt(&parse_quote!(Self), traits.iter().cloned(), zerocopy_crate))
1904 }
1905 };
1906
1907 let bounds = self
1908 .input
1909 .generics
1910 .where_clause
1911 .as_ref()
1912 .map(|where_clause| where_clause.predicates.iter())
1913 .into_iter()
1914 .flatten()
1915 .chain(field_type_bounds.iter())
1916 .chain(padding_check_bound.iter())
1917 .chain(self_bounds.iter());
1918
1919 let params = self.input.generics.params.clone().into_iter().map(|mut param| {
1921 match &mut param {
1922 GenericParam::Type(ty) => ty.default = None,
1923 GenericParam::Const(cnst) => cnst.default = None,
1924 GenericParam::Lifetime(_) => {}
1925 }
1926 quote!(#param)
1927 });
1928
1929 let param_idents = self.input.generics.params.iter().map(|param| match param {
1932 GenericParam::Type(ty) => {
1933 let ident = &ty.ident;
1934 quote!(#ident)
1935 }
1936 GenericParam::Lifetime(l) => {
1937 let ident = &l.lifetime;
1938 quote!(#ident)
1939 }
1940 GenericParam::Const(cnst) => {
1941 let ident = &cnst.ident;
1942 quote!({#ident})
1943 }
1944 });
1945
1946 let inner_extras = self.inner_extras;
1947 let impl_tokens = quote! {
1948 #[allow(deprecated, non_local_definitions)]
1949 #[automatically_derived]
1952 unsafe impl < #(#params),* > #trait_path for #type_ident < #(#param_idents),* >
1953 where
1954 #(#bounds,)*
1955 {
1956 fn only_derive_is_allowed_to_implement_this_trait() {}
1957
1958 #inner_extras
1959 }
1960 };
1961
1962 if let Some(outer_extras) = self.outer_extras.filter(|e| !e.is_empty()) {
1963 quote! {
1966 #[allow(deprecated, non_local_definitions)]
1967 #[automatically_derived]
1970 const _: () = {
1971 #impl_tokens
1972
1973 #outer_extras
1974 };
1975 }
1976 } else {
1977 impl_tokens
1978 }
1979 }
1980}
1981
1982#[allow(unused)]
1990trait BoolExt {
1991 fn then_some<T>(self, t: T) -> Option<T>;
1992}
1993
1994impl BoolExt for bool {
1995 fn then_some<T>(self, t: T) -> Option<T> {
1996 if self {
1997 Some(t)
1998 } else {
1999 None
2000 }
2001 }
2002}