Repository: rust
Issue: Confusing diagnostic for mismatched opaque types
Labels: A-diagnostics,T-compiler

I just spent some time being quite confused by an error message I got when mistakenly using an `async fn` whose return type contains an `impl Trait` in multiple branches of a `match` statement. On my local toolchain, which is still on 1.62, the corresponding output is:

```
error[E0308]: `match` arms have incompatible types
   --> tools/src/bin/volt-test-device-comms.rs:243:35
    |
236 |                   let read_fut = match self.register {
    |                                  ------------------- `match` arms have incompatible types
237 |                       Discrete(reg) => with_timeout(client.read_discrete_inputs(reg, 1))
    |  ______________________________________-
238 | |                         .await
239 | |                         .map(|res| res.map(|_| ())),
    | |___________________________________________________- this is found to be of type `Result<Result<(), std::io::Error>, impl std::error::Error>`
240 |                       Coil(reg) => with_timeout(client.read_coils(reg, 1))
    |  __________________________________-
241 | |                         .await
242 | |                         .map(|res| res.map(|_| ())),
    | |___________________________________________________- this is found to be of type `Result<Result<(), std::io::Error>, impl std::error::Error>`
243 |                       Input(reg) => with_timeout(client.read_input_registers(reg, 1))
    |  ___________________________________^
244 | |                         .await
245 | |                         .map(|res| res.map(|_| ())),
    | |___________________________________________________^ expected `bool`, found `u16`
...
307 |   async fn with_timeout<O, F: Future<Output = O>>(future: F) -> Result<O, impl std::error::Error> {
    |                                                                           ----------------------
    |                                                                           |
    |                                                                           the expected opaque type
    |                                                                           the found opaque type
    |
    = note: expected enum `Result<_, impl std::error::Error>` (`bool`)
               found enum `Result<_, impl std::error::Error>` (`u16`)
```

(This is using `tokio_modbus`, there's a reduced repro below.) On current stable (1.64) and nightly (1.66.0-nightly (2022-09-25 f5193a9fcc73dc09e41a)), the message seems to have improved with some additional information about what other types the compiler has looked at while trying to match the opaque type. So currently, given the following code ([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=296d5ee74fbb3e428cb84fb4bbb236a6)): 

```rust
use std::fmt;
use std::future::Future;

#[derive(Debug, PartialEq)]
struct MyError(());

impl fmt::Display for MyError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "my message")
    }
}

impl std::error::Error for MyError {}

fn main() {}

async fn test() {
    let _ = match 0u16 {
        0 => maybe(one(), true).await.map(|res| res.map(|_| ())),
        _ => maybe(r#true(), true).await.map(|res| res.map(|_| ())),
    };
}

async fn maybe<O, F: Future<Output = O>>(future: F, run: bool) -> Result<O, impl std::error::Error> { 
    if !run {
        Err(MyError(()))
    } else {
        Ok(future.await)
    }
}

async fn one() -> Result<usize, std::io::Error> {
    Ok(1)
}
async fn r#true() -> Result<bool, std::io::Error> {
    Ok(true)
}
```

The output is:

```
error[[E0308]](https://doc.rust-lang.org/stable/error-index.html#E0308): `match` arms have incompatible types
  --> src/main.rs:20:14
   |
18 |       let _ = match 0u16 {
   |  _____________-
19 | |         0 => maybe(one(), true).await.map(|res| res.map(|_| ())),
   | |              --------------------------------------------------- this is found to be of type `Result<Result<(), std::io::Error>, impl std::error::Error>`
20 | |         _ => maybe(r#true(), true).await.map(|res| res.map(|_| ())),
   | |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool`
21 | |     };
   | |_____- `match` arms have incompatible types
...
24 |   async fn maybe<O, F: Future<Output = O>>(future: F, run: bool) -> Result<O, impl std::error::Error> { 
   |                                                                               ----------------------
   |                                                                               |
   |                                                                               one of the expected opaque types
   |                                                                               one of the found opaque types
   |
note: while checking the return type of the `async fn`
  --> src/main.rs:32:19
   |
32 | async fn one() -> Result<usize, std::io::Error> {
   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ checked the `Output` of this `async fn`, one of the expected opaque types
note: while checking the return type of the `async fn`
  --> src/main.rs:35:22
   |
35 | async fn r#true() -> Result<bool, std::io::Error> {
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ checked the `Output` of this `async fn`, one of the found opaque types
   = note: expected enum `Result<_, impl std::error::Error>` (`usize`)
              found enum `Result<_, impl std::error::Error>` (`bool`)

For more information about this error, try `rustc --explain E0308`.
```

While this gives more context on what code might be involved in the error,
 * the primary diagnostic still says "expected `usize`, found `bool`", which might be true in some sense, but the actual mismatched types are different instances of `impl Error` whose only relation to the mentioned primitive types is that the latter occur in the type parameters to the function that produces the `impl Error`,
 * the source location for "one of the expected opaque types" and "one of the found opaque types" is identical. Both point to the code that declares the `impl Error` return type, which is always confusing because it makes it seem that expected and found are the same (in which case, why is there an error),
 * the `note`s claim that the `Future::Output` parameters of the `async fn`s used to invoke `maybe` are expected/found opaque types. This is still confusing to me, because (as in the case above) the error is about the `impl Error`, and in this case `Output = Result<usize, std::io::Error>`, which is neither opaque nor the problem (the only thing that would somewhat make sense to me here is that `async fn` desugars to `impl Future`, which is opaque. But that's not present in the diagnostic at all),
 * the ``(`bool`)`` and ``(`usize`)`` in the final `note` were confusing to me, because they don't appear anywhere in the type (neither the printed abbreviation **nor** the real type, which at the point of the error is aready `Result<Result<(), io::Error>, impl Error>`) and the message doesn't say what they mean and what they've got to do with the error, and
 * nowhere in the message is it mentioned that `impl Trait` return types are different when instantiating generic functions with different parameters
   * with my actual issue, this was even more confusing to me because the first two branches did not produce an error, even though the futures, also a generic parameter of the function returning the opaque type, were different. So I would have expected that to already cause the error if that was the problem. This seems better on current stable, where adding a second branch with an `async fn two() -> Result<usize, io::Error>` produces an error which points to the locations of the two functions ([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=296d5ee74fbb3e428cb84fb4bbb236a6))

Possible output that would have helped me better identify the issue:
```
error[[E0308]](https://doc.rust-lang.org/stable/error-index.html#E0308): `match` arms have incompatible types
  --> src/main.rs:20:14
   |
18 |       let _ = match 0u16 {
   |  _____________-
19 | |         0 => maybe(one(), true).await.map(|res| res.map(|_| ())),
   | |              --------------------------------------------------- this is found to be of type `Result<Result<(), std::io::Error>, impl std::error::Error>`
20 | |         _ => maybe(r#true(), true).await.map(|res| res.map(|_| ())),
   | |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `impl std::error::Error` is different here
21 | |     };
   | |_____- `match` arms have incompatible types
...
24 |   async fn maybe<O, F: Future<Output = O>>(future: F, run: bool) -> Result<O, impl std::error::Error> { 
   |                                                                               ----------------------
   |                                                                               |
   |                                                                               opaque type defined here
   |
   = note: when returning a type containing `impl Trait` from a generic function, its concrete type is different if the function is called with different generic arguments
note: while checking the return type of the `async fn`
  --> src/main.rs:32:19
   |
32 | async fn one() -> Result<usize, std::io::Error> {
   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ checked the `Output` of this `async fn`, which is `O` in <src/main.rs:19:20>
note: while checking the return type of the `async fn`
  --> src/main.rs:35:22
   |
35 | async fn r#true() -> Result<bool, std::io::Error> {
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ checked the `Output` of this `async fn`, which is `O` in <src/main.rs:20:20>
   = note: mismatch between `usize` and `bool`

For more information about this error, try `rustc --explain E0308`.
```

For completeness, the error is resolved as expected by replacing the opaque type with its concrete type.

The latest change to the message for this error that I could find is #63167, but that doesn't yet include the ``(`bool`)`` (it just says "opaque type" and adds a source location). #93519 might be similar.