Skip to content

Commit dbca3e1

Browse files
committed
Add Non-Send/Sync support
1 parent a93ce20 commit dbca3e1

File tree

4 files changed

+51
-14
lines changed

4 files changed

+51
-14
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ can't be that badly broken.
9797
- :thumbsup: Default implementations provided by the trait;
9898
- :thumbsup: Elided lifetimes;
9999
- :thumbsup: Dyn-capable traits.
100+
- :thumbsup: Non-Send/Sync traits
100101

101102
<br>
102103

src/expand.rs

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ enum Context<'a> {
3737

3838
type Supertraits = Punctuated<TypeParamBound, Token![+]>;
3939

40-
pub fn expand(input: &mut Item) {
40+
pub fn expand(input: &mut Item, is_local: bool) {
4141
match input {
4242
Item::Trait(input) => {
4343
let context = Context::Trait {
@@ -49,10 +49,10 @@ pub fn expand(input: &mut Item) {
4949
if let TraitItem::Method(method) = inner {
5050
if method.sig.asyncness.is_some() {
5151
if let Some(block) = &mut method.default {
52-
transform_block(context, &method.sig, block);
52+
transform_block(context, &method.sig, block, is_local);
5353
}
5454
let has_default = method.default.is_some();
55-
transform_sig(context, &mut method.sig, has_default);
55+
transform_sig(context, &mut method.sig, has_default, is_local);
5656
}
5757
}
5858
}
@@ -66,8 +66,8 @@ pub fn expand(input: &mut Item) {
6666
for inner in &mut input.items {
6767
if let ImplItem::Method(method) = inner {
6868
if method.sig.asyncness.is_some() {
69-
transform_block(context, &method.sig, &mut method.block);
70-
transform_sig(context, &mut method.sig, false);
69+
transform_block(context, &method.sig, &mut method.block, is_local);
70+
transform_sig(context, &mut method.sig, false, is_local);
7171
}
7272
}
7373
}
@@ -88,7 +88,7 @@ pub fn expand(input: &mut Item) {
8888
// 'life1: 'async_trait,
8989
// T: 'async_trait,
9090
// Self: Sync + 'async_trait;
91-
fn transform_sig(context: Context, sig: &mut MethodSig, has_default: bool) {
91+
fn transform_sig(context: Context, sig: &mut MethodSig, has_default: bool, is_local: bool) {
9292
sig.decl.fn_token.span = sig.asyncness.take().unwrap().span;
9393

9494
let ret = match &sig.decl.output {
@@ -187,9 +187,15 @@ fn transform_sig(context: Context, sig: &mut MethodSig, has_default: bool) {
187187
}
188188
}
189189

190+
let bounds: Supertraits = if is_local {
191+
parse_quote!(#lifetime)
192+
} else {
193+
parse_quote!(#lifetime + core::marker::Send)
194+
};
195+
190196
sig.decl.output = parse_quote! {
191197
-> core::pin::Pin<Box<
192-
dyn core::future::Future<Output = #ret> + core::marker::Send + #lifetime
198+
dyn core::future::Future<Output = #ret> + #bounds
193199
>>
194200
};
195201
}
@@ -204,7 +210,7 @@ fn transform_sig(context: Context, sig: &mut MethodSig, has_default: bool) {
204210
// _self + x
205211
// }
206212
// Pin::from(Box::new(async_trait_method::<T, Self>(self, x)))
207-
fn transform_block(context: Context, sig: &MethodSig, block: &mut Block) {
213+
fn transform_block(context: Context, sig: &MethodSig, block: &mut Block, is_local: bool) {
208214
let inner = Ident::new(&format!("__{}", sig.ident), sig.ident.span());
209215
let args = sig
210216
.decl
@@ -271,9 +277,15 @@ fn transform_block(context: Context, sig: &MethodSig, block: &mut Block) {
271277
_self: &#lifetime #mutability AsyncTrait
272278
};
273279
let (_, generics, _) = generics.split_for_impl();
274-
standalone.decl.generics.params.push(parse_quote! {
275-
AsyncTrait: ?Sized + #name #generics + core::marker::#bound
276-
});
280+
if is_local {
281+
standalone.decl.generics.params.push(parse_quote! {
282+
AsyncTrait: ?Sized + #name #generics
283+
});
284+
} else {
285+
standalone.decl.generics.params.push(parse_quote! {
286+
AsyncTrait: ?Sized + #name #generics + core::marker::#bound
287+
});
288+
}
277289
types.push(Ident::new("Self", Span::call_site()));
278290
}
279291
Context::Impl { receiver, .. } => {

src/lib.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@
9191
//!
9292
//! # Supported features
9393
//!
94+
//! ## Non-Send/Sync Async traits
95+
//! Not all async traits need Send/Sync TypeBounds. To avoid having them placed
96+
//! on your generated methods, annotate your traits as `#[async_trait(local)]`
97+
//!
98+
//!
9499
//! It is the intention that all features of Rust traits should work nicely with
95100
//! #\[async_trait\], but the edge cases are numerous. Please file an issue if
96101
//! you see unexpected borrow checker errors, type errors, or warnings. There is
@@ -305,15 +310,19 @@ mod parse;
305310
mod receiver;
306311

307312
use crate::expand::expand;
308-
use crate::parse::{Item, Nothing};
313+
use crate::parse::Item;
309314
use proc_macro::TokenStream;
310315
use quote::quote;
311316
use syn::parse_macro_input;
312317

318+
mod kw {
319+
syn::custom_keyword!(local);
320+
}
321+
313322
#[proc_macro_attribute]
314323
pub fn async_trait(args: TokenStream, input: TokenStream) -> TokenStream {
315-
parse_macro_input!(args as Nothing);
324+
let local = parse_macro_input!(args as Option<kw::local>).is_some();
316325
let mut item = parse_macro_input!(input as Item);
317-
expand(&mut item);
326+
expand(&mut item, local);
318327
TokenStream::from(quote!(#item))
319328
}

tests/test.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,21 @@ pub async fn test_object_safe_with_default() {
116116
object.f().await;
117117
}
118118

119+
pub async fn test_object_no_send() {
120+
#[async_trait(local)]
121+
trait ObjectSafe: Sync {
122+
async fn f(&self) {}
123+
}
124+
125+
#[async_trait(local)]
126+
impl ObjectSafe for Struct {
127+
async fn f(&self) {}
128+
}
129+
130+
let object = &Struct as &dyn ObjectSafe;
131+
object.f().await;
132+
}
133+
119134
// https://github.com/dtolnay/async-trait/issues/1
120135
mod issue1 {
121136
use async_trait::async_trait;

0 commit comments

Comments
 (0)