dtolnay / async-trait

Type erasure for async trait methods
Apache License 2.0
1.81k stars 84 forks source link

Self keyword inside macro invocations in trait impl #92

Closed joshuabach closed 4 years ago

joshuabach commented 4 years ago

Issue #73 only revealed part of the problem, and only part of it was fixed.

Since Merge Request #74 Self inside macro invocation works when used in the trait declaration, but the issue persists in the trait impl. See this example:

use async_trait::async_trait;

struct Foo;

#[async_trait]
pub trait Example {
    const ASSOCIATED: &'static str;

    async fn associated(&self) {
        println!("Associated:{}", Self::ASSOCIATED); // <-- This works now
    }

    async fn associated2(&self);
}

#[async_trait]
impl Example for Foo {
    const ASSOCIATED: &'static str = "foo";

    async fn associated2(&self) {
        println!("Associated:{}", Self::ASSOCIATED); // <-- This does not
    }
}

Compiler output:

   Compiling foo v0.1.0 (/tmp/foo)
error[E0433]: failed to resolve: use of undeclared type or module `AsyncTrait`
  --> src/lib.rs:21:35
   |
21 |         println!("Associated:{}", Self::ASSOCIATED);
   |                                   ^^^^ use of undeclared type or module `AsyncTrait`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0433`.
error: could not compile `foo`.

To learn more, run the command again with --verbose.

Tested with 0.1.30.

Workaround from original issue (let-binding) also works.

irevoire commented 4 years ago

I ran into the same bug, it's still present in 0.1.31 and the workaround still works.

the-notable commented 4 years ago

Ran into this issue on 0.1.36.

Just to be clear for everyone who arrived here with the same error, the work workaround is to first assign the table name with let, and then use that in the macro:

Instead of:

println!("Associated:{}", Self::ASSOCIATED);

Use:

let a = Self::ASSOCIATED;
println!("Associated:{}", a);
taiki-e commented 4 years ago

@the-notable: Do you mean this issue has not been fixed yet? (0.1.36 is a newer version than 0.1.32 (#100) which closed this issue)

the-notable commented 4 years ago

@taiki-e The following code produced this error on both versions 0.1.36 and 0.1.39

EDIT: I see this issue was related to trait impl rather than trait declarations. I'm having this issue with trait declarations, which were supposed to be fixed in #74 ?

pub trait Model {
    fn table_name() -> &'static str;
}

#[async_trait]
pub trait FindAll: Model {
    async fn find_all<T, Q: Queryable>(q: &mut Q) -> Result<Vec<T>> {
        let sql = format!("SELECT * FROM {}", Self::table_name());
        find(q, sql.as_str()).await
    }
}
error[E0433]: failed to resolve: use of undeclared type or module `AsyncTrait`
  --> src/model/mod.rs:77:47
   |
77 |         let sql = format!("SELECT * FROM {}", Self::table_name());
   |                                               ^^^^ use of undeclared type or module `AsyncTrait`

This, however, compiles:

pub trait Model {
    fn table_name() -> &'static str;
}

#[async_trait]
pub trait FindAll: Model {
    async fn find_all<T, Q: Queryable>(q: &mut Q) -> Result<Vec<T>> {
        // Assign tn with let because #[async_trait] macro won't work with Self inside other macros
        let tn = Self::table_name();
        let sql = format!("SELECT * FROM {}", tn);
        find(q, sql.as_str()).await
    }
}
taiki-e commented 4 years ago

@the-notable Thanks. I'll look into that.

taiki-e commented 4 years ago

Ok, this is due to HasSelf doesn't check Self inside macros.
First, here is minimized repro:

macro_rules! mac {
    ($($tt:tt)*) => {
        $($tt)*
    };
}

pub trait Trait1 {
    fn func1();
}

#[async_trait::async_trait]
pub trait Trait2: Trait1 {
    async fn func2() {
        mac!(Self::func1());
    }
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=480675f5ec156ef27ae54a817aba5dde

And the following async function is generated:

async fn __func2() {
    AsyncTrait::func1();
}

But the async function that should be generated (the function generated when macro does not exist) here is:

async fn __func2<AsyncTrait: ?Sized + Trait2>() {
    <AsyncTrait>::func1()
}

The type parameter AsyncTrait is generated when the trait method has the self argument or when Self keyword is used in the method. So that means HasSelf that detects Self keyword is not working for the macro. I'll send a fix.

taiki-e commented 4 years ago

Filed #124