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