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 };
tag_ident(variant_ident: &Ident) -> Ident17 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.
generate_tag_consts(data: &DataEnum) -> TokenStream32 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
variant_struct_ident(variant_ident: &Ident) -> Ident63 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.
generate_variant_structs(ctx: &Ctx, data: &DataEnum) -> TokenStream78 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
variants_union_field_ident(ident: &Ident) -> Ident127 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
generate_variants_union(ctx: &Ctx, data: &DataEnum) -> TokenStream133 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>
derive_is_bit_valid( ctx: &Ctx, data: &DataEnum, repr: &EnumRepr, ) -> Result<TokenStream, Error>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 }
derive_try_from_bytes(ctx: &Ctx, top_level: Trait) -> Result<TokenStream, Error>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 }
derive_has_field_struct_union(ctx: &Ctx, data: &dyn DataExt) -> TokenStream453 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!(("__z{}", 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!(("__z{}", 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,
535 field_id,
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 }
derive_try_from_bytes_struct( ctx: &Ctx, strct: &DataStruct, top_level: Trait, ) -> Result<TokenStream, Error>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 }
derive_try_from_bytes_union(ctx: &Ctx, unn: &DataUnion, top_level: Trait) -> TokenStream602 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 }
derive_try_from_bytes_enum( ctx: &Ctx, enm: &DataEnum, top_level: Trait, ) -> Result<TokenStream, Error>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 }
try_gen_trivial_is_bit_valid(ctx: &Ctx, top_level: Trait) -> Option<proc_macro2::TokenStream>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`.
gen_trivial_is_bit_valid_unchecked(ctx: &Ctx) -> proc_macro2::TokenStream749 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