dtolnay / async-trait

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

Improperly reported compilation errors when using async_trait #91

Closed cjhopman closed 4 years ago

cjhopman commented 4 years ago

Note: not sure if this is a compiler bug or an async_trait bug.

This code reports an error incorrectly:

#[macro_use]
use async_trait::async_trait; // 0.1.24

use std::path::*;

struct DiceCtx();

impl DiceCtx {
    async fn compute<T: Key>(&self, v: &T) -> <T as Key>::Value {
        todo!()
    }
}

#[async_trait]
trait InterpreterFileOps {
    async fn read_dir(&self, path: &Path) -> Result<(), ()>;
}

#[async_trait]
trait Key {
    type Value;
    async fn compute(&self, ctx: &DiceCtx) -> Self::Value;
}

struct DiceInterpreterFileOps(DiceCtx);

#[async_trait]
impl InterpreterFileOps for DiceInterpreterFileOps {
    async fn read_dir(&self, path: &Path) -> Result<(), ()> {
        struct ReadDirKey(PathBuf);

        #[async_trait]
        impl Key for ReadDirKey {
            type Value = Result<Vec<()>>;

            // This correctly reports the error
            // type Value = Result<(Vec<()>)>;

            async fn compute(&self, ctx: &DiceCtx) -> Self::Value {
                Unknown{}.foo()
            }
        }

        self.0.compute(&ReadDirKey(path.to_owned())).await
    }
}

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

It reports the error as:

Compiling playground v0.0.1 (/playground)
error[E0422]: cannot find struct, variant or union type `Unknown` in this scope
  --> src/lib.rs:34:9
   |
34 |         #[async_trait]
   |         ^^^^^^^^^^^^^^ not found in this scope
   |
help: possible candidates are found in other modules, you can import them into scope
   |

Note that it marks the macro invocation itself. Making the change that is commented (i.e. adding a redundant () around Vec<()> will make it report the error correctly:

error[E0422]: cannot find struct, variant or union type `Unknown` in this scope
  --> src/lib.rs:42:17
   |
42 |                 Unknown{}.foo()
   |                 ^^^^^^^ not found in this scope
   |
help: possible candidates are found in other modules, you can import them into scope
   |

It does this even worse for some other errors. If you replace that Unknown{}.foo() with todo!() (https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=95645128b3ff65057921bbd952f0005b) you'll get this error:

error[E0107]: wrong number of type arguments: expected 2, found 1

error: aborting due to previous error

That doesn't even point to the async_trait use at all (which makes it quite difficult to diagnose).

Again, adding the () around Vec<()> fixes the reporting:

error[E0107]: wrong number of type arguments: expected 2, found 1
  --> src/lib.rs:36:26
   |
36 |             type Value = Result<(Vec<()>)>;
   |                          ^^^^^^^^^^^^^^^^^ expected 2 type arguments

error: aborting due to previous error
dtolnay commented 4 years ago

Both of these are a consequence of this compiler bug: https://github.com/rust-lang/rust/issues/43081. It would be better not to nest attribute macro invocations on items until that is fixed.

Aaron1011 commented 4 years ago

This will be fixed by https://github.com/rust-lang/rust/pull/72306

dtolnay commented 4 years ago

Confirmed fixed in rustc master. The error from the above repro looks like:

error[E0422]: cannot find struct, variant or union type `Unknown` in this scope
  --> src/main.rs:42:17
   |
42 |                 Unknown{}.foo()
   |                 ^^^^^^^ not found in this scope
   |
help: consider importing one of these items
   |
2  | use core::fmt::rt::v1::Alignment::Unknown;
   |
2  | use std::fmt::rt::v1::Alignment::Unknown;
   |

error[E0107]: wrong number of type arguments: expected 2, found 1
  --> src/main.rs:36:26
   |
36 |             type Value = Result<Vec<()>>;
   |                          ^^^^^^^^^^^^^^^ expected 2 type arguments