Repository: dtolnay/async-trait (Rust)

Issue:
Combine generic parameters from impl block and method
```rust
#![feature(async_await)]

use async_trait::async_trait;

#[async_trait]
trait Trait {
    async fn f<U>(&self);
}

#[async_trait]
impl<T> Trait for Vec<T> {
    async fn f<U>(&self) {}
}
```

Currently this fails to compile because the nested freestanding async fn we generate is missing the `T` type parameter:

```console
error[E0401]: can't use generic parameters from outer function
  --> src/main.rs:11:23
   |
7  |     async fn f<U>(&self);
   |              - try adding a local generic parameter in this method instead
...
11 | impl<T> Trait for Vec<T> {
   |      -                ^ use of generic parameter from outer function
   |      |
   |      type parameter from outer function
```

Expanded code:

```rust
impl<T> Trait for Vec<T> {
    fn f<'life0, 'async_trait, U>(
        &'life0 self,
    ) -> std::pin::Pin<
        std::boxed::Box<dyn std::future::Future<Output = ()> + std::marker::Send + 'async_trait>,
    >
    where
        U: 'async_trait,
        'life0: 'async_trait,
        Self: 'async_trait,
    {
        async fn __f<'async_trait, U>(_self: &Vec<T>) {}
        std::pin::Pin::from(std::boxed::Box::new(__f::<U>(self)))
    }
}
```

We need to merge the syn::Generics from the impl block with the syn::Generics on the method to produce the generics that go on the inner function. In this case it should be:

```rust
        async fn __f<'async_trait, T, U>(_self: &Vec<T>) {}
```


Discussion:
why dont you use an async move block?
@withoutboats for working code it would be fine and the implementation is simpler, but during development the error messages would be worse using async move. For example:

```rust
type Elided<'a> = &'a usize;

#[async_trait]
trait Trait {
    async fn f(elided: Elided) {
        println!("{}", *elided);
    }
}
```

Here is the error when using async move (#3). It only mentions `'static` which is probably not the right fix in context.

```console
error[E0621]: explicit lifetime required in the type of `elided`
 --> tests/dev.rs:7:1
  |
7 | #[async_trait]
  | ^^^^^^^^^^^^^^ lifetime `'static` required
8 | trait Trait {
9 |     async fn f(elided: Elided) {
  |                        ------ help: add explicit lifetime `'static` to the type of `elided`: `&'static usize`
```

Here is the error in the current implementation. Together with the elided lifetimes note in the readme, this should quickly get the author on the right track.

```console
error[E0726]: implicit elided lifetime not allowed here
 --> tests/dev.rs:9:24
  |
9 |     async fn f(elided: Elided) {
  |                        ^^^^^^- help: indicate the anonymous lifetime: `<'_>`

error[E0621]: explicit lifetime required in the type of `elided`
 --> tests/dev.rs:9:16
  |
9 |     async fn f(elided: Elided) {
  |                ^^^^^^  ------ help: add explicit lifetime `'static` to the type of `elided`: `&'static usize`
  |                |
  |                lifetime `'static` required
```



Patch:
diff --git a/src/expand.rs b/src/expand.rs
index 33ffd14..6c7d179 100644
--- a/src/expand.rs
+++ b/src/expand.rs
@@ -3,6 +3,7 @@ use crate::parse::Item;
 use crate::receiver::ReplaceReceiver;
 use proc_macro2::{Span, TokenStream};
 use quote::{quote, ToTokens};
+use std::mem;
 use syn::punctuated::Punctuated;
 use syn::visit_mut::VisitMut;
 use syn::{
@@ -28,6 +29,7 @@ enum Context<'a> {
         supertraits: &'a Supertraits,
     },
     Impl {
+        impl_generics: &'a Generics,
         receiver: &'a Type,
         as_trait: &'a Path,
     },
@@ -57,6 +59,7 @@ pub fn expand(input: &mut Item) {
         }
         Item::Impl(input) => {
             let context = Context::Impl {
+                impl_generics: &input.generics,
                 receiver: &input.self_ty,
                 as_trait: &input.trait_.as_ref().unwrap().1,
             };
@@ -203,12 +206,6 @@ fn transform_sig(context: Context, sig: &mut MethodSig, has_default: bool) {
 //     Pin::from(Box::new(async_trait_method::<T, Self>(self, x)))
 fn transform_block(context: Context, sig: &MethodSig, block: &mut Block) {
     let inner = Ident::new(&format!("__{}", sig.ident), sig.ident.span());
-    let mut types = sig
-        .decl
-        .generics
-        .type_params()
-        .map(|param| param.ident.clone())
-        .collect::<Vec<_>>();
     let args = sig
         .decl
         .inputs
@@ -225,11 +222,35 @@ fn transform_block(context: Context, sig: &MethodSig, block: &mut Block) {
 
     let mut standalone = sig.clone();
     standalone.ident = inner.clone();
+
+    let outer_generics = match context {
+        Context::Trait { generics, .. } => generics,
+        Context::Impl { impl_generics, .. } => impl_generics,
+    };
+    let fn_generics = mem::replace(&mut standalone.decl.generics, outer_generics.clone());
+    standalone.decl.generics.params.extend(fn_generics.params);
+    if let Some(where_clause) = fn_generics.where_clause {
+        standalone
+            .decl
+            .generics
+            .make_where_clause()
+            .predicates
+            .extend(where_clause.predicates);
+    }
+
     standalone
         .decl
         .generics
         .params
         .push(parse_quote!('async_trait));
+
+    let mut types = standalone
+        .decl
+        .generics
+        .type_params()
+        .map(|param| param.ident.clone())
+        .collect::<Vec<_>>();
+
     match standalone.decl.inputs.iter_mut().next() {
         Some(arg @ FnArg::SelfRef(_)) => {
             let (lifetime, mutability) = match arg {
@@ -291,9 +312,9 @@ fn transform_block(context: Context, sig: &MethodSig, block: &mut Block) {
 
     let mut replace = match context {
         Context::Trait { .. } => ReplaceReceiver::with(parse_quote!(AsyncTrait)),
-        Context::Impl { receiver, as_trait } => {
-            ReplaceReceiver::with_as_trait(receiver.clone(), as_trait.clone())
-        }
+        Context::Impl {
+            receiver, as_trait, ..
+        } => ReplaceReceiver::with_as_trait(receiver.clone(), as_trait.clone()),
     };
     replace.visit_method_sig_mut(&mut standalone);
     replace.visit_block_mut(block);


Test patch:
diff --git a/tests/test.rs b/tests/test.rs
index e55b68d..9bde405 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -116,6 +116,21 @@ pub async fn test_object_safe_with_default() {
     object.f().await;
 }
 
+// https://github.com/dtolnay/async-trait/issues/1
+mod issue1 {
+    use async_trait::async_trait;
+
+    #[async_trait]
+    trait Trait {
+        async fn f<U>(&self);
+    }
+
+    #[async_trait]
+    impl<T: Sync> Trait for Vec<T> {
+        async fn f<U>(&self) {}
+    }
+}
+
 // https://github.com/dtolnay/async-trait/issues/2
 mod issue2 {
     use async_trait::async_trait;