xref: /linux/rust/pin-init/internal/src/pin_data.rs (revision e6405dca10de4136a880d218b015663a41d92a54)
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