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};
5use 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
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
38struct FieldInfo<'a> {
39    field: &'a Field,
40    pinned: bool,
41    cfg_attrs: Vec<&'a Attribute>,
42}
43
44pub(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
138fn 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
162fn 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
216fn 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
265fn 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
360fn 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
463struct SelfReplacer(PathSegment);
464
465impl 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}