Skip to content

Commit

Permalink
Fix 'Self' in where clause
Browse files Browse the repository at this point in the history
  • Loading branch information
taiki-e committed Nov 20, 2019
1 parent 5ee380f commit 8b037b4
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 49 deletions.
82 changes: 80 additions & 2 deletions pin-project-internal/src/pin_project/derive.rs
Original file line number Diff line number Diff line change
@@ -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},
*,
};

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -219,6 +223,13 @@ impl<'a> Context<'a> {
) -> Result<Self> {
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());
Expand Down Expand Up @@ -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 = || {
Expand Down Expand Up @@ -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<QSelf>, 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` -> `<Receiver>::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.
}
}
2 changes: 1 addition & 1 deletion pin-project-internal/src/pinned_drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
32 changes: 32 additions & 0 deletions tests/pin_project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>
where
Self: Trait,
{
x: T,
}

impl<T> Trait for Struct1<T> {}

pub trait Trait2 {
type Foo;
}

#[pin_project]
pub struct Struct2<T>
where
Self: Trait2<Foo = Struct1<T>>,
<Self as Trait2>::Foo: Trait,
{
x: T,
}

impl<T> Trait2 for Struct2<T> {
type Foo = Struct1<T>;
}
}
14 changes: 0 additions & 14 deletions tests/ui/pin_project/self-in-where-clause.rs

This file was deleted.

32 changes: 0 additions & 32 deletions tests/ui/pin_project/self-in-where-clause.stderr

This file was deleted.

0 comments on commit 8b037b4

Please sign in to comment.