diff --git a/pin-project-internal/src/pin_project/derive.rs b/pin-project-internal/src/pin_project/derive.rs index 1ed1c569..30318f04 100644 --- a/pin-project-internal/src/pin_project/derive.rs +++ b/pin-project-internal/src/pin_project/derive.rs @@ -196,6 +196,9 @@ struct ProjectedType { lifetime: Lifetime, /// Generics of the projected type. generics: Generics, + /// `where` clause of the projected type. This has an additional + /// bound generated by `insert_lifetime_and_bound` + where_clause: WhereClause, } struct Context { @@ -230,7 +233,16 @@ impl Context { let lifetime = Lifetime::new(&lifetime_name, Span::call_site()); let mut proj_generics = generics.clone(); - insert_lifetime(&mut proj_generics, lifetime.clone()); + let ty_generics = generics.split_for_impl().1; + let ty_generics_as_generics = syn::parse_quote!(#ty_generics); + let pred = insert_lifetime_and_bound( + &mut proj_generics, + lifetime.clone(), + &ty_generics_as_generics, + ident.clone(), + ); + let mut where_clause = generics.clone().make_where_clause().clone(); + where_clause.predicates.push(pred); Ok(Self { proj: ProjectedType { @@ -239,6 +251,7 @@ impl Context { ref_ident: proj_ident(&ident, Immutable), lifetime, generics: proj_generics, + where_clause, }, orig: OriginalType { attrs, vis, ident, generics }, pinned_drop, @@ -250,7 +263,7 @@ impl Context { fn parse_struct(&mut self, fields: &Fields) -> Result { let (proj_pat, proj_init, proj_fields, proj_ref_fields) = match fields { Fields::Named(fields) => self.visit_named(fields)?, - Fields::Unnamed(fields) => self.visit_unnamed(fields, true)?, + Fields::Unnamed(fields) => self.visit_unnamed(fields)?, Fields::Unit => unreachable!(), }; @@ -259,7 +272,7 @@ impl Context { let proj_ref_ident = &self.proj.ref_ident; let vis = &self.proj.vis; let proj_generics = &self.proj.generics; - let where_clause = self.orig.generics.split_for_impl().2; + let 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 }` @@ -268,7 +281,7 @@ impl Context { (quote!(#where_clause #proj_fields), quote!(#where_clause #proj_ref_fields)) } Fields::Unnamed(_) => { - (quote!(#proj_fields #where_clause), quote!(#proj_ref_fields #where_clause)) + (quote!(#proj_fields #where_clause;), quote!(#proj_ref_fields #where_clause;)) } Fields::Unit => unreachable!(), }; @@ -303,7 +316,7 @@ impl Context { let proj_ref_ident = &self.proj.ref_ident; let vis = &self.proj.vis; let proj_generics = &self.proj.generics; - let where_clause = self.orig.generics.split_for_impl().2; + let where_clause = &self.proj.where_clause; let mut proj_items = quote! { #[allow(clippy::mut_mut)] // This lint warns `&mut &mut `. @@ -344,7 +357,7 @@ impl Context { for Variant { ident, fields, .. } in variants { let (proj_pat, proj_body, proj_fields, proj_ref_fields) = match fields { Fields::Named(fields) => self.visit_named(fields)?, - Fields::Unnamed(fields) => self.visit_unnamed(fields, false)?, + Fields::Unnamed(fields) => self.visit_unnamed(fields)?, Fields::Unit => { (TokenStream::new(), TokenStream::new(), TokenStream::new(), TokenStream::new()) } @@ -422,7 +435,6 @@ impl Context { fn visit_unnamed( &mut self, FieldsUnnamed { unnamed: fields, .. }: &FieldsUnnamed, - is_struct: bool, ) -> Result<(TokenStream, TokenStream, TokenStream, TokenStream)> { let mut proj_pat = Vec::with_capacity(fields.len()); let mut proj_body = Vec::with_capacity(fields.len()); @@ -460,11 +472,8 @@ impl Context { let proj_pat = quote!((#(#proj_pat),*)); let proj_body = quote!((#(#proj_body),*)); - let (proj_fields, proj_ref_fields) = if is_struct { - (quote!((#(#proj_fields),*);), quote!((#(#proj_ref_fields),*);)) - } else { - (quote!((#(#proj_fields),*)), quote!((#(#proj_ref_fields),*))) - }; + let (proj_fields, proj_ref_fields) = + (quote!((#(#proj_fields),*)), quote!((#(#proj_ref_fields),*))); Ok((proj_pat, proj_body, proj_fields, proj_ref_fields)) } diff --git a/pin-project-internal/src/utils.rs b/pin-project-internal/src/utils.rs index b96aa569..1890afcf 100644 --- a/pin-project-internal/src/utils.rs +++ b/pin-project-internal/src/utils.rs @@ -61,6 +61,29 @@ pub(crate) fn determine_lifetime_name( } } +/// Like `insert_lifetime`, but also generates a bound of the form +/// `OriginalType: 'lifetime`. Used when generating the definition +/// of a projection type +pub(crate) fn insert_lifetime_and_bound( + generics: &mut Generics, + lifetime: Lifetime, + orig_generics: &Generics, + orig_ident: Ident, +) -> WherePredicate { + insert_lifetime(generics, lifetime.clone()); + + let orig_type: syn::Type = syn::parse_quote!(#orig_ident #orig_generics); + let mut punct = Punctuated::new(); + punct.push(TypeParamBound::Lifetime(lifetime)); + + WherePredicate::Type(PredicateType { + lifetimes: None, + bounded_ty: orig_type, + colon_token: syn::token::Colon::default(), + bounds: punct, + }) +} + /// Inserts a `lifetime` at position `0` of `generics.params`. pub(crate) fn insert_lifetime(generics: &mut Generics, lifetime: Lifetime) { if generics.lt_token.is_none() { diff --git a/tests/no_infer_outlives.rs b/tests/no_infer_outlives.rs new file mode 100644 index 00000000..6602b62c --- /dev/null +++ b/tests/no_infer_outlives.rs @@ -0,0 +1,19 @@ +use pin_project::{pin_project, project}; + +trait Bar { + type Y; +} + +struct Example(A); + +impl Bar for Example { + type Y = Option; +} + +#[pin_project] +struct Foo { + _x: as Bar>::Y, +} + +#[project] +impl Foo {}