537 lines · 18.8 KB · Rust
RawDownload
// SPDX-License-Identifier: Apache-2.0 OR MIT use proc_macro2::TokenStream;use quote::{format_ident, quote, ToTokens};use syn::{    parse::{End, Nothing, Parse},    parse_quote, parse_quote_spanned,    spanned::Spanned,    visit_mut::VisitMut,    Field, Fields, Generics, Ident, Item, PathSegment, Type, TypePath, Visibility, WhereClause,}; use crate::diagnostics::{DiagCtxt, ErrorGuaranteed}; pub(crate) mod kw {    syn::custom_keyword!(PinnedDrop);} pub(crate) enum Args {    Nothing(Nothing),    #[allow(dead_code)]    PinnedDrop(kw::PinnedDrop),} impl Parse for Args {    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {        let lh = input.lookahead1();        if lh.peek(End) {            input.parse().map(Self::Nothing)        } else if lh.peek(kw::PinnedDrop) {            input.parse().map(Self::PinnedDrop)        } else {            Err(lh.error())        }    }} impl ToTokens for Args {    fn to_tokens(&self, tokens: &mut TokenStream) {        match self {            Self::Nothing(_) => (),            Self::PinnedDrop(kw) => kw.to_tokens(tokens),        }    }} struct FieldInfo<'a> {    field: &'a Field,    pinned: bool,} pub(crate) fn pin_data(    args: Args,    input: Item,    dcx: &mut DiagCtxt,) -> Result<TokenStream, ErrorGuaranteed> {    let mut struct_ = match input {        Item::Struct(struct_) => struct_,        Item::Enum(enum_) => {            return Err(dcx.error(                enum_.enum_token,                "`#[pin_data]` only supports structs for now",            ));        }        Item::Union(union) => {            return Err(dcx.error(                union.union_token,                "`#[pin_data]` only supports structs for now",            ));        }        rest => {            return Err(dcx.error(                rest,                "`#[pin_data]` can only be applied to struct, enum and union definitions",            ));        }    };     // Handling cfg can gets very complicated, especially for tuple structs. Therefore, resolve all    // field cfgs first before continuing.    //    // We need to perform this after parsing so we can reliably detect field cfgs.    for (field_idx, field) in struct_.fields.iter_mut().enumerate() {        let cfg: Vec<_> = field            .attrs            .iter()            .filter(|a| a.path().is_ident("cfg"))            .map(|a| {                a.parse_args::<TokenStream>()                    .expect("parse as token stream cannot fail")            })            .collect();         if cfg.is_empty() {            continue;        }         field.attrs.retain(|a| !a.path().is_ident("cfg"));        let cfg_true_struct = quote!(#struct_);         let punctuated = match &mut struct_.fields {            Fields::Named(fields) => &mut fields.named,            Fields::Unnamed(fields) => &mut fields.unnamed,            Fields::Unit => unreachable!(),        };        *punctuated = std::mem::take(punctuated)            .into_pairs()            .enumerate()            .filter(|&(i, _)| i != field_idx)            .map(|(_, p)| p)            .collect();        let cfg_false_struct = quote!(#struct_);         // Resolve one field at a time until we've got no more field cfgs.        //        // This is linear time because macro invocations with false cfg will not be expanded.        return Ok(quote!(            #[cfg(all(#(#cfg,)*))]            #[::pin_init::pin_data(#args)]            #cfg_true_struct             #[cfg(not(all(#(#cfg,)*)))]            #[::pin_init::pin_data(#args)]            #cfg_false_struct        ));    }     // The generics might contain the `Self` type. Since this macro will define a new type with the    // same generics and bounds, this poses a problem: `Self` will refer to the new type as opposed    // to this struct definition. Therefore we have to replace `Self` with the concrete name.    let mut replacer = {        let name = &struct_.ident;        let (_, ty_generics, _) = struct_.generics.split_for_impl();        SelfReplacer(parse_quote!(#name #ty_generics))    };    replacer.visit_generics_mut(&mut struct_.generics);    replacer.visit_fields_mut(&mut struct_.fields);     let fields: Vec<FieldInfo<'_>> = struct_        .fields        .iter_mut()        .map(|field| {            let len = field.attrs.len();            field.attrs.retain(|a| !a.path().is_ident("pin"));            let pinned_count = len - field.attrs.len();            if pinned_count > 1 {                dcx.error(&field, "#[pin] attribute specified more than once");            }             assert!(                !field.attrs.iter().any(|a| a.path().is_ident("cfg")),                "cfgs should be all resolved at this point"            );             FieldInfo {                field: &*field,                pinned: pinned_count != 0,            }        })        .collect();     for field in &fields {        let ident = field.field.ident.as_ref().unwrap();         if !field.pinned && is_phantom_pinned(&field.field.ty) {            dcx.warn(                field.field,                format!(                    "The field `{ident}` of type `PhantomPinned` only has an effect \                    if it has the `#[pin]` attribute",                ),            );        }    }     let unpin_impl = generate_unpin_impl(&struct_.ident, &struct_.generics, &fields);    let drop_impl = generate_drop_impl(&struct_.ident, &struct_.generics, args);    let projections =        generate_projections(&struct_.vis, &struct_.ident, &struct_.generics, &fields);    let the_pin_data =        generate_the_pin_data(&struct_.vis, &struct_.ident, &struct_.generics, &fields);     Ok(quote! {        #struct_        #projections        // We put the rest into this const item, because it then will not be accessible to anything        // outside.        const _: () = {            #the_pin_data            #unpin_impl            #drop_impl        };    })} fn is_phantom_pinned(ty: &Type) -> bool {    match ty {        Type::Path(TypePath { qself: None, path }) => {            // Cannot possibly refer to `PhantomPinned` (except alias, but that's on the user).            if path.segments.len() > 3 {                return false;            }            // If there is a `::`, then the path needs to be `::core::marker::PhantomPinned` or            // `::std::marker::PhantomPinned`.            if path.leading_colon.is_some() && path.segments.len() != 3 {                return false;            }            let expected: Vec<&[&str]> = vec![&["PhantomPinned"], &["marker"], &["core", "std"]];            for (actual, expected) in path.segments.iter().rev().zip(expected) {                if !actual.arguments.is_empty() || expected.iter().all(|e| actual.ident != e) {                    return false;                }            }            true        }        _ => false,    }} fn generate_unpin_impl(    ident: &Ident,    generics: &Generics,    fields: &[FieldInfo<'_>],) -> TokenStream {    let (_, ty_generics, _) = generics.split_for_impl();    let mut generics_with_pin_lt = generics.clone();    generics_with_pin_lt.params.insert(0, parse_quote!('__pin));    generics_with_pin_lt.make_where_clause();    let (        impl_generics_with_pin_lt,        ty_generics_with_pin_lt,        Some(WhereClause {            where_token,            predicates,        }),    ) = generics_with_pin_lt.split_for_impl()    else {        unreachable!()    };    let pinned_fields = fields.iter().filter(|f| f.pinned).map(|f| {        let ident = f.field.ident.as_ref().unwrap();        let ty = &f.field.ty;        quote!(            #ident: #ty        )    });    quote! {        // This struct will be used for the unpin analysis. It is needed, because only structurally        // pinned fields are relevant whether the struct should implement `Unpin`.        #[allow(            dead_code, // The fields below are never used.            non_snake_case // The warning will be emitted on the struct definition.        )]        struct __Unpin #generics_with_pin_lt        #where_token            #predicates        {            __phantom_pin: ::pin_init::__internal::PhantomInvariantLifetime<'__pin>,            __phantom: ::pin_init::__internal::PhantomInvariant<#ident #ty_generics>,            #(#pinned_fields),*        }         #[doc(hidden)]        impl #impl_generics_with_pin_lt ::core::marker::Unpin for #ident #ty_generics        #where_token            __Unpin #ty_generics_with_pin_lt: ::core::marker::Unpin,            #predicates        {}    }} fn generate_drop_impl(ident: &Ident, generics: &Generics, args: Args) -> TokenStream {    let (impl_generics, ty_generics, whr) = generics.split_for_impl();    let has_pinned_drop = matches!(args, Args::PinnedDrop(_));    // We need to disallow normal `Drop` implementation, the exact behavior depends on whether    // `PinnedDrop` was specified in `args`.    if has_pinned_drop {        // When `PinnedDrop` was specified we just implement `Drop` and delegate.        quote! {            impl #impl_generics ::core::ops::Drop for #ident #ty_generics                #whr            {                fn drop(&mut self) {                    // SAFETY: Since this is a destructor, `self` will not move after this function                    // terminates, since it is inaccessible.                    let pinned = unsafe { ::core::pin::Pin::new_unchecked(self) };                    // SAFETY: Since this is a drop function, we can create this token to call the                    // pinned destructor of this type.                    let token = unsafe { ::pin_init::__internal::OnlyCallFromDrop::new() };                    ::pin_init::PinnedDrop::drop(pinned, token);                }            }        }    } else {        // When no `PinnedDrop` was specified, then we have to prevent implementing drop.        quote! {            // We prevent this by creating a trait that will be implemented for all types implementing            // `Drop`. Additionally we will implement this trait for the struct leading to a conflict,            // if it also implements `Drop`            trait MustNotImplDrop {}            impl<T: ::core::ops::Drop + ?::core::marker::Sized> MustNotImplDrop for T {}            impl #impl_generics MustNotImplDrop for #ident #ty_generics                #whr            {}            // We also take care to prevent users from writing a useless `PinnedDrop` implementation.            // They might implement `PinnedDrop` correctly for the struct, but forget to give            // `PinnedDrop` as the parameter to `#[pin_data]`.            trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {}            impl<T: ::pin_init::PinnedDrop + ?::core::marker::Sized>                UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {}            impl #impl_generics                UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for #ident #ty_generics                #whr            {}        }    }} fn generate_projections(    vis: &Visibility,    ident: &Ident,    generics: &Generics,    fields: &[FieldInfo<'_>],) -> TokenStream {    let (impl_generics, ty_generics, _) = generics.split_for_impl();    let mut generics_with_pin_lt = generics.clone();    generics_with_pin_lt.params.insert(0, parse_quote!('__pin));    let (_, ty_generics_with_pin_lt, whr) = generics_with_pin_lt.split_for_impl();    let projection = format_ident!("{ident}Projection");    let this = format_ident!("this");     let (fields_decl, fields_proj): (Vec<_>, Vec<_>) = fields        .iter()        .map(|field| {            let Field { vis, ident, ty, .. } = &field.field;             let ident = ident                .as_ref()                .expect("only structs with named fields are supported");            if field.pinned {                (                    quote!(                        #vis #ident: ::core::pin::Pin<&'__pin mut #ty>,                    ),                    quote!(                        // SAFETY: this field is structurally pinned.                        #ident: unsafe { ::core::pin::Pin::new_unchecked(&mut #this.#ident) },                    ),                )            } else {                (                    quote!(                        #vis #ident: &'__pin mut #ty,                    ),                    quote!(                        #ident: &mut #this.#ident,                    ),                )            }        })        .collect();    let structurally_pinned_fields_docs = fields        .iter()        .filter(|f| f.pinned)        .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap()));    let not_structurally_pinned_fields_docs = fields        .iter()        .filter(|f| !f.pinned)        .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap()));    let docs = format!(" Pin-projections of [`{ident}`]");    quote! {        #[doc = #docs]        // Allow `non_snake_case` since the same warning will be emitted on        // the struct definition.        #[allow(dead_code, non_snake_case)]        #[doc(hidden)]        #vis struct #projection #generics_with_pin_lt            #whr        {            #(#fields_decl)*            ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>,        }         impl #impl_generics #ident #ty_generics            #whr        {            /// Pin-projects all fields of `Self`.            ///            /// These fields are structurally pinned:            #(#[doc = #structurally_pinned_fields_docs])*            ///            /// These fields are **not** structurally pinned:            #(#[doc = #not_structurally_pinned_fields_docs])*            #[inline]            #vis fn project<'__pin>(                self: ::core::pin::Pin<&'__pin mut Self>,            ) -> #projection #ty_generics_with_pin_lt {                // SAFETY: we only give access to `&mut` for fields not structurally pinned.                let #this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) };                #projection {                    #(#fields_proj)*                    ___pin_phantom_data: ::core::marker::PhantomData,                }            }        }    }} fn generate_the_pin_data(    vis: &Visibility,    struct_name: &Ident,    generics: &Generics,    fields: &[FieldInfo<'_>],) -> TokenStream {    let (impl_generics, ty_generics, whr) = generics.split_for_impl();     // For every field, we create an initializing projection function according to its projection    // type. If a field is structurally pinned, we create a `Slot` with `Pinned` which must be    // initialized via `PinInit`; if it is not structurally pinned, then we create a `Slot` with    // `Unpinned` which allows initialization via `Init`.    let field_accessors = fields        .iter()        .map(|f| {            let Field { vis, ident, ty, .. } = f.field;             let field_name = ident                .as_ref()                .expect("only structs with named fields are supported");            let pin_marker = if f.pinned {                quote!(Pinned)            } else {                quote!(Unpinned)            };            quote! {                /// # Safety                ///                /// - `slot` is valid and properly aligned.                /// - `(*slot).#field_name` is properly aligned.                /// - `(*slot).#field_name` points to uninitialized and exclusively accessed                ///   memory.                // Allow `non_snake_case` since the same warning will be emitted on                // the struct definition.                #[allow(non_snake_case)]                #[inline(always)]                #vis unsafe fn #field_name(                    self,                    slot: *mut #struct_name #ty_generics,                ) -> ::pin_init::__internal::Slot<::pin_init::__internal::#pin_marker, #ty> {                    // SAFETY:                    // - If `#pin_marker` is `Pinned`, the corresponding field is structurally                    //   pinned.                    // - Other safety requirements follows the safety requirement.                    unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).#field_name) }                }            }        })        .collect::<TokenStream>();    quote! {        // We declare this struct which will host all of the projection function for our type. It        // will be invariant over all generic parameters which are inherited from the struct.        #[doc(hidden)]        #vis struct __ThePinData #generics            #whr        {            __phantom: ::pin_init::__internal::PhantomInvariant<#struct_name #ty_generics>,        }         impl #impl_generics ::core::clone::Clone for __ThePinData #ty_generics            #whr        {            #[inline]            fn clone(&self) -> Self { *self }        }         impl #impl_generics ::core::marker::Copy for __ThePinData #ty_generics            #whr        {}         #[allow(dead_code)] // Some functions might never be used and private.        impl #impl_generics __ThePinData #ty_generics            #whr        {            /// Type inference helper function.            #[inline(always)]            #vis fn __make_closure<__F, __E>(self, f: __F) -> __F            where                __F: FnOnce(*mut #struct_name #ty_generics) ->                    ::core::result::Result<::pin_init::__internal::InitOk, __E>,            {                f            }             #field_accessors        }         // SAFETY: We have added the correct projection functions above to `__ThePinData` and        // we also use the least restrictive generics possible.        unsafe impl #impl_generics ::pin_init::__internal::HasPinData for #struct_name #ty_generics            #whr        {

Lines 1–500 of 537.