1 // SPDX-License-Identifier: Apache-2.0 OR MIT 2 3 use proc_macro2::TokenStream; 4 use quote::{format_ident, quote}; 5 use syn::{ 6 parse::{End, Nothing, Parse}, 7 parse_quote, parse_quote_spanned, 8 spanned::Spanned, 9 visit_mut::VisitMut, 10 Attribute, Field, Generics, Ident, Item, PathSegment, Type, TypePath, Visibility, WhereClause, 11 }; 12 13 use crate::diagnostics::{DiagCtxt, ErrorGuaranteed}; 14 15 pub(crate) mod kw { 16 syn::custom_keyword!(PinnedDrop); 17 } 18 19 pub(crate) enum Args { 20 Nothing(Nothing), 21 #[allow(dead_code)] 22 PinnedDrop(kw::PinnedDrop), 23 } 24 25 impl Parse for Args { 26 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> { 27 let lh = input.lookahead1(); 28 if lh.peek(End) { 29 input.parse().map(Self::Nothing) 30 } else if lh.peek(kw::PinnedDrop) { 31 input.parse().map(Self::PinnedDrop) 32 } else { 33 Err(lh.error()) 34 } 35 } 36 } 37 38 struct FieldInfo<'a> { 39 field: &'a Field, 40 pinned: bool, 41 cfg_attrs: Vec<&'a Attribute>, 42 } 43 44 pub(crate) fn pin_data( 45 args: Args, 46 input: Item, 47 dcx: &mut DiagCtxt, 48 ) -> Result<TokenStream, ErrorGuaranteed> { 49 let mut struct_ = match input { 50 Item::Struct(struct_) => struct_, 51 Item::Enum(enum_) => { 52 return Err(dcx.error( 53 enum_.enum_token, 54 "`#[pin_data]` only supports structs for now", 55 )); 56 } 57 Item::Union(union) => { 58 return Err(dcx.error( 59 union.union_token, 60 "`#[pin_data]` only supports structs for now", 61 )); 62 } 63 rest => { 64 return Err(dcx.error( 65 rest, 66 "`#[pin_data]` can only be applied to struct, enum and union definitions", 67 )); 68 } 69 }; 70 71 // The generics might contain the `Self` type. Since this macro will define a new type with the 72 // same generics and bounds, this poses a problem: `Self` will refer to the new type as opposed 73 // to this struct definition. Therefore we have to replace `Self` with the concrete name. 74 let mut replacer = { 75 let name = &struct_.ident; 76 let (_, ty_generics, _) = struct_.generics.split_for_impl(); 77 SelfReplacer(parse_quote!(#name #ty_generics)) 78 }; 79 replacer.visit_generics_mut(&mut struct_.generics); 80 replacer.visit_fields_mut(&mut struct_.fields); 81 82 let fields: Vec<FieldInfo<'_>> = struct_ 83 .fields 84 .iter_mut() 85 .map(|field| { 86 let len = field.attrs.len(); 87 field.attrs.retain(|a| !a.path().is_ident("pin")); 88 let pinned = len != field.attrs.len(); 89 90 let cfg_attrs = field 91 .attrs 92 .iter() 93 .filter(|a| a.path().is_ident("cfg")) 94 .collect(); 95 96 FieldInfo { 97 field: &*field, 98 pinned, 99 cfg_attrs, 100 } 101 }) 102 .collect(); 103 104 for field in &fields { 105 let ident = field.field.ident.as_ref().unwrap(); 106 107 if !field.pinned && is_phantom_pinned(&field.field.ty) { 108 dcx.warn( 109 field.field, 110 format!( 111 "The field `{ident}` of type `PhantomPinned` only has an effect \ 112 if it has the `#[pin]` attribute", 113 ), 114 ); 115 } 116 } 117 118 let unpin_impl = generate_unpin_impl(&struct_.ident, &struct_.generics, &fields); 119 let drop_impl = generate_drop_impl(&struct_.ident, &struct_.generics, args); 120 let projections = 121 generate_projections(&struct_.vis, &struct_.ident, &struct_.generics, &fields); 122 let the_pin_data = 123 generate_the_pin_data(&struct_.vis, &struct_.ident, &struct_.generics, &fields); 124 125 Ok(quote! { 126 #struct_ 127 #projections 128 // We put the rest into this const item, because it then will not be accessible to anything 129 // outside. 130 const _: () = { 131 #the_pin_data 132 #unpin_impl 133 #drop_impl 134 }; 135 }) 136 } 137 138 fn is_phantom_pinned(ty: &Type) -> bool { 139 match ty { 140 Type::Path(TypePath { qself: None, path }) => { 141 // Cannot possibly refer to `PhantomPinned` (except alias, but that's on the user). 142 if path.segments.len() > 3 { 143 return false; 144 } 145 // If there is a `::`, then the path needs to be `::core::marker::PhantomPinned` or 146 // `::std::marker::PhantomPinned`. 147 if path.leading_colon.is_some() && path.segments.len() != 3 { 148 return false; 149 } 150 let expected: Vec<&[&str]> = vec![&["PhantomPinned"], &["marker"], &["core", "std"]]; 151 for (actual, expected) in path.segments.iter().rev().zip(expected) { 152 if !actual.arguments.is_empty() || expected.iter().all(|e| actual.ident != e) { 153 return false; 154 } 155 } 156 true 157 } 158 _ => false, 159 } 160 } 161 162 fn generate_unpin_impl( 163 ident: &Ident, 164 generics: &Generics, 165 fields: &[FieldInfo<'_>], 166 ) -> TokenStream { 167 let (_, ty_generics, _) = generics.split_for_impl(); 168 let mut generics_with_pin_lt = generics.clone(); 169 generics_with_pin_lt.params.insert(0, parse_quote!('__pin)); 170 generics_with_pin_lt.make_where_clause(); 171 let ( 172 impl_generics_with_pin_lt, 173 ty_generics_with_pin_lt, 174 Some(WhereClause { 175 where_token, 176 predicates, 177 }), 178 ) = generics_with_pin_lt.split_for_impl() 179 else { 180 unreachable!() 181 }; 182 let pinned_fields = fields.iter().filter(|f| f.pinned).map(|f| { 183 let ident = f.field.ident.as_ref().unwrap(); 184 let ty = &f.field.ty; 185 let cfg_attrs = &f.cfg_attrs; 186 quote!( 187 #(#cfg_attrs)* 188 #ident: #ty 189 ) 190 }); 191 quote! { 192 // This struct will be used for the unpin analysis. It is needed, because only structurally 193 // pinned fields are relevant whether the struct should implement `Unpin`. 194 #[allow( 195 dead_code, // The fields below are never used. 196 non_snake_case // The warning will be emitted on the struct definition. 197 )] 198 struct __Unpin #generics_with_pin_lt 199 #where_token 200 #predicates 201 { 202 __phantom_pin: ::pin_init::__internal::PhantomInvariantLifetime<'__pin>, 203 __phantom: ::pin_init::__internal::PhantomInvariant<#ident #ty_generics>, 204 #(#pinned_fields),* 205 } 206 207 #[doc(hidden)] 208 impl #impl_generics_with_pin_lt ::core::marker::Unpin for #ident #ty_generics 209 #where_token 210 __Unpin #ty_generics_with_pin_lt: ::core::marker::Unpin, 211 #predicates 212 {} 213 } 214 } 215 216 fn generate_drop_impl(ident: &Ident, generics: &Generics, args: Args) -> TokenStream { 217 let (impl_generics, ty_generics, whr) = generics.split_for_impl(); 218 let has_pinned_drop = matches!(args, Args::PinnedDrop(_)); 219 // We need to disallow normal `Drop` implementation, the exact behavior depends on whether 220 // `PinnedDrop` was specified in `args`. 221 if has_pinned_drop { 222 // When `PinnedDrop` was specified we just implement `Drop` and delegate. 223 quote! { 224 impl #impl_generics ::core::ops::Drop for #ident #ty_generics 225 #whr 226 { 227 fn drop(&mut self) { 228 // SAFETY: Since this is a destructor, `self` will not move after this function 229 // terminates, since it is inaccessible. 230 let pinned = unsafe { ::core::pin::Pin::new_unchecked(self) }; 231 // SAFETY: Since this is a drop function, we can create this token to call the 232 // pinned destructor of this type. 233 let token = unsafe { ::pin_init::__internal::OnlyCallFromDrop::new() }; 234 ::pin_init::PinnedDrop::drop(pinned, token); 235 } 236 } 237 } 238 } else { 239 // When no `PinnedDrop` was specified, then we have to prevent implementing drop. 240 quote! { 241 // We prevent this by creating a trait that will be implemented for all types implementing 242 // `Drop`. Additionally we will implement this trait for the struct leading to a conflict, 243 // if it also implements `Drop` 244 trait MustNotImplDrop {} 245 #[expect(drop_bounds)] 246 impl<T: ::core::ops::Drop + ?::core::marker::Sized> MustNotImplDrop for T {} 247 impl #impl_generics MustNotImplDrop for #ident #ty_generics 248 #whr 249 {} 250 // We also take care to prevent users from writing a useless `PinnedDrop` implementation. 251 // They might implement `PinnedDrop` correctly for the struct, but forget to give 252 // `PinnedDrop` as the parameter to `#[pin_data]`. 253 #[expect(non_camel_case_types)] 254 trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {} 255 impl<T: ::pin_init::PinnedDrop + ?::core::marker::Sized> 256 UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {} 257 impl #impl_generics 258 UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for #ident #ty_generics 259 #whr 260 {} 261 } 262 } 263 } 264 265 fn generate_projections( 266 vis: &Visibility, 267 ident: &Ident, 268 generics: &Generics, 269 fields: &[FieldInfo<'_>], 270 ) -> TokenStream { 271 let (impl_generics, ty_generics, _) = generics.split_for_impl(); 272 let mut generics_with_pin_lt = generics.clone(); 273 generics_with_pin_lt.params.insert(0, parse_quote!('__pin)); 274 let (_, ty_generics_with_pin_lt, whr) = generics_with_pin_lt.split_for_impl(); 275 let projection = format_ident!("{ident}Projection"); 276 let this = format_ident!("this"); 277 278 let (fields_decl, fields_proj): (Vec<_>, Vec<_>) = fields 279 .iter() 280 .map(|field| { 281 let Field { vis, ident, ty, .. } = &field.field; 282 let cfg_attrs = &field.cfg_attrs; 283 284 let ident = ident 285 .as_ref() 286 .expect("only structs with named fields are supported"); 287 if field.pinned { 288 ( 289 quote!( 290 #(#cfg_attrs)* 291 #vis #ident: ::core::pin::Pin<&'__pin mut #ty>, 292 ), 293 quote!( 294 #(#cfg_attrs)* 295 // SAFETY: this field is structurally pinned. 296 #ident: unsafe { ::core::pin::Pin::new_unchecked(&mut #this.#ident) }, 297 ), 298 ) 299 } else { 300 ( 301 quote!( 302 #(#cfg_attrs)* 303 #vis #ident: &'__pin mut #ty, 304 ), 305 quote!( 306 #(#cfg_attrs)* 307 #ident: &mut #this.#ident, 308 ), 309 ) 310 } 311 }) 312 .collect(); 313 let structurally_pinned_fields_docs = fields 314 .iter() 315 .filter(|f| f.pinned) 316 .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap())); 317 let not_structurally_pinned_fields_docs = fields 318 .iter() 319 .filter(|f| !f.pinned) 320 .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap())); 321 let docs = format!(" Pin-projections of [`{ident}`]"); 322 quote! { 323 #[doc = #docs] 324 // Allow `non_snake_case` since the same warning will be emitted on 325 // the struct definition. 326 #[allow(dead_code, non_snake_case)] 327 #[doc(hidden)] 328 #vis struct #projection #generics_with_pin_lt 329 #whr 330 { 331 #(#fields_decl)* 332 ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>, 333 } 334 335 impl #impl_generics #ident #ty_generics 336 #whr 337 { 338 /// Pin-projects all fields of `Self`. 339 /// 340 /// These fields are structurally pinned: 341 #(#[doc = #structurally_pinned_fields_docs])* 342 /// 343 /// These fields are **not** structurally pinned: 344 #(#[doc = #not_structurally_pinned_fields_docs])* 345 #[inline] 346 #vis fn project<'__pin>( 347 self: ::core::pin::Pin<&'__pin mut Self>, 348 ) -> #projection #ty_generics_with_pin_lt { 349 // SAFETY: we only give access to `&mut` for fields not structurally pinned. 350 let #this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; 351 #projection { 352 #(#fields_proj)* 353 ___pin_phantom_data: ::core::marker::PhantomData, 354 } 355 } 356 } 357 } 358 } 359 360 fn generate_the_pin_data( 361 vis: &Visibility, 362 struct_name: &Ident, 363 generics: &Generics, 364 fields: &[FieldInfo<'_>], 365 ) -> TokenStream { 366 let (impl_generics, ty_generics, whr) = generics.split_for_impl(); 367 368 // For every field, we create an initializing projection function according to its projection 369 // type. If a field is structurally pinned, we create a `Slot` with `Pinned` which must be 370 // initialized via `PinInit`; if it is not structurally pinned, then we create a `Slot` with 371 // `Unpinned` which allows initialization via `Init`. 372 let field_accessors = fields 373 .iter() 374 .map(|f| { 375 let Field { vis, ident, ty, .. } = f.field; 376 let cfg_attrs = &f.cfg_attrs; 377 378 let field_name = ident 379 .as_ref() 380 .expect("only structs with named fields are supported"); 381 let pin_marker = if f.pinned { 382 quote!(Pinned) 383 } else { 384 quote!(Unpinned) 385 }; 386 quote! { 387 /// # Safety 388 /// 389 /// - `slot` is valid and properly aligned. 390 /// - `(*slot).#field_name` is properly aligned. 391 /// - `(*slot).#field_name` points to uninitialized and exclusively accessed 392 /// memory. 393 #(#cfg_attrs)* 394 // Allow `non_snake_case` since the same warning will be emitted on 395 // the struct definition. 396 #[allow(non_snake_case)] 397 #[inline(always)] 398 #vis unsafe fn #field_name( 399 self, 400 slot: *mut #struct_name #ty_generics, 401 ) -> ::pin_init::__internal::Slot<::pin_init::__internal::#pin_marker, #ty> { 402 // SAFETY: 403 // - If `#pin_marker` is `Pinned`, the corresponding field is structurally 404 // pinned. 405 // - Other safety requirements follows the safety requirement. 406 unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).#field_name) } 407 } 408 } 409 }) 410 .collect::<TokenStream>(); 411 quote! { 412 // We declare this struct which will host all of the projection function for our type. It 413 // will be invariant over all generic parameters which are inherited from the struct. 414 #[doc(hidden)] 415 #vis struct __ThePinData #generics 416 #whr 417 { 418 __phantom: ::pin_init::__internal::PhantomInvariant<#struct_name #ty_generics>, 419 } 420 421 impl #impl_generics ::core::clone::Clone for __ThePinData #ty_generics 422 #whr 423 { 424 fn clone(&self) -> Self { *self } 425 } 426 427 impl #impl_generics ::core::marker::Copy for __ThePinData #ty_generics 428 #whr 429 {} 430 431 #[allow(dead_code)] // Some functions might never be used and private. 432 #[expect(clippy::missing_safety_doc)] 433 impl #impl_generics __ThePinData #ty_generics 434 #whr 435 { 436 /// Type inference helper function. 437 #[inline(always)] 438 #vis fn __make_closure<__F, __E>(self, f: __F) -> __F 439 where 440 __F: FnOnce(*mut #struct_name #ty_generics) -> 441 ::core::result::Result<::pin_init::__internal::InitOk, __E>, 442 { 443 f 444 } 445 446 #field_accessors 447 } 448 449 // SAFETY: We have added the correct projection functions above to `__ThePinData` and 450 // we also use the least restrictive generics possible. 451 unsafe impl #impl_generics ::pin_init::__internal::HasPinData for #struct_name #ty_generics 452 #whr 453 { 454 type PinData = __ThePinData #ty_generics; 455 456 unsafe fn __pin_data() -> Self::PinData { 457 __ThePinData { __phantom: ::pin_init::__internal::PhantomInvariant::new() } 458 } 459 } 460 } 461 } 462 463 struct SelfReplacer(PathSegment); 464 465 impl VisitMut for SelfReplacer { 466 fn visit_path_mut(&mut self, i: &mut syn::Path) { 467 if i.is_ident("Self") { 468 let span = i.span(); 469 let seg = &self.0; 470 *i = parse_quote_spanned!(span=> #seg); 471 } else { 472 syn::visit_mut::visit_path_mut(self, i); 473 } 474 } 475 476 fn visit_path_segment_mut(&mut self, seg: &mut PathSegment) { 477 if seg.ident == "Self" { 478 let span = seg.span(); 479 let this = &self.0; 480 *seg = parse_quote_spanned!(span=> #this); 481 } else { 482 syn::visit_mut::visit_path_segment_mut(self, seg); 483 } 484 } 485 486 fn visit_item_mut(&mut self, _: &mut Item) { 487 // Do not descend into items, since items reset/change what `Self` refers to. 488 } 489 } 490