Skip to content

Commit

Permalink
FIX: async fn
Browse files Browse the repository at this point in the history
Fixed a bug that caused the definition of traits with immutable references as receivers to fail.
  • Loading branch information
not-elm committed Feb 25, 2024
1 parent 6809078 commit f598bb4
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 36 deletions.
10 changes: 7 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ path = "./examples/structs/super_trait.rs"


[[example]]
name = "aync_trait"
name = "async_trait"
path = "./examples/structs/async_trait.rs"


Expand Down Expand Up @@ -88,6 +88,10 @@ path = "tests/generics/progress.rs"
name = "async_trait"
path = "tests/async_trait/progress.rs"

[[test]]
name = "t"
path = "tests/async_trait/test05_immutable_self_receiver.rs"


[dependencies]
auto-delegate-impl = { version = "0.1.1", path = "impl" }
Expand All @@ -96,5 +100,5 @@ auto-delegate-impl = { version = "0.1.1", path = "impl" }
[dev-dependencies]
trybuild = "1.0.80"
criterion = { version = "0.5.1", features = ["html_reports"] }
async-trait = "0.1.68"
tokio = { version = "1.28.0", features = ["full"] }
async-trait = "0.1.77"
tokio = { version = "1.28.0", features = ["full"] }
49 changes: 22 additions & 27 deletions examples/structs/async_trait.rs
Original file line number Diff line number Diff line change
@@ -1,55 +1,50 @@
use std::path::Path;
use async_trait::async_trait;

use auto_delegate::{delegate, Delegate};

pub struct EmailAddress(String);

