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

Compilation error reporting: Incomplete match over types #38234

Closed
ghost opened this issue Dec 8, 2016 · 14 comments
Closed

Compilation error reporting: Incomplete match over types #38234

ghost opened this issue Dec 8, 2016 · 14 comments
Labels
A-diagnostics Area: Messages for errors, warnings, and lints C-enhancement Category: An issue proposing an enhancement or a PR with one.

Comments

@ghost
Copy link

ghost commented Dec 8, 2016

Heya,

I'm currently learning rust through a small systems-related project, with a knowledgeable mentor ( @GuillaumeGomez ) ;)

When (badly) attempting to match over the Result<EnumType, String> returned by one of my functions, I only made a match explicit for one Ok case, and was expecting to have one kitchen-sink case to catch all other cases (This was aimed at testing).

The error I got rightly claimed that I was using incompatible types, but did not make it clear that I was either missing some potential cases (Ok over some other types of my enum) .

I do not know whether this would be easy or feasible, but it could be nice to have a suggestion pointing at the potential error that the match may deserve to be more detailed.

Here is a sample test code:

use std::fs::{self,File};
use std::net::{self,TcpStream,TcpListener};

enum FdImplementor {
    File(File),
    TcpStream(TcpStream),
    TcpListener(TcpListener),
}

fn do_smthg() -> Result<FdImplementor, String> {
    Ok(FdImplementor::File(fs::File::create("/tmp/pathtest.txt").map_err(|_| "failed to create file")?))
}

fn test() -> Result<bool, String> {
    match do_smthg() {
        Ok(FdImplementor::File(file)) => Ok(true),
        e => e
    }
}

fn main() {
    test();
}

And the error:

rustc 1.13.0 (2c6933acc 2016-11-07)
error[E0308]: match arms have incompatible types
  --> <anon>:15:5
   |
15 |     match do_smthg() {
   |     ^ expected bool, found enum `FdImplementor`
   |
   = note: expected type `std::result::Result<bool, std::string::String>`
   = note:    found type `std::result::Result<FdImplementor, std::string::String>`
note: match arm with an incompatible type
  --> <anon>:17:14
   |
17 |         e => e
   |              ^

error: aborting due to previous error
@durka
Copy link
Contributor

durka commented Dec 8, 2016

I'm not sure what you are asking for here? Once you cover the other cases you will still have to write Err(e) => Err(e) instead of e => e, since the Result types are not the same (it's inferred to Err::<FdImplementor, String>(e) => Err::<bool, String>(e)).

@GuillaumeGomez
Copy link
Member

@durka: The thing is that the error isn't very intuitive for beginners because they bind a super specific case and then all the others without thinking (in this case) that Ok(_) is also bound. So maybe a little suggestion from the compiler might come in handy!

@durka
Copy link
Contributor

durka commented Dec 8, 2016

Indeed if you leave out the bad arm or fix it, the error is amazing:

error[E0004]: non-exhaustive patterns: `Ok(TcpStream(_))` and `TcpListener(_)` not covered
  --> <anon>:15:11
   |
15 |     match do_smthg() {
   |           ^^^^^^^^^^ patterns `Ok(TcpStream(_))` and `TcpListener(_)` not covered

error: aborting due to previous error

so I guess the focus should be improving "match arm with incompatible type" so you can get through to "non-exhaustive patterns".

@durka
Copy link
Contributor

durka commented Dec 8, 2016

Although... even that has a small bug... it should say "Ok(TcpStream(_)) and Ok(TcpListener(_)) not covered"!

@GuillaumeGomez
Copy link
Member

The fact is that they're covered. But it's shadowed by the other arms. But indeed, a similar error/note would be awesome.

@ghost
Copy link
Author

ghost commented Dec 8, 2016

Indeed, it would have made much more sense to me as a beginner than a "type mismatch" kind of error.

@Mark-Simulacrum Mark-Simulacrum added the A-diagnostics Area: Messages for errors, warnings, and lints label May 16, 2017
@Mark-Simulacrum Mark-Simulacrum added the C-enhancement Category: An issue proposing an enhancement or a PR with one. label Jul 26, 2017
@estebank
Copy link
Contributor

Current output:

error[E0308]: match arms have incompatible types
  --> src/main.rs:15:5
   |
15 | /     match do_smthg() {
16 | |         Ok(FdImplementor::File(file)) => Ok(true),
17 | |         e => e
18 | |     }
   | |_____^ expected bool, found enum `FdImplementor`
   |
   = note: expected type `std::result::Result<bool, _>`
              found type `std::result::Result<FdImplementor, _>`
note: match arm with an incompatible type
  --> src/main.rs:17:14
   |
17 |         e => e
   |              ^

Would it make sense if the output were:

error[E0308]: match arms have incompatible types
  --> src/main.rs:15:5
   |
15 | /     match do_smthg() {
16 | |         Ok(FdImplementor::File(file)) => Ok(true),
17 | |         e => e
18 | |     }
   | |_____^ expected bool, found enum `FdImplementor`
   |
   = note: expected type `std::result::Result<bool, _>`
              found type `std::result::Result<FdImplementor, _>`
note: match arm with an incompatible type
  --> src/main.rs:17:14
   |
17 |         e => e
   |              ^
note: this match arm covers multiple cases
  --> src/main.rs:17:14
   |
17 |         e => e
   |         ^
   = note: cases covered:
           - `Ok(FdImplementor::TcpStream(TcpStream))`
           - `Ok(FdImplementor::TcpListener(TcpListener))`
           - `Err(_)`

Looking at this it looks a bit long, I feel it could be pruned a bit by doing:

error[E0308]: match arms have incompatible types
  --> src/main.rs:15:5
   |
17 |         e => e
   |              ^ expected bool, found enum `FdImplementor`
   = note: expected type `std::result::Result<bool, _>`
              found type `std::result::Result<FdImplementor, _>`
note: this match arm covers multiple cases
  --> src/main.rs:17:14
   |
17 |         e => e
   |         ^
   = note: cases covered:
           - `Ok(FdImplementor::TcpStream(TcpStream))`
           - `Ok(FdImplementor::TcpListener(TcpListener))`
           - `Err(_)`

@ghost
Copy link
Author

ghost commented Sep 22, 2017

The last message you are suggesting looks pretty neat, imho.
It provides the information I was missing at the time:

  • fact that the second arm matches multiple cases (which was my expectation)
  • List of cases covered (neat!)

Note that I'm still a bit wary of the incompatible types and the Result<bool, _> thing, as it was one of the confusing bits for me; but the improvement you suggest is already great !

Long story short: Yes it would make sense :)

@estebank
Copy link
Contributor

I've been looking at this and the MatchVisitor that checks exhaustiveness of match arms is run after typechecking that catches this problem. With the current flow of the compiler we either have to duplicate the pattern checking logic (bad), we switch around the order (would need to rework librustc_const_eval/_match to accept non typechecked code), or we settle on the following output (not ideal):

error[E0308]: match arms have incompatible types
  --> src/main.rs:15:5
   |
17 |         e => e
   |              ^ expected bool, found enum `FdImplementor`
   = note: expected type `std::result::Result<bool, _>`
              found type `std::result::Result<FdImplementor, _>`
note: this match arm covers multiple cases
  --> src/main.rs:17:14
   |
17 |         e => e
   |         ^

@arielb1
Copy link
Contributor

arielb1 commented Sep 25, 2017

@estebank

But this would still be an error if you covered all of the Ok branch:

use std::fs::{self,File};
use std::net::{self,TcpStream,TcpListener};

enum FdImplementor {
    File(File),
    TcpStream(TcpStream),
    TcpListener(TcpListener),
}

fn do_smthg() -> Result<FdImplementor, String> {
    Ok(FdImplementor::File(fs::File::create("/tmp/pathtest.txt").map_err(|_| "failed to create file")?))
}

fn test() -> Result<bool, String> {
    match do_smthg() {
        Ok(_) => Ok(true),
        e => e //~ ERROR still a type error, you still want Err(e)
    }
}

fn main() {
    test();
}

I think we should just recommend wrapping a variant in this case, but I'm not sure how generic to make it.

I don't think exhaustiveness needs to be integrated - after you'll fix the type error, you'll get the correct exhaustiveness error.

Centril added a commit to Centril/rust that referenced this issue Feb 14, 2019
Tweak "incompatible match arms" error

- Point at the body expression of the match arm with the type error.
- Point at the prior match arms explicitly stating the evaluated type.
- Point at the entire match expr in a secondary span, instead of primary.
- For type errors in the first match arm, the cause is outside of the
  match, treat as implicit block error to give a more appropriate error.

Fix rust-lang#46776, fix rust-lang#57206.
CC rust-lang#24157, rust-lang#38234.
Centril added a commit to Centril/rust that referenced this issue Feb 14, 2019
Tweak "incompatible match arms" error

- Point at the body expression of the match arm with the type error.
- Point at the prior match arms explicitly stating the evaluated type.
- Point at the entire match expr in a secondary span, instead of primary.
- For type errors in the first match arm, the cause is outside of the
  match, treat as implicit block error to give a more appropriate error.

Fix rust-lang#46776, fix rust-lang#57206.
CC rust-lang#24157, rust-lang#38234.
@estebank
Copy link
Contributor

estebank commented Mar 6, 2019

Current output:

error[E0308]: match arms have incompatible types
  --> src/main.rs:17:14
   |
15 | /     match do_smthg() {
16 | |         Ok(FdImplementor::File(file)) => Ok(true),
   | |                                          -------- this is found to be of type `std::result::Result<_, _>`
17 | |         e => e
   | |              ^ expected bool, found enum `FdImplementor`
18 | |     }
   | |_____- `match` arms have incompatible types
   |
   = note: expected type `std::result::Result<bool, _>`
              found type `std::result::Result<FdImplementor, _>`

@ghost
Copy link
Author

ghost commented Mar 6, 2019

This is indeed much better, in the sense that the "returned" e is described as being the source of the type error. Took me some time to understand it, though (since I didn't get back to this since then, so I'm still a newbie which is good to provide feedback here imho).

I think it can be considered ok to close this. The only difficulty left here for beginners is to understand that the expected return type of std::result::Result<bool,_> is coming from the first arm's resolution; and defined as the expected return type for the match expression, but then that the second arm provides a conflict on that type resolution.

I'll leave it up to you to determine whether you want to close this issue or not at this point, but I personally think this is already a great improvement :) 👍

@estebank estebank closed this as completed May 1, 2019
@estebank
Copy link
Contributor

estebank commented May 1, 2019

Closing in view of the output after #60455 closing the last somewhat confusing part (missing types in the match arm):

Screen Shot 2019-05-01 at 3 06 34 PM

I see very little improvement we could make over this.

@Joacchim
Copy link

Joacchim commented May 2, 2019

@estebank Looks fine, we have both the expected return type, and the expectation vs reality on the mismatching match arm. All info is there, and I agree it looks hard to improve :)

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.
Projects
None yet
Development

No branches or pull requests

6 participants