// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT//// Copyright 2019 The Fuchsia Authors//// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.// This file may not be copied, modified, or distributed except according to// those terms. use std::num::NonZeroU32; use proc_macro2::{Span, TokenStream};use quote::{quote, quote_spanned, ToTokens};use syn::{ parse_quote, spanned::Spanned as _, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Error, Expr, ExprLit, Field, GenericParam, Ident, Index, Lit, LitStr, Meta, Path, Type, Variant, Visibility, WherePredicate,}; use crate::repr::{CompoundRepr, EnumRepr, PrimitiveRepr, Repr, Spanned}; pub(crate) struct Ctx { pub(crate) ast: DeriveInput, pub(crate) zerocopy_crate: Path, // The value of the last `#[zerocopy(on_error = ...)]` attribute, or `false` // if none is provided. pub(crate) skip_on_error: bool, // The span of the last `#[zerocopy(on_error = ...)]` attribute, if any. pub(crate) on_error_span: Option<proc_macro2::Span>,} impl Ctx { /// Attempt to extract a crate path from the provided attributes. Defaults to /// `::zerocopy` if not found. pub(crate) fn try_from_derive_input(ast: DeriveInput) -> Result<Self, Error> { let mut path = parse_quote!(::zerocopy); let mut skip_on_error = false; let mut on_error_span = None; for attr in &ast.attrs { if let Meta::List(ref meta_list) = attr.meta { if meta_list.path.is_ident("zerocopy") { attr.parse_nested_meta(|meta| { if meta.path.is_ident("crate") { let expr = meta.value().and_then(|value| value.parse()); if let Ok(Expr::Lit(ExprLit { lit: Lit::Str(lit), .. })) = expr { if let Ok(path_lit) = lit.parse::<Ident>() { path = parse_quote!(::#path_lit); return Ok(()); } } return Err(Error::new( Span::call_site(), "`crate` attribute requires a path as the value", )); } if meta.path.is_ident("on_error") { on_error_span = Some(meta.path.span()); let value = meta.value()?; let s: LitStr = value.parse()?; match s.value().as_str() { "skip" => skip_on_error = true, "fail" => skip_on_error = false, _ => return Err(Error::new( s.span(), "unrecognized value for `on_error` attribute from `zerocopy`; expected `skip` or `fail`", )), } return Ok(()); } Err(Error::new( Span::call_site(), format!( "unknown attribute encountered: {}", meta.path.into_token_stream() ), )) })?; } } } Ok(Self { ast, zerocopy_crate: path, skip_on_error, on_error_span }) } pub(crate) fn with_input(&self, input: &DeriveInput) -> Self { Self { ast: input.clone(), zerocopy_crate: self.zerocopy_crate.clone(), skip_on_error: self.skip_on_error, on_error_span: self.on_error_span, } } pub(crate) fn skip_on_error(mut self) -> Self { self.skip_on_error = true; self } pub(crate) fn core_path(&self) -> TokenStream { let zerocopy_crate = &self.zerocopy_crate; quote!(#zerocopy_crate::util::macro_util::core_reexport) } pub(crate) fn cfg_compile_error(&self) -> TokenStream { // By checking both during the compilation of the proc macro *and* in // the generated code, we ensure that `--cfg // zerocopy_unstable_linux` need only be passed *either* when // compiling this crate *or* when compiling the user's crate. The former // is preferable, but in some situations (such as when cross-compiling // using `cargo build --target`), it doesn't get propagated to this // crate's build by default. if cfg!(zerocopy_unstable_linux) { quote!() } else if let Some(span) = self.on_error_span { let core = self.core_path(); let error_message = "`on_error` is experimental; pass '--cfg zerocopy_unstable_linux' to enable"; quote::quote_spanned! {span=> #[allow(unused_attributes, unexpected_cfgs)] const _: () = { #[cfg(not(zerocopy_unstable_linux))] #core::compile_error!(#error_message); }; } } else { quote!() } } pub(crate) fn error_or_skip<E>(&self, error: E) -> Result<TokenStream, E> { if self.skip_on_error { Ok(self.cfg_compile_error()) } else { Err(error) } }} pub(crate) trait DataExt { /// Extracts the names and types of all fields. For enums, extracts the /// names and types of fields from each variant. For tuple structs, the /// names are the indices used to index into the struct (ie, `0`, `1`, etc). /// /// FIXME: Extracting field names for enums doesn't really make sense. Types /// makes sense because we don't care about where they live - we just care /// about transitive ownership. But for field names, we'd only use them when /// generating is_bit_valid, which cares about where they live. fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)>; fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)>; fn tag(&self) -> Option<Ident>;} impl DataExt for Data { fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> { match self { Data::Struct(strc) => strc.fields(), Data::Enum(enm) => enm.fields(), Data::Union(un) => un.fields(), } } fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> { match self { Data::Struct(strc) => strc.variants(), Data::Enum(enm) => enm.variants(), Data::Union(un) => un.variants(), } } fn tag(&self) -> Option<Ident> { match self { Data::Struct(strc) => strc.tag(), Data::Enum(enm) => enm.tag(), Data::Union(un) => un.tag(), } }} impl DataExt for DataStruct { fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> { map_fields(&self.fields) } fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> { vec![(None, self.fields())] } fn tag(&self) -> Option<Ident> { None }} impl DataExt for DataEnum { fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> { map_fields(self.variants.iter().flat_map(|var| &var.fields)) } fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> { self.variants.iter().map(|var| (Some(var), map_fields(&var.fields))).collect() } fn tag(&self) -> Option<Ident> { Some(Ident::new("___ZerocopyTag", Span::call_site())) }} impl DataExt for DataUnion { fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> { map_fields(&self.fields.named) } fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> { vec![(None, self.fields())] } fn tag(&self) -> Option<Ident> { None }} fn map_fields<'a>( fields: impl 'a + IntoIterator<Item = &'a Field>,) -> Vec<(&'a Visibility, TokenStream, &'a Type)> { fields .into_iter() .enumerate() .map(|(idx, f)| { ( &f.vis, f.ident .as_ref() .map(ToTokens::to_token_stream) .unwrap_or_else(|| Index::from(idx).to_token_stream()), &f.ty, ) }) .collect()} pub(crate) fn to_ident_str(t: &impl ToString) -> String { let s = t.to_string(); if let Some(stripped) = s.strip_prefix("r#") { stripped.to_string() } else { s }} /// This enum describes what kind of padding check needs to be generated for the/// associated impl.pub(crate) enum PaddingCheck { /// Check that the sum of the fields' sizes exactly equals the struct's /// size. Struct, /// Check that a `repr(C)` struct has no padding. ReprCStruct, /// Check that the size of each field exactly equals the union's size. Union, /// Check that every variant of the enum contains no padding. /// /// Because doing so requires a tag enum, this padding check requires an /// additional `TokenStream` which defines the tag enum as `___ZerocopyTag`. Enum { tag_type_definition: TokenStream },} impl PaddingCheck { /// Returns the idents of the trait to use and the macro to call in order to /// validate that a type passes the relevant padding check. pub(crate) fn validator_trait_and_macro_idents(&self) -> (Ident, Ident) { let (trt, mcro) = match self { PaddingCheck::Struct => ("PaddingFree", "struct_padding"), PaddingCheck::ReprCStruct => ("DynamicPaddingFree", "repr_c_struct_has_padding"), PaddingCheck::Union => ("PaddingFree", "union_padding"), PaddingCheck::Enum { .. } => ("PaddingFree", "enum_padding"), }; let trt = Ident::new(trt, Span::call_site()); let mcro = Ident::new(mcro, Span::call_site()); (trt, mcro) } /// Sometimes performing the padding check requires some additional /// "context" code. For enums, this is the definition of the tag enum. pub(crate) fn validator_macro_context(&self) -> Option<&TokenStream> { match self { PaddingCheck::Struct | PaddingCheck::ReprCStruct | PaddingCheck::Union => None, PaddingCheck::Enum { tag_type_definition } => Some(tag_type_definition), } }} #[derive(Clone)]pub(crate) enum Trait { KnownLayout, HasTag, HasField { variant_id: Box<Expr>, field: Box<Type>, field_id: Box<Expr>, }, ProjectField { variant_id: Box<Expr>, field: Box<Type>, field_id: Box<Expr>, invariants: Box<Type>, }, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned, Sized, ByteHash, ByteEq, SplitAt,} impl ToTokens for Trait { fn to_tokens(&self, tokens: &mut TokenStream) { // According to [1], the format of the derived `Debug`` output is not // stable and therefore not guaranteed to represent the variant names. // Indeed with the (unstable) `fmt-debug` compiler flag [2], it can // return only a minimalized output or empty string. To make sure this // code will work in the future and independent of the compiler flag, we // translate the variants to their names manually here. // // [1] https://doc.rust-lang.org/1.81.0/std/fmt/trait.Debug.html#stability // [2] https://doc.rust-lang.org/beta/unstable-book/compiler-flags/fmt-debug.html let s = match self { Trait::HasField { .. } => "HasField", Trait::ProjectField { .. } => "ProjectField", Trait::KnownLayout => "KnownLayout", Trait::HasTag => "HasTag", Trait::Immutable => "Immutable", Trait::TryFromBytes => "TryFromBytes", Trait::FromZeros => "FromZeros", Trait::FromBytes => "FromBytes", Trait::IntoBytes => "IntoBytes", Trait::Unaligned => "Unaligned", Trait::Sized => "Sized", Trait::ByteHash => "ByteHash", Trait::ByteEq => "ByteEq", Trait::SplitAt => "SplitAt", }; let ident = Ident::new(s, Span::call_site()); let arguments: Option<syn::AngleBracketedGenericArguments> = match self { Trait::HasField { variant_id, field, field_id } => { Some(parse_quote!(<#field, #variant_id, #field_id>)) } Trait::ProjectField { variant_id, field, field_id, invariants } => { Some(parse_quote!(<#field, #invariants, #variant_id, #field_id>)) } Trait::KnownLayout | Trait::HasTag | Trait::Immutable | Trait::TryFromBytes | Trait::FromZeros | Trait::FromBytes | Trait::IntoBytes | Trait::Unaligned | Trait::Sized | Trait::ByteHash | Trait::ByteEq | Trait::SplitAt => None, }; tokens.extend(quote!(#ident #arguments)); }} impl Trait { pub(crate) fn crate_path(&self, ctx: &Ctx) -> Path { let zerocopy_crate = &ctx.zerocopy_crate; let core = ctx.core_path(); match self { Self::Sized => parse_quote!(#core::marker::#self), _ => parse_quote!(#zerocopy_crate::#self), } }} pub(crate) enum TraitBound { Slf, Other(Trait),} pub(crate) enum FieldBounds<'a> { None, All(&'a [TraitBound]), Trailing(&'a [TraitBound]), Explicit(Vec<WherePredicate>),} impl<'a> FieldBounds<'a> { pub(crate) const ALL_SELF: FieldBounds<'a> = FieldBounds::All(&[TraitBound::Slf]); pub(crate) const TRAILING_SELF: FieldBounds<'a> = FieldBounds::Trailing(&[TraitBound::Slf]);} pub(crate) enum SelfBounds<'a> { None, All(&'a [Trait]),} // FIXME(https://github.com/rust-lang/rust-clippy/issues/12908): This is a false// positive. Explicit lifetimes are actually necessary here.#[allow(clippy::needless_lifetimes)]impl<'a> SelfBounds<'a> { pub(crate) const SIZED: Self = Self::All(&[Trait::Sized]);} /// Normalizes a slice of bounds by replacing [`TraitBound::Slf`] with `slf`.pub(crate) fn normalize_bounds<'a>( slf: &'a Trait, bounds: &'a [TraitBound],) -> impl 'a + Iterator<Item = Trait> { bounds.iter().map(move |bound| match bound { TraitBound::Slf => slf.clone(), TraitBound::Other(trt) => trt.clone(), })} pub(crate) struct ImplBlockBuilder<'a> { ctx: &'a Ctx, data: &'a dyn DataExt, trt: Trait, field_type_trait_bounds: FieldBounds<'a>, self_type_trait_bounds: SelfBounds<'a>, padding_check: Option<PaddingCheck>, param_extras: Vec<GenericParam>, inner_extras: Option<TokenStream>, outer_extras: Option<TokenStream>,} impl<'a> ImplBlockBuilder<'a> { pub(crate) fn new( ctx: &'a Ctx, data: &'a dyn DataExt, trt: Trait, field_type_trait_bounds: FieldBounds<'a>, ) -> Self { Self { ctx, data, trt, field_type_trait_bounds, self_type_trait_bounds: SelfBounds::None, padding_check: None, param_extras: Vec::new(), inner_extras: None, outer_extras: None, } } pub(crate) fn self_type_trait_bounds(mut self, self_type_trait_bounds: SelfBounds<'a>) -> Self { self.self_type_trait_bounds = self_type_trait_bounds; self } pub(crate) fn padding_check<P: Into<Option<PaddingCheck>>>(mut self, padding_check: P) -> Self { self.padding_check = padding_check.into(); self } pub(crate) fn param_extras(mut self, param_extras: Vec<GenericParam>) -> Self { self.param_extras.extend(param_extras); self } pub(crate) fn inner_extras(mut self, inner_extras: TokenStream) -> Self { self.inner_extras = Some(inner_extras); self } pub(crate) fn outer_extras<T: Into<Option<TokenStream>>>(mut self, outer_extras: T) -> Self { self.outer_extras = outer_extras.into(); self } pub(crate) fn build(self) -> TokenStream { // In this documentation, we will refer to this hypothetical struct: // // #[derive(FromBytes)] // struct Foo<T, I: Iterator> // where // T: Copy, // I: Clone, // I::Item: Clone, // { // a: u8, // b: T, // c: I::Item,Lines 1–500 of 863. Load more
// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT // // Copyright 2019 The Fuchsia Authors // // Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 // <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. // This file may not be copied, modified, or distributed except according to // those terms. use std::num::NonZeroU32; use proc_macro2::{Span, TokenStream}; use quote::{quote, quote_spanned, ToTokens}; use syn::{ parse_quote, spanned::Spanned as _, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Error, Expr, ExprLit, Field, GenericParam, Ident, Index, Lit, LitStr, Meta, Path, Type, Variant, Visibility, WherePredicate, }; use crate::repr::{CompoundRepr, EnumRepr, PrimitiveRepr, Repr, Spanned}; pub(crate) struct Ctx { pub(crate) ast: DeriveInput, pub(crate) zerocopy_crate: Path, // The value of the last `#[zerocopy(on_error = ...)]` attribute, or `false` // if none is provided. pub(crate) skip_on_error: bool, // The span of the last `#[zerocopy(on_error = ...)]` attribute, if any. pub(crate) on_error_span: Option<proc_macro2::Span>, } impl Ctx { /// Attempt to extract a crate path from the provided attributes. Defaults to /// `::zerocopy` if not found. pub(crate) fn try_from_derive_input(ast: DeriveInput) -> Result<Self, Error> { let mut path = parse_quote!(::zerocopy); let mut skip_on_error = false; let mut on_error_span = None; for attr in &ast.attrs { if let Meta::List(ref meta_list) = attr.meta { if meta_list.path.is_ident("zerocopy") { attr.parse_nested_meta(|meta| { if meta.path.is_ident("crate") { let expr = meta.value().and_then(|value| value.parse()); if let Ok(Expr::Lit(ExprLit { lit: Lit::Str(lit), .. })) = expr { if let Ok(path_lit) = lit.parse::<Ident>() { path = parse_quote!(::#path_lit); return Ok(()); } } return Err(Error::new( Span::call_site(), "`crate` attribute requires a path as the value", )); } if meta.path.is_ident("on_error") { on_error_span = Some(meta.path.span()); let value = meta.value()?; let s: LitStr = value.parse()?; match s.value().as_str() { "skip" => skip_on_error = true, "fail" => skip_on_error = false, _ => return Err(Error::new( s.span(), "unrecognized value for `on_error` attribute from `zerocopy`; expected `skip` or `fail`", )), } return Ok(()); } Err(Error::new( Span::call_site(), format!( "unknown attribute encountered: {}", meta.path.into_token_stream() ), )) })?; } } } Ok(Self { ast, zerocopy_crate: path, skip_on_error, on_error_span }) } pub(crate) fn with_input(&self, input: &DeriveInput) -> Self { Self { ast: input.clone(), zerocopy_crate: self.zerocopy_crate.clone(), skip_on_error: self.skip_on_error, on_error_span: self.on_error_span, } } pub(crate) fn skip_on_error(mut self) -> Self { self.skip_on_error = true; self } pub(crate) fn core_path(&self) -> TokenStream { let zerocopy_crate = &self.zerocopy_crate; quote!(#zerocopy_crate::util::macro_util::core_reexport) } pub(crate) fn cfg_compile_error(&self) -> TokenStream { // By checking both during the compilation of the proc macro *and* in // the generated code, we ensure that `--cfg // zerocopy_unstable_linux` need only be passed *either* when // compiling this crate *or* when compiling the user's crate. The former // is preferable, but in some situations (such as when cross-compiling // using `cargo build --target`), it doesn't get propagated to this // crate's build by default. if cfg!(zerocopy_unstable_linux) { quote!() } else if let Some(span) = self.on_error_span { let core = self.core_path(); let error_message = "`on_error` is experimental; pass '--cfg zerocopy_unstable_linux' to enable"; quote::quote_spanned! {span=> #[allow(unused_attributes, unexpected_cfgs)] const _: () = { #[cfg(not(zerocopy_unstable_linux))] #core::compile_error!(#error_message); }; } } else { quote!() } } pub(crate) fn error_or_skip<E>(&self, error: E) -> Result<TokenStream, E> { if self.skip_on_error { Ok(self.cfg_compile_error()) } else { Err(error) } } } pub(crate) trait DataExt { /// Extracts the names and types of all fields. For enums, extracts the /// names and types of fields from each variant. For tuple structs, the /// names are the indices used to index into the struct (ie, `0`, `1`, etc). /// /// FIXME: Extracting field names for enums doesn't really make sense. Types /// makes sense because we don't care about where they live - we just care /// about transitive ownership. But for field names, we'd only use them when /// generating is_bit_valid, which cares about where they live. fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)>; fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)>; fn tag(&self) -> Option<Ident>; } impl DataExt for Data { fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> { match self { Data::Struct(strc) => strc.fields(), Data::Enum(enm) => enm.fields(), Data::Union(un) => un.fields(), } } fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> { match self { Data::Struct(strc) => strc.variants(), Data::Enum(enm) => enm.variants(), Data::Union(un) => un.variants(), } } fn tag(&self) -> Option<Ident> { match self { Data::Struct(strc) => strc.tag(), Data::Enum(enm) => enm.tag(), Data::Union(un) => un.tag(), } } } impl DataExt for DataStruct { fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> { map_fields(&self.fields) } fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> { vec![(None, self.fields())] } fn tag(&self) -> Option<Ident> { None } } impl DataExt for DataEnum { fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> { map_fields(self.variants.iter().flat_map(|var| &var.fields)) } fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> { self.variants.iter().map(|var| (Some(var), map_fields(&var.fields))).collect() } fn tag(&self) -> Option<Ident> { Some(Ident::new("___ZerocopyTag", Span::call_site())) } } impl DataExt for DataUnion { fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> { map_fields(&self.fields.named) } fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> { vec![(None, self.fields())] } fn tag(&self) -> Option<Ident> { None } } fn map_fields<'a>( fields: impl 'a + IntoIterator<Item = &'a Field>, ) -> Vec<(&'a Visibility, TokenStream, &'a Type)> { fields .into_iter() .enumerate() .map(|(idx, f)| { ( &f.vis, f.ident .as_ref() .map(ToTokens::to_token_stream) .unwrap_or_else(|| Index::from(idx).to_token_stream()), &f.ty, ) }) .collect() } pub(crate) fn to_ident_str(t: &impl ToString) -> String { let s = t.to_string(); if let Some(stripped) = s.strip_prefix("r#") { stripped.to_string() } else { s } } /// This enum describes what kind of padding check needs to be generated for the /// associated impl. pub(crate) enum PaddingCheck { /// Check that the sum of the fields' sizes exactly equals the struct's /// size. Struct, /// Check that a `repr(C)` struct has no padding. ReprCStruct, /// Check that the size of each field exactly equals the union's size. Union, /// Check that every variant of the enum contains no padding. /// /// Because doing so requires a tag enum, this padding check requires an /// additional `TokenStream` which defines the tag enum as `___ZerocopyTag`. Enum { tag_type_definition: TokenStream }, } impl PaddingCheck { /// Returns the idents of the trait to use and the macro to call in order to /// validate that a type passes the relevant padding check. pub(crate) fn validator_trait_and_macro_idents(&self) -> (Ident, Ident) { let (trt, mcro) = match self { PaddingCheck::Struct => ("PaddingFree", "struct_padding"), PaddingCheck::ReprCStruct => ("DynamicPaddingFree", "repr_c_struct_has_padding"), PaddingCheck::Union => ("PaddingFree", "union_padding"), PaddingCheck::Enum { .. } => ("PaddingFree", "enum_padding"), }; let trt = Ident::new(trt, Span::call_site()); let mcro = Ident::new(mcro, Span::call_site()); (trt, mcro) } /// Sometimes performing the padding check requires some additional /// "context" code. For enums, this is the definition of the tag enum. pub(crate) fn validator_macro_context(&self) -> Option<&TokenStream> { match self { PaddingCheck::Struct | PaddingCheck::ReprCStruct | PaddingCheck::Union => None, PaddingCheck::Enum { tag_type_definition } => Some(tag_type_definition), } } } #[derive(Clone)] pub(crate) enum Trait { KnownLayout, HasTag, HasField { variant_id: Box<Expr>, field: Box<Type>, field_id: Box<Expr>, }, ProjectField { variant_id: Box<Expr>, field: Box<Type>, field_id: Box<Expr>, invariants: Box<Type>, }, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned, Sized, ByteHash, ByteEq, SplitAt, } impl ToTokens for Trait { fn to_tokens(&self, tokens: &mut TokenStream) { // According to [1], the format of the derived `Debug`` output is not // stable and therefore not guaranteed to represent the variant names. // Indeed with the (unstable) `fmt-debug` compiler flag [2], it can // return only a minimalized output or empty string. To make sure this // code will work in the future and independent of the compiler flag, we // translate the variants to their names manually here. // // [1] https://doc.rust-lang.org/1.81.0/std/fmt/trait.Debug.html#stability // [2] https://doc.rust-lang.org/beta/unstable-book/compiler-flags/fmt-debug.html let s = match self { Trait::HasField { .. } => "HasField", Trait::ProjectField { .. } => "ProjectField", Trait::KnownLayout => "KnownLayout", Trait::HasTag => "HasTag", Trait::Immutable => "Immutable", Trait::TryFromBytes => "TryFromBytes", Trait::FromZeros => "FromZeros", Trait::FromBytes => "FromBytes", Trait::IntoBytes => "IntoBytes", Trait::Unaligned => "Unaligned", Trait::Sized => "Sized", Trait::ByteHash => "ByteHash", Trait::ByteEq => "ByteEq", Trait::SplitAt => "SplitAt", }; let ident = Ident::new(s, Span::call_site()); let arguments: Option<syn::AngleBracketedGenericArguments> = match self { Trait::HasField { variant_id, field, field_id } => { Some(parse_quote!(<#field, #variant_id, #field_id>)) } Trait::ProjectField { variant_id, field, field_id, invariants } => { Some(parse_quote!(<#field, #invariants, #variant_id, #field_id>)) } Trait::KnownLayout | Trait::HasTag | Trait::Immutable | Trait::TryFromBytes | Trait::FromZeros | Trait::FromBytes | Trait::IntoBytes | Trait::Unaligned | Trait::Sized | Trait::ByteHash | Trait::ByteEq | Trait::SplitAt => None, }; tokens.extend(quote!(#ident #arguments)); } } impl Trait { pub(crate) fn crate_path(&self, ctx: &Ctx) -> Path { let zerocopy_crate = &ctx.zerocopy_crate; let core = ctx.core_path(); match self { Self::Sized => parse_quote!(#core::marker::#self), _ => parse_quote!(#zerocopy_crate::#self), } } } pub(crate) enum TraitBound { Slf, Other(Trait), } pub(crate) enum FieldBounds<'a> { None, All(&'a [TraitBound]), Trailing(&'a [TraitBound]), Explicit(Vec<WherePredicate>), } impl<'a> FieldBounds<'a> { pub(crate) const ALL_SELF: FieldBounds<'a> = FieldBounds::All(&[TraitBound::Slf]); pub(crate) const TRAILING_SELF: FieldBounds<'a> = FieldBounds::Trailing(&[TraitBound::Slf]); } pub(crate) enum SelfBounds<'a> { None, All(&'a [Trait]), } // FIXME(https://github.com/rust-lang/rust-clippy/issues/12908): This is a false // positive. Explicit lifetimes are actually necessary here. #[allow(clippy::needless_lifetimes)] impl<'a> SelfBounds<'a> { pub(crate) const SIZED: Self = Self::All(&[Trait::Sized]); } /// Normalizes a slice of bounds by replacing [`TraitBound::Slf`] with `slf`. pub(crate) fn normalize_bounds<'a>( slf: &'a Trait, bounds: &'a [TraitBound], ) -> impl 'a + Iterator<Item = Trait> { bounds.iter().map(move |bound| match bound { TraitBound::Slf => slf.clone(), TraitBound::Other(trt) => trt.clone(), }) } pub(crate) struct ImplBlockBuilder<'a> { ctx: &'a Ctx, data: &'a dyn DataExt, trt: Trait, field_type_trait_bounds: FieldBounds<'a>, self_type_trait_bounds: SelfBounds<'a>, padding_check: Option<PaddingCheck>, param_extras: Vec<GenericParam>, inner_extras: Option<TokenStream>, outer_extras: Option<TokenStream>, } impl<'a> ImplBlockBuilder<'a> { pub(crate) fn new( ctx: &'a Ctx, data: &'a dyn DataExt, trt: Trait, field_type_trait_bounds: FieldBounds<'a>, ) -> Self { Self { ctx, data, trt, field_type_trait_bounds, self_type_trait_bounds: SelfBounds::None, padding_check: None, param_extras: Vec::new(), inner_extras: None, outer_extras: None, } } pub(crate) fn self_type_trait_bounds(mut self, self_type_trait_bounds: SelfBounds<'a>) -> Self { self.self_type_trait_bounds = self_type_trait_bounds; self } pub(crate) fn padding_check<P: Into<Option<PaddingCheck>>>(mut self, padding_check: P) -> Self { self.padding_check = padding_check.into(); self } pub(crate) fn param_extras(mut self, param_extras: Vec<GenericParam>) -> Self { self.param_extras.extend(param_extras); self } pub(crate) fn inner_extras(mut self, inner_extras: TokenStream) -> Self { self.inner_extras = Some(inner_extras); self } pub(crate) fn outer_extras<T: Into<Option<TokenStream>>>(mut self, outer_extras: T) -> Self { self.outer_extras = outer_extras.into(); self } pub(crate) fn build(self) -> TokenStream { // In this documentation, we will refer to this hypothetical struct: // // #[derive(FromBytes)] // struct Foo<T, I: Iterator> // where // T: Copy, // I: Clone, // I::Item: Clone, // { // a: u8, // b: T, // c: I::Item,