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