use proc_macro2::{Delimiter, Group, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; use syn::{ parse::{Parse, ParseStream}, spanned::Spanned, visit_mut::VisitMut, *, }; use super::PIN; use crate::utils::{ determine_lifetime_name, determine_visibility, insert_lifetime_and_bound, ParseBufferExt, ProjKind, ReplaceReceiver, SliceExt, Variants, }; pub(super) fn parse_derive(input: TokenStream) -> Result { let mut input: DeriveInput = syn::parse2(input)?; let ident = &input.ident; let ty_generics = input.generics.split_for_impl().1; let self_ty = parse_quote!(#ident #ty_generics); let mut visitor = ReplaceReceiver(&self_ty); visitor.visit_generics_mut(&mut input.generics); visitor.visit_data_mut(&mut input.data); let mut cx = Context::new(&input.attrs, &input.vis, &input.ident, &mut input.generics)?; let packed_check; let (mut items, scoped_items) = match &input.data { Data::Struct(data) => { // Do this first for a better error message. packed_check = Some(cx.ensure_not_packed(&data.fields)?); cx.parse_struct(data)? } Data::Enum(data) => { // We don't need to check for `#[repr(packed)]`, // since it does not apply to enums. packed_check = None; cx.parse_enum(data)? } Data::Union(_) => { return Err(error!( input, "#[pin_project] attribute may only be used on structs or enums" )); } }; let unpin_impl = cx.make_unpin_impl(); let drop_impl = cx.make_drop_impl(); let dummy_const = if cfg!(underscore_consts) { format_ident!("_") } else { format_ident!("__SCOPE_{}", ident) }; items.extend(quote! { // All items except projected types are generated inside a `const` scope. // This makes it impossible for user code to refer to these types. // However, this prevents Rustdoc from displaying docs for any // of our types. In particular, users cannot see the // automatically generated `Unpin` impl for the '__UnpinStruct' types // // Previously, we provided a flag to correctly document the // automatically generated `Unpin` impl by using def-site hygiene, // but it is now removed. // // Refs: // * https://github.com/rust-lang/rust/issues/63281 // * https://github.com/taiki-e/pin-project/pull/53#issuecomment-525906867 // * https://github.com/taiki-e/pin-project/pull/70 #[doc(hidden)] #[allow(non_upper_case_globals)] #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 #[allow(clippy::used_underscore_binding)] const #dummy_const: () = { #scoped_items #unpin_impl #drop_impl #packed_check }; }); Ok(items) } fn validate_struct(ident: &Ident, fields: &Fields) -> Result<()> { if fields.is_empty() { let msg = "#[pin_project] attribute may not be used on structs with zero fields"; if let Fields::Unit = fields { Err(error!(ident, msg)) } else { Err(error!(fields, msg)) } } else { Ok(()) } } fn validate_enum(brace_token: token::Brace, variants: &Variants) -> Result<()> { if variants.is_empty() { return Err(Error::new( brace_token.span, "#[pin_project] attribute may not be used on enums without variants", )); } let has_field = variants.iter().try_fold(false, |has_field, v| { if let Some((_, e)) = &v.discriminant { Err(error!(e, "#[pin_project] attribute may not be used on enums with discriminants")) } else if let Some(attr) = v.attrs.find(PIN) { Err(error!(attr, "#[pin] attribute may only be used on fields of structs or variants")) } else if v.fields.is_empty() { Ok(has_field) } else { Ok(true) } })?; if has_field { Ok(()) } else { Err(error!(variants, "#[pin_project] attribute may not be used on enums with zero fields")) } } struct Args { /// `PinnedDrop` argument. pinned_drop: Option, /// `UnsafeUnpin` or `!Unpin` argument. unpin_impl: UnpinImpl, /// `project = ` argument. project: Option, /// `project_ref = ` argument. project_ref: Option, /// `project_replace [= ]` or `Replace` argument. project_replace: ProjReplace, } enum ProjReplace { None, /// `project_replace` or `Replace`. Unnamed { span: Span, }, /// `project_replace = `. Named { span: Span, ident: Ident, }, } impl ProjReplace { fn span(&self) -> Option { match self { ProjReplace::None => None, ProjReplace::Named { span, .. } | ProjReplace::Unnamed { span, .. } => Some(*span), } } fn ident(&self) -> Option<&Ident> { if let ProjReplace::Named { ident, .. } = self { Some(ident) } else { None } } } const DUPLICATE_PIN: &str = "duplicate #[pin] attribute"; impl Args { fn get(attrs: &[Attribute]) -> Result { // `(__private())` -> `` struct Input(Option); impl Parse for Input { fn parse(input: ParseStream<'_>) -> Result { Ok(Self((|| { let content = input.parenthesized().ok()?; let private = content.parse::().ok()?; if private == "__private" { content.parenthesized().ok()?.parse::().ok() } else { None } })())) } } if let Some(attr) = attrs.find("pin_project") { return Err(error!(attr, "duplicate #[pin_project] attribute")); } let mut attrs = attrs.iter().filter(|attr| attr.path.is_ident(PIN)); let prev = if let Some(attr) = attrs.next() { (attr, syn::parse2::(attr.tokens.clone()).unwrap().0) } else { // This only fails if another macro removes `#[pin]`. return Err(error!(TokenStream::new(), "#[pin_project] attribute has been removed")); }; if let Some(attr) = attrs.next() { let (prev_attr, prev_res) = &prev; // As the `#[pin]` attribute generated by `#[pin_project]` // has the same span as `#[pin_project]`, it is possible // that a useless error message will be generated. let res = syn::parse2::(attr.tokens.clone()).unwrap().0; let span = match (&prev_res, res) { (Some(_), _) => attr, (_, Some(_)) => prev_attr, (None, None) => prev_attr, }; Err(error!(span, DUPLICATE_PIN)) } else { // This `unwrap` only fails if another macro removes `#[pin]` and inserts own `#[pin]`. syn::parse2(prev.1.unwrap()) } } } impl Parse for Args { fn parse(input: ParseStream<'_>) -> Result { mod kw { syn::custom_keyword!(Unpin); } // Parses `= ` in ` = ` and returns value and span of name-value pair. fn parse_value( input: ParseStream<'_>, name: &Ident, has_prev: bool, ) -> Result<(Ident, TokenStream)> { if input.is_empty() { return Err(error!(name, "expected `{0} = `, found `{0}`", name)); } let eq_token: Token![=] = input.parse()?; if input.is_empty() { let span = quote!(#name #eq_token); return Err(error!(span, "expected `{0} = `, found `{0} =`", name)); } let value: Ident = input.parse()?; let span = quote!(#name #value); if has_prev { Err(error!(span, "duplicate `{}` argument", name)) } else { Ok((value, span)) } } let mut pinned_drop = None; let mut unsafe_unpin = None; let mut not_unpin = None; let mut project = None; let mut project_ref = None; let mut replace = None; let mut project_replace_value = None; let mut project_replace_span = None; while !input.is_empty() { if input.peek(Token![!]) { let bang: Token![!] = input.parse()?; if input.is_empty() { return Err(error!(bang, "expected `!Unpin`, found `!`")); } let unpin: kw::Unpin = input.parse()?; let span = quote!(#bang #unpin); if not_unpin.replace(span.span()).is_some() { return Err(error!(span, "duplicate `!Unpin` argument")); } } else { let token = input.parse::()?; match &*token.to_string() { "PinnedDrop" => { if pinned_drop.replace(token.span()).is_some() { return Err(error!(token, "duplicate `PinnedDrop` argument")); } } "UnsafeUnpin" => { if unsafe_unpin.replace(token.span()).is_some() { return Err(error!(token, "duplicate `UnsafeUnpin` argument")); } } "project" => { project = Some(parse_value(input, &token, project.is_some())?.0); } "project_ref" => { project_ref = Some(parse_value(input, &token, project_ref.is_some())?.0); } "project_replace" => { if input.peek(Token![=]) { let (value, span) = parse_value(input, &token, project_replace_span.is_some())?; project_replace_value = Some(value); project_replace_span = Some(span.span()); } else if project_replace_span.is_some() { return Err(error!(token, "duplicate `project_replace` argument")); } else { project_replace_span = Some(token.span()); } } "Replace" => { if replace.replace(token.span()).is_some() { return Err(error!(token, "duplicate `Replace` argument")); } } _ => return Err(error!(token, "unexpected argument: {}", token)), } } if input.is_empty() { break; } let _: Token![,] = input.parse()?; } if project.is_some() || project_ref.is_some() { if project == project_ref { return Err(error!( project_ref, "name `{}` is already specified by `project` argument", project_ref.as_ref().unwrap() )); } if let Some(ident) = &project_replace_value { if project == project_replace_value { return Err(error!( ident, "name `{}` is already specified by `project` argument", ident )); } else if project_ref == project_replace_value { return Err(error!( ident, "name `{}` is already specified by `project_ref` argument", ident )); } } } if let Some(span) = pinned_drop { if project_replace_span.is_some() { return Err(Error::new( span, "arguments `PinnedDrop` and `project_replace` are mutually exclusive", )); } else if replace.is_some() { return Err(Error::new( span, "arguments `PinnedDrop` and `Replace` are mutually exclusive", )); } } let project_replace = match (project_replace_span, project_replace_value, replace) { (None, _, None) => ProjReplace::None, // If both `project_replace` and `Replace` are specified, // We always prefer `project_replace`'s span, (Some(span), Some(ident), _) => ProjReplace::Named { ident, span }, (Some(span), ..) | (None, _, Some(span)) => ProjReplace::Unnamed { span }, }; let unpin_impl = match (unsafe_unpin, not_unpin) { (None, None) => UnpinImpl::Default, (Some(span), None) => UnpinImpl::Unsafe(span), (None, Some(span)) => UnpinImpl::Negative(span), (Some(span), Some(_)) => { return Err(Error::new( span, "arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive", )); } }; Ok(Self { pinned_drop, unpin_impl, project, project_ref, project_replace }) } } struct OriginalType<'a> { /// Attributes of the original type. attrs: &'a [Attribute], /// Visibility of the original type. vis: &'a Visibility, /// Name of the original type. ident: &'a Ident, /// Generics of the original type. generics: &'a Generics, } struct ProjectedType { /// Visibility of the projected types. vis: Visibility, /// Name of the projected type returned by `project` method. mut_ident: Ident, /// Name of the projected type returned by `project_ref` method. ref_ident: Ident, /// Name of the projected type returned by `project_replace` method. own_ident: Ident, /// Lifetime on the generated projected types. lifetime: Lifetime, /// Generics of the projected types. generics: Generics, /// `where` clause of the projected types. This has an additional /// bound generated by `insert_lifetime_and_bound` where_clause: WhereClause, } struct ProjectedVariants { proj_variants: TokenStream, proj_ref_variants: TokenStream, proj_own_variants: TokenStream, proj_arms: TokenStream, proj_ref_arms: TokenStream, proj_own_arms: TokenStream, } #[derive(Default)] struct ProjectedFields { proj_pat: TokenStream, proj_body: TokenStream, proj_own_body: TokenStream, proj_fields: TokenStream, proj_ref_fields: TokenStream, proj_own_fields: TokenStream, } struct Context<'a> { /// The original type. orig: OriginalType<'a>, /// The projected types. proj: ProjectedType, /// Types of the pinned fields. pinned_fields: Vec, /// `PinnedDrop` argument. pinned_drop: Option, /// `UnsafeUnpin` or `!Unpin` argument. unpin_impl: UnpinImpl, /// `project` argument. project: bool, /// `project_ref` argument. project_ref: bool, /// `project_replace [= ]` or `Replace` argument. project_replace: ProjReplace, } #[derive(Clone, Copy)] enum UnpinImpl { Default, /// `UnsafeUnpin`. Unsafe(Span), /// `!Unpin`. Negative(Span), } impl<'a> Context<'a> { fn new( attrs: &'a [Attribute], vis: &'a Visibility, ident: &'a Ident, generics: &'a mut Generics, ) -> Result { let Args { pinned_drop, unpin_impl, project, project_ref, project_replace } = Args::get(attrs)?; let mut lifetime_name = String::from("'pin"); determine_lifetime_name(&mut lifetime_name, generics); let lifetime = Lifetime::new(&lifetime_name, Span::call_site()); let ty_generics = generics.split_for_impl().1; let ty_generics_as_generics = parse_quote!(#ty_generics); let mut proj_generics = generics.clone(); let pred = insert_lifetime_and_bound( &mut proj_generics, lifetime.clone(), &ty_generics_as_generics, ident, ); let mut where_clause = generics.make_where_clause().clone(); where_clause.predicates.push(pred); let own_ident = project_replace.ident().cloned().unwrap_or_else(|| ProjKind::Owned.proj_ident(ident)); Ok(Self { pinned_drop, unpin_impl, project: project.is_some(), project_ref: project_ref.is_some(), project_replace, proj: ProjectedType { vis: determine_visibility(vis), mut_ident: project.unwrap_or_else(|| ProjKind::Mutable.proj_ident(ident)), ref_ident: project_ref.unwrap_or_else(|| ProjKind::Immutable.proj_ident(ident)), own_ident, lifetime, generics: proj_generics, where_clause, }, orig: OriginalType { attrs, vis, ident, generics }, pinned_fields: Vec::new(), }) } /// Returns attributes used on projected types. fn proj_attrs(&self) -> (TokenStream, TokenStream, TokenStream) { // If the user gave it a name, it should appear in the document. let doc_attr = quote!(#[doc(hidden)]); let doc_proj = if self.project { None } else { Some(&doc_attr) }; let doc_proj_ref = if self.project_ref { None } else { Some(&doc_attr) }; let doc_proj_own = if self.project_replace.ident().is_some() { None } else { Some(&doc_attr) }; let proj_mut = quote! { #doc_proj #[allow(dead_code)] // This lint warns unused fields/variants. #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 #[allow(clippy::mut_mut)] // This lint warns `&mut &mut `. #[allow(clippy::type_repetition_in_bounds)] // https://github.com/rust-lang/rust-clippy/issues/4326} }; let proj_ref = quote! { #doc_proj_ref #[allow(dead_code)] // This lint warns unused fields/variants. #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 #[allow(clippy::type_repetition_in_bounds)] // https://github.com/rust-lang/rust-clippy/issues/4326 }; let proj_own = quote! { #doc_proj_own #[allow(dead_code)] // This lint warns unused fields/variants. #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 #[allow(unreachable_pub)] // This lint warns `pub` field in private struct. }; (proj_mut, proj_ref, proj_own) } fn parse_struct( &mut self, DataStruct { fields, .. }: &DataStruct, ) -> Result<(TokenStream, TokenStream)> { validate_struct(self.orig.ident, fields)?; let ProjectedFields { proj_pat, proj_body, proj_fields, proj_ref_fields, proj_own_fields, proj_own_body, } = match fields { Fields::Named(_) => self.visit_fields(None, fields, Delimiter::Brace)?, Fields::Unnamed(_) => self.visit_fields(None, fields, Delimiter::Parenthesis)?, Fields::Unit => unreachable!(), }; let proj_ident = &self.proj.mut_ident; let proj_ref_ident = &self.proj.ref_ident; let proj_own_ident = &self.proj.own_ident; let vis = &self.proj.vis; let mut orig_generics = self.orig.generics.clone(); let orig_where_clause = orig_generics.where_clause.take(); let proj_generics = &self.proj.generics; let proj_where_clause = &self.proj.where_clause; // For tuple structs, we need to generate `(T1, T2) where Foo: Bar` // For non-tuple structs, we need to generate `where Foo: Bar { field1: T }` let (where_clause_fields, where_clause_ref_fields, where_clause_own_fields) = match fields { Fields::Named(_) => ( quote!(#proj_where_clause #proj_fields), quote!(#proj_where_clause #proj_ref_fields), quote!(#orig_where_clause #proj_own_fields), ), Fields::Unnamed(_) => ( quote!(#proj_fields #proj_where_clause;), quote!(#proj_ref_fields #proj_where_clause;), quote!(#proj_own_fields #orig_where_clause;), ), Fields::Unit => unreachable!(), }; let (proj_attrs, proj_ref_attrs, proj_own_attrs) = self.proj_attrs(); let mut proj_items = quote! { #proj_attrs #vis struct #proj_ident #proj_generics #where_clause_fields #proj_ref_attrs #vis struct #proj_ref_ident #proj_generics #where_clause_ref_fields }; if self.project_replace.span().is_some() { proj_items.extend(quote! { #proj_own_attrs #vis struct #proj_own_ident #orig_generics #where_clause_own_fields }); } let proj_mut_body = quote! { let Self #proj_pat = self.get_unchecked_mut(); #proj_ident #proj_body }; let proj_ref_body = quote! { let Self #proj_pat = self.get_ref(); #proj_ref_ident #proj_body }; let proj_own_body = quote! { let __self_ptr: *mut Self = self.get_unchecked_mut(); let Self #proj_pat = &mut *__self_ptr; #proj_own_body }; let proj_impl = self.make_proj_impl(&proj_mut_body, &proj_ref_body, &proj_own_body); Ok((proj_items, proj_impl)) } fn parse_enum( &mut self, DataEnum { brace_token, variants, .. }: &DataEnum, ) -> Result<(TokenStream, TokenStream)> { validate_enum(*brace_token, variants)?; let ProjectedVariants { proj_variants, proj_ref_variants, proj_own_variants, proj_arms, proj_ref_arms, proj_own_arms, } = self.visit_variants(variants)?; let proj_ident = &self.proj.mut_ident; let proj_ref_ident = &self.proj.ref_ident; let proj_own_ident = &self.proj.own_ident; let vis = &self.proj.vis; let mut orig_generics = self.orig.generics.clone(); let orig_where_clause = orig_generics.where_clause.take(); let proj_generics = &self.proj.generics; let proj_where_clause = &self.proj.where_clause; let (proj_attrs, proj_ref_attrs, proj_own_attrs) = self.proj_attrs(); let mut proj_items = quote! { #proj_attrs #vis enum #proj_ident #proj_generics #proj_where_clause { #proj_variants } #proj_ref_attrs #vis enum #proj_ref_ident #proj_generics #proj_where_clause { #proj_ref_variants } }; if self.project_replace.span().is_some() { proj_items.extend(quote! { #proj_own_attrs #vis enum #proj_own_ident #orig_generics #orig_where_clause { #proj_own_variants } }); } let proj_mut_body = quote! { match self.get_unchecked_mut() { #proj_arms } }; let proj_ref_body = quote! { match self.get_ref() { #proj_ref_arms } }; let proj_own_body = quote! { let __self_ptr: *mut Self = self.get_unchecked_mut(); match &mut *__self_ptr { #proj_own_arms } }; let proj_impl = self.make_proj_impl(&proj_mut_body, &proj_ref_body, &proj_own_body); Ok((proj_items, proj_impl)) } fn visit_variants(&mut self, variants: &Variants) -> Result { let mut proj_variants = TokenStream::new(); let mut proj_ref_variants = TokenStream::new(); let mut proj_own_variants = TokenStream::new(); let mut proj_arms = TokenStream::new(); let mut proj_ref_arms = TokenStream::new(); let mut proj_own_arms = TokenStream::new(); for Variant { ident, fields, .. } in variants { let ProjectedFields { proj_pat, proj_body, proj_fields, proj_ref_fields, proj_own_fields, proj_own_body, } = match fields { Fields::Named(_) => self.visit_fields(Some(ident), fields, Delimiter::Brace)?, Fields::Unnamed(_) => { self.visit_fields(Some(ident), fields, Delimiter::Parenthesis)? } Fields::Unit => ProjectedFields { proj_own_body: self.proj_own_body(Some(ident), None, &[]), ..ProjectedFields::default() }, }; let orig_ident = self.orig.ident; let proj_ident = &self.proj.mut_ident; let proj_ref_ident = &self.proj.ref_ident; proj_variants.extend(quote! { #ident #proj_fields, }); proj_ref_variants.extend(quote! { #ident #proj_ref_fields, }); proj_own_variants.extend(quote! { #ident #proj_own_fields, }); proj_arms.extend(quote! { #orig_ident::#ident #proj_pat => { #proj_ident::#ident #proj_body } }); proj_ref_arms.extend(quote! { #orig_ident::#ident #proj_pat => { #proj_ref_ident::#ident #proj_body } }); proj_own_arms.extend(quote! { #orig_ident::#ident #proj_pat => { #proj_own_body } }); } Ok(ProjectedVariants { proj_variants, proj_ref_variants, proj_own_variants, proj_arms, proj_ref_arms, proj_own_arms, }) } fn visit_fields( &mut self, variant_ident: Option<&Ident>, fields: &Fields, delim: Delimiter, ) -> Result { let mut proj_pat = TokenStream::new(); let mut proj_body = TokenStream::new(); let mut proj_fields = TokenStream::new(); let mut proj_ref_fields = TokenStream::new(); let mut proj_own_fields = TokenStream::new(); let mut proj_move = TokenStream::new(); let mut pinned_bindings = Vec::with_capacity(fields.len()); for (i, Field { attrs, vis, ident, colon_token, ty, .. }) in fields.iter().enumerate() { let binding = ident.clone().unwrap_or_else(|| format_ident!("_{}", i)); proj_pat.extend(quote!(#binding,)); if attrs.position_exact(PIN)?.is_some() { let lifetime = &self.proj.lifetime; proj_fields.extend(quote! { #vis #ident #colon_token ::pin_project::__private::Pin<&#lifetime mut (#ty)>, }); proj_ref_fields.extend(quote! { #vis #ident #colon_token ::pin_project::__private::Pin<&#lifetime (#ty)>, }); proj_own_fields.extend(quote! { #vis #ident #colon_token ::pin_project::__private::PhantomData<#ty>, }); proj_body.extend(quote! { #ident #colon_token ::pin_project::__private::Pin::new_unchecked(#binding), }); proj_move.extend(quote! { #ident #colon_token ::pin_project::__private::PhantomData, }); self.pinned_fields.push(ty.clone()); pinned_bindings.push(binding); } else { let lifetime = &self.proj.lifetime; proj_fields.extend(quote! { #vis #ident #colon_token &#lifetime mut (#ty), }); proj_ref_fields.extend(quote! { #vis #ident #colon_token &#lifetime (#ty), }); proj_own_fields.extend(quote! { #vis #ident #colon_token #ty, }); proj_body.extend(quote! { #binding, }); proj_move.extend(quote! { #ident #colon_token ::pin_project::__private::ptr::read(#binding), }); } } fn surround(delim: Delimiter, tokens: TokenStream) -> TokenStream { Group::new(delim, tokens).into_token_stream() } let proj_pat = surround(delim, proj_pat); let proj_body = surround(delim, proj_body); let proj_fields = surround(delim, proj_fields); let proj_ref_fields = surround(delim, proj_ref_fields); let proj_own_fields = surround(delim, proj_own_fields); let proj_move = Group::new(delim, proj_move); let proj_own_body = self.proj_own_body(variant_ident, Some(proj_move), &pinned_bindings); Ok(ProjectedFields { proj_pat, proj_body, proj_own_body, proj_fields, proj_ref_fields, proj_own_fields, }) } /// Generates the processing that `project_replace` does for the struct or each variant. fn proj_own_body( &self, variant_ident: Option<&'a Ident>, proj_move: Option, pinned_fields: &[Ident], ) -> TokenStream { let ident = &self.proj.own_ident; let proj_own = match variant_ident { Some(variant_ident) => quote!(#ident::#variant_ident), None => quote!(#ident), }; quote! { // First, extract all the unpinned fields let __result = #proj_own #proj_move; // Destructors will run in reverse order, so next create a guard to overwrite // `self` with the replacement value without calling destructors. let __guard = ::pin_project::__private::UnsafeOverwriteGuard { target: __self_ptr, value: ::pin_project::__private::ManuallyDrop::new(__replacement), }; // Now create guards to drop all the pinned fields // // Due to a compiler bug (https://github.com/rust-lang/rust/issues/47949) // this must be in its own scope, or else `__result` will not be dropped // if any of the destructors panic. { #( let __guard = ::pin_project::__private::UnsafeDropInPlaceGuard(#pinned_fields); )* } // Finally, return the result __result } } /// Creates `Unpin` implementation for original type. fn make_unpin_impl(&self) -> TokenStream { match self.unpin_impl { UnpinImpl::Unsafe(span) => { let mut proj_generics = self.proj.generics.clone(); let orig_ident = self.orig.ident; let lifetime = &self.proj.lifetime; // Make the error message highlight `UnsafeUnpin` argument. proj_generics.make_where_clause().predicates.push(parse_quote_spanned! { span => ::pin_project::__private::Wrapper<#lifetime, Self>: ::pin_project::UnsafeUnpin }); let (impl_generics, _, where_clause) = proj_generics.split_for_impl(); let ty_generics = self.orig.generics.split_for_impl().1; quote_spanned! { span => impl #impl_generics ::pin_project::__private::Unpin for #orig_ident #ty_generics #where_clause { } } } UnpinImpl::Negative(span) => { let mut proj_generics = self.proj.generics.clone(); let orig_ident = self.orig.ident; let lifetime = &self.proj.lifetime; proj_generics.make_where_clause().predicates.push(parse_quote! { ::pin_project::__private::Wrapper< #lifetime, ::pin_project::__private::PhantomPinned >: ::pin_project::__private::Unpin }); let (proj_impl_generics, _, proj_where_clause) = proj_generics.split_for_impl(); let (impl_generics, ty_generics, orig_where_clause) = self.orig.generics.split_for_impl(); // For interoperability with `forbid(unsafe_code)`, `unsafe` token should be // call-site span. let unsafety = ::default(); quote_spanned! { span => impl #proj_impl_generics ::pin_project::__private::Unpin for #orig_ident #ty_generics #proj_where_clause { } // A dummy impl of `UnsafeUnpin`, to ensure that the user cannot implement it. // // To ensure that users don't accidentally write a non-functional `UnsafeUnpin` // impls, we emit one ourselves. If the user ends up writing an `UnsafeUnpin` // impl, they'll get a "conflicting implementations of trait" error when // coherence checks are run. #unsafety impl #impl_generics ::pin_project::UnsafeUnpin for #orig_ident #ty_generics #orig_where_clause { } } } UnpinImpl::Default => { let mut full_where_clause = self.orig.generics.where_clause.clone().unwrap(); // Generate a field in our new struct for every // pinned field in the original type. let fields = self.pinned_fields.iter().enumerate().map(|(i, ty)| { let field_ident = format_ident!("__field{}", i); quote!(#field_ident: #ty) }); // We could try to determine the subset of type parameters // and lifetimes that are actually used by the pinned fields // (as opposed to those only used by unpinned fields). // However, this would be tricky and error-prone, since // it's possible for users to create types that would alias // with generic parameters (e.g. 'struct T'). // // Instead, we generate a use of every single type parameter // and lifetime used in the original struct. For type parameters, // we generate code like this: // // ```rust // struct AlwaysUnpin(PhantomData) {} // impl Unpin for AlwaysUnpin {} // // ... // _field: AlwaysUnpin<(A, B, C)> // ``` // // This ensures that any unused type parameters // don't end up with `Unpin` bounds. let lifetime_fields = self.orig.generics.lifetimes().enumerate().map( |(i, LifetimeDef { lifetime, .. })| { let field_ident = format_ident!("__lifetime{}", i); quote!(#field_ident: &#lifetime ()) }, ); let orig_ident = self.orig.ident; let struct_ident = format_ident!("__{}", orig_ident); let vis = self.orig.vis; let lifetime = &self.proj.lifetime; let type_params = self.orig.generics.type_params().map(|t| &t.ident); let proj_generics = &self.proj.generics; let (proj_impl_generics, proj_ty_generics, _) = proj_generics.split_for_impl(); let (impl_generics, ty_generics, where_clause) = self.orig.generics.split_for_impl(); full_where_clause.predicates.push(parse_quote! { #struct_ident #proj_ty_generics: ::pin_project::__private::Unpin }); quote! { // This needs to have the same visibility as the original type, // due to the limitations of the 'public in private' error. // // Our goal is to implement the public trait `Unpin` for // a potentially public user type. Because of this, rust // requires that any types mentioned in the where clause of // our `Unpin` impl also be public. This means that our generated // `__UnpinStruct` type must also be public. // However, we ensure that the user can never actually reference // this 'public' type by creating this type in the inside of `const`. #vis struct #struct_ident #proj_generics #where_clause { __pin_project_use_generics: ::pin_project::__private::AlwaysUnpin< #lifetime, (#(::pin_project::__private::PhantomData<#type_params>),*) >, #(#fields,)* #(#lifetime_fields,)* } impl #proj_impl_generics ::pin_project::__private::Unpin for #orig_ident #ty_generics #full_where_clause { } // A dummy impl of `UnsafeUnpin`, to ensure that the user cannot implement it. // // To ensure that users don't accidentally write a non-functional `UnsafeUnpin` // impls, we emit one ourselves. If the user ends up writing an `UnsafeUnpin` // impl, they'll get a "conflicting implementations of trait" error when // coherence checks are run. unsafe impl #impl_generics ::pin_project::UnsafeUnpin for #orig_ident #ty_generics #where_clause { } } } } } /// Creates `Drop` implementation for original type. fn make_drop_impl(&self) -> TokenStream { let ident = self.orig.ident; let (impl_generics, ty_generics, where_clause) = self.orig.generics.split_for_impl(); if let Some(span) = self.pinned_drop { // For interoperability with `forbid(unsafe_code)`, `unsafe` token should be // call-site span. let unsafety = ::default(); quote_spanned! { span => impl #impl_generics ::pin_project::__private::Drop for #ident #ty_generics #where_clause { fn drop(&mut self) { // Safety - we're in 'drop', so we know that 'self' will // never move again. let pinned_self = #unsafety { ::pin_project::__private::Pin::new_unchecked(self) }; // We call `pinned_drop` only once. Since `PinnedDrop::drop` // is an unsafe method and a private API, it is never called again in safe // code *unless the user uses a maliciously crafted macro*. #unsafety { ::pin_project::__private::PinnedDrop::drop(pinned_self); } } } } } else { // If the user does not provide a `PinnedDrop` impl, // we need to ensure that they don't provide a `Drop` impl of their // own. // Based on https://github.com/upsuper/assert-impl/blob/f503255b292ab0ba8d085b657f4065403cfa46eb/src/lib.rs#L80-L87 // // We create a new identifier for each struct, so that the traits // for different types do not conflict with each other. // // Another approach would be to provide an empty Drop impl, // which would conflict with a user-provided Drop impl. // However, this would trigger the compiler's special handling // of Drop types (e.g. fields cannot be moved out of a Drop type). // This approach prevents the creation of needless Drop impls, // giving users more flexibility. let trait_ident = format_ident!("{}MustNotImplDrop", ident); quote! { // There are two possible cases: // 1. The user type does not implement Drop. In this case, // the first blanked impl will not apply to it. This code // will compile, as there is only one impl of MustNotImplDrop for the user type // 2. The user type does impl Drop. This will make the blanket impl applicable, // which will then conflict with the explicit MustNotImplDrop impl below. // This will result in a compilation error, which is exactly what we want. trait #trait_ident {} #[allow(clippy::drop_bounds)] impl #trait_ident for T {} impl #impl_generics #trait_ident for #ident #ty_generics #where_clause {} // A dummy impl of `PinnedDrop`, to ensure that the user cannot implement it. // Since the user did not pass `PinnedDrop` to `#[pin_project]`, any `PinnedDrop` // impl will not actually be called. Unfortunately, we can't detect this situation // directly from either the `#[pin_project]` or `#[pinned_drop]` attributes, since // we don't know what other attirbutes/impl may exist. // // To ensure that users don't accidentally write a non-functional `PinnedDrop` // impls, we emit one ourselves. If the user ends up writing a `PinnedDrop` impl, // they'll get a "conflicting implementations of trait" error when coherence // checks are run. impl #impl_generics ::pin_project::__private::PinnedDrop for #ident #ty_generics #where_clause { unsafe fn drop(self: ::pin_project::__private::Pin<&mut Self>) {} } } } } /// Creates an implementation of the projection method. fn make_proj_impl( &self, proj_body: &TokenStream, proj_ref_body: &TokenStream, proj_own_body: &TokenStream, ) -> TokenStream { let vis = &self.proj.vis; let lifetime = &self.proj.lifetime; let orig_ident = self.orig.ident; let proj_ident = &self.proj.mut_ident; let proj_ref_ident = &self.proj.ref_ident; let proj_own_ident = &self.proj.own_ident; let orig_ty_generics = self.orig.generics.split_for_impl().1; let proj_ty_generics = self.proj.generics.split_for_impl().1; let (impl_generics, ty_generics, where_clause) = self.orig.generics.split_for_impl(); let replace_impl = self.project_replace.span().map(|span| { // For interoperability with `forbid(unsafe_code)`, `unsafe` token should be // call-site span. let unsafety = ::default(); quote_spanned! { span => #vis fn project_replace( self: ::pin_project::__private::Pin<&mut Self>, __replacement: Self, ) -> #proj_own_ident #orig_ty_generics { #unsafety { #proj_own_body } } } }); quote! { impl #impl_generics #orig_ident #ty_generics #where_clause { #vis fn project<#lifetime>( self: ::pin_project::__private::Pin<&#lifetime mut Self>, ) -> #proj_ident #proj_ty_generics { unsafe { #proj_body } } #vis fn project_ref<#lifetime>( self: ::pin_project::__private::Pin<&#lifetime Self>, ) -> #proj_ref_ident #proj_ty_generics { unsafe { #proj_ref_body } } #replace_impl } } } fn ensure_not_packed(&self, fields: &Fields) -> Result { for meta in self.orig.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { if let Meta::List(list) = meta { if list.path.is_ident("repr") { for repr in list.nested.iter() { match repr { NestedMeta::Meta(Meta::Path(path)) | NestedMeta::Meta(Meta::List(MetaList { path, .. })) if path.is_ident("packed") => { return Err(error!( repr, "#[pin_project] attribute may not be used on #[repr(packed)] types" )); } _ => {} } } } } } // As proc-macro-derive can't rewrite the structure definition, // it's probably no longer necessary, but it keeps it for now. // Workaround for https://github.com/taiki-e/pin-project/issues/32 // Through the tricky use of proc macros, it's possible to bypass // the above check for the `repr` attribute. // To ensure that it's impossible to use pin projections on a `#[repr(packed)]` // struct, we generate code like this: // // ```rust // #[deny(safe_packed_borrows)] // fn assert_not_repr_packed(val: &MyStruct) { // let _field1 = &val.field1; // let _field2 = &val.field2; // ... // let _fieldn = &val.fieldn; // } // ``` // // Taking a reference to a packed field is unsafe, and applying // `#[deny(safe_packed_borrows)]` makes sure that doing this without // an `unsafe` block (which we deliberately do not generate) // is a hard error. // // If the struct ends up having `#[repr(packed)]` applied somehow, // this will generate an (unfriendly) error message. Under all reasonable // circumstances, we'll detect the `#[repr(packed)]` attribute, and generate // a much nicer error above. // // There is one exception: If the type of a struct field has an alignment of 1 // (e.g. u8), it is always safe to take a reference to it, even if the struct // is `#[repr(packed)]`. If the struct is composed entirely of types of // alignment 1, our generated method will not trigger an error if the // struct is `#[repr(packed)]`. // // Fortunately, this should have no observable consequence - `#[repr(packed)]` // is essentially a no-op on such a type. Nevertheless, we include a test // to ensure that the compiler doesn't ever try to copy the fields on // such a struct when trying to drop it - which is reason we prevent // `#[repr(packed)]` in the first place. // // See also https://github.com/taiki-e/pin-project/pull/34. let mut field_refs = vec![]; match fields { Fields::Named(FieldsNamed { named, .. }) => { for Field { ident, .. } in named { field_refs.push(quote!(&val.#ident;)); } } Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => { for (index, _) in unnamed.iter().enumerate() { let index = Index::from(index); field_refs.push(quote!(&val.#index;)); } } Fields::Unit => {} } let (impl_generics, ty_generics, where_clause) = self.orig.generics.split_for_impl(); let ident = self.orig.ident; Ok(quote! { #[deny(safe_packed_borrows)] fn __assert_not_repr_packed #impl_generics (val: &#ident #ty_generics) #where_clause { #(#field_refs)* } }) } }