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