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::{parse_quote, Data, Error, Type}; 6 7 use crate::{ 8 repr::StructUnionRepr, 9 util::{Ctx, DataExt, FieldBounds, ImplBlockBuilder, SelfBounds, Trait}, 10 }; 11 12 fn derive_known_layout_for_repr_c_struct<'a>( 13 ctx: &'a Ctx, 14 repr: &StructUnionRepr, 15 fields: &[(&'a syn::Visibility, TokenStream, &'a Type)], 16 ) -> Option<(SelfBounds<'a>, TokenStream, Option<TokenStream>)> { 17 let (trailing_field, leading_fields) = fields.split_last()?; 18 19 let (_vis, trailing_field_name, trailing_field_ty) = trailing_field; 20 let leading_fields_tys = leading_fields.iter().map(|(_vis, _name, ty)| ty); 21 22 let core = ctx.core_path(); 23 let repr_align = repr 24 .get_align() 25 .map(|align| { 26 let align = align.t.get(); 27 quote!(#core::num::NonZeroUsize::new(#align as usize)) 28 }) 29 .unwrap_or_else(|| quote!(#core::option::Option::None)); 30 let repr_packed = repr 31 .get_packed() 32 .map(|packed| { 33 let packed = packed.get(); 34 quote!(#core::num::NonZeroUsize::new(#packed as usize)) 35 }) 36 .unwrap_or_else(|| quote!(#core::option::Option::None)); 37 38 let zerocopy_crate = &ctx.zerocopy_crate; 39 let make_methods = |trailing_field_ty| { 40 quote! { 41 // SAFETY: 42 // - The returned pointer has the same address and provenance as 43 // `bytes`: 44 // - The recursive call to `raw_from_ptr_len` preserves both 45 // address and provenance. 46 // - The `as` cast preserves both address and provenance. 47 // - `NonNull::new_unchecked` preserves both address and 48 // provenance. 49 // - If `Self` is a slice DST, the returned pointer encodes 50 // `elems` elements in the trailing slice: 51 // - This is true of the recursive call to `raw_from_ptr_len`. 52 // - `trailing.as_ptr() as *mut Self` preserves trailing slice 53 // element count [1]. 54 // - `NonNull::new_unchecked` preserves trailing slice element 55 // count. 56 // 57 // [1] Per https://doc.rust-lang.org/reference/expressions/operator-expr.html#pointer-to-pointer-cast: 58 // 59 // `*const T`` / `*mut T` can be cast to `*const U` / `*mut U` 60 // with the following behavior: 61 // ... 62 // - If `T` and `U` are both unsized, the pointer is also 63 // returned unchanged. In particular, the metadata is 64 // preserved exactly. 65 // 66 // For instance, a cast from `*const [T]` to `*const [U]` 67 // preserves the number of elements. ... The same holds 68 // for str and any compound type whose unsized tail is a 69 // slice type, such as struct `Foo(i32, [u8])` or 70 // `(u64, Foo)`. 71 #[inline(always)] 72 fn raw_from_ptr_len( 73 bytes: #core::ptr::NonNull<u8>, 74 meta: <Self as #zerocopy_crate::KnownLayout>::PointerMetadata, 75 ) -> #core::ptr::NonNull<Self> { 76 let trailing = <#trailing_field_ty as #zerocopy_crate::KnownLayout>::raw_from_ptr_len(bytes, meta); 77 let slf = trailing.as_ptr() as *mut Self; 78 // SAFETY: Constructed from `trailing`, which is non-null. 79 unsafe { #core::ptr::NonNull::new_unchecked(slf) } 80 } 81 82 #[inline(always)] 83 fn pointer_to_metadata(ptr: *mut Self) -> <Self as #zerocopy_crate::KnownLayout>::PointerMetadata { 84 <#trailing_field_ty>::pointer_to_metadata(ptr as *mut _) 85 } 86 } 87 }; 88 89 let inner_extras = { 90 let leading_fields_tys = leading_fields_tys.clone(); 91 let methods = make_methods(*trailing_field_ty); 92 let (_, ty_generics, _) = ctx.ast.generics.split_for_impl(); 93 94 quote!( 95 type PointerMetadata = <#trailing_field_ty as #zerocopy_crate::KnownLayout>::PointerMetadata; 96 97 type MaybeUninit = __ZerocopyKnownLayoutMaybeUninit #ty_generics; 98 99 // SAFETY: `LAYOUT` accurately describes the layout of `Self`. 100 // The documentation of `DstLayout::for_repr_c_struct` vows that 101 // invocations in this manner will accurately describe a type, 102 // so long as: 103 // 104 // - that type is `repr(C)`, 105 // - its fields are enumerated in the order they appear, 106 // - the presence of `repr_align` and `repr_packed` are 107 // correctly accounted for. 108 // 109 // We respect all three of these preconditions here. This 110 // expansion is only used if `is_repr_c_struct`, we enumerate 111 // the fields in order, and we extract the values of `align(N)` 112 // and `packed(N)`. 113 const LAYOUT: #zerocopy_crate::DstLayout = #zerocopy_crate::DstLayout::for_repr_c_struct( 114 #repr_align, 115 #repr_packed, 116 &[ 117 #(#zerocopy_crate::DstLayout::for_type::<#leading_fields_tys>(),)* 118 <#trailing_field_ty as #zerocopy_crate::KnownLayout>::LAYOUT 119 ], 120 ); 121 122 #methods 123 ) 124 }; 125 126 let outer_extras = { 127 let ident = &ctx.ast.ident; 128 let vis = &ctx.ast.vis; 129 let params = &ctx.ast.generics.params; 130 let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl(); 131 132 let predicates = if let Some(where_clause) = where_clause { 133 where_clause.predicates.clone() 134 } else { 135 Default::default() 136 }; 137 138 // Generate a valid ident for a type-level handle to a field of a 139 // given `name`. 140 let field_index = |name: &TokenStream| ident!(("__Zerocopy_Field_{}", name), ident.span()); 141 142 let field_indices: Vec<_> = 143 fields.iter().map(|(_vis, name, _ty)| field_index(name)).collect(); 144 145 // Define the collection of type-level field handles. 146 let field_defs = field_indices.iter().zip(fields).map(|(idx, (vis, _, _))| { 147 quote! { 148 #vis struct #idx; 149 } 150 }); 151 152 let field_impls = field_indices.iter().zip(fields).map(|(idx, (_, _, ty))| quote! { 153 // SAFETY: `#ty` is the type of `#ident`'s field at `#idx`. 154 // 155 // We implement `Field` for each field of the struct to create a 156 // projection from the field index to its type. This allows us 157 // to refer to the field's type in a way that respects `Self` 158 // hygiene. If we just copy-pasted the tokens of `#ty`, we 159 // would not respect `Self` hygiene, as `Self` would refer to 160 // the helper struct we are generating, not the derive target 161 // type. 162 unsafe impl #impl_generics #zerocopy_crate::util::macro_util::Field<#idx> for #ident #ty_generics 163 where 164 #predicates 165 { 166 type Type = #ty; 167 } 168 }); 169 170 let trailing_field_index = field_index(trailing_field_name); 171 let leading_field_indices = 172 leading_fields.iter().map(|(_vis, name, _ty)| field_index(name)); 173 174 // We use `Field` to project the type of the trailing field. This is 175 // required to ensure that if the field type uses `Self`, it 176 // resolves to the derive target type, not the helper struct we are 177 // generating. 178 let trailing_field_ty = quote! { 179 <#ident #ty_generics as 180 #zerocopy_crate::util::macro_util::Field<#trailing_field_index> 181 >::Type 182 }; 183 184 let methods = make_methods(&parse_quote! { 185 <#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit 186 }); 187 188 let core = ctx.core_path(); 189 190 quote! { 191 #(#field_defs)* 192 193 #(#field_impls)* 194 195 // SAFETY: This has the same layout as the derive target type, 196 // except that it admits uninit bytes. This is ensured by using 197 // the same repr as the target type, and by using field types 198 // which have the same layout as the target type's fields, 199 // except that they admit uninit bytes. We indirect through 200 // `Field` to ensure that occurrences of `Self` resolve to 201 // `#ty`, not `__ZerocopyKnownLayoutMaybeUninit` (see #2116). 202 #repr 203 #[doc(hidden)] 204 #vis struct __ZerocopyKnownLayoutMaybeUninit<#params> ( 205 #(#core::mem::MaybeUninit< 206 <#ident #ty_generics as 207 #zerocopy_crate::util::macro_util::Field<#leading_field_indices> 208 >::Type 209 >,)* 210 // NOTE(#2302): We wrap in `ManuallyDrop` here in case the 211 // type we're operating on is both generic and 212 // `repr(packed)`. In that case, Rust needs to know that the 213 // type is *either* `Sized` or has a trivial `Drop`. 214 // `ManuallyDrop` has a trivial `Drop`, and so satisfies 215 // this requirement. 216 #core::mem::ManuallyDrop< 217 <#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit 218 > 219 ) 220 where 221 #trailing_field_ty: #zerocopy_crate::KnownLayout, 222 #predicates; 223 224 // SAFETY: We largely defer to the `KnownLayout` implementation 225 // on the derive target type (both by using the same tokens, and 226 // by deferring to impl via type-level indirection). This is 227 // sound, since `__ZerocopyKnownLayoutMaybeUninit` is guaranteed 228 // to have the same layout as the derive target type, except 229 // that `__ZerocopyKnownLayoutMaybeUninit` admits uninit bytes. 230 unsafe impl #impl_generics #zerocopy_crate::KnownLayout for __ZerocopyKnownLayoutMaybeUninit #ty_generics 231 where 232 #trailing_field_ty: #zerocopy_crate::KnownLayout, 233 #predicates 234 { 235 fn only_derive_is_allowed_to_implement_this_trait() {} 236 237 type PointerMetadata = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::PointerMetadata; 238 239 type MaybeUninit = Self; 240 241 const LAYOUT: #zerocopy_crate::DstLayout = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::LAYOUT; 242 243 #methods 244 } 245 } 246 }; 247 248 Some((SelfBounds::None, inner_extras, Some(outer_extras))) 249 } 250 251 pub(crate) fn derive(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> { 252 // If this is a `repr(C)` struct, then `c_struct_repr` contains the entire 253 // `repr` attribute. 254 let c_struct_repr = match &ctx.ast.data { 255 Data::Struct(..) => { 256 let repr = StructUnionRepr::from_attrs(&ctx.ast.attrs)?; 257 if repr.is_c() { 258 Some(repr) 259 } else { 260 None 261 } 262 } 263 Data::Enum(..) | Data::Union(..) => None, 264 }; 265 266 let fields = ctx.ast.data.fields(); 267 268 let (self_bounds, inner_extras, outer_extras) = c_struct_repr 269 .as_ref() 270 .and_then(|repr| { 271 derive_known_layout_for_repr_c_struct(ctx, repr, &fields) 272 }) 273 .unwrap_or_else(|| { 274 let zerocopy_crate = &ctx.zerocopy_crate; 275 let core = ctx.core_path(); 276 277 // For enums, unions, and non-`repr(C)` structs, we require that 278 // `Self` is sized, and as a result don't need to reason about the 279 // internals of the type. 280 ( 281 SelfBounds::SIZED, 282 quote!( 283 type PointerMetadata = (); 284 type MaybeUninit = 285 #core::mem::MaybeUninit<Self>; 286 287 // SAFETY: `LAYOUT` is guaranteed to accurately describe the 288 // layout of `Self`, because that is the documented safety 289 // contract of `DstLayout::for_type`. 290 const LAYOUT: #zerocopy_crate::DstLayout = #zerocopy_crate::DstLayout::for_type::<Self>(); 291 292 // SAFETY: `.cast` preserves address and provenance. 293 // 294 // FIXME(#429): Add documentation to `.cast` that promises that 295 // it preserves provenance. 296 #[inline(always)] 297 fn raw_from_ptr_len(bytes: #core::ptr::NonNull<u8>, _meta: ()) -> #core::ptr::NonNull<Self> { 298 bytes.cast::<Self>() 299 } 300 301 #[inline(always)] 302 fn pointer_to_metadata(_ptr: *mut Self) -> () {} 303 ), 304 None, 305 ) 306 }); 307 Ok(match &ctx.ast.data { 308 Data::Struct(strct) => { 309 let require_trait_bound_on_field_types = 310 if matches!(self_bounds, SelfBounds::All(&[Trait::Sized])) { 311 FieldBounds::None 312 } else { 313 FieldBounds::TRAILING_SELF 314 }; 315 316 // A bound on the trailing field is required, since structs are 317 // unsized if their trailing field is unsized. Reflecting the layout 318 // of an usized trailing field requires that the field is 319 // `KnownLayout`. 320 ImplBlockBuilder::new( 321 ctx, 322 strct, 323 Trait::KnownLayout, 324 require_trait_bound_on_field_types, 325 ) 326 .self_type_trait_bounds(self_bounds) 327 .inner_extras(inner_extras) 328 .outer_extras(outer_extras) 329 .build() 330 } 331 Data::Enum(enm) => { 332 // A bound on the trailing field is not required, since enums cannot 333 // currently be unsized. 334 ImplBlockBuilder::new(ctx, enm, Trait::KnownLayout, FieldBounds::None) 335 .self_type_trait_bounds(SelfBounds::SIZED) 336 .inner_extras(inner_extras) 337 .outer_extras(outer_extras) 338 .build() 339 } 340 Data::Union(unn) => { 341 // A bound on the trailing field is not required, since unions 342 // cannot currently be unsized. 343 ImplBlockBuilder::new(ctx, unn, Trait::KnownLayout, FieldBounds::None) 344 .self_type_trait_bounds(SelfBounds::SIZED) 345 .inner_extras(inner_extras) 346 .outer_extras(outer_extras) 347 .build() 348 } 349 }) 350 } 351