Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] calling async trait fn gives compiler error 'type annotations needed' when type annotation is already provided #119893

Open
BaxHugh opened this issue Jan 12, 2024 · 3 comments
Labels
A-inference Area: Type inference C-bug Category: This is a bug. D-confusing Diagnostics: Confusing error or lint that should be reworked. S-has-mcve Status: A Minimal Complete and Verifiable Example has been found for this issue

Comments

@BaxHugh
Copy link

BaxHugh commented Jan 12, 2024

In the case that an async trait fn returns a trait level generic T, if that trait is implemented for multiple T, the compiler complains that type annotations are needed (to specify T) even though that type annotation is specified. It's suggestion is to try using a fully qualified path to specify the types. When the fully qualified trait::fn path is used, it does compile, but this is unnecessary and is not needed for the synchronous couterpart.

Code to reproduce with explanations in the comments.

    trait Get<Item> {
        // synchronous version for comparison
        fn get_one(&self) -> Item {
            todo!()
        }
        // asynchronous version which give the bug
        async fn get_one_async(&self) -> Item {
            todo!()
        }
    }

    // Here's how it looks using the async_trait macro for earlier versions of rust. (This works)
    #[async_trait::async_trait]
    trait GetUsingAsyncTraitMacro<Item> {
        async fn get_one_async_trait_macro(&self) -> Item {
            todo!()
        }
    }

    struct Getter;
    struct Foo;
    struct Bar;

    // Impl Get<T> for multiple Get<T>
    impl Get<Foo> for Getter {}

    impl Get<Bar> for Getter {}

    #[async_trait::async_trait]
    impl GetUsingAsyncTraitMacro<Bar> for Getter {}
    #[async_trait::async_trait]
    impl GetUsingAsyncTraitMacro<Foo> for Getter {}

    async fn demo() -> Result<(), ()> {
        let mut getter = Getter;
        // The synchronous version works fine
        let item1: Bar = getter.get_one();
        // The pre rust async fn in trait, async_trait::async_trait, works fine
        let item3: Bar = getter.get_one_async_trait_macro().await;

        // This has compiler error but it should not as type annotations are provided
        let item2: Bar = getter.get_one_async().await;
        //error[E0284]: type annotations needed
        // = note: cannot satisfy `<foo::Getter as foo::Get<_>>::{opaque#0}<'_> == _`
        //  help: try using a fully qualified path to specify the expected types

        // Try the fully qualified path:
        // This compiler but is not ideal syntactically
        let item2 = <Getter as Get<Bar>>::get_one_async(&getter).await;

        Ok(())
    }

The signatures of the sync and async fns are the same, so the code calling the async fn should compile if it works in the sync and async_trait cases.

Meta

rustc --version --verbose:

rustc 1.75.0 (82e1608df 2023-12-21)
binary: rustc
commit-hash: 82e1608dfa6e0b5569232559e3d385fea5a93112
commit-date: 2023-12-21
host: x86_64-unknown-linux-gnu
release: 1.75.0
LLVM version: 17.0.6

rustc 1.77.0-nightly (62d7ed4a6 2024-01-11)
binary: rustc
commit-hash: 62d7ed4a6775c4490e493093ca98ef7c215b835b
commit-date: 2024-01-11
host: x86_64-unknown-linux-gnu
release: 1.77.0-nightly
LLVM version: 17.0.6
Backtrace

   Compiling async-bug v0.1.0 (/tmp/async-bug)
error[E0284]: type annotations needed
  --> src/main.rs:41:29
   |
41 |     let item2: Bar = getter.get_one_async().await;
   |                             ^^^^^^^^^^^^^
   |
   = note: cannot satisfy `<Getter as Get<_>>::{opaque#0}<'_> == _`
help: try using a fully qualified path to specify the expected types
   |
41 |     let item2: Bar = <Getter as Get<Item>>::get_one_async(&getter).await;
   |                      ++++++++++++++++++++++++++++++++++++++      ~

error[E0283]: type annotations needed
  --> src/main.rs:41:29
   |
41 |     let item2: Bar = getter.get_one_async().await;
   |                             ^^^^^^^^^^^^^
   |
note: multiple `impl`s satisfying `Getter: Get<_>` found
  --> src/main.rs:24:1
   |
24 | impl Get<Foo> for Getter {}
   | ^^^^^^^^^^^^^^^^^^^^^^^^
25 |
26 | impl Get<Bar> for Getter {}
   | ^^^^^^^^^^^^^^^^^^^^^^^^
help: try using a fully qualified path to specify the expected types
   |
41 |     let item2: Bar = <Getter as Get<Item>>::get_one_async(&getter).await;
   |                      ++++++++++++++++++++++++++++++++++++++      ~

Some errors have detailed explanations: E0283, E0284.
For more information about an error, try `rustc --explain E0283`.
error: could not compile `async-bug` (bin "async-bug") due to 2 previous errors

@BaxHugh BaxHugh added the C-bug Category: This is a bug. label Jan 12, 2024
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Jan 12, 2024
@BaxHugh BaxHugh changed the title calling async trait fn gives compiler error 'type annotations needed' when type annotation is already provided [BUG] calling async trait fn gives compiler error 'type annotations needed' when type annotation is already provided Jan 12, 2024
@BaxHugh
Copy link
Author

BaxHugh commented Jan 12, 2024

This is similar to the bug in issues-113538

@compiler-errors
Copy link
Member

compiler-errors commented Jan 12, 2024

This represents a somewhat hard limitation of inference in the trait solver. At the point which we've called .await, we don't have enough information to select which implementation provides <Getter as Get<?0>>::get_one_async. All we know is that it's some type (let's call it ?1), but we don't integrate the information that such as the fact that ?1: Future<Output = i32> must hold for the assignment to be valid.

This same issue can be rewritten using associated types, so it's not only a problem with async-fn-in-trait, though I absolutely see how that feature makes it easier to encounter:

use futures::future::BoxFuture;
use core::future::Future;

trait X<U> {
    type F: Future<Output = U>;
    fn f(&self) -> Self::F;
}

impl X<i32> for () {
    type F = BoxFuture<'static, i32>;
    fn f(&self) -> Self::F { todo!() }
}
impl X<u32> for () {
    type F = BoxFuture<'static, u32>;
    fn f(&self) -> Self::F { todo!() }
}

async fn test() {
    let x: u32 = ().f().await;
}

@jieyouxu
Copy link
Member

@rustbot label +A-inference +S-has-mcve +D-confusing -needs-triage

@rustbot rustbot added A-inference Area: Type inference D-confusing Diagnostics: Confusing error or lint that should be reworked. S-has-mcve Status: A Minimal Complete and Verifiable Example has been found for this issue and removed needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Feb 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-inference Area: Type inference C-bug Category: This is a bug. D-confusing Diagnostics: Confusing error or lint that should be reworked. S-has-mcve Status: A Minimal Complete and Verifiable Example has been found for this issue
Projects
None yet
Development

No branches or pull requests

4 participants