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::ParseBuffer, parse_quote, spanned::Spanned as _, token::PathSep, Data, DataEnum, 17 DataStruct, DataUnion, DeriveInput, Error, Expr, ExprLit, Field, GenericParam, Ident, Index, 18 Lit, LitStr, Meta, Path, Type, Variant, 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 /// The first field invariant on the source type, if any. 35 pub(crate) invariant_span: Option<Span>, 36 } 37 38 #[derive(Eq, PartialEq)] 39 enum CratePath { 40 External, 41 CrateRelative, 42 ModuleRelative, 43 } 44 45 fn validate_crate_path(path: &Path) -> Result<CratePath, ()> { 46 if path.segments.is_empty() { 47 return Err(()); 48 } 49 50 enum ModuleRelative { 51 Yes, 52 No, 53 } 54 let first = path.segments[0].ident.to_string(); 55 let (mut prev_segment, path_type) = match first.as_str() { 56 "Self" => return Err(()), 57 "crate" => { 58 if path.leading_colon.is_some() { 59 return Err(()); 60 } 61 (ModuleRelative::No, CratePath::CrateRelative) 62 } 63 "self" | "super" => { 64 if path.leading_colon.is_some() { 65 return Err(()); 66 } 67 (ModuleRelative::Yes, CratePath::ModuleRelative) 68 } 69 _ => (ModuleRelative::No, CratePath::External), 70 }; 71 72 for seg in path.segments.iter().skip(1) { 73 let ident = seg.ident.to_string(); 74 match ident.as_str() { 75 "Self" | "crate" | "self" => return Err(()), 76 "super" => match prev_segment { 77 ModuleRelative::Yes => {} 78 ModuleRelative::No => return Err(()), 79 }, 80 _ => { 81 prev_segment = ModuleRelative::No; 82 } 83 } 84 } 85 Ok(path_type) 86 } 87 88 impl Ctx { 89 /// Attempt to extract a crate path from the provided attributes. Defaults to 90 /// `::zerocopy` if not found. 91 pub(crate) fn try_from_derive_input(ast: DeriveInput) -> Result<Self, Error> { 92 let mut path = parse_quote!(::zerocopy); 93 let mut skip_on_error = false; 94 let mut on_error_span = None; 95 96 for attr in &ast.attrs { 97 if let Meta::List(ref meta_list) = attr.meta { 98 if path_is_ident(&meta_list.path, "zerocopy") { 99 attr.parse_nested_meta(|meta| { 100 if path_is_ident(&meta.path, "crate") { 101 let expr = meta.value().and_then(ParseBuffer::parse); 102 if let Ok(Expr::Lit(ExprLit { lit: Lit::Str(lit), .. })) = expr { 103 if let Ok(mut path_lit) = lit.parse_with(Path::parse_mod_style) { 104 if let Ok(crate_path) = validate_crate_path(&path_lit) { 105 // If not expressly relative, absolutize. 106 if path_lit.leading_colon.is_none() && crate_path == CratePath::External { 107 path_lit.leading_colon = Some(PathSep::default()); 108 } 109 path = path_lit; 110 return Ok(()); 111 } 112 113 return Err(Error::new( 114 lit.span(), 115 "`crate` attribute requires a valid module path", 116 )); 117 } 118 119 return Err(Error::new( 120 lit.span(), 121 "`crate` attribute requires a path as the value", 122 )); 123 } 124 125 return Err(Error::new( 126 meta.path.span(), 127 "`crate` attribute requires a path as the value", 128 )); 129 } 130 131 if path_is_ident(&meta.path, "on_error") { 132 on_error_span = Some(meta.path.span()); 133 let value = meta.value()?; 134 let s: LitStr = value.parse()?; 135 match s.value().as_str() { 136 "skip" => skip_on_error = true, 137 "fail" => skip_on_error = false, 138 _ => return Err(Error::new( 139 s.span(), 140 "unrecognized value for `on_error` attribute from `zerocopy`; expected `skip` or `fail`", 141 )), 142 } 143 return Ok(()); 144 } 145 146 Err(Error::new( 147 Span::call_site(), 148 format!( 149 "unknown attribute encountered: {}", 150 meta.path.into_token_stream() 151 ), 152 )) 153 })?; 154 } 155 } 156 } 157 158 let invariant_span = crate::invariant::validate(&ast)?; 159 Ok(Self { ast, zerocopy_crate: path, skip_on_error, on_error_span, invariant_span }) 160 } 161 162 pub(crate) fn with_input(&self, input: &DeriveInput) -> Self { 163 Self { 164 ast: input.clone(), 165 zerocopy_crate: self.zerocopy_crate.clone(), 166 skip_on_error: self.skip_on_error, 167 on_error_span: self.on_error_span, 168 invariant_span: self.invariant_span, 169 } 170 } 171 172 pub(crate) fn skip_on_error(mut self) -> Self { 173 self.skip_on_error = true; 174 self 175 } 176 177 pub(crate) fn core_path(&self) -> TokenStream { 178 let zerocopy_crate = &self.zerocopy_crate; 179 quote!(#zerocopy_crate::util::macro_util::core_reexport) 180 } 181 182 /// Choose an implementation name absent from the caller's tokens. This is 183 /// best-effort; names introduced by nested macros are not considered. 184 /// Unlike local variables, type parameters are not hidden by mixed-site 185 /// hygiene. 186 pub(crate) fn fresh_ident(&self, name: &str) -> Ident { 187 let idents = crate::invariant::idents(self.ast.to_token_stream()); 188 let mut name = name.to_owned(); 189 while idents.iter().any(|ident| ident.to_string().trim_start_matches("r#") == name) { 190 name.push('_'); 191 } 192 Ident::new(&name, Span::mixed_site()) 193 } 194 195 /// Caller-authored invariant expressions must inherit the caller's lint 196 /// policy, rather than the blanket allowances for generated glue. 197 pub(crate) fn const_block( 198 &self, 199 items: impl IntoIterator<Item = Option<TokenStream>>, 200 ) -> TokenStream { 201 if self.invariant_span.is_none() { 202 const_block(items) 203 } else { 204 let items = items.into_iter().flatten(); 205 quote! { const _: () = { #(#items)* }; } 206 } 207 } 208 209 pub(crate) fn cfg_compile_error(&self) -> TokenStream { 210 // By checking both during the compilation of the proc macro *and* in 211 // the generated code, we ensure that each cfg need only be passed when 212 // compiling this crate *or* when compiling the user's crate. The former 213 // is preferable, but in some situations (such as when cross-compiling 214 // using `cargo build --target`), it doesn't get propagated to this 215 // crate's build by default. 216 let on_error = if cfg!(zerocopy_unstable_linux) { 217 quote!() 218 } else if let Some(span) = self.on_error_span { 219 let core = self.core_path(); 220 let error_message = 221 "`on_error` is experimental; pass '--cfg zerocopy_unstable_linux' to enable"; 222 quote::quote_spanned! {span=> 223 #[allow(unused_attributes, unexpected_cfgs)] 224 const _: () = { 225 #[cfg(not(zerocopy_unstable_linux))] 226 #core::compile_error!(#error_message); 227 }; 228 } 229 } else { 230 quote!() 231 }; 232 let invariant = if cfg!(zerocopy_unstable_ptr) { 233 quote!() 234 } else if let Some(span) = self.invariant_span { 235 let core = self.core_path(); 236 let error_message = 237 "`invariant` is experimental; pass '--cfg zerocopy_unstable_ptr' to enable"; 238 quote::quote_spanned! {span=> 239 #[allow(unused_attributes, unexpected_cfgs)] 240 const _: () = { 241 #[cfg(not(zerocopy_unstable_ptr))] 242 #core::compile_error!(#error_message); 243 }; 244 } 245 } else { 246 quote!() 247 }; 248 quote!(#on_error #invariant) 249 } 250 251 pub(crate) fn error_or_skip<E>(&self, error: E) -> Result<TokenStream, E> { 252 if self.skip_on_error { 253 Ok(self.cfg_compile_error()) 254 } else { 255 Err(error) 256 } 257 } 258 } 259 260 pub(crate) trait DataExt { 261 /// Extracts the names and types of all fields. For enums, extracts the 262 /// names and types of fields from each variant. For tuple structs, the 263 /// names are the indices used to index into the struct (ie, `0`, `1`, etc). 264 /// 265 /// FIXME: Extracting field names for enums doesn't really make sense. Types 266 /// makes sense because we don't care about where they live - we just care 267 /// about transitive ownership. But for field names, we'd only use them when 268 /// generating is_safe, which cares about where they live. 269 fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)>; 270 271 fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)>; 272 273 fn tag(&self) -> Option<Ident>; 274 } 275 276 impl DataExt for Data { 277 fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> { 278 match self { 279 Data::Struct(strc) => strc.fields(), 280 Data::Enum(enm) => enm.fields(), 281 Data::Union(un) => un.fields(), 282 } 283 } 284 285 fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> { 286 match self { 287 Data::Struct(strc) => strc.variants(), 288 Data::Enum(enm) => enm.variants(), 289 Data::Union(un) => un.variants(), 290 } 291 } 292 293 fn tag(&self) -> Option<Ident> { 294 match self { 295 Data::Struct(strc) => strc.tag(), 296 Data::Enum(enm) => enm.tag(), 297 Data::Union(un) => un.tag(), 298 } 299 } 300 } 301 302 impl DataExt for DataStruct { 303 fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> { 304 map_fields(&self.fields) 305 } 306 307 fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> { 308 vec![(None, self.fields())] 309 } 310 311 fn tag(&self) -> Option<Ident> { 312 None 313 } 314 } 315 316 impl DataExt for DataEnum { 317 fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> { 318 map_fields(self.variants.iter().flat_map(|var| &var.fields)) 319 } 320 321 fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> { 322 self.variants.iter().map(|var| (Some(var), map_fields(&var.fields))).collect() 323 } 324 325 fn tag(&self) -> Option<Ident> { 326 Some(Ident::new("___ZerocopyTag", Span::call_site())) 327 } 328 } 329 330 impl DataExt for DataUnion { 331 fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> { 332 map_fields(&self.fields.named) 333 } 334 335 fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> { 336 vec![(None, self.fields())] 337 } 338 339 fn tag(&self) -> Option<Ident> { 340 None 341 } 342 } 343 344 fn map_fields<'a>( 345 fields: impl 'a + IntoIterator<Item = &'a Field>, 346 ) -> Vec<(&'a Visibility, TokenStream, &'a Type)> { 347 fields 348 .into_iter() 349 .enumerate() 350 .map(|(idx, f)| { 351 ( 352 &f.vis, 353 f.ident 354 .as_ref() 355 .map(ToTokens::to_token_stream) 356 .unwrap_or_else(|| Index::from(idx).to_token_stream()), 357 &f.ty, 358 ) 359 }) 360 .collect() 361 } 362 363 pub(crate) fn to_ident_str(t: &impl ToString) -> String { 364 let s = t.to_string(); 365 if let Some(stripped) = s.strip_prefix("r#") { 366 stripped.to_string() 367 } else { 368 s 369 } 370 } 371 372 /// Does `path` consist solely of the identifier `expected`? 373 /// 374 /// Unlike [`Path::is_ident`], this treats a raw identifier and its ordinary 375 /// spelling as the same identifier. Rust applies that same normalization when 376 /// interpreting attribute names and arguments. 377 pub(crate) fn path_is_ident(path: &Path, expected: &str) -> bool { 378 match path.get_ident() { 379 Some(ident) => to_ident_str(ident) == expected, 380 None => false, 381 } 382 } 383 384 /// This enum describes what kind of padding check needs to be generated for the 385 /// associated impl. 386 pub(crate) enum PaddingCheck { 387 /// Check that the sum of the fields' sizes exactly equals the struct's 388 /// size. 389 Struct, 390 /// Check that a `repr(C)` struct has no padding. 391 ReprCStruct, 392 /// Check that the size of each field exactly equals the union's size. 393 Union, 394 /// Check that every variant of the enum contains no padding. 395 /// 396 /// Because doing so requires a tag enum, this padding check requires an 397 /// additional `TokenStream` which defines the tag enum as `___ZerocopyTag`. 398 Enum { tag_type_definition: TokenStream }, 399 } 400 401 impl PaddingCheck { 402 /// Returns the idents of the trait to use and the macro to call in order to 403 /// validate that a type passes the relevant padding check. 404 pub(crate) fn validator_trait_and_macro_idents(&self) -> (Ident, Ident) { 405 let (trt, mcro) = match self { 406 PaddingCheck::Struct => ("PaddingFree", "struct_padding"), 407 PaddingCheck::ReprCStruct => ("DynamicPaddingFree", "repr_c_struct_has_padding"), 408 PaddingCheck::Union => ("PaddingFree", "union_padding"), 409 PaddingCheck::Enum { .. } => ("PaddingFree", "enum_padding"), 410 }; 411 412 let trt = Ident::new(trt, Span::call_site()); 413 let mcro = Ident::new(mcro, Span::call_site()); 414 (trt, mcro) 415 } 416 417 /// Sometimes performing the padding check requires some additional 418 /// "context" code. For enums, this is the definition of the tag enum. 419 pub(crate) fn validator_macro_context(&self) -> Option<&TokenStream> { 420 match self { 421 PaddingCheck::Struct | PaddingCheck::ReprCStruct | PaddingCheck::Union => None, 422 PaddingCheck::Enum { tag_type_definition } => Some(tag_type_definition), 423 } 424 } 425 } 426 427 #[derive(Copy, Clone)] 428 pub(crate) enum Client { 429 ProjectDerive, 430 TryFromBytesDerive, 431 } 432 433 impl ToTokens for Client { 434 fn to_tokens(&self, tokens: &mut TokenStream) { 435 let s = match self { 436 Client::ProjectDerive => "ProjectDerive", 437 Client::TryFromBytesDerive => "TryFromBytesDerive", 438 }; 439 let ident = Ident::new(s, Span::call_site()); 440 tokens.extend(quote!(project_clients::#ident)); 441 } 442 } 443 444 impl Client { 445 pub(crate) fn crate_path(&self, ctx: &Ctx) -> Path { 446 let zerocopy_crate = &ctx.zerocopy_crate; 447 parse_quote!(#zerocopy_crate::#self) 448 } 449 } 450 451 /// Bundles the trait parameters for `HasField` and `ProjectField`. 452 #[derive(Clone)] 453 pub(crate) struct FieldProjection { 454 pub(crate) variant_id: Box<Expr>, 455 pub(crate) field: Box<Type>, 456 pub(crate) field_id: Box<Expr>, 457 } 458 459 #[derive(Clone)] 460 pub(crate) enum Trait { 461 KnownLayout, 462 HasTag { client: Client }, 463 HasField { client: Client, projection: FieldProjection }, 464 ProjectField { client: Client, projection: FieldProjection, invariants: Box<Type> }, 465 Immutable, 466 Project, 467 TryFromBytes, 468 FromZeros, 469 FromBytes, 470 IntoBytes, 471 Unaligned, 472 Sized, 473 ByteHash, 474 ByteEq, 475 SplitAt, 476 } 477 478 impl ToTokens for Trait { 479 fn to_tokens(&self, tokens: &mut TokenStream) { 480 // According to [1], the format of the derived `Debug`` output is not 481 // stable and therefore not guaranteed to represent the variant names. 482 // Indeed with the (unstable) `fmt-debug` compiler flag [2], it can 483 // return only a minimalized output or empty string. To make sure this 484 // code will work in the future and independent of the compiler flag, we 485 // translate the variants to their names manually here. 486 // 487 // [1] https://doc.rust-lang.org/1.81.0/std/fmt/trait.Debug.html#stability 488 // [2] https://doc.rust-lang.org/beta/unstable-book/compiler-flags/fmt-debug.html 489 let s = match self { 490 Trait::HasField { .. } => "HasField", 491 Trait::ProjectField { .. } => "ProjectField", 492 Trait::KnownLayout => "KnownLayout", 493 Trait::HasTag { .. } => "HasTag", 494 Trait::Immutable => "Immutable", 495 Trait::Project => "Project", 496 Trait::TryFromBytes => "TryFromBytes", 497 Trait::FromZeros => "FromZeros", 498 Trait::FromBytes => "FromBytes", 499 Trait::IntoBytes => "IntoBytes", 500 Trait::Unaligned => "Unaligned", 501 Trait::Sized => "Sized", 502 Trait::ByteHash => "ByteHash", 503 Trait::ByteEq => "ByteEq", 504 Trait::SplitAt => "SplitAt", 505 }; 506 let ident = Ident::new(s, Span::call_site()); 507 let arguments: Option<syn::AngleBracketedGenericArguments> = match self { 508 Trait::HasTag { client } => Some(parse_quote!(<#client>)), 509 Trait::HasField { client, projection } => { 510 let FieldProjection { variant_id, field, field_id } = projection; 511 Some(parse_quote!(<#client, #field, #variant_id, #field_id>)) 512 } 513 Trait::ProjectField { client, projection, invariants } => { 514 let FieldProjection { variant_id, field, field_id } = projection; 515 Some(parse_quote!(<#client, #field, #invariants, #variant_id, #field_id>)) 516 } 517 Trait::KnownLayout 518 | Trait::Immutable 519 | Trait::Project 520 | Trait::TryFromBytes 521 | Trait::FromZeros 522 | Trait::FromBytes 523 | Trait::IntoBytes 524 | Trait::Unaligned 525 | Trait::Sized 526 | Trait::ByteHash 527 | Trait::ByteEq 528 | Trait::SplitAt => None, 529 }; 530 tokens.extend(quote!(#ident #arguments)); 531 } 532 } 533 534 impl Trait { 535 pub(crate) fn crate_path(&self, ctx: &Ctx) -> Path { 536 let zerocopy_crate = &ctx.zerocopy_crate; 537 let core = ctx.core_path(); 538 match self { 539 Self::Sized => parse_quote!(#core::marker::#self), 540 Self::HasTag { client } => { 541 let client = client.crate_path(ctx); 542 parse_quote!(#zerocopy_crate::HasTag<#client>) 543 } 544 Self::HasField { client, projection } => { 545 let FieldProjection { variant_id, field, field_id } = projection; 546 let client = client.crate_path(ctx); 547 parse_quote!(#zerocopy_crate::HasField<#client, #field, #variant_id, #field_id>) 548 } 549 Self::ProjectField { client, projection, invariants } => { 550 let FieldProjection { variant_id, field, field_id } = projection; 551 let client = client.crate_path(ctx); 552 parse_quote!( 553 #zerocopy_crate::ProjectField< 554 #client, 555 #field, 556 #invariants, 557 #variant_id, 558 #field_id 559 > 560 ) 561 } 562 _ => parse_quote!(#zerocopy_crate::#self), 563 } 564 } 565 } 566 567 pub(crate) enum TraitBound { 568 Slf, 569 Other(Trait), 570 } 571 572 pub(crate) enum FieldBounds<'a> { 573 None, 574 All(&'a [TraitBound]), 575 Trailing(&'a [TraitBound]), 576 Explicit(Vec<WherePredicate>), 577 } 578 579 impl<'a> FieldBounds<'a> { 580 pub(crate) const ALL_SELF: FieldBounds<'a> = FieldBounds::All(&[TraitBound::Slf]); 581 pub(crate) const TRAILING_SELF: FieldBounds<'a> = FieldBounds::Trailing(&[TraitBound::Slf]); 582 } 583 584 pub(crate) enum SelfBounds<'a> { 585 None, 586 All(&'a [Trait]), 587 } 588 589 // FIXME(https://github.com/rust-lang/rust-clippy/issues/12908): This is a false 590 // positive. Explicit lifetimes are actually necessary here. 591 #[allow(clippy::needless_lifetimes)] 592 impl<'a> SelfBounds<'a> { 593 pub(crate) const SIZED: Self = Self::All(&[Trait::Sized]); 594 } 595 596 /// Normalizes a slice of bounds by replacing [`TraitBound::Slf`] with `slf`. 597 pub(crate) fn normalize_bounds<'a>( 598 slf: &'a Trait, 599 bounds: &'a [TraitBound], 600 ) -> impl 'a + Iterator<Item = Trait> { 601 bounds.iter().map(move |bound| match bound { 602 TraitBound::Slf => slf.clone(), 603 TraitBound::Other(trt) => trt.clone(), 604 }) 605 } 606 607 pub(crate) struct ImplBlockBuilder<'a> { 608 ctx: &'a Ctx, 609 data: &'a dyn DataExt, 610 trt: Trait, 611 field_type_trait_bounds: FieldBounds<'a>, 612 self_type_trait_bounds: SelfBounds<'a>, 613 padding_check: Option<PaddingCheck>, 614 param_extras: Vec<GenericParam>, 615 inner_extras: Option<TokenStream>, 616 outer_extras: Option<TokenStream>, 617 } 618 619 impl<'a> ImplBlockBuilder<'a> { 620 pub(crate) fn new( 621 ctx: &'a Ctx, 622 data: &'a dyn DataExt, 623 trt: Trait, 624 field_type_trait_bounds: FieldBounds<'a>, 625 ) -> Self { 626 Self { 627 ctx, 628 data, 629 trt, 630 field_type_trait_bounds, 631 self_type_trait_bounds: SelfBounds::None, 632 padding_check: None, 633 param_extras: Vec::new(), 634 inner_extras: None, 635 outer_extras: None, 636 } 637 } 638 639 pub(crate) fn self_type_trait_bounds(mut self, self_type_trait_bounds: SelfBounds<'a>) -> Self { 640 self.self_type_trait_bounds = self_type_trait_bounds; 641 self 642 } 643 644 pub(crate) fn padding_check<P: Into<Option<PaddingCheck>>>(mut self, padding_check: P) -> Self { 645 self.padding_check = padding_check.into(); 646 self 647 } 648 649 pub(crate) fn param_extras(mut self, param_extras: Vec<GenericParam>) -> Self { 650 self.param_extras.extend(param_extras); 651 self 652 } 653 654 pub(crate) fn inner_extras(mut self, inner_extras: TokenStream) -> Self { 655 self.inner_extras = Some(inner_extras); 656 self 657 } 658 659 pub(crate) fn outer_extras<T: Into<Option<TokenStream>>>(mut self, outer_extras: T) -> Self { 660 self.outer_extras = outer_extras.into(); 661 self 662 } 663 664 pub(crate) fn build(self) -> TokenStream { 665 // In this documentation, we will refer to this hypothetical struct: 666 // 667 // #[derive(FromBytes)] 668 // struct Foo<T, I: Iterator> 669 // where 670 // T: Copy, 671 // I: Clone, 672 // I::Item: Clone, 673 // { 674 // a: u8, 675 // b: T, 676 // c: I::Item, 677 // } 678 // 679 // We extract the field types, which in this case are `u8`, `T`, and 680 // `I::Item`. We re-use the existing parameters and where clauses. If 681 // `require_trait_bound == true` (as it is for `FromBytes), we add where 682 // bounds for each field's type: 683 // 684 // impl<T, I: Iterator> FromBytes for Foo<T, I> 685 // where 686 // T: Copy, 687 // I: Clone, 688 // I::Item: Clone, 689 // T: FromBytes, 690 // I::Item: FromBytes, 691 // { 692 // } 693 // 694 // NOTE: It is standard practice to only emit bounds for the type 695 // parameters themselves, not for field types based on those parameters 696 // (e.g., `T` vs `T::Foo`). For a discussion of why this is standard 697 // practice, see https://github.com/rust-lang/rust/issues/26925. 698 // 699 // The reason we diverge from this standard is that doing it that way 700 // for us would be unsound. E.g., consider a type, `T` where `T: 701 // FromBytes` but `T::Foo: !FromBytes`. It would not be sound for us to 702 // accept a type with a `T::Foo` field as `FromBytes` simply because `T: 703 // FromBytes`. 704 // 705 // While there's no getting around this requirement for us, it does have 706 // the pretty serious downside that, when lifetimes are involved, the 707 // trait solver ties itself in knots: 708 // 709 // #[derive(Unaligned)] 710 // #[repr(C)] 711 // struct Dup<'a, 'b> { 712 // a: PhantomData<&'a u8>, 713 // b: PhantomData<&'b u8>, 714 // } 715 // 716 // error[E0283]: type annotations required: cannot resolve `core::marker::PhantomData<&'a u8>: zerocopy::Unaligned` 717 // --> src/main.rs:6:10 718 // | 719 // 6 | #[derive(Unaligned)] 720 // | ^^^^^^^^^ 721 // | 722 // = note: required by `zerocopy::Unaligned` 723 724 let type_ident = &self.ctx.ast.ident; 725 let trait_path = self.trt.crate_path(self.ctx); 726 let fields = self.data.fields(); 727 let variants = self.data.variants(); 728 let tag = self.data.tag(); 729 let zerocopy_crate = &self.ctx.zerocopy_crate; 730 731 fn bound_tt(ty: &Type, traits: impl Iterator<Item = Trait>, ctx: &Ctx) -> WherePredicate { 732 let traits = traits.map(|t| t.crate_path(ctx)); 733 parse_quote!(#ty: #(#traits)+*) 734 } 735 let field_type_bounds: Vec<_> = match (self.field_type_trait_bounds, &fields[..]) { 736 (FieldBounds::All(traits), _) => fields 737 .iter() 738 .map(|(_vis, _name, ty)| { 739 bound_tt(ty, normalize_bounds(&self.trt, traits), self.ctx) 740 }) 741 .collect(), 742 (FieldBounds::None, _) | (FieldBounds::Trailing(..), []) => vec![], 743 (FieldBounds::Trailing(traits), [.., last]) => { 744 vec![bound_tt(last.2, normalize_bounds(&self.trt, traits), self.ctx)] 745 } 746 (FieldBounds::Explicit(bounds), _) => bounds, 747 }; 748 749 let padding_check_bound = self 750 .padding_check 751 .map(|check| { 752 // Parse the repr for `align` and `packed` modifiers. Note that 753 // `Repr::<PrimitiveRepr, NonZeroU32>` is more permissive than 754 // what Rust supports for structs, enums, or unions, and thus 755 // reliably extracts these modifiers for any kind of type. 756 let repr = 757 Repr::<PrimitiveRepr, NonZeroU32>::from_attrs(&self.ctx.ast.attrs).unwrap(); 758 let core = self.ctx.core_path(); 759 let option = quote! { #core::option::Option }; 760 let nonzero = quote! { #core::num::NonZeroUsize }; 761 let none = quote! { #option::None::<#nonzero> }; 762 let repr_align = 763 repr.get_align().map(|spanned| { 764 let n = spanned.t.get(); 765 quote_spanned! { spanned.span => (#nonzero::new(#n as usize)) } 766 }).unwrap_or(quote! { (#none) }); 767 let repr_packed = 768 repr.get_packed().map(|packed| { 769 let n = packed.get(); 770 quote! { (#nonzero::new(#n as usize)) } 771 }).unwrap_or(quote! { (#none) }); 772 let variant_types = variants.iter().map(|(_, fields)| { 773 let types = fields.iter().map(|(_vis, _name, ty)| ty); 774 quote!([#((#types)),*]) 775 }); 776 let validator_context = check.validator_macro_context(); 777 let (trt, validator_macro) = check.validator_trait_and_macro_idents(); 778 let t = tag.iter(); 779 parse_quote! { 780 (): #zerocopy_crate::util::macro_util::#trt< 781 Self, 782 { 783 #validator_context 784 #zerocopy_crate::#validator_macro!(Self, #repr_align, #repr_packed, #(#t,)* #(#variant_types),*) 785 } 786 > 787 } 788 }); 789 790 let self_bounds: Option<WherePredicate> = match self.self_type_trait_bounds { 791 SelfBounds::None => None, 792 SelfBounds::All(traits) => { 793 Some(bound_tt(&parse_quote!(Self), traits.iter().cloned(), self.ctx)) 794 } 795 }; 796 797 let zerocopy_bounds = 798 field_type_bounds 799 .into_iter() 800 .chain(padding_check_bound) 801 .chain(self_bounds) 802 .map(|bound| { 803 if self.ctx.skip_on_error { 804 parse_quote!(for<'zc> #bound) 805 } else { 806 bound.clone() 807 } 808 }) 809 .collect::<Vec<_>>(); 810 811 let bounds = self 812 .ctx 813 .ast 814 .generics 815 .where_clause 816 .as_ref() 817 .map(|where_clause| where_clause.predicates.iter()) 818 .into_iter() 819 .flatten() 820 .chain(zerocopy_bounds.iter()); 821 822 // The parameters with trait bounds, but without type defaults. 823 let mut params: Vec<_> = self 824 .ctx 825 .ast 826 .generics 827 .params 828 .clone() 829 .into_iter() 830 .map(|mut param| { 831 match &mut param { 832 GenericParam::Type(ty) => ty.default = None, 833 GenericParam::Const(cnst) => cnst.default = None, 834 GenericParam::Lifetime(_) => {} 835 } 836 parse_quote!(#param) 837 }) 838 .chain(self.param_extras) 839 .collect(); 840 841 // For MSRV purposes, ensure that lifetimes precede types precede const 842 // generics. 843 params.sort_by_cached_key(|param| match param { 844 GenericParam::Lifetime(_) => 0, 845 GenericParam::Type(_) => 1, 846 GenericParam::Const(_) => 2, 847 }); 848 849 // The identifiers of the parameters without trait bounds or type 850 // defaults. 851 let param_idents = self.ctx.ast.generics.params.iter().map(|param| match param { 852 GenericParam::Type(ty) => { 853 let ident = &ty.ident; 854 quote!(#ident) 855 } 856 GenericParam::Lifetime(l) => { 857 let ident = &l.lifetime; 858 quote!(#ident) 859 } 860 GenericParam::Const(cnst) => { 861 let ident = &cnst.ident; 862 quote!({#ident}) 863 } 864 }); 865 866 let inner_extras = self.inner_extras; 867 let allow_trivial_bounds = 868 if self.ctx.skip_on_error { quote!(#[allow(trivial_bounds)]) } else { quote!() }; 869 let impl_tokens = quote! { 870 #allow_trivial_bounds 871 unsafe impl < #(#params),* > #trait_path for #type_ident < #(#param_idents),* > 872 where 873 #(#bounds,)* 874 { 875 fn only_derive_is_allowed_to_implement_this_trait() {} 876 877 #inner_extras 878 } 879 }; 880 881 let outer_extras = self.outer_extras.filter(|e| !e.is_empty()); 882 let cfg_compile_error = self.ctx.cfg_compile_error(); 883 let items = [Some(cfg_compile_error), Some(impl_tokens), outer_extras]; 884 if matches!(self.trt, Trait::TryFromBytes) { 885 self.ctx.const_block(items) 886 } else { 887 const_block(items) 888 } 889 } 890 } 891 892 // A polyfill for `Option::then_some`, which was added after our MSRV. 893 // 894 // The `#[allow(unused)]` is necessary because, on sufficiently recent toolchain 895 // versions, `b.then_some(...)` resolves to the inherent method rather than to 896 // this trait, and so this trait is considered unused. 897 // 898 // FIXME(#67): Remove this once our MSRV is >= 1.62. 899 #[allow(unused)] 900 trait BoolExt { 901 fn then_some<T>(self, t: T) -> Option<T>; 902 } 903 904 impl BoolExt for bool { 905 fn then_some<T>(self, t: T) -> Option<T> { 906 if self { 907 Some(t) 908 } else { 909 None 910 } 911 } 912 } 913 914 pub(crate) fn allow_generated_code() -> TokenStream { 915 quote! { 916 #[allow( 917 // FIXME(#553): Add a test that generates a warning when 918 // `#[allow(deprecated)]` isn't present. 919 deprecated, 920 // Required on some rustc versions due to a lint that is only 921 // triggered when `derive(KnownLayout)` is applied to `repr(C)` 922 // structs that are generated by macros. See #2177 for details. 923 private_bounds, 924 non_local_definitions, 925 non_camel_case_types, 926 non_upper_case_globals, 927 non_snake_case, 928 non_ascii_idents, 929 clippy::missing_inline_in_public_items, 930 )] 931 } 932 } 933 934 pub(crate) fn const_block(items: impl IntoIterator<Item = Option<TokenStream>>) -> TokenStream { 935 let items = items.into_iter().flatten(); 936 let allow = allow_generated_code(); 937 quote! { 938 #allow 939 #[deny(ambiguous_associated_items)] 940 // While there are not currently any warnings that this suppresses 941 // (that we're aware of), it's good future-proofing hygiene. 942 #[automatically_derived] 943 const _: () = { 944 #(#items)* 945 }; 946 } 947 } 948 pub(crate) fn generate_tag_enum(ctx: &Ctx, repr: &EnumRepr, data: &DataEnum) -> TokenStream { 949 let zerocopy_crate = &ctx.zerocopy_crate; 950 let variants = data.variants.iter().map(|v| { 951 let ident = &v.ident; 952 if let Some((eq, discriminant)) = &v.discriminant { 953 quote! { #ident #eq #discriminant } 954 } else { 955 quote! { #ident } 956 } 957 }); 958 959 // Don't include any `repr(align)` when generating the tag enum, as that 960 // could add padding after the tag but before any variants, which is not the 961 // correct behavior. 962 let repr = match repr { 963 EnumRepr::Transparent(span) => quote::quote_spanned! { *span => #[repr(transparent)] }, 964 EnumRepr::Compound(c, _) => quote! { #c }, 965 }; 966 967 quote! { 968 #repr 969 #[allow(dead_code, clippy::derive_partial_eq_without_eq)] 970 #[derive(Copy, Clone, PartialEq)] 971 pub enum ___ZerocopyTag { 972 #(#variants,)* 973 } 974 975 // SAFETY: `___ZerocopyTag` has no fields, and so it does not permit 976 // interior mutation. 977 unsafe impl #zerocopy_crate::Immutable for ___ZerocopyTag { 978 fn only_derive_is_allowed_to_implement_this_trait() {} 979 } 980 } 981 } 982 pub(crate) fn enum_size_from_repr(repr: &EnumRepr) -> Result<usize, Error> { 983 use CompoundRepr::*; 984 use PrimitiveRepr::*; 985 use Repr::*; 986 match repr { 987 Transparent(span) 988 | Compound( 989 Spanned { 990 t: C | Rust | Primitive(U32 | I32 | U64 | I64 | U128 | I128 | Usize | Isize), 991 span, 992 }, 993 _, 994 ) => Err(Error::new( 995 *span, 996 "`FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16`", 997 )), 998 Compound(Spanned { t: Primitive(U8 | I8), span: _ }, _align) => Ok(8), 999 Compound(Spanned { t: Primitive(U16 | I16), span: _ }, _align) => Ok(16), 1000 } 1001 } 1002 1003 #[cfg(test)] 1004 pub(crate) mod testutil { 1005 use proc_macro2::TokenStream; 1006 use syn::visit::{self, Visit}; 1007 1008 /// Checks for hygiene violations in the generated code. 1009 /// 1010 /// # Panics 1011 /// 1012 /// Panics if a hygiene violation is found. 1013 pub(crate) fn check_hygiene(ts: TokenStream) { 1014 struct AmbiguousItemVisitor; 1015 1016 fn panic_ambiguous_path(path: &impl quote::ToTokens) -> ! { 1017 panic!( 1018 "Found ambiguous path `{}` in generated output. \ 1019 All associated item access must be fully qualified (e.g., `<Self as Trait>::Item`) \ 1020 to prevent hygiene issues.", 1021 quote::quote!(#path) 1022 ); 1023 } 1024 1025 impl<'ast> Visit<'ast> for AmbiguousItemVisitor { 1026 fn visit_expr_path(&mut self, i: &'ast syn::ExprPath) { 1027 if let Some(qself) = &i.qself { 1028 if qself.position == 0 { 1029 panic_ambiguous_path(i); 1030 } 1031 } 1032 visit::visit_expr_path(self, i); 1033 } 1034 1035 fn visit_path(&mut self, i: &'ast syn::Path) { 1036 if i.segments.len() > 1 && i.segments.first().unwrap().ident == "Self" { 1037 panic_ambiguous_path(i); 1038 } 1039 visit::visit_path(self, i); 1040 } 1041 1042 fn visit_type_path(&mut self, i: &'ast syn::TypePath) { 1043 if let Some(qself) = &i.qself { 1044 if qself.position == 0 { 1045 panic_ambiguous_path(i); 1046 } 1047 } 1048 visit::visit_type_path(self, i); 1049 } 1050 } 1051 1052 let file = syn::parse2::<syn::File>(ts).expect("failed to parse generated output as File"); 1053 AmbiguousItemVisitor.visit_file(&file); 1054 } 1055 1056 #[test] 1057 fn test_check_hygiene_success() { 1058 check_hygiene(quote::quote! { 1059 fn foo() { 1060 let _ = <Self as Trait>::Item; 1061 } 1062 }); 1063 } 1064 1065 #[test] 1066 #[should_panic(expected = "Found ambiguous path `Self :: Ambiguous`")] 1067 fn test_check_hygiene_failure() { 1068 check_hygiene(quote::quote! { 1069 fn foo() { 1070 let _ = Self::Ambiguous; 1071 } 1072 }); 1073 } 1074 1075 #[test] 1076 #[should_panic(expected = "Found ambiguous path `< T > :: Ambiguous`")] 1077 fn test_check_hygiene_type_relative_expr_failure() { 1078 check_hygiene(quote::quote! { 1079 fn foo<T>() { 1080 let _ = <T>::Ambiguous; 1081 } 1082 }); 1083 } 1084 1085 #[test] 1086 #[should_panic(expected = "Found ambiguous path `< T > :: Ambiguous`")] 1087 fn test_check_hygiene_type_relative_type_failure() { 1088 check_hygiene(quote::quote! { 1089 fn foo<T>() { 1090 let _: <T>::Ambiguous; 1091 } 1092 }); 1093 } 1094 1095 #[test] 1096 fn test_validate_crate_path() { 1097 use syn::parse_str; 1098 1099 let valid = [ 1100 "zerocopy", 1101 "crate", 1102 "crate::foo::bar", 1103 "self", 1104 "self::foo", 1105 "self::super::foo", 1106 "super", 1107 "super::foo", 1108 "super::super::foo", 1109 "super::super::super", 1110 "foo::bar::baz", 1111 "::foo::bar", 1112 ]; 1113 1114 for path_str in valid { 1115 let path = parse_str::<syn::Path>(path_str).unwrap(); 1116 assert!( 1117 super::validate_crate_path(&path).is_ok(), 1118 "expected valid path for `{}`", 1119 path_str 1120 ); 1121 } 1122 1123 let invalid = [ 1124 "::crate::foo", 1125 "::self::foo", 1126 "::super::foo", 1127 "Self", 1128 "Self::foo", 1129 "foo::Self::bar", 1130 "foo::crate::bar", 1131 "foo::super::bar", 1132 "foo::self", 1133 "super::foo::super", 1134 "super::crate::foo", 1135 "crate::super::foo", 1136 "self::self::foo", 1137 ]; 1138 1139 for path_str in invalid { 1140 let path = parse_str::<syn::Path>(path_str).unwrap(); 1141 assert!( 1142 super::validate_crate_path(&path).is_err(), 1143 "expected invalid path for `{}`", 1144 path_str 1145 ); 1146 } 1147 } 1148 1149 #[test] 1150 fn test_path_is_ident() { 1151 use syn::parse_str; 1152 1153 for (expected, ordinary, raw) in [ 1154 ("doc", "doc", "r#doc"), 1155 ("repr", "repr", "r#repr"), 1156 ("zerocopy", "zerocopy", "r#zerocopy"), 1157 ("on_error", "on_error", "r#on_error"), 1158 ] { 1159 let ordinary = parse_str::<syn::Path>(ordinary).unwrap(); 1160 let raw = parse_str::<syn::Path>(raw).unwrap(); 1161 assert!(super::path_is_ident(&ordinary, expected)); 1162 assert!(super::path_is_ident(&raw, expected)); 1163 } 1164 1165 // `r#crate` is forbidden by Rust's raw identifier grammar. 1166 let crate_path = parse_str::<syn::Path>("crate").unwrap(); 1167 assert!(super::path_is_ident(&crate_path, "crate")); 1168 1169 for path in ["::zerocopy", "module::zerocopy"] { 1170 let path = parse_str::<syn::Path>(path).unwrap(); 1171 assert!(!super::path_is_ident(&path, "zerocopy")); 1172 } 1173 } 1174 1175 #[test] 1176 fn test_raw_zerocopy_attributes() { 1177 let ast: syn::DeriveInput = syn::parse_quote! { 1178 #[r#zerocopy(crate = "renamed", r#on_error = "skip")] 1179 struct Foo; 1180 }; 1181 1182 let ctx = super::Ctx::try_from_derive_input(ast).unwrap(); 1183 let path = &ctx.zerocopy_crate; 1184 assert_eq!(quote::quote!(#path).to_string(), ":: renamed"); 1185 assert!(ctx.skip_on_error); 1186 } 1187 } 1188