dtolnay / async-trait

Type erasure for async trait methods
Apache License 2.0
1.84k stars 85 forks source link

self parameter gets expanded as module self in macro #46

Closed Marwes closed 4 years ago

Marwes commented 5 years ago

Minimal reproduction

macro_rules! implement_commands {
    (
        $tyargs:ident : $ty:ident
    ) => {
        #[async_trait]
        pub trait AsyncCommands: Sized {
            async fn f<$tyargs: $ty>(&mut self, x: $tyargs) {
                self.f(x).await
            }
        }
    };
}

implement_commands! { K: Send }
error[E0424]: expected value, found module `self`
   --> tests/test.rs:434:9
    |
434 |         #[async_trait]
    |         ^^^^^^^^^^^^^^ `self` value is a keyword only available in methods with `self` parameter
...
443 | implement_commands! { K: Send }
    | ------------------------------- in this macro invocation
dtolnay commented 5 years ago

Thanks! Looks like some kind of span issue because the code we are emitting does work by itself. I will take a look in the next couple days (but I would gladly accept a PR to fix this if you get to it first).

Here is the code we currently expand this to:

pub trait AsyncCommands: Sized {
    fn f<'life0, 'async_trait, K: Send>(
        &'life0 mut self,
        x: K,
    ) -> ::core::pin::Pin<
        Box<dyn ::core::future::Future<Output = ()> + ::core::marker::Send + 'async_trait>,
    >
    where
        K: 'async_trait,
        'life0: 'async_trait,
        Self: ::core::marker::Send + 'async_trait,
    {
        #[allow(clippy::used_underscore_binding)]
        async fn __f<K: Send, AsyncTrait: ?Sized + AsyncCommands + ::core::marker::Send>(
            _self: &mut AsyncTrait,
            x: K,
        ) {
            _self.f(x).await
        }
        Box::pin(__f::<K, Self>(self, x))
    }
}
dtolnay commented 5 years ago

This is being caused by https://github.com/rust-lang/rust/issues/43081 which means unfortunately there is nothing we can do about it in async-trait. The compiler loses all span information on the macro input before it's even passed in to async-trait, which makes self in the input meaningless.

TokenStream [
    Ident {
        ident: "pub",
        span: #0 bytes(0..0),
    },
    Ident {
        ident: "trait",
        span: #0 bytes(0..0),
    },
    Ident {
        ident: "AsyncCommands",
        span: #0 bytes(0..0),
    },
    Punct {
        ch: ':',
        spacing: Alone,
        span: #0 bytes(0..0),
    },
    Ident {
        ident: "Sized",
        span: #0 bytes(0..0),
    },
    Group {

As a workaround, for some reason the compiler doesn't mess up if you use $:tt in the macro variables instead.

($tyargs:tt : $ty:tt) => {
tyranron commented 4 years ago

@dtolnay is this a compiler bug which will be fixed eventually, or intended compiler behavior?