1 // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT 2 3 use proc_macro2::TokenStream; 4 use quote::quote; 5 use syn::{ 6 parse_quote, spanned::Spanned as _, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Error, 7 Expr, Fields, Ident, Index, Type, 8 }; 9 10 use crate::{ 11 repr::{EnumRepr, StructUnionRepr}, 12 util::{ 13 const_block, enum_size_from_repr, generate_tag_enum, Ctx, DataExt, FieldBounds, 14 ImplBlockBuilder, Trait, TraitBound, 15 }, 16 }; 17 fn tag_ident(variant_ident: &Ident) -> Ident { 18 ident!(("___ZEROCOPY_TAG_{}", variant_ident), variant_ident.span()) 19 } 20 21 /// Generates a constant for the tag associated with each variant of the enum. 22 /// When we match on the enum's tag, each arm matches one of these constants. We 23 /// have to use constants here because: 24 /// 25 /// - The type that we're matching on is not the type of the tag, it's an 26 /// integer of the same size as the tag type and with the same bit patterns. 27 /// - We can't read the enum tag as an enum because the bytes may not represent 28 /// a valid variant. 29 /// - Patterns do not currently support const expressions, so we have to assign 30 /// these constants to names rather than use them inline in the `match` 31 /// statement. 32 fn generate_tag_consts(data: &DataEnum) -> TokenStream { 33 let tags = data.variants.iter().map(|v| { 34 let variant_ident = &v.ident; 35 let tag_ident = tag_ident(variant_ident); 36 37 quote! { 38 // This casts the enum variant to its discriminant, and then 39 // converts the discriminant to the target integral type via a 40 // numeric cast [1]. 41 // 42 // Because these are the same size, this is defined to be a no-op 43 // and therefore is a lossless conversion [2]. 44 // 45 // [1] Per https://doc.rust-lang.org/1.81.0/reference/expressions/operator-expr.html#enum-cast: 46 // 47 // Casts an enum to its discriminant. 48 // 49 // [2] Per https://doc.rust-lang.org/1.81.0/reference/expressions/operator-expr.html#numeric-cast: 50 // 51 // Casting between two integers of the same size (e.g. i32 -> u32) 52 // is a no-op. 53 const #tag_ident: ___ZerocopyTagPrimitive = 54 ___ZerocopyTag::#variant_ident as ___ZerocopyTagPrimitive; 55 } 56 }); 57 58 quote! { 59 #(#tags)* 60 } 61 } 62 63 fn variant_struct_ident(variant_ident: &Ident) -> Ident { 64 ident!(("___ZerocopyVariantStruct_{}", variant_ident), variant_ident.span()) 65 } 66 67 /// Generates variant structs for the given enum variant. 68 /// 69 /// These are structs associated with each variant of an enum. They are 70 /// `repr(C)` tuple structs with the same fields as the variant after a 71 /// `MaybeUninit<___ZerocopyInnerTag>`. 72 /// 73 /// In order to unify the generated types for `repr(C)` and `repr(int)` enums, 74 /// we use a "fused" representation with fields for both an inner tag and an 75 /// outer tag. Depending on the repr, we will set one of these tags to the tag 76 /// type and the other to `()`. This lets us generate the same code but put the 77 /// tags in different locations. 78 fn generate_variant_structs(ctx: &Ctx, data: &DataEnum) -> TokenStream { 79 let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl(); 80 81 let enum_name = &ctx.ast.ident; 82 83 // All variant structs have a `PhantomData<MyEnum<...>>` field because we 84 // don't know which generic parameters each variant will use, and unused 85 // generic parameters are a compile error. 86 let core = ctx.core_path(); 87 let phantom_ty = quote! { 88 #core::marker::PhantomData<#enum_name #ty_generics> 89 }; 90 91 let variant_structs = data.variants.iter().filter_map(|variant| { 92 // We don't generate variant structs for unit variants because we only 93 // need to check the tag. This helps cut down our generated code a bit. 94 if matches!(variant.fields, Fields::Unit) { 95 return None; 96 } 97 98 let variant_struct_ident = variant_struct_ident(&variant.ident); 99 let field_types = variant.fields.iter().map(|f| &f.ty); 100 101 let variant_struct = parse_quote! { 102 #[repr(C)] 103 struct #variant_struct_ident #impl_generics ( 104 #core::mem::MaybeUninit<___ZerocopyInnerTag>, 105 #(#field_types,)* 106 #phantom_ty, 107 ) #where_clause; 108 }; 109 110 // We do this rather than emitting `#[derive(::zerocopy::TryFromBytes)]` 111 // because that is not hygienic, and this is also more performant. 112 let try_from_bytes_impl = 113 derive_try_from_bytes(&ctx.with_input(&variant_struct), Trait::TryFromBytes) 114 .expect("derive_try_from_bytes should not fail on synthesized type"); 115 116 Some(quote! { 117 #variant_struct 118 #try_from_bytes_impl 119 }) 120 }); 121 122 quote! { 123 #(#variant_structs)* 124 } 125 } 126 127 fn variants_union_field_ident(ident: &Ident) -> Ident { 128 // Field names are prefixed with `__field_` to prevent name collision 129 // with the `__nonempty` field. 130 ident!(("__field_{}", ident), ident.span()) 131 } 132 133 fn generate_variants_union(ctx: &Ctx, data: &DataEnum) -> TokenStream { 134 let generics = &ctx.ast.generics; 135 let (_, ty_generics, _) = generics.split_for_impl(); 136 137 let fields = data.variants.iter().filter_map(|variant| { 138 // We don't generate variant structs for unit variants because we only 139 // need to check the tag. This helps cut down our generated code a bit. 140 if matches!(variant.fields, Fields::Unit) { 141 return None; 142 } 143 144 let field_name = variants_union_field_ident(&variant.ident); 145 let variant_struct_ident = variant_struct_ident(&variant.ident); 146 147 let core = ctx.core_path(); 148 Some(quote! { 149 #field_name: #core::mem::ManuallyDrop<#variant_struct_ident #ty_generics>, 150 }) 151 }); 152 153 let variants_union = parse_quote! { 154 #[repr(C)] 155 union ___ZerocopyVariants #generics { 156 #(#fields)* 157 // Enums can have variants with no fields, but unions must 158 // have at least one field. So we just add a trailing unit 159 // to ensure that this union always has at least one field. 160 // Because this union is `repr(C)`, this unit type does not 161 // affect the layout. 162 __nonempty: (), 163 } 164 }; 165 166 let has_field = 167 derive_has_field_struct_union(&ctx.with_input(&variants_union), &variants_union.data); 168 169 quote! { 170 #variants_union 171 #has_field 172 } 173 } 174 175 /// Generates an implementation of `is_bit_valid` for an arbitrary enum. 176 /// 177 /// The general process is: 178 /// 179 /// 1. Generate a tag enum. This is an enum with the same repr, variants, and 180 /// corresponding discriminants as the original enum, but without any fields 181 /// on the variants. This gives us access to an enum where the variants have 182 /// the same discriminants as the one we're writing `is_bit_valid` for. 183 /// 2. Make constants from the variants of the tag enum. We need these because 184 /// we can't put const exprs in match arms. 185 /// 3. Generate variant structs. These are structs which have the same fields as 186 /// each variant of the enum, and are `#[repr(C)]` with an optional "inner 187 /// tag". 188 /// 4. Generate a variants union, with one field for each variant struct type. 189 /// 5. And finally, our raw enum is a `#[repr(C)]` struct of an "outer tag" and 190 /// the variants union. 191 /// 192 /// See these reference links for fully-worked example decompositions. 193 /// 194 /// - `repr(C)`: <https://doc.rust-lang.org/reference/type-layout.html#reprc-enums-with-fields> 195 /// - `repr(int)`: <https://doc.rust-lang.org/reference/type-layout.html#primitive-representation-of-enums-with-fields> 196 /// - `repr(C, int)`: <https://doc.rust-lang.org/reference/type-layout.html#combining-primitive-representations-of-enums-with-fields-and-reprc> 197 pub(crate) fn derive_is_bit_valid( 198 ctx: &Ctx, 199 data: &DataEnum, 200 repr: &EnumRepr, 201 ) -> Result<TokenStream, Error> { 202 let trait_path = Trait::TryFromBytes.crate_path(ctx); 203 let tag_enum = generate_tag_enum(ctx, repr, data); 204 let tag_consts = generate_tag_consts(data); 205 206 let (outer_tag_type, inner_tag_type) = if repr.is_c() { 207 (quote! { ___ZerocopyTag }, quote! { () }) 208 } else if repr.is_primitive() { 209 (quote! { () }, quote! { ___ZerocopyTag }) 210 } else { 211 return Err(Error::new( 212 ctx.ast.span(), 213 "must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout", 214 )); 215 }; 216 217 let variant_structs = generate_variant_structs(ctx, data); 218 let variants_union = generate_variants_union(ctx, data); 219 220 let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl(); 221 222 let zerocopy_crate = &ctx.zerocopy_crate; 223 let has_tag = ImplBlockBuilder::new(ctx, data, Trait::HasTag, FieldBounds::None) 224 .inner_extras(quote! { 225 type Tag = ___ZerocopyTag; 226 type ProjectToTag = #zerocopy_crate::pointer::cast::CastSized; 227 }) 228 .build(); 229 let has_fields = data.variants().into_iter().flat_map(|(variant, fields)| { 230 let variant_ident = &variant.unwrap().ident; 231 let variants_union_field_ident = variants_union_field_ident(variant_ident); 232 let field: Box<syn::Type> = parse_quote!(()); 233 fields.into_iter().enumerate().map(move |(idx, (vis, ident, ty))| { 234 // Rust does not presently support explicit visibility modifiers on 235 // enum fields, but we guard against the possibility to ensure this 236 // derive remains sound. 237 assert!(matches!(vis, syn::Visibility::Inherited)); 238 let variant_struct_field_index = Index::from(idx + 1); 239 let (_, ty_generics, _) = ctx.ast.generics.split_for_impl(); 240 let has_field_trait = Trait::HasField { 241 variant_id: parse_quote!({ #zerocopy_crate::ident_id!(#variant_ident) }), 242 // Since Rust does not presently support explicit visibility 243 // modifiers on enum fields, any public type is suitable here; 244 // we use `()`. 245 field: field.clone(), 246 field_id: parse_quote!({ #zerocopy_crate::ident_id!(#ident) }), 247 }; 248 let has_field_path = has_field_trait.crate_path(ctx); 249 let has_field = ImplBlockBuilder::new( 250 ctx, 251 data, 252 has_field_trait, 253 FieldBounds::None, 254 ) 255 .inner_extras(quote! { 256 type Type = #ty; 257 258 #[inline(always)] 259 fn project(slf: #zerocopy_crate::pointer::PtrInner<'_, Self>) -> *mut <Self as #has_field_path>::Type { 260 use #zerocopy_crate::pointer::cast::{CastSized, Projection}; 261 262 slf.project::<___ZerocopyRawEnum #ty_generics, CastSized>() 263 .project::<_, Projection<_, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(variants) }>>() 264 .project::<_, Projection<_, { #zerocopy_crate::REPR_C_UNION_VARIANT_ID }, { #zerocopy_crate::ident_id!(#variants_union_field_ident) }>>() 265 .project::<_, Projection<_, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(value) }>>() 266 .project::<_, Projection<_, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(#variant_struct_field_index) }>>() 267 .as_ptr() 268 } 269 }) 270 .build(); 271 272 let project = ImplBlockBuilder::new( 273 ctx, 274 data, 275 Trait::ProjectField { 276 variant_id: parse_quote!({ #zerocopy_crate::ident_id!(#variant_ident) }), 277 // Since Rust does not presently support explicit visibility 278 // modifiers on enum fields, any public type is suitable 279 // here; we use `()`. 280 field: field.clone(), 281 field_id: parse_quote!({ #zerocopy_crate::ident_id!(#ident) }), 282 invariants: parse_quote!((Aliasing, Alignment, #zerocopy_crate::invariant::Initialized)), 283 }, 284 FieldBounds::None, 285 ) 286 .param_extras(vec![ 287 parse_quote!(Aliasing: #zerocopy_crate::invariant::Aliasing), 288 parse_quote!(Alignment: #zerocopy_crate::invariant::Alignment), 289 ]) 290 .inner_extras(quote! { 291 type Error = #zerocopy_crate::util::macro_util::core_reexport::convert::Infallible; 292 type Invariants = (Aliasing, Alignment, #zerocopy_crate::invariant::Initialized); 293 }) 294 .build(); 295 296 quote! { 297 #has_field 298 #project 299 } 300 }) 301 }); 302 303 let core = ctx.core_path(); 304 let match_arms = data.variants.iter().map(|variant| { 305 let tag_ident = tag_ident(&variant.ident); 306 let variant_struct_ident = variant_struct_ident(&variant.ident); 307 let variants_union_field_ident = variants_union_field_ident(&variant.ident); 308 309 if matches!(variant.fields, Fields::Unit) { 310 // Unit variants don't need any further validation beyond checking 311 // the tag. 312 quote! { 313 #tag_ident => true 314 } 315 } else { 316 quote! { 317 #tag_ident => { 318 // SAFETY: Since we know that the tag is `#tag_ident`, we 319 // know that no other `&`s exist which refer to this enum 320 // as any other variant. 321 let variant_md = variants.cast::< 322 _, 323 #zerocopy_crate::pointer::cast::Projection< 324 // #zerocopy_crate::ReadOnly<_>, 325 _, 326 { #zerocopy_crate::REPR_C_UNION_VARIANT_ID }, 327 { #zerocopy_crate::ident_id!(#variants_union_field_ident) } 328 >, 329 _ 330 >(); 331 let variant = variant_md.cast::< 332 #zerocopy_crate::ReadOnly<#variant_struct_ident #ty_generics>, 333 #zerocopy_crate::pointer::cast::CastSized, 334 (#zerocopy_crate::pointer::BecauseRead, _) 335 >(); 336 < 337 #variant_struct_ident #ty_generics as #trait_path 338 >::is_bit_valid(variant) 339 } 340 } 341 } 342 }); 343 344 let generics = &ctx.ast.generics; 345 let raw_enum: DeriveInput = parse_quote! { 346 #[repr(C)] 347 struct ___ZerocopyRawEnum #generics { 348 tag: ___ZerocopyOuterTag, 349 variants: ___ZerocopyVariants #ty_generics, 350 } 351 }; 352 353 let self_ident = &ctx.ast.ident; 354 let invariants_eq_impl = quote! { 355 // SAFETY: `___ZerocopyRawEnum` is designed to have the same layout, 356 // validity, and invariants as `Self`. 357 unsafe impl #impl_generics #zerocopy_crate::pointer::InvariantsEq<___ZerocopyRawEnum #ty_generics> for #self_ident #ty_generics #where_clause {} 358 }; 359 360 let raw_enum_projections = 361 derive_has_field_struct_union(&ctx.with_input(&raw_enum), &raw_enum.data); 362 363 let raw_enum = quote! { 364 #raw_enum 365 #invariants_eq_impl 366 #raw_enum_projections 367 }; 368 369 Ok(quote! { 370 // SAFETY: We use `is_bit_valid` to validate that the bit pattern of the 371 // enum's tag corresponds to one of the enum's discriminants. Then, we 372 // check the bit validity of each field of the corresponding variant. 373 // Thus, this is a sound implementation of `is_bit_valid`. 374 #[inline] 375 fn is_bit_valid<___ZcAlignment>( 376 mut candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>, 377 ) -> #core::primitive::bool 378 where 379 ___ZcAlignment: #zerocopy_crate::invariant::Alignment, 380 { 381 #tag_enum 382 383 type ___ZerocopyTagPrimitive = #zerocopy_crate::util::macro_util::SizeToTag< 384 { #core::mem::size_of::<___ZerocopyTag>() }, 385 >; 386 387 #tag_consts 388 389 type ___ZerocopyOuterTag = #outer_tag_type; 390 type ___ZerocopyInnerTag = #inner_tag_type; 391 392 #variant_structs 393 394 #variants_union 395 396 #raw_enum 397 398 #has_tag 399 400 #(#has_fields)* 401 402 let tag = { 403 // SAFETY: 404 // - The provided cast addresses a subset of the bytes addressed 405 // by `candidate` because it addresses the starting tag of the 406 // enum. 407 // - Because the pointer is cast from `candidate`, it has the 408 // same provenance as it. 409 // - There are no `UnsafeCell`s in the tag because it is a 410 // primitive integer. 411 // - `tag_ptr` is casted from `candidate`, whose referent is 412 // `Initialized`. Since we have not written uninitialized 413 // bytes into the referent, `tag_ptr` is also `Initialized`. 414 // 415 // FIXME(#2874): Revise this to a `cast` once `candidate` 416 // references a `ReadOnly<Self>`. 417 let tag_ptr = unsafe { 418 candidate.reborrow().project_transmute_unchecked::< 419 _, 420 #zerocopy_crate::invariant::Initialized, 421 #zerocopy_crate::pointer::cast::CastSized 422 >() 423 }; 424 tag_ptr.recall_validity::<_, (_, (_, _))>().read::<#zerocopy_crate::BecauseImmutable>() 425 }; 426 427 let mut raw_enum = candidate.cast::< 428 #zerocopy_crate::ReadOnly<___ZerocopyRawEnum #ty_generics>, 429 #zerocopy_crate::pointer::cast::CastSized, 430 (#zerocopy_crate::pointer::BecauseRead, _) 431 >(); 432 433 let variants = #zerocopy_crate::into_inner!(raw_enum.project::< 434 _, 435 { #zerocopy_crate::STRUCT_VARIANT_ID }, 436 { #zerocopy_crate::ident_id!(variants) } 437 >()); 438 439 match tag { 440 #(#match_arms,)* 441 _ => false, 442 } 443 } 444 }) 445 } 446 pub(crate) fn derive_try_from_bytes(ctx: &Ctx, top_level: Trait) -> Result<TokenStream, Error> { 447 match &ctx.ast.data { 448 Data::Struct(strct) => derive_try_from_bytes_struct(ctx, strct, top_level), 449 Data::Enum(enm) => derive_try_from_bytes_enum(ctx, enm, top_level), 450 Data::Union(unn) => Ok(derive_try_from_bytes_union(ctx, unn, top_level)), 451 } 452 } 453 fn derive_has_field_struct_union(ctx: &Ctx, data: &dyn DataExt) -> TokenStream { 454 let fields = ctx.ast.data.fields(); 455 if fields.is_empty() { 456 return quote! {}; 457 } 458 459 let field_tokens = fields.iter().map(|(vis, ident, _)| { 460 let ident = ident!(("ẕ{}", ident), ident.span()); 461 quote!( 462 #vis enum #ident {} 463 ) 464 }); 465 466 let zerocopy_crate = &ctx.zerocopy_crate; 467 let variant_id: Box<Expr> = match &ctx.ast.data { 468 Data::Struct(_) => parse_quote!({ #zerocopy_crate::STRUCT_VARIANT_ID }), 469 Data::Union(_) => { 470 let is_repr_c = StructUnionRepr::from_attrs(&ctx.ast.attrs) 471 .map(|repr| repr.is_c()) 472 .unwrap_or(false); 473 if is_repr_c { 474 parse_quote!({ #zerocopy_crate::REPR_C_UNION_VARIANT_ID }) 475 } else { 476 parse_quote!({ #zerocopy_crate::UNION_VARIANT_ID }) 477 } 478 } 479 _ => unreachable!(), 480 }; 481 482 let core = ctx.core_path(); 483 let has_tag = ImplBlockBuilder::new(ctx, data, Trait::HasTag, FieldBounds::None) 484 .inner_extras(quote! { 485 type Tag = (); 486 type ProjectToTag = #zerocopy_crate::pointer::cast::CastToUnit; 487 }) 488 .build(); 489 let has_fields = fields.iter().map(move |(_, ident, ty)| { 490 let field_token = ident!(("ẕ{}", ident), ident.span()); 491 let field: Box<Type> = parse_quote!(#field_token); 492 let field_id: Box<Expr> = parse_quote!({ #zerocopy_crate::ident_id!(#ident) }); 493 let has_field_trait = Trait::HasField { 494 variant_id: variant_id.clone(), 495 field: field.clone(), 496 field_id: field_id.clone(), 497 }; 498 let has_field_path = has_field_trait.crate_path(ctx); 499 ImplBlockBuilder::new( 500 ctx, 501 data, 502 has_field_trait, 503 FieldBounds::None, 504 ) 505 .inner_extras(quote! { 506 type Type = #ty; 507 508 #[inline(always)] 509 fn project(slf: #zerocopy_crate::pointer::PtrInner<'_, Self>) -> *mut <Self as #has_field_path>::Type { 510 let slf = slf.as_ptr(); 511 // SAFETY: By invariant on `PtrInner`, `slf` is a non-null 512 // pointer whose referent is zero-sized or lives in a valid 513 // allocation. Since `#ident` is a struct or union field of 514 // `Self`, this projection preserves or shrinks the referent 515 // size, and so the resulting referent also fits in the same 516 // allocation. 517 unsafe { #core::ptr::addr_of_mut!((*slf).#ident) } 518 } 519 }).outer_extras(if matches!(&ctx.ast.data, Data::Struct(..)) { 520 let fields_preserve_alignment = StructUnionRepr::from_attrs(&ctx.ast.attrs) 521 .map(|repr| repr.get_packed().is_none()) 522 .unwrap(); 523 let alignment = if fields_preserve_alignment { 524 quote! { Alignment } 525 } else { 526 quote! { #zerocopy_crate::invariant::Unaligned } 527 }; 528 // SAFETY: See comments on items. 529 ImplBlockBuilder::new( 530 ctx, 531 data, 532 Trait::ProjectField { 533 variant_id: variant_id.clone(), 534 field: field.clone(), 535 field_id: field_id.clone(), 536 invariants: parse_quote!((Aliasing, Alignment, #zerocopy_crate::invariant::Initialized)), 537 }, 538 FieldBounds::None, 539 ) 540 .param_extras(vec![ 541 parse_quote!(Aliasing: #zerocopy_crate::invariant::Aliasing), 542 parse_quote!(Alignment: #zerocopy_crate::invariant::Alignment), 543 ]) 544 .inner_extras(quote! { 545 // SAFETY: Projection into structs is always infallible. 546 type Error = #zerocopy_crate::util::macro_util::core_reexport::convert::Infallible; 547 // SAFETY: The alignment of the projected `Ptr` is `Unaligned` 548 // if the structure is packed; otherwise inherited from the 549 // outer `Ptr`. If the validity of the outer pointer is 550 // `Initialized`, so too is the validity of its fields. 551 type Invariants = (Aliasing, #alignment, #zerocopy_crate::invariant::Initialized); 552 }) 553 .build() 554 } else { 555 quote! {} 556 }) 557 .build() 558 }); 559 560 const_block(field_tokens.into_iter().chain(Some(has_tag)).chain(has_fields).map(Some)) 561 } 562 fn derive_try_from_bytes_struct( 563 ctx: &Ctx, 564 strct: &DataStruct, 565 top_level: Trait, 566 ) -> Result<TokenStream, Error> { 567 let extras = try_gen_trivial_is_bit_valid(ctx, top_level).unwrap_or_else(|| { 568 let zerocopy_crate = &ctx.zerocopy_crate; 569 let fields = strct.fields(); 570 let field_names = fields.iter().map(|(_vis, name, _ty)| name); 571 let field_tys = fields.iter().map(|(_vis, _name, ty)| ty); 572 let core = ctx.core_path(); 573 quote!( 574 // SAFETY: We use `is_bit_valid` to validate that each field is 575 // bit-valid, and only return `true` if all of them are. The bit 576 // validity of a struct is just the composition of the bit 577 // validities of its fields, so this is a sound implementation 578 // of `is_bit_valid`. 579 #[inline] 580 fn is_bit_valid<___ZcAlignment>( 581 mut candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>, 582 ) -> #core::primitive::bool 583 where 584 ___ZcAlignment: #zerocopy_crate::invariant::Alignment, 585 { 586 true #(&& { 587 let field_candidate = #zerocopy_crate::into_inner!(candidate.reborrow().project::< 588 _, 589 { #zerocopy_crate::STRUCT_VARIANT_ID }, 590 { #zerocopy_crate::ident_id!(#field_names) } 591 >()); 592 <#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate) 593 })* 594 } 595 ) 596 }); 597 Ok(ImplBlockBuilder::new(ctx, strct, Trait::TryFromBytes, FieldBounds::ALL_SELF) 598 .inner_extras(extras) 599 .outer_extras(derive_has_field_struct_union(ctx, strct)) 600 .build()) 601 } 602 fn derive_try_from_bytes_union(ctx: &Ctx, unn: &DataUnion, top_level: Trait) -> TokenStream { 603 let field_type_trait_bounds = FieldBounds::All(&[TraitBound::Slf]); 604 605 let zerocopy_crate = &ctx.zerocopy_crate; 606 let variant_id: Box<Expr> = { 607 let is_repr_c = 608 StructUnionRepr::from_attrs(&ctx.ast.attrs).map(|repr| repr.is_c()).unwrap_or(false); 609 if is_repr_c { 610 parse_quote!({ #zerocopy_crate::REPR_C_UNION_VARIANT_ID }) 611 } else { 612 parse_quote!({ #zerocopy_crate::UNION_VARIANT_ID }) 613 } 614 }; 615 616 let extras = try_gen_trivial_is_bit_valid(ctx, top_level).unwrap_or_else(|| { 617 let fields = unn.fields(); 618 let field_names = fields.iter().map(|(_vis, name, _ty)| name); 619 let field_tys = fields.iter().map(|(_vis, _name, ty)| ty); 620 let core = ctx.core_path(); 621 quote!( 622 // SAFETY: We use `is_bit_valid` to validate that any field is 623 // bit-valid; we only return `true` if at least one of them is. 624 // The bit validity of a union is not yet well defined in Rust, 625 // but it is guaranteed to be no more strict than this 626 // definition. See #696 for a more in-depth discussion. 627 #[inline] 628 fn is_bit_valid<___ZcAlignment>( 629 mut candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>, 630 ) -> #core::primitive::bool 631 where 632 ___ZcAlignment: #zerocopy_crate::invariant::Alignment, 633 { 634 false #(|| { 635 // SAFETY: 636 // - Since `ReadOnly<Self>: Immutable` unconditionally, 637 // neither `*slf` nor the returned pointer's referent 638 // permit interior mutation. 639 // - Both source and destination validity are 640 // `Initialized`, which is always a sound 641 // transmutation. 642 let field_candidate = unsafe { 643 candidate.reborrow().project_transmute_unchecked::< 644 _, 645 _, 646 #zerocopy_crate::pointer::cast::Projection< 647 _, 648 #variant_id, 649 { #zerocopy_crate::ident_id!(#field_names) } 650 > 651 >() 652 }; 653 654 <#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate) 655 })* 656 } 657 ) 658 }); 659 ImplBlockBuilder::new(ctx, unn, Trait::TryFromBytes, field_type_trait_bounds) 660 .inner_extras(extras) 661 .outer_extras(derive_has_field_struct_union(ctx, unn)) 662 .build() 663 } 664 fn derive_try_from_bytes_enum( 665 ctx: &Ctx, 666 enm: &DataEnum, 667 top_level: Trait, 668 ) -> Result<TokenStream, Error> { 669 let repr = EnumRepr::from_attrs(&ctx.ast.attrs)?; 670 671 // If an enum has no fields, it has a well-defined integer representation, 672 // and every possible bit pattern corresponds to a valid discriminant tag, 673 // then it *could* be `FromBytes` (even if the user hasn't derived 674 // `FromBytes`). This holds if, for `repr(uN)` or `repr(iN)`, there are 2^N 675 // variants. 676 let could_be_from_bytes = enum_size_from_repr(&repr) 677 .map(|size| enm.fields().is_empty() && enm.variants.len() == 1usize << size) 678 .unwrap_or(false); 679 680 let trivial_is_bit_valid = try_gen_trivial_is_bit_valid(ctx, top_level); 681 let extra = match (trivial_is_bit_valid, could_be_from_bytes) { 682 (Some(is_bit_valid), _) => is_bit_valid, 683 // SAFETY: It would be sound for the enum to implement `FromBytes`, as 684 // required by `gen_trivial_is_bit_valid_unchecked`. 685 (None, true) => unsafe { gen_trivial_is_bit_valid_unchecked(ctx) }, 686 (None, false) => match derive_is_bit_valid(ctx, enm, &repr) { 687 Ok(extra) => extra, 688 Err(_) if ctx.skip_on_error => return Ok(TokenStream::new()), 689 Err(e) => return Err(e), 690 }, 691 }; 692 693 Ok(ImplBlockBuilder::new(ctx, enm, Trait::TryFromBytes, FieldBounds::ALL_SELF) 694 .inner_extras(extra) 695 .build()) 696 } 697 fn try_gen_trivial_is_bit_valid(ctx: &Ctx, top_level: Trait) -> Option<proc_macro2::TokenStream> { 698 // If the top-level trait is `FromBytes` and `Self` has no type parameters, 699 // then the `FromBytes` derive will fail compilation if `Self` is not 700 // actually soundly `FromBytes`, and so we can rely on that for our 701 // `is_bit_valid` impl. It's plausible that we could make changes - or Rust 702 // could make changes (such as the "trivial bounds" language feature) - that 703 // make this no longer true. To hedge against these, we include an explicit 704 // `Self: FromBytes` check in the generated `is_bit_valid`, which is 705 // bulletproof. 706 // 707 // If `ctx.skip_on_error` is true, we can't rely on the `FromBytes` derive 708 // to fail compilation if `Self` is not actually soundly `FromBytes`. 709 if matches!(top_level, Trait::FromBytes) 710 && ctx.ast.generics.params.is_empty() 711 && !ctx.skip_on_error 712 { 713 let zerocopy_crate = &ctx.zerocopy_crate; 714 let core = ctx.core_path(); 715 Some(quote!( 716 // SAFETY: See inline. 717 #[inline(always)] 718 fn is_bit_valid<___ZcAlignment>( 719 _candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>, 720 ) -> #core::primitive::bool 721 where 722 ___ZcAlignment: #zerocopy_crate::invariant::Alignment, 723 { 724 if false { 725 fn assert_is_from_bytes<T>() 726 where 727 T: #zerocopy_crate::FromBytes, 728 T: ?#core::marker::Sized, 729 { 730 } 731 732 assert_is_from_bytes::<Self>(); 733 } 734 735 // SAFETY: The preceding code only compiles if `Self: 736 // FromBytes`. Thus, this code only compiles if all initialized 737 // byte sequences represent valid instances of `Self`. 738 true 739 } 740 )) 741 } else { 742 None 743 } 744 } 745 746 /// # Safety 747 /// 748 /// All initialized bit patterns must be valid for `Self`. 749 unsafe fn gen_trivial_is_bit_valid_unchecked(ctx: &Ctx) -> proc_macro2::TokenStream { 750 let zerocopy_crate = &ctx.zerocopy_crate; 751 let core = ctx.core_path(); 752 quote!( 753 // SAFETY: The caller of `gen_trivial_is_bit_valid_unchecked` has 754 // promised that all initialized bit patterns are valid for `Self`. 755 #[inline(always)] 756 fn is_bit_valid<___ZcAlignment>( 757 _candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>, 758 ) -> #core::primitive::bool 759 where 760 ___ZcAlignment: #zerocopy_crate::invariant::Alignment, 761 { 762 true 763 } 764 ) 765 } 766