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