From 8b037b49607f6bbafdcb899f378382d02bf152b5 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 21 Nov 2019 02:51:46 +0900 Subject: [PATCH] Fix 'Self' in where clause --- .../src/pin_project/derive.rs | 82 ++++++++++++++++++- pin-project-internal/src/pinned_drop.rs | 2 +- tests/pin_project.rs | 32 ++++++++ tests/ui/pin_project/self-in-where-clause.rs | 14 ---- .../pin_project/self-in-where-clause.stderr | 32 -------- 5 files changed, 113 insertions(+), 49 deletions(-) delete mode 100644 tests/ui/pin_project/self-in-where-clause.rs delete mode 100644 tests/ui/pin_project/self-in-where-clause.stderr diff --git a/pin-project-internal/src/pin_project/derive.rs b/pin-project-internal/src/pin_project/derive.rs index ca0b7ea3..d810a47f 100644 --- a/pin-project-internal/src/pin_project/derive.rs +++ b/pin-project-internal/src/pin_project/derive.rs @@ -1,7 +1,11 @@ +use std::mem; + use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; use syn::{ parse::{Parse, ParseBuffer, ParseStream}, + punctuated::Punctuated, + visit_mut::{self, VisitMut}, *, }; @@ -183,7 +187,7 @@ struct OriginalType<'a> { /// Name of the original type. ident: &'a Ident, /// Generics of the original type. - generics: &'a mut Generics, + generics: &'a Generics, } struct ProjectedType { @@ -219,6 +223,13 @@ impl<'a> Context<'a> { ) -> Result { let Args { pinned_drop, unsafe_unpin } = Args::get(attrs)?; + { + let ty_generics = generics.split_for_impl().1; + let self_ty = syn::parse_quote!(#ident #ty_generics); + let mut visitor = ReplaceSelf::new(&self_ty); + visitor.visit_where_clause_mut(generics.make_where_clause()); + } + let mut lifetime_name = String::from(DEFAULT_LIFETIME_NAME); determine_lifetime_name(&mut lifetime_name, &generics.params); let lifetime = Lifetime::new(&lifetime_name, Span::call_site()); @@ -475,7 +486,7 @@ impl<'a> Context<'a> { impl #impl_generics ::core::marker::Unpin for #orig_ident #ty_generics #where_clause {} } } else { - let mut full_where_clause = self.orig.generics.make_where_clause().clone(); + let mut full_where_clause = self.orig.generics.where_clause.as_ref().cloned().unwrap(); let orig_ident = self.orig.ident; let make_span = || { @@ -790,3 +801,70 @@ impl<'a> Context<'a> { }) } } + +// Replace `Self` with `self_ty`. +// Based on https://github.com/dtolnay/async-trait/blob/1.0.15/src/receiver.rs + +struct ReplaceSelf<'a> { + self_ty: &'a Type, +} + +impl<'a> ReplaceSelf<'a> { + fn new(self_ty: &'a Type) -> Self { + Self { self_ty } + } + + fn self_to_qself(&mut self, qself: &mut Option, path: &mut Path) { + if path.leading_colon.is_some() { + return; + } + + let first = &path.segments[0]; + if first.ident != "Self" || !first.arguments.is_empty() { + return; + } + + match path.segments.pairs().next().unwrap().punct() { + Some(colon) => path.leading_colon = Some(**colon), + None => return, + } + + *qself = Some(QSelf { + lt_token: token::Lt::default(), + ty: Box::new(self.self_ty.clone()), + position: 0, + as_token: None, + gt_token: token::Gt::default(), + }); + + let segments = mem::replace(&mut path.segments, Punctuated::new()); + path.segments = segments.into_pairs().skip(1).collect(); + } +} + +impl VisitMut for ReplaceSelf<'_> { + // `Self` -> `Receiver` + fn visit_type_mut(&mut self, ty: &mut Type) { + if let Type::Path(node) = ty { + if node.qself.is_none() && node.path.is_ident("Self") { + *ty = self.self_ty.clone(); + } else { + self.visit_type_path_mut(node); + } + } else { + visit_mut::visit_type_mut(self, ty); + } + } + + // `Self::Assoc` -> `::Assoc` + fn visit_type_path_mut(&mut self, ty: &mut TypePath) { + if ty.qself.is_none() { + self.self_to_qself(&mut ty.qself, &mut ty.path); + } + visit_mut::visit_type_path_mut(self, ty); + } + + fn visit_item_mut(&mut self, _: &mut Item) { + // Do not recurse into nested items. + } +} diff --git a/pin-project-internal/src/pinned_drop.rs b/pin-project-internal/src/pinned_drop.rs index 15f02854..39c5bed5 100644 --- a/pin-project-internal/src/pinned_drop.rs +++ b/pin-project-internal/src/pinned_drop.rs @@ -203,7 +203,7 @@ fn expand_item(item: &mut ItemImpl) { }}; } -// Replace `self` and `Self` with `__self` and `Receiver`. +// Replace `self` and `Self` with `__self` and `self_ty`. // Based on https://github.com/dtolnay/async-trait/blob/1.0.15/src/receiver.rs struct ReplaceReceiver<'a> { diff --git a/tests/pin_project.rs b/tests/pin_project.rs index ad20d949..a8964fbe 100644 --- a/tests/pin_project.rs +++ b/tests/pin_project.rs @@ -469,3 +469,35 @@ fn dyn_type() { f: dyn core::fmt::Debug + Send, } } + +#[test] +fn self_in_where_clause() { + pub trait Trait {} + + #[pin_project] + pub struct Struct1 + where + Self: Trait, + { + x: T, + } + + impl Trait for Struct1 {} + + pub trait Trait2 { + type Foo; + } + + #[pin_project] + pub struct Struct2 + where + Self: Trait2>, + ::Foo: Trait, + { + x: T, + } + + impl Trait2 for Struct2 { + type Foo = Struct1; + } +} diff --git a/tests/ui/pin_project/self-in-where-clause.rs b/tests/ui/pin_project/self-in-where-clause.rs deleted file mode 100644 index e9b2d434..00000000 --- a/tests/ui/pin_project/self-in-where-clause.rs +++ /dev/null @@ -1,14 +0,0 @@ -use pin_project::pin_project; - -trait Trait {} - -#[pin_project] -pub struct Struct -where - Self: Trait, //~ ERROR cannot find type `Self` in this scope [E0411] -{ - x: usize, - y: T, -} - -fn main() {} diff --git a/tests/ui/pin_project/self-in-where-clause.stderr b/tests/ui/pin_project/self-in-where-clause.stderr deleted file mode 100644 index 122be5bb..00000000 --- a/tests/ui/pin_project/self-in-where-clause.stderr +++ /dev/null @@ -1,32 +0,0 @@ -error[E0411]: cannot find type `Self` in this scope - --> $DIR/self-in-where-clause.rs:8:5 - | -8 | Self: Trait, //~ ERROR cannot find type `Self` in this scope [E0411] - | ^^^^ `Self` is only available in impls, traits, and type definitions - -error[E0277]: the trait bound `__Struct<'pin, T>: Trait` is not satisfied - --> $DIR/self-in-where-clause.rs:5:1 - | -5 | #[pin_project] - | ^^^^^^^^^^^^^^ - | | - | the trait `Trait` is not implemented for `__Struct<'pin, T>` - | required by `__Struct` - -error[E0277]: the trait bound `__StructProjection<'pin, T>: Trait` is not satisfied - --> $DIR/self-in-where-clause.rs:5:1 - | -5 | #[pin_project] - | ^^^^^^^^^^^^^^ - | | - | the trait `Trait` is not implemented for `__StructProjection<'pin, T>` - | required by `__StructProjection` - -error[E0277]: the trait bound `__StructProjectionRef<'pin, T>: Trait` is not satisfied - --> $DIR/self-in-where-clause.rs:5:1 - | -5 | #[pin_project] - | ^^^^^^^^^^^^^^ - | | - | the trait `Trait` is not implemented for `__StructProjectionRef<'pin, T>` - | required by `__StructProjectionRef`