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, Item, PathSegment, Type, TypePath, Visibility, WhereClause,
11};
12
13use crate::diagnostics::{DiagCtxt, ErrorGuaranteed};
14
15pub(crate) mod kw {
16    syn::custom_keyword!(PinnedDrop);
17}
18
19pub(crate) enum Args {
20    Nothing(Nothing),
21    #[allow(dead_code)]
22    PinnedDrop(kw::PinnedDrop),
23}
24
25impl 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
38impl 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
47struct FieldInfo<'a> {
48    field: &'a Field,
49    pinned: bool,
50}
51
52pub(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
196fn 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
220fn 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
272fn 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
319fn 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
409fn 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
511struct SelfReplacer(PathSegment);
512
513impl 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}