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_count = len - field.attrs.len();
89            if pinned_count > 1 {
90                dcx.error(&field, "#[pin] attribute specified more than once");
91            }
92
93            let cfg_attrs = field
94                .attrs
95                .iter()
96                .filter(|a| a.path().is_ident("cfg"))
97                .collect();
98
99            FieldInfo {
100                field: &*field,
101                pinned: pinned_count != 0,
102                cfg_attrs,
103            }
104        })
105        .collect();
106
107    for field in &fields {
108        let ident = field.field.ident.as_ref().unwrap();
109
110        if !field.pinned && is_phantom_pinned(&field.field.ty) {
111            dcx.warn(
112                field.field,
113                format!(
114                    "The field `{ident}` of type `PhantomPinned` only has an effect \
115                    if it has the `#[pin]` attribute",
116                ),
117            );
118        }
119    }
120
121    let unpin_impl = generate_unpin_impl(&struct_.ident, &struct_.generics, &fields);
122    let drop_impl = generate_drop_impl(&struct_.ident, &struct_.generics, args);
123    let projections =
124        generate_projections(&struct_.vis, &struct_.ident, &struct_.generics, &fields);
125    let the_pin_data =
126        generate_the_pin_data(&struct_.vis, &struct_.ident, &struct_.generics, &fields);
127
128    Ok(quote! {
129        #struct_
130        #projections
131        // We put the rest into this const item, because it then will not be accessible to anything
132        // outside.
133        const _: () = {
134            #the_pin_data
135            #unpin_impl
136            #drop_impl
137        };
138    })
139}
140
141fn is_phantom_pinned(ty: &Type) -> bool {
142    match ty {
143        Type::Path(TypePath { qself: None, path }) => {
144            // Cannot possibly refer to `PhantomPinned` (except alias, but that's on the user).
145            if path.segments.len() > 3 {
146                return false;
147            }
148            // If there is a `::`, then the path needs to be `::core::marker::PhantomPinned` or
149            // `::std::marker::PhantomPinned`.
150            if path.leading_colon.is_some() && path.segments.len() != 3 {
151                return false;
152            }
153            let expected: Vec<&[&str]> = vec![&["PhantomPinned"], &["marker"], &["core", "std"]];
154            for (actual, expected) in path.segments.iter().rev().zip(expected) {
155                if !actual.arguments.is_empty() || expected.iter().all(|e| actual.ident != e) {
156                    return false;
157                }
158            }
159            true
160        }
161        _ => false,
162    }
163}
164
165fn generate_unpin_impl(
166    ident: &Ident,
167    generics: &Generics,
168    fields: &[FieldInfo<'_>],
169) -> TokenStream {
170    let (_, ty_generics, _) = generics.split_for_impl();
171    let mut generics_with_pin_lt = generics.clone();
172    generics_with_pin_lt.params.insert(0, parse_quote!('__pin));
173    generics_with_pin_lt.make_where_clause();
174    let (
175        impl_generics_with_pin_lt,
176        ty_generics_with_pin_lt,
177        Some(WhereClause {
178            where_token,
179            predicates,
180        }),
181    ) = generics_with_pin_lt.split_for_impl()
182    else {
183        unreachable!()
184    };
185    let pinned_fields = fields.iter().filter(|f| f.pinned).map(|f| {
186        let ident = f.field.ident.as_ref().unwrap();
187        let ty = &f.field.ty;
188        let cfg_attrs = &f.cfg_attrs;
189        quote!(
190            #(#cfg_attrs)*
191            #ident: #ty
192        )
193    });
194    quote! {
195        // This struct will be used for the unpin analysis. It is needed, because only structurally
196        // pinned fields are relevant whether the struct should implement `Unpin`.
197        #[allow(
198            dead_code, // The fields below are never used.
199            non_snake_case // The warning will be emitted on the struct definition.
200        )]
201        struct __Unpin #generics_with_pin_lt
202        #where_token
203            #predicates
204        {
205            __phantom_pin: ::pin_init::__internal::PhantomInvariantLifetime<'__pin>,
206            __phantom: ::pin_init::__internal::PhantomInvariant<#ident #ty_generics>,
207            #(#pinned_fields),*
208        }
209
210        #[doc(hidden)]
211        impl #impl_generics_with_pin_lt ::core::marker::Unpin for #ident #ty_generics
212        #where_token
213            __Unpin #ty_generics_with_pin_lt: ::core::marker::Unpin,
214            #predicates
215        {}
216    }
217}
218
219fn generate_drop_impl(ident: &Ident, generics: &Generics, args: Args) -> TokenStream {
220    let (impl_generics, ty_generics, whr) = generics.split_for_impl();
221    let has_pinned_drop = matches!(args, Args::PinnedDrop(_));
222    // We need to disallow normal `Drop` implementation, the exact behavior depends on whether
223    // `PinnedDrop` was specified in `args`.
224    if has_pinned_drop {
225        // When `PinnedDrop` was specified we just implement `Drop` and delegate.
226        quote! {
227            impl #impl_generics ::core::ops::Drop for #ident #ty_generics
228                #whr
229            {
230                fn drop(&mut self) {
231                    // SAFETY: Since this is a destructor, `self` will not move after this function
232                    // terminates, since it is inaccessible.
233                    let pinned = unsafe { ::core::pin::Pin::new_unchecked(self) };
234                    // SAFETY: Since this is a drop function, we can create this token to call the
235                    // pinned destructor of this type.
236                    let token = unsafe { ::pin_init::__internal::OnlyCallFromDrop::new() };
237                    ::pin_init::PinnedDrop::drop(pinned, token);
238                }
239            }
240        }
241    } else {
242        // When no `PinnedDrop` was specified, then we have to prevent implementing drop.
243        quote! {
244            // We prevent this by creating a trait that will be implemented for all types implementing
245            // `Drop`. Additionally we will implement this trait for the struct leading to a conflict,
246            // if it also implements `Drop`
247            trait MustNotImplDrop {}
248            impl<T: ::core::ops::Drop + ?::core::marker::Sized> MustNotImplDrop for T {}
249            impl #impl_generics MustNotImplDrop for #ident #ty_generics
250                #whr
251            {}
252            // We also take care to prevent users from writing a useless `PinnedDrop` implementation.
253            // They might implement `PinnedDrop` correctly for the struct, but forget to give
254            // `PinnedDrop` as the parameter to `#[pin_data]`.
255            trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {}
256            impl<T: ::pin_init::PinnedDrop + ?::core::marker::Sized>
257                UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {}
258            impl #impl_generics
259                UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for #ident #ty_generics
260                #whr
261            {}
262        }
263    }
264}
265
266fn generate_projections(
267    vis: &Visibility,
268    ident: &Ident,
269    generics: &Generics,
270    fields: &[FieldInfo<'_>],
271) -> TokenStream {
272    let (impl_generics, ty_generics, _) = generics.split_for_impl();
273    let mut generics_with_pin_lt = generics.clone();
274    generics_with_pin_lt.params.insert(0, parse_quote!('__pin));
275    let (_, ty_generics_with_pin_lt, whr) = generics_with_pin_lt.split_for_impl();
276    let projection = format_ident!("{ident}Projection");
277    let this = format_ident!("this");
278
279    let (fields_decl, fields_proj): (Vec<_>, Vec<_>) = fields
280        .iter()
281        .map(|field| {
282            let Field { vis, ident, ty, .. } = &field.field;
283            let cfg_attrs = &field.cfg_attrs;
284
285            let ident = ident
286                .as_ref()
287                .expect("only structs with named fields are supported");
288            if field.pinned {
289                (
290                    quote!(
291                        #(#cfg_attrs)*
292                        #vis #ident: ::core::pin::Pin<&'__pin mut #ty>,
293                    ),
294                    quote!(
295                        #(#cfg_attrs)*
296                        // SAFETY: this field is structurally pinned.
297                        #ident: unsafe { ::core::pin::Pin::new_unchecked(&mut #this.#ident) },
298                    ),
299                )
300            } else {
301                (
302                    quote!(
303                        #(#cfg_attrs)*
304                        #vis #ident: &'__pin mut #ty,
305                    ),
306                    quote!(
307                        #(#cfg_attrs)*
308                        #ident: &mut #this.#ident,
309                    ),
310                )
311            }
312        })
313        .collect();
314    let structurally_pinned_fields_docs = fields
315        .iter()
316        .filter(|f| f.pinned)
317        .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap()));
318    let not_structurally_pinned_fields_docs = fields
319        .iter()
320        .filter(|f| !f.pinned)
321        .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap()));
322    let docs = format!(" Pin-projections of [`{ident}`]");
323    quote! {
324        #[doc = #docs]
325        // Allow `non_snake_case` since the same warning will be emitted on
326        // the struct definition.
327        #[allow(dead_code, non_snake_case)]
328        #[doc(hidden)]
329        #vis struct #projection #generics_with_pin_lt
330            #whr
331        {
332            #(#fields_decl)*
333            ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>,
334        }
335
336        impl #impl_generics #ident #ty_generics
337            #whr
338        {
339            /// Pin-projects all fields of `Self`.
340            ///
341            /// These fields are structurally pinned:
342            #(#[doc = #structurally_pinned_fields_docs])*
343            ///
344            /// These fields are **not** structurally pinned:
345            #(#[doc = #not_structurally_pinned_fields_docs])*
346            #[inline]
347            #vis fn project<'__pin>(
348                self: ::core::pin::Pin<&'__pin mut Self>,
349            ) -> #projection #ty_generics_with_pin_lt {
350                // SAFETY: we only give access to `&mut` for fields not structurally pinned.
351                let #this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) };
352                #projection {
353                    #(#fields_proj)*
354                    ___pin_phantom_data: ::core::marker::PhantomData,
355                }
356            }
357        }
358    }
359}
360
361fn generate_the_pin_data(
362    vis: &Visibility,
363    struct_name: &Ident,
364    generics: &Generics,
365    fields: &[FieldInfo<'_>],
366) -> TokenStream {
367    let (impl_generics, ty_generics, whr) = generics.split_for_impl();
368
369    // For every field, we create an initializing projection function according to its projection
370    // type. If a field is structurally pinned, we create a `Slot` with `Pinned` which must be
371    // initialized via `PinInit`; if it is not structurally pinned, then we create a `Slot` with
372    // `Unpinned` which allows initialization via `Init`.
373    let field_accessors = fields
374        .iter()
375        .map(|f| {
376            let Field { vis, ident, ty, .. } = f.field;
377            let cfg_attrs = &f.cfg_attrs;
378
379            let field_name = ident
380                .as_ref()
381                .expect("only structs with named fields are supported");
382            let pin_marker = if f.pinned {
383                quote!(Pinned)
384            } else {
385                quote!(Unpinned)
386            };
387            quote! {
388                /// # Safety
389                ///
390                /// - `slot` is valid and properly aligned.
391                /// - `(*slot).#field_name` is properly aligned.
392                /// - `(*slot).#field_name` points to uninitialized and exclusively accessed
393                ///   memory.
394                #(#cfg_attrs)*
395                // Allow `non_snake_case` since the same warning will be emitted on
396                // the struct definition.
397                #[allow(non_snake_case)]
398                #[inline(always)]
399                #vis unsafe fn #field_name(
400                    self,
401                    slot: *mut #struct_name #ty_generics,
402                ) -> ::pin_init::__internal::Slot<::pin_init::__internal::#pin_marker, #ty> {
403                    // SAFETY:
404                    // - If `#pin_marker` is `Pinned`, the corresponding field is structurally
405                    //   pinned.
406                    // - Other safety requirements follows the safety requirement.
407                    unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).#field_name) }
408                }
409            }
410        })
411        .collect::<TokenStream>();
412    quote! {
413        // We declare this struct which will host all of the projection function for our type. It
414        // will be invariant over all generic parameters which are inherited from the struct.
415        #[doc(hidden)]
416        #vis struct __ThePinData #generics
417            #whr
418        {
419            __phantom: ::pin_init::__internal::PhantomInvariant<#struct_name #ty_generics>,
420        }
421
422        impl #impl_generics ::core::clone::Clone for __ThePinData #ty_generics
423            #whr
424        {
425            fn clone(&self) -> Self { *self }
426        }
427
428        impl #impl_generics ::core::marker::Copy for __ThePinData #ty_generics
429            #whr
430        {}
431
432        #[allow(dead_code)] // Some functions might never be used and private.
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}