From 70bf4b02431ab46129494c0b675e9718bc9134d0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 22 Jan 2023 13:13:32 -0800 Subject: [PATCH 1/2] Add regression test for issue 232 Currently fails to compile: error: future cannot be sent between threads safely --> tests/test.rs:1506:41 | 1506 | async fn take_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` --> tests/test.rs:1506:34 | 1504 | #[async_trait] | -------------- in this procedural macro expansion 1505 | impl Generic for One { 1506 | async fn take_ref(&self, _: &T) {} | ^ has type `&T` which is not `Send`, because `T` is not `Sync` = note: required for the cast from `[async block@tests/test.rs:1506:41: 1506:43]` to the object type `dyn futures::Future + std::marker::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` | 1505 | impl Generic for One { | +++++++++++++++++++ error[E0277]: `T` cannot be shared between threads safely --> tests/test.rs:1523:60 | 1523 | async fn take_ref(&self, (_a, _b, _c): &(T, T, T)) {} | ^^ `T` cannot be shared between threads safely | = note: required because it appears within the type `(T, T, T)` = note: required for `&(T, T, T)` to implement `std::marker::Send` note: required because it's used within this `async` block --> tests/test.rs:1523:60 | 1523 | async fn take_ref(&self, (_a, _b, _c): &(T, T, T)) {} | ^^ = note: required for the cast from `[async block@tests/test.rs:1523:60: 1523:62]` to the object type `dyn futures::Future + std::marker::Send` help: consider restricting type parameter `T` | 1522 | impl Generic<(T, T, T)> for Three { | +++++++++++++++++++ --- tests/test.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) 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 { + async fn take_ref(&self, thing: &T); + } + + pub struct One; + + #[async_trait] + impl Generic for One { + async fn take_ref(&self, _: &T) {} + } + + pub struct Two; + + #[async_trait] + impl Generic<(T, T)> for Two { + async fn take_ref(&self, (a, b): &(T, T)) { + let _ = a; + let _ = b; + } + } + + pub struct Three; + + #[async_trait] + impl Generic<(T, T, T)> for Three { + async fn take_ref(&self, (_a, _b, _c): &(T, T, T)) {} + } +} From ba930258fa48cb51e6bedaeafca15bd6352ecd77 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 22 Jan 2023 12:55:41 -0800 Subject: [PATCH 2/2] Bypass Sync bound implied by non-existent drop of reference --- src/expand.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) 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);