impl EmailAddress {
#[allow(unused)]
pub fn raw(&self) -> &str {
self.0.as_str()
}
#[delegate]
trait ReadFile {
async fn read_file(&self, path: impl AsRef<Path>) -> String;
}


impl Default for EmailAddress {
fn default() -> Self {
Self(String::from("rust@gmail.com"))
}
}


#[async_trait]
#[delegate]
pub trait EmailReadable {
async fn read_email<'a>(&'a self) -> &'a EmailAddress;
#[async_trait]
trait ReadFileBufLen {
async fn read_file_buf_len(&self, path: impl AsRef<Path> + Send) -> usize;
}


#[derive(Default)]
pub struct EmailReader {
email: EmailAddress,
struct Io;

impl ReadFile for Io{
async fn read_file(&self, path: impl AsRef<Path>) -> String {
tokio::fs::read_to_string(path).await.unwrap()
}
}


#[async_trait]
impl EmailReadable for EmailReader {
async fn read_email<'a>(&'a self) -> &'a EmailAddress {
&self.email
impl ReadFileBufLen for Io {
async fn read_file_buf_len(&self, path: impl AsRef<Path> + Send) -> usize {
tokio::fs::read_to_string(path).await.unwrap().len()
}
}


#[derive(Default, Delegate)]
struct Parent {
#[to(EmailReadable)]
child: EmailReader,
#[to(ReadFile, ReadFileBufLen)]
child: Io,
}


#[tokio::main]
async fn main() {
const PATH: &str = "examples/structs/async_trait.txt";
let parent = Parent::default();

assert_eq!(parent.read_email().await.raw(), "rust@gmail.com");

assert_eq!(parent.read_file(PATH).await, "HELLO");
assert_eq!(parent.read_file_buf_len(PATH).await, 5);
}
1 change: 1 addition & 0 deletions examples/structs/async_trait.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
HELLO
17 changes: 12 additions & 5 deletions impl/src/delegate_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use proc_macro::TokenStream;

use proc_macro2::Span;
use quote::quote;
use syn::{Attribute, GenericParam, ItemTrait, LifetimeParam, TypeParam, TypeParamBound};
use syn::{Attribute, GenericParam, ItemTrait, LifetimeParam, TraitItem, TypeParam, TypeParamBound};
use syn::__private::TokenStream2;
use syn::punctuated::Punctuated;
use syn::token::Plus;
Expand Down Expand Up @@ -44,14 +44,14 @@ fn expand_impl_trait(item: &mut ItemTrait) -> syn::Result<TokenStream2> {
let super_traits = super_traits_bound(&item.supertraits);
let where_generics = expand_where_bound_without_where_token(&item.generics);
let async_trait_attr = async_trait_attr(item);
let send = send_bound(&async_trait_attr);
let send_and_sync = send_and_sync_bound(item, &async_trait_attr);

Ok(quote::quote! {
#item

#async_trait_attr
impl #generics #trait_name for #impl_generic
where #impl_generic: #delegatable #super_traits #send,
where #impl_generic: #delegatable #super_traits #send_and_sync,
<#impl_generic as #delegatable>::A : #lifetime_bound,
<#impl_generic as #delegatable>::B : #lifetime_bound,
<#impl_generic as #delegatable>::C : #lifetime_bound,
Expand Down Expand Up @@ -83,7 +83,7 @@ fn async_trait_attr(item: &ItemTrait) -> Option<&Attribute> {
})
}

fn send_bound(async_trait_attr: &Option<&Attribute>) -> Option<TokenStream2> {
fn send_and_sync_bound(item: &ItemTrait, async_trait_attr: &Option<&Attribute>) -> Option<TokenStream2> {
let attr = async_trait_attr.as_ref()?;
if let Ok(list) = attr.meta.require_list() {
let args = syn::parse::<AsyncTraitArgs>(list.tokens.clone().into()).unwrap();
Expand All @@ -92,7 +92,9 @@ fn send_bound(async_trait_attr: &Option<&Attribute>) -> Option<TokenStream2> {
} else {
Some(quote!(+ Send))
}
} else {
}else if has_immutable_self_receiver(item.items.clone()) {
Some(quote ! ( + Send + Sync))
}else {
Some(quote!(+ Send))
}
}
Expand All @@ -106,6 +108,11 @@ fn super_traits_bound(super_traits: &Punctuated<TypeParamBound, Plus>) -> Option
}


fn has_immutable_self_receiver(items: Vec<TraitItem>) -> bool {
TraitFunctions::new(items).any(|meta| meta.has_immutable_self_ref_receiver())
}


fn trait_functions(item: ItemTrait) -> syn::Result<Vec<TokenStream2>> {
let mut trait_fn: Vec<TokenStream2> = Vec::new();
let delegatable_ident = delegatable_ident_with_generics(item.ident);
Expand Down
8 changes: 7 additions & 1 deletion impl/src/trait_item/fn_meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ impl TraitFnMeta {
Self(item_fn)
}

pub fn has_immutable_self_ref_receiver(&self) -> bool {
self.0.sig.receiver().map_or(false, |recv| {
recv.reference.is_some() && recv.mutability.is_none()
})
}

pub fn expand_fn(&mut self, delegatable_ident: &TokenStream2) -> syn::Result<TokenStream2> {
let stmt = self.delegate_stmt(delegatable_ident)?;

Expand Down Expand Up @@ -86,7 +92,7 @@ impl TraitFnMeta {
fn call_stmt(&self) -> TokenStream2 {
let inputs = self.expand_inputs();
let fn_name = &self.0.sig.ident;
let asyncness = self.0.sig.asyncness.map(|_|quote!(.await));
let asyncness = self.0.sig.asyncness.map(|_| quote!(.await));
if self.0.sig.generics.params.is_empty() {
quote!(#fn_name(#inputs)#asyncness)
} else {
Expand Down
2 changes: 2 additions & 0 deletions tests/async_trait/progress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod test02_async_trait_attribute_full_path;
mod test03_async_trait_attribute_short_path;

mod test04_async_trait_with_fn_lifetime;
mod test05_immutable_self_receiver;

#[cfg(test)]
#[test]
Expand All @@ -14,5 +15,6 @@ fn tests() {
t.pass("tests/async_trait/test02_async_trait_attribute_full_path.rs");
t.pass("tests/async_trait/test03_async_trait_attribute_short_path.rs");
t.pass("tests/async_trait/test04_async_trait_with_fn_lifetime.rs");
t.pass("tests/async_trait/test05_immutable_self_receiver.rs");
}

43 changes: 43 additions & 0 deletions tests/async_trait/test05_immutable_self_receiver.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use async_trait::async_trait;
use auto_delegate_impl::{delegate, Delegate};

#[delegate]
#[async_trait]
trait AsyncTrait {
async fn parse_usize(&mut self, num_str: &str) -> usize;

async fn cache(&self) -> usize;
}

#[derive(Default)]
struct Child{
cache: usize
}

#[async_trait]
impl AsyncTrait for Child{
async fn parse_usize(&mut self, num_str: &str) -> usize {
let num: usize = num_str.parse().unwrap();
self.cache = num;
num
}

async fn cache(&self) -> usize {
self.cache
}
}

#[derive(Default, Delegate)]
struct Parent {
#[to(AsyncTrait, RegularAsync)]
child: Child,
}


#[tokio::main]
async fn main() {
let mut parent = Parent::default();

assert_eq!(parent.parse_usize("2").await, 2);
assert_eq!(parent.cache().await, 2);
}

0 comments on commit f598bb4

Please sign in to comment.