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