Repository: dtolnay/async-trait (rust)

Issue:
Do not require Sync on unused shared reference arguments
```rust
use async_trait::async_trait;

#[async_trait]
trait Generic<T> {
    async fn takes_ref(&self, thing: &T);
}

#[async_trait]
impl<T> Generic<T> for () {
    async fn takes_ref(&self, _: &T) {}
}
```

This currently fails because it expands to something like:

```rust
impl<T> Generic<T> for () {
    fn takes_ref<'life0, 'life1, 'async_trait>(
        &'life0 self,
        __arg1: &'life1 T,
    ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
    where
        'life0: 'async_trait,
        'life1: 'async_trait,
        Self: 'async_trait,
    {
        Box::pin(async move {
            let __self = self;
            let __arg1 = __arg1;
            let _: () = {};
        })
    }
}
```

in which the anonymous future type captures a local variable of type `&T`, implying a need for `T: Sync` in order to get a `Send` future.

```console
error: future cannot be sent between threads safely
  --> src/main.rs:10:38
   |
10 |     async fn takes_ref(&self, _: &T) {}
   |                                      ^^ future created by async block is not `Send`
   |
note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync`
  --> src/main.rs:10:31
   |
8  | #[async_trait]
   | -------------- in this procedural macro expansion
9  | impl<T> Generic<T> for () {
10 |     async fn takes_ref(&self, _: &T) {}
   |                               ^ has type `&T` which is not `Send`, because `T` is not `Sync`
   = note: required for the cast from `[async block@src/main.rs:10:38: 10:40]` to the object type `dyn Future<Output = ()> + Send`
   = note: this error originates in the attribute macro `async_trait` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider restricting type parameter `T`
   |
9  | impl<T: std::marker::Sync> Generic<T> for () {
   |       +++++++++++++++++++
```

The following compiles:

```rust
#[async_trait]
impl<T: Sync> Generic<T> for () {
    async fn takes_ref(&self, _: &T) {}
}
```

However that `T: Sync` bound should be unnecessary as long as the `&T` argument is unused within the future.

Pull request:
Bypass Sync bound implied by non-existent drop of reference
Fixes #232.

Patch:
diff --git a/src/expand.rs b/src/expand.rs
index 3fdcb51..7747e68 100644
--- a/src/expand.rs
+++ b/src/expand.rs
@@ -297,10 +297,16 @@ fn transform_sig(
             }) => {}
             FnArg::Receiver(arg) => arg.mutability = None,
             FnArg::Typed(arg) => {
-                if let Pat::Ident(ident) = &mut *arg.pat {
-                    ident.by_ref = None;
-                    ident.mutability = None;
-                } else {
+                let type_is_reference = match *arg.ty {
+                    Type::Reference(_) => true,
+                    _ => false,
+                };
+                if let Pat::Ident(pat) = &mut *arg.pat {
+                    if pat.ident == "self" || !type_is_reference {
+                        pat.by_ref = None;
+                        pat.mutability = None;
+                    }
+                } else if !type_is_reference {
                     let positional = positional_arg(i, &arg.pat);
                     let m = mut_pat(&mut arg.pat);
                     arg.pat = parse_quote!(#m #positional);
@@ -376,12 +382,16 @@ fn transform_block(context: Context, sig: &mut Signature, block: &mut Block) {
                         self_span = Some(ident.span());
                         let prefixed = Ident::new("__self", ident.span());
                         quote!(let #mutability #prefixed = #ident;)
+                    } else if let Type::Reference(_) = *arg.ty {
+                        quote!()
                     } else {
                         quote! {
                             #(#attrs)*
                             let #mutability #ident = #ident;
                         }
                     }
+                } else if let Type::Reference(_) = *arg.ty {
+                    quote!()
                 } else {
                     let pat = &arg.pat;
                     let ident = positional_arg(i, pat);


Test patch:
diff --git a/tests/test.rs b/tests/test.rs
index 3982638..211387a 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -1489,3 +1489,37 @@ pub mod issue226 {
         }
     }
 }
+
+// https://github.com/dtolnay/async-trait/issues/232
+pub mod issue232 {
+    use async_trait::async_trait;
+
+    #[async_trait]
+    pub trait Generic<T> {
+        async fn take_ref(&self, thing: &T);
+    }
+
+    pub struct One;
+
+    #[async_trait]
+    impl<T> Generic<T> for One {
+        async fn take_ref(&self, _: &T) {}
+    }
+
+    pub struct Two;
+
+    #[async_trait]
+    impl<T: Sync> Generic<(T, T)> for Two {
+        async fn take_ref(&self, (a, b): &(T, T)) {
+            let _ = a;
+            let _ = b;
+        }
+    }
+
+    pub struct Three;
+
+    #[async_trait]
+    impl<T> Generic<(T, T, T)> for Three {
+        async fn take_ref(&self, (_a, _b, _c): &(T, T, T)) {}
+    }
+}