xref: /linux/rust/zerocopy-derive/util.rs (revision 5f85604cf0877b0369dfd68cd50cf61c0f134819)
1 // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT
2 
3 // Copyright 2019 The Fuchsia Authors
4 //
5 // Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
6 // <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
7 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
8 // This file may not be copied, modified, or distributed except according to
9 // those terms.
10 
11 use std::num::NonZeroU32;
12 
13 use proc_macro2::{Span, TokenStream};
14 use quote::{quote, quote_spanned, ToTokens};
15 use syn::{
16     parse_quote, spanned::Spanned as _, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Error,
17     Expr, ExprLit, Field, GenericParam, Ident, Index, Lit, LitStr, Meta, Path, Type, Variant,
18     Visibility, WherePredicate,
19 };
20 
21 use crate::repr::{CompoundRepr, EnumRepr, PrimitiveRepr, Repr, Spanned};
22 
23 pub(crate) struct Ctx {
24     pub(crate) ast: DeriveInput,
25     pub(crate) zerocopy_crate: Path,
26 
27     // The value of the last `#[zerocopy(on_error = ...)]` attribute, or `false`
28     // if none is provided.
29     pub(crate) skip_on_error: bool,
30 
31     // The span of the last `#[zerocopy(on_error = ...)]` attribute, if any.
32     pub(crate) on_error_span: Option<proc_macro2::Span>,
33 }
34 
35 impl Ctx {
36     /// Attempt to extract a crate path from the provided attributes. Defaults to
37     /// `::zerocopy` if not found.
38     pub(crate) fn try_from_derive_input(ast: DeriveInput) -> Result<Self, Error> {
39         let mut path = parse_quote!(::zerocopy);
40         let mut skip_on_error = false;
41         let mut on_error_span = None;
42 
43         for attr in &ast.attrs {
44             if let Meta::List(ref meta_list) = attr.meta {
45                 if meta_list.path.is_ident("zerocopy") {
46                     attr.parse_nested_meta(|meta| {
47                         if meta.path.is_ident("crate") {
48                             let expr = meta.value().and_then(|value| value.parse());
49                             if let Ok(Expr::Lit(ExprLit { lit: Lit::Str(lit), .. })) = expr {
50                                 if let Ok(path_lit) = lit.parse::<Ident>() {
51                                     path = parse_quote!(::#path_lit);
52                                     return Ok(());
53                                 }
54                             }
55 
56                             return Err(Error::new(
57                                 Span::call_site(),
58                                 "`crate` attribute requires a path as the value",
59                             ));
60                         }
61 
62                         if meta.path.is_ident("on_error") {
63                             on_error_span = Some(meta.path.span());
64                             let value = meta.value()?;
65                             let s: LitStr = value.parse()?;
66                             match s.value().as_str() {
67                                 "skip" => skip_on_error = true,
68                                 "fail" => skip_on_error = false,
69                                 _ => return Err(Error::new(
70                                     s.span(),
71                                     "unrecognized value for `on_error` attribute from `zerocopy`; expected `skip` or `fail`",
72                                 )),
73                             }
74                             return Ok(());
75                         }
76 
77                         Err(Error::new(
78                             Span::call_site(),
79                             format!(
80                                 "unknown attribute encountered: {}",
81                                 meta.path.into_token_stream()
82                             ),
83                         ))
84                     })?;
85                 }
86             }
87         }
88 
89         Ok(Self { ast, zerocopy_crate: path, skip_on_error, on_error_span })
90     }
91 
92     pub(crate) fn with_input(&self, input: &DeriveInput) -> Self {
93         Self {
94             ast: input.clone(),
95             zerocopy_crate: self.zerocopy_crate.clone(),
96             skip_on_error: self.skip_on_error,
97             on_error_span: self.on_error_span,
98         }
99     }
100 
101     pub(crate) fn core_path(&self) -> TokenStream {
102         let zerocopy_crate = &self.zerocopy_crate;
103         quote!(#zerocopy_crate::util::macro_util::core_reexport)
104     }
105 
106     pub(crate) fn cfg_compile_error(&self) -> TokenStream {
107         // By checking both during the compilation of the proc macro *and* in
108         // the generated code, we ensure that `--cfg
109         // zerocopy_unstable_derive_on_error` need only be passed *either* when
110         // compiling this crate *or* when compiling the user's crate. The former
111         // is preferable, but in some situations (such as when cross-compiling
112         // using `cargo build --target`), it doesn't get propagated to this
113         // crate's build by default.
114         if cfg!(zerocopy_unstable_derive_on_error) {
115             quote!()
116         } else if let Some(span) = self.on_error_span {
117             let core = self.core_path();
118             let error_message = "`on_error` is experimental; pass '--cfg zerocopy_unstable_derive_on_error' to enable";
119             quote::quote_spanned! {span=>
120                 #[allow(unused_attributes, unexpected_cfgs)]
121                 const _: () = {
122                     #[cfg(not(zerocopy_unstable_derive_on_error))]
123                     #core::compile_error!(#error_message);
124                 };
125             }
126         } else {
127             quote!()
128         }
129     }
130 
131     pub(crate) fn error_or_skip<E>(&self, error: E) -> Result<TokenStream, E> {
132         if self.skip_on_error {
133             Ok(self.cfg_compile_error())
134         } else {
135             Err(error)
136         }
137     }
138 }
139 
140 pub(crate) trait DataExt {
141     /// Extracts the names and types of all fields. For enums, extracts the
142     /// names and types of fields from each variant. For tuple structs, the
143     /// names are the indices used to index into the struct (ie, `0`, `1`, etc).
144     ///
145     /// FIXME: Extracting field names for enums doesn't really make sense. Types
146     /// makes sense because we don't care about where they live - we just care
147     /// about transitive ownership. But for field names, we'd only use them when
148     /// generating is_bit_valid, which cares about where they live.
149     fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)>;
150 
151     fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)>;
152 
153     fn tag(&self) -> Option<Ident>;
154 }
155 
156 impl DataExt for Data {
157     fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> {
158         match self {
159             Data::Struct(strc) => strc.fields(),
160             Data::Enum(enm) => enm.fields(),
161             Data::Union(un) => un.fields(),
162         }
163     }
164 
165     fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> {
166         match self {
167             Data::Struct(strc) => strc.variants(),
168             Data::Enum(enm) => enm.variants(),
169             Data::Union(un) => un.variants(),
170         }
171     }
172 
173     fn tag(&self) -> Option<Ident> {
174         match self {
175             Data::Struct(strc) => strc.tag(),
176             Data::Enum(enm) => enm.tag(),
177             Data::Union(un) => un.tag(),
178         }
179     }
180 }
181 
182 impl DataExt for DataStruct {
183     fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> {
184         map_fields(&self.fields)
185     }
186 
187     fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> {
188         vec![(None, self.fields())]
189     }
190 
191     fn tag(&self) -> Option<Ident> {
192         None
193     }
194 }
195 
196 impl DataExt for DataEnum {
197     fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> {
198         map_fields(self.variants.iter().flat_map(|var| &var.fields))
199     }
200 
201     fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> {
202         self.variants.iter().map(|var| (Some(var), map_fields(&var.fields))).collect()
203     }
204 
205     fn tag(&self) -> Option<Ident> {
206         Some(Ident::new("___ZerocopyTag", Span::call_site()))
207     }
208 }
209 
210 impl DataExt for DataUnion {
211     fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> {
212         map_fields(&self.fields.named)
213     }
214 
215     fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> {
216         vec![(None, self.fields())]
217     }
218 
219     fn tag(&self) -> Option<Ident> {
220         None
221     }
222 }
223 
224 fn map_fields<'a>(
225     fields: impl 'a + IntoIterator<Item = &'a Field>,
226 ) -> Vec<(&'a Visibility, TokenStream, &'a Type)> {
227     fields
228         .into_iter()
229         .enumerate()
230         .map(|(idx, f)| {
231             (
232                 &f.vis,
233                 f.ident
234                     .as_ref()
235                     .map(ToTokens::to_token_stream)
236                     .unwrap_or_else(|| Index::from(idx).to_token_stream()),
237                 &f.ty,
238             )
239         })
240         .collect()
241 }
242 
243 pub(crate) fn to_ident_str(t: &impl ToString) -> String {
244     let s = t.to_string();
245     if let Some(stripped) = s.strip_prefix("r#") {
246         stripped.to_string()
247     } else {
248         s
249     }
250 }
251 
252 /// This enum describes what kind of padding check needs to be generated for the
253 /// associated impl.
254 pub(crate) enum PaddingCheck {
255     /// Check that the sum of the fields' sizes exactly equals the struct's
256     /// size.
257     Struct,
258     /// Check that a `repr(C)` struct has no padding.
259     ReprCStruct,
260     /// Check that the size of each field exactly equals the union's size.
261     Union,
262     /// Check that every variant of the enum contains no padding.
263     ///
264     /// Because doing so requires a tag enum, this padding check requires an
265     /// additional `TokenStream` which defines the tag enum as `___ZerocopyTag`.
266     Enum { tag_type_definition: TokenStream },
267 }
268 
269 impl PaddingCheck {
270     /// Returns the idents of the trait to use and the macro to call in order to
271     /// validate that a type passes the relevant padding check.
272     pub(crate) fn validator_trait_and_macro_idents(&self) -> (Ident, Ident) {
273         let (trt, mcro) = match self {
274             PaddingCheck::Struct => ("PaddingFree", "struct_padding"),
275             PaddingCheck::ReprCStruct => ("DynamicPaddingFree", "repr_c_struct_has_padding"),
276             PaddingCheck::Union => ("PaddingFree", "union_padding"),
277             PaddingCheck::Enum { .. } => ("PaddingFree", "enum_padding"),
278         };
279 
280         let trt = Ident::new(trt, Span::call_site());
281         let mcro = Ident::new(mcro, Span::call_site());
282         (trt, mcro)
283     }
284 
285     /// Sometimes performing the padding check requires some additional
286     /// "context" code. For enums, this is the definition of the tag enum.
287     pub(crate) fn validator_macro_context(&self) -> Option<&TokenStream> {
288         match self {
289             PaddingCheck::Struct | PaddingCheck::ReprCStruct | PaddingCheck::Union => None,
290             PaddingCheck::Enum { tag_type_definition } => Some(tag_type_definition),
291         }
292     }
293 }
294 
295 #[derive(Clone)]
296 pub(crate) enum Trait {
297     KnownLayout,
298     HasTag,
299     HasField {
300         variant_id: Box<Expr>,
301         field: Box<Type>,
302         field_id: Box<Expr>,
303     },
304     ProjectField {
305         variant_id: Box<Expr>,
306         field: Box<Type>,
307         field_id: Box<Expr>,
308         invariants: Box<Type>,
309     },
310     Immutable,
311     TryFromBytes,
312     FromZeros,
313     FromBytes,
314     IntoBytes,
315     Unaligned,
316     Sized,
317     ByteHash,
318     ByteEq,
319     SplitAt,
320 }
321 
322 impl ToTokens for Trait {
323     fn to_tokens(&self, tokens: &mut TokenStream) {
324         // According to [1], the format of the derived `Debug`` output is not
325         // stable and therefore not guaranteed to represent the variant names.
326         // Indeed with the (unstable) `fmt-debug` compiler flag [2], it can
327         // return only a minimalized output or empty string. To make sure this
328         // code will work in the future and independent of the compiler flag, we
329         // translate the variants to their names manually here.
330         //
331         // [1] https://doc.rust-lang.org/1.81.0/std/fmt/trait.Debug.html#stability
332         // [2] https://doc.rust-lang.org/beta/unstable-book/compiler-flags/fmt-debug.html
333         let s = match self {
334             Trait::HasField { .. } => "HasField",
335             Trait::ProjectField { .. } => "ProjectField",
336             Trait::KnownLayout => "KnownLayout",
337             Trait::HasTag => "HasTag",
338             Trait::Immutable => "Immutable",
339             Trait::TryFromBytes => "TryFromBytes",
340             Trait::FromZeros => "FromZeros",
341             Trait::FromBytes => "FromBytes",
342             Trait::IntoBytes => "IntoBytes",
343             Trait::Unaligned => "Unaligned",
344             Trait::Sized => "Sized",
345             Trait::ByteHash => "ByteHash",
346             Trait::ByteEq => "ByteEq",
347             Trait::SplitAt => "SplitAt",
348         };
349         let ident = Ident::new(s, Span::call_site());
350         let arguments: Option<syn::AngleBracketedGenericArguments> = match self {
351             Trait::HasField { variant_id, field, field_id } => {
352                 Some(parse_quote!(<#field, #variant_id, #field_id>))
353             }
354             Trait::ProjectField { variant_id, field, field_id, invariants } => {
355                 Some(parse_quote!(<#field, #invariants, #variant_id, #field_id>))
356             }
357             Trait::KnownLayout
358             | Trait::HasTag
359             | Trait::Immutable
360             | Trait::TryFromBytes
361             | Trait::FromZeros
362             | Trait::FromBytes
363             | Trait::IntoBytes
364             | Trait::Unaligned
365             | Trait::Sized
366             | Trait::ByteHash
367             | Trait::ByteEq
368             | Trait::SplitAt => None,
369         };
370         tokens.extend(quote!(#ident #arguments));
371     }
372 }
373 
374 impl Trait {
375     pub(crate) fn crate_path(&self, ctx: &Ctx) -> Path {
376         let zerocopy_crate = &ctx.zerocopy_crate;
377         let core = ctx.core_path();
378         match self {
379             Self::Sized => parse_quote!(#core::marker::#self),
380             _ => parse_quote!(#zerocopy_crate::#self),
381         }
382     }
383 }
384 
385 pub(crate) enum TraitBound {
386     Slf,
387     Other(Trait),
388 }
389 
390 pub(crate) enum FieldBounds<'a> {
391     None,
392     All(&'a [TraitBound]),
393     Trailing(&'a [TraitBound]),
394     Explicit(Vec<WherePredicate>),
395 }
396 
397 impl<'a> FieldBounds<'a> {
398     pub(crate) const ALL_SELF: FieldBounds<'a> = FieldBounds::All(&[TraitBound::Slf]);
399     pub(crate) const TRAILING_SELF: FieldBounds<'a> = FieldBounds::Trailing(&[TraitBound::Slf]);
400 }
401 
402 pub(crate) enum SelfBounds<'a> {
403     None,
404     All(&'a [Trait]),
405 }
406 
407 // FIXME(https://github.com/rust-lang/rust-clippy/issues/12908): This is a false
408 // positive. Explicit lifetimes are actually necessary here.
409 #[allow(clippy::needless_lifetimes)]
410 impl<'a> SelfBounds<'a> {
411     pub(crate) const SIZED: Self = Self::All(&[Trait::Sized]);
412 }
413 
414 /// Normalizes a slice of bounds by replacing [`TraitBound::Slf`] with `slf`.
415 pub(crate) fn normalize_bounds<'a>(
416     slf: &'a Trait,
417     bounds: &'a [TraitBound],
418 ) -> impl 'a + Iterator<Item = Trait> {
419     bounds.iter().map(move |bound| match bound {
420         TraitBound::Slf => slf.clone(),
421         TraitBound::Other(trt) => trt.clone(),
422     })
423 }
424 
425 pub(crate) struct ImplBlockBuilder<'a> {
426     ctx: &'a Ctx,
427     data: &'a dyn DataExt,
428     trt: Trait,
429     field_type_trait_bounds: FieldBounds<'a>,
430     self_type_trait_bounds: SelfBounds<'a>,
431     padding_check: Option<PaddingCheck>,
432     param_extras: Vec<GenericParam>,
433     inner_extras: Option<TokenStream>,
434     outer_extras: Option<TokenStream>,
435 }
436 
437 impl<'a> ImplBlockBuilder<'a> {
438     pub(crate) fn new(
439         ctx: &'a Ctx,
440         data: &'a dyn DataExt,
441         trt: Trait,
442         field_type_trait_bounds: FieldBounds<'a>,
443     ) -> Self {
444         Self {
445             ctx,
446             data,
447             trt,
448             field_type_trait_bounds,
449             self_type_trait_bounds: SelfBounds::None,
450             padding_check: None,
451             param_extras: Vec::new(),
452             inner_extras: None,
453             outer_extras: None,
454         }
455     }
456 
457     pub(crate) fn self_type_trait_bounds(mut self, self_type_trait_bounds: SelfBounds<'a>) -> Self {
458         self.self_type_trait_bounds = self_type_trait_bounds;
459         self
460     }
461 
462     pub(crate) fn padding_check<P: Into<Option<PaddingCheck>>>(mut self, padding_check: P) -> Self {
463         self.padding_check = padding_check.into();
464         self
465     }
466 
467     pub(crate) fn param_extras(mut self, param_extras: Vec<GenericParam>) -> Self {
468         self.param_extras.extend(param_extras);
469         self
470     }
471 
472     pub(crate) fn inner_extras(mut self, inner_extras: TokenStream) -> Self {
473         self.inner_extras = Some(inner_extras);
474         self
475     }
476 
477     pub(crate) fn outer_extras<T: Into<Option<TokenStream>>>(mut self, outer_extras: T) -> Self {
478         self.outer_extras = outer_extras.into();
479         self
480     }
481 
482     pub(crate) fn build(self) -> TokenStream {
483         // In this documentation, we will refer to this hypothetical struct:
484         //
485         //   #[derive(FromBytes)]
486         //   struct Foo<T, I: Iterator>
487         //   where
488         //       T: Copy,
489         //       I: Clone,
490         //       I::Item: Clone,
491         //   {
492         //       a: u8,
493         //       b: T,
494         //       c: I::Item,
495         //   }
496         //
497         // We extract the field types, which in this case are `u8`, `T`, and
498         // `I::Item`. We re-use the existing parameters and where clauses. If
499         // `require_trait_bound == true` (as it is for `FromBytes), we add where
500         // bounds for each field's type:
501         //
502         //   impl<T, I: Iterator> FromBytes for Foo<T, I>
503         //   where
504         //       T: Copy,
505         //       I: Clone,
506         //       I::Item: Clone,
507         //       T: FromBytes,
508         //       I::Item: FromBytes,
509         //   {
510         //   }
511         //
512         // NOTE: It is standard practice to only emit bounds for the type
513         // parameters themselves, not for field types based on those parameters
514         // (e.g., `T` vs `T::Foo`). For a discussion of why this is standard
515         // practice, see https://github.com/rust-lang/rust/issues/26925.
516         //
517         // The reason we diverge from this standard is that doing it that way
518         // for us would be unsound. E.g., consider a type, `T` where `T:
519         // FromBytes` but `T::Foo: !FromBytes`. It would not be sound for us to
520         // accept a type with a `T::Foo` field as `FromBytes` simply because `T:
521         // FromBytes`.
522         //
523         // While there's no getting around this requirement for us, it does have
524         // the pretty serious downside that, when lifetimes are involved, the
525         // trait solver ties itself in knots:
526         //
527         //     #[derive(Unaligned)]
528         //     #[repr(C)]
529         //     struct Dup<'a, 'b> {
530         //         a: PhantomData<&'a u8>,
531         //         b: PhantomData<&'b u8>,
532         //     }
533         //
534         //     error[E0283]: type annotations required: cannot resolve `core::marker::PhantomData<&'a u8>: zerocopy::Unaligned`
535         //      --> src/main.rs:6:10
536         //       |
537         //     6 | #[derive(Unaligned)]
538         //       |          ^^^^^^^^^
539         //       |
540         //       = note: required by `zerocopy::Unaligned`
541 
542         let type_ident = &self.ctx.ast.ident;
543         let trait_path = self.trt.crate_path(self.ctx);
544         let fields = self.data.fields();
545         let variants = self.data.variants();
546         let tag = self.data.tag();
547         let zerocopy_crate = &self.ctx.zerocopy_crate;
548 
549         fn bound_tt(ty: &Type, traits: impl Iterator<Item = Trait>, ctx: &Ctx) -> WherePredicate {
550             let traits = traits.map(|t| t.crate_path(ctx));
551             parse_quote!(#ty: #(#traits)+*)
552         }
553         let field_type_bounds: Vec<_> = match (self.field_type_trait_bounds, &fields[..]) {
554             (FieldBounds::All(traits), _) => fields
555                 .iter()
556                 .map(|(_vis, _name, ty)| {
557                     bound_tt(ty, normalize_bounds(&self.trt, traits), self.ctx)
558                 })
559                 .collect(),
560             (FieldBounds::None, _) | (FieldBounds::Trailing(..), []) => vec![],
561             (FieldBounds::Trailing(traits), [.., last]) => {
562                 vec![bound_tt(last.2, normalize_bounds(&self.trt, traits), self.ctx)]
563             }
564             (FieldBounds::Explicit(bounds), _) => bounds,
565         };
566 
567         let padding_check_bound = self
568             .padding_check
569             .map(|check| {
570                 // Parse the repr for `align` and `packed` modifiers. Note that
571                 // `Repr::<PrimitiveRepr, NonZeroU32>` is more permissive than
572                 // what Rust supports for structs, enums, or unions, and thus
573                 // reliably extracts these modifiers for any kind of type.
574                 let repr =
575                     Repr::<PrimitiveRepr, NonZeroU32>::from_attrs(&self.ctx.ast.attrs).unwrap();
576                 let core = self.ctx.core_path();
577                 let option = quote! { #core::option::Option };
578                 let nonzero = quote! { #core::num::NonZeroUsize };
579                 let none = quote! { #option::None::<#nonzero> };
580                 let repr_align =
581                     repr.get_align().map(|spanned| {
582                         let n = spanned.t.get();
583                         quote_spanned! { spanned.span => (#nonzero::new(#n as usize)) }
584                     }).unwrap_or(quote! { (#none) });
585                 let repr_packed =
586                     repr.get_packed().map(|packed| {
587                         let n = packed.get();
588                         quote! { (#nonzero::new(#n as usize)) }
589                     }).unwrap_or(quote! { (#none) });
590                 let variant_types = variants.iter().map(|(_, fields)| {
591                     let types = fields.iter().map(|(_vis, _name, ty)| ty);
592                     quote!([#((#types)),*])
593                 });
594                 let validator_context = check.validator_macro_context();
595                 let (trt, validator_macro) = check.validator_trait_and_macro_idents();
596                 let t = tag.iter();
597                 parse_quote! {
598                     (): #zerocopy_crate::util::macro_util::#trt<
599                         Self,
600                         {
601                             #validator_context
602                             #zerocopy_crate::#validator_macro!(Self, #repr_align, #repr_packed, #(#t,)* #(#variant_types),*)
603                         }
604                     >
605                 }
606             });
607 
608         let self_bounds: Option<WherePredicate> = match self.self_type_trait_bounds {
609             SelfBounds::None => None,
610             SelfBounds::All(traits) => {
611                 Some(bound_tt(&parse_quote!(Self), traits.iter().cloned(), self.ctx))
612             }
613         };
614 
615         let bounds = self
616             .ctx
617             .ast
618             .generics
619             .where_clause
620             .as_ref()
621             .map(|where_clause| where_clause.predicates.iter())
622             .into_iter()
623             .flatten()
624             .chain(field_type_bounds.iter())
625             .chain(padding_check_bound.iter())
626             .chain(self_bounds.iter());
627 
628         // The parameters with trait bounds, but without type defaults.
629         let mut params: Vec<_> = self
630             .ctx
631             .ast
632             .generics
633             .params
634             .clone()
635             .into_iter()
636             .map(|mut param| {
637                 match &mut param {
638                     GenericParam::Type(ty) => ty.default = None,
639                     GenericParam::Const(cnst) => cnst.default = None,
640                     GenericParam::Lifetime(_) => {}
641                 }
642                 parse_quote!(#param)
643             })
644             .chain(self.param_extras)
645             .collect();
646 
647         // For MSRV purposes, ensure that lifetimes precede types precede const
648         // generics.
649         params.sort_by_cached_key(|param| match param {
650             GenericParam::Lifetime(_) => 0,
651             GenericParam::Type(_) => 1,
652             GenericParam::Const(_) => 2,
653         });
654 
655         // The identifiers of the parameters without trait bounds or type
656         // defaults.
657         let param_idents = self.ctx.ast.generics.params.iter().map(|param| match param {
658             GenericParam::Type(ty) => {
659                 let ident = &ty.ident;
660                 quote!(#ident)
661             }
662             GenericParam::Lifetime(l) => {
663                 let ident = &l.lifetime;
664                 quote!(#ident)
665             }
666             GenericParam::Const(cnst) => {
667                 let ident = &cnst.ident;
668                 quote!({#ident})
669             }
670         });
671 
672         let inner_extras = self.inner_extras;
673         let allow_trivial_bounds =
674             if self.ctx.skip_on_error { quote!(#[allow(trivial_bounds)]) } else { quote!() };
675         let impl_tokens = quote! {
676             #allow_trivial_bounds
677             unsafe impl < #(#params),* > #trait_path for #type_ident < #(#param_idents),* >
678             where
679                 #(#bounds,)*
680             {
681                 fn only_derive_is_allowed_to_implement_this_trait() {}
682 
683                 #inner_extras
684             }
685         };
686 
687         let outer_extras = self.outer_extras.filter(|e| !e.is_empty());
688         let cfg_compile_error = self.ctx.cfg_compile_error();
689         const_block([Some(cfg_compile_error), Some(impl_tokens), outer_extras])
690     }
691 }
692 
693 // A polyfill for `Option::then_some`, which was added after our MSRV.
694 //
695 // The `#[allow(unused)]` is necessary because, on sufficiently recent toolchain
696 // versions, `b.then_some(...)` resolves to the inherent method rather than to
697 // this trait, and so this trait is considered unused.
698 //
699 // FIXME(#67): Remove this once our MSRV is >= 1.62.
700 #[allow(unused)]
701 trait BoolExt {
702     fn then_some<T>(self, t: T) -> Option<T>;
703 }
704 
705 impl BoolExt for bool {
706     fn then_some<T>(self, t: T) -> Option<T> {
707         if self {
708             Some(t)
709         } else {
710             None
711         }
712     }
713 }
714 
715 pub(crate) fn const_block(items: impl IntoIterator<Item = Option<TokenStream>>) -> TokenStream {
716     let items = items.into_iter().flatten();
717     quote! {
718         #[allow(
719             // FIXME(#553): Add a test that generates a warning when
720             // `#[allow(deprecated)]` isn't present.
721             deprecated,
722             // Required on some rustc versions due to a lint that is only
723             // triggered when `derive(KnownLayout)` is applied to `repr(C)`
724             // structs that are generated by macros. See #2177 for details.
725             private_bounds,
726             non_local_definitions,
727             non_camel_case_types,
728             non_upper_case_globals,
729             non_snake_case,
730             non_ascii_idents,
731             clippy::missing_inline_in_public_items,
732         )]
733         #[deny(ambiguous_associated_items)]
734         // While there are not currently any warnings that this suppresses
735         // (that we're aware of), it's good future-proofing hygiene.
736         #[automatically_derived]
737         const _: () = {
738             #(#items)*
739         };
740     }
741 }
742 pub(crate) fn generate_tag_enum(ctx: &Ctx, repr: &EnumRepr, data: &DataEnum) -> TokenStream {
743     let zerocopy_crate = &ctx.zerocopy_crate;
744     let variants = data.variants.iter().map(|v| {
745         let ident = &v.ident;
746         if let Some((eq, discriminant)) = &v.discriminant {
747             quote! { #ident #eq #discriminant }
748         } else {
749             quote! { #ident }
750         }
751     });
752 
753     // Don't include any `repr(align)` when generating the tag enum, as that
754     // could add padding after the tag but before any variants, which is not the
755     // correct behavior.
756     let repr = match repr {
757         EnumRepr::Transparent(span) => quote::quote_spanned! { *span => #[repr(transparent)] },
758         EnumRepr::Compound(c, _) => quote! { #c },
759     };
760 
761     quote! {
762         #repr
763         #[allow(dead_code)]
764         pub enum ___ZerocopyTag {
765             #(#variants,)*
766         }
767 
768         // SAFETY: `___ZerocopyTag` has no fields, and so it does not permit
769         // interior mutation.
770         unsafe impl #zerocopy_crate::Immutable for ___ZerocopyTag {
771             fn only_derive_is_allowed_to_implement_this_trait() {}
772         }
773     }
774 }
775 pub(crate) fn enum_size_from_repr(repr: &EnumRepr) -> Result<usize, Error> {
776     use CompoundRepr::*;
777     use PrimitiveRepr::*;
778     use Repr::*;
779     match repr {
780         Transparent(span)
781         | Compound(
782             Spanned {
783                 t: C | Rust | Primitive(U32 | I32 | U64 | I64 | U128 | I128 | Usize | Isize),
784                 span,
785             },
786             _,
787         ) => Err(Error::new(
788             *span,
789             "`FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16`",
790         )),
791         Compound(Spanned { t: Primitive(U8 | I8), span: _ }, _align) => Ok(8),
792         Compound(Spanned { t: Primitive(U16 | I16), span: _ }, _align) => Ok(16),
793     }
794 }
795 
796 #[cfg(test)]
797 pub(crate) mod testutil {
798     use proc_macro2::TokenStream;
799     use syn::visit::{self, Visit};
800 
801     /// Checks for hygiene violations in the generated code.
802     ///
803     /// # Panics
804     ///
805     /// Panics if a hygiene violation is found.
806     pub(crate) fn check_hygiene(ts: TokenStream) {
807         struct AmbiguousItemVisitor;
808 
809         impl<'ast> Visit<'ast> for AmbiguousItemVisitor {
810             fn visit_path(&mut self, i: &'ast syn::Path) {
811                 if i.segments.len() > 1 && i.segments.first().unwrap().ident == "Self" {
812                     panic!(
813                     "Found ambiguous path `{}` in generated output. \
814                      All associated item access must be fully qualified (e.g., `<Self as Trait>::Item`) \
815                      to prevent hygiene issues.",
816                     quote::quote!(#i)
817                 );
818                 }
819                 visit::visit_path(self, i);
820             }
821         }
822 
823         let file = syn::parse2::<syn::File>(ts).expect("failed to parse generated output as File");
824         AmbiguousItemVisitor.visit_file(&file);
825     }
826 
827     #[test]
828     fn test_check_hygiene_success() {
829         check_hygiene(quote::quote! {
830             fn foo() {
831                 let _ = <Self as Trait>::Item;
832             }
833         });
834     }
835 
836     #[test]
837     #[should_panic(expected = "Found ambiguous path `Self :: Ambiguous`")]
838     fn test_check_hygiene_failure() {
839         check_hygiene(quote::quote! {
840             fn foo() {
841                 let _ = Self::Ambiguous;
842             }
843         });
844     }
845 }
846