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

"try expression alternatives have incompatible types" is confusing wording #71309

Closed
comex opened this issue Apr 19, 2020 · 1 comment · Fixed by #86382
Closed

"try expression alternatives have incompatible types" is confusing wording #71309

comex opened this issue Apr 19, 2020 · 1 comment · Fixed by #86382
Labels
A-diagnostics Area: Messages for errors, warnings, and lints C-enhancement Category: An issue proposing an enhancement or a PR with one. D-confusing Diagnostics: Confusing error or lint that should be reworked. D-incorrect Diagnostics: A diagnostic that is giving misleading or incorrect information. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. D-papercut Diagnostics: An error or lint that needs small tweaks. F-try_blocks `#![feature(try_blocks)]` T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@comex
Copy link
Contributor

comex commented Apr 19, 2020

Simple example:

fn foo(x: Result<i32, ()>) -> Result<(), ()> {
    let y: u32 = x?;
    Ok(())
}

Compilation result (rust version 1.44.0-nightly (52fa23add 2020-04-18)):

error[E0308]: try expression alternatives have incompatible types
 --> src/lib.rs:2:18
  |
2 |     let y: u32 = x?;
  |                  ^^ expected `u32`, found `i32`
  |
help: you can convert an `i32` to `u32` and panic if the converted value wouldn't fit
  |
2 |     let y: u32 = x?.try_into().unwrap();
  |                  ^^^^^^^^^^^^^^^^^^^^^^

Parts of this diagnostic are great, showing the mismatched types (u32 and i32) and even suggesting a valid possible fix. But the wording of the error itself is confusing. What "try expression alternatives"? I don't see any alternatives.

Taking a glance at the compiler implementation, it's referring to the arms of the match block that ? desugars into. But it's confusing to talk about those, because:

  • They're not visible to the user.

  • Why would there be a type mismatch between the arms in the first place, if there are only two arms and one of them always ends in a return rather than producing a value? Did type inference infer the error branch as having type u32 due to the context?

    If so, the message is technically accurate, but unhelpful: from the user's point of view, the 'real' issue is a mismatch between the expected type of x? (namely i32) and the context. As far as I can imagine, this is the only possible cause of a type mismatch between the arms of a desugared ?. So it seems like it would be fine to just change the error message to not mention the arms.

As a sidenote, if I take the -Zunpretty=hir output and try to compile that:

#![feature(try_trait)]
use std::ops::Try;
use std::convert::From;
fn foo(x: Result<i32, ()>) -> Result<(), ()> {
    let y: u32 = match Try::into_result(x) {
        Ok(val) => val,
        Err(err) => return Try::from_error(From::from(err)),
    };
    Ok(())
}

...oddly enough, I don't get a "match arms have incompatible types" error, but a more helpful one instead:

error[E0308]: mismatched types
 --> q.rs:6:20
  |
6 |         Ok(val) => val,
  |                    ^^^ expected `u32`, found `i32`
  |
help: you can convert an `i32` to `u32` and panic if the converted value wouldn't fit
  |
6 |         Ok(val) => val.try_into().unwrap(),
  |                    ^^^^
@estebank
Copy link
Contributor

This is because of the explicit check here

MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => {
Error0308(match source {
hir::MatchSource::IfLetDesugar { .. } => {
"`if let` arms have incompatible types"
}
hir::MatchSource::TryDesugar => {
"try expression alternatives have incompatible types"
}
_ => "`match` arms have incompatible types",
})
}

@estebank estebank added A-diagnostics Area: Messages for errors, warnings, and lints D-confusing Diagnostics: Confusing error or lint that should be reworked. D-incorrect Diagnostics: A diagnostic that is giving misleading or incorrect information. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. D-papercut Diagnostics: An error or lint that needs small tweaks. F-try_blocks `#![feature(try_blocks)]` T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Apr 19, 2020
@JohnTitor JohnTitor added the C-enhancement Category: An issue proposing an enhancement or a PR with one. label Apr 19, 2020
@bors bors closed this as completed in f4f7704 Sep 17, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints C-enhancement Category: An issue proposing an enhancement or a PR with one. D-confusing Diagnostics: Confusing error or lint that should be reworked. D-incorrect Diagnostics: A diagnostic that is giving misleading or incorrect information. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. D-papercut Diagnostics: An error or lint that needs small tweaks. F-try_blocks `#![feature(try_blocks)]` T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
3 participants