Skip to main content

pin_init_internal/
pin_data.rs

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