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

Exhaustive match of number treated as non-exhaustive #12483

Closed
ebiggers opened this Issue Feb 23, 2014 · 17 comments

Comments

Projects
None yet
10 participants
@ebiggers
Copy link
Contributor

ebiggers commented Feb 23, 2014

The following function does not compile because rustc determines that the patterns in the match statement are non-exhaustive. However, the pattern matches every possible value of the integer type, and the compiler should be able to detect this.

fn wont_compile(x : u8) { 
    match (x) {
        0x00 .. 0xff => { }
    }
}
@jkleint

This comment has been minimized.

Copy link

jkleint commented Oct 12, 2014

I assume this is the same issue:

fn main() {
    println!("{}", match 1i {
        n if n > 0  => "greater",
        n if n < 0  => "less",
        n if n == 0 => "equal",
    });
}
// error: non-exhaustive patterns: `_` not covered [E0004]
@thecoshman

This comment has been minimized.

Copy link

thecoshman commented Nov 10, 2014

Whilst I agree that your example is exhaustive, it's not a good example.

match 1i {
    n if n > 0  => "greater",
    n if n < 0  => "less",
    _ => "equal",
}

This is effectively the same, except it compiles. Keep in mind match checks the patterns in order. You wouldn't write:

if (n > 0) { "greater" } 
else if (n < 0) { "less" }
else if (n == 0) { "equal" }

you would just drop the 'if (n == 0)' from that last clause.

@drewm1980

This comment has been minimized.

Copy link
Contributor

drewm1980 commented Dec 6, 2014

Doing the test for n==0 is less efficient, but IMHO more readable. I would probably use your pattern but leave n==0 commented out.

@steveklabnik

This comment has been minimized.

Copy link
Member

steveklabnik commented Jan 23, 2015

The compiler is never going to be able to fully determine these kinds of things, as they would involve evaluating certain things at compile time. For example:

0x00 ... (0xff + 1 - 1) => { }

This arm would be equivalent, but would report as non-exhaustive, too.

As such, I'm giving this a close. I don't think we can improve this.

@chris-morgan

This comment has been minimized.

Copy link
Member

chris-morgan commented Jul 22, 2015

@steveklabnik I disagree with your closing of this issue; could you please open it again? The issue is entirely genuine, is blocking correct code (though admittedly there is an easy workaround: change the last pattern to an _) and is a nuisance.

Moreover, your fabricated example is invalid, because you can’t do arithmetic in patterns.

@jkleint Your example is a completely different issue and is not solvable. n > 0 || n < 0 || n == 0 is not necessarily true, though it is declared by the programmer to be true if Eq and Ord are implemented for the type.

@steveklabnik

This comment has been minimized.

Copy link
Member

steveklabnik commented Jul 22, 2015

@chris-morgan I'm not understanding how this issue is solvable. To grok the range, we need to evaluate code at compile time, it can't work.

@chris-morgan

This comment has been minimized.

Copy link
Member

chris-morgan commented Jul 22, 2015

@steveklabnik it’s a pattern, so the numbers are literals or constants. No additional evaluation required. The compiler needs to know them in order to compile the code anyway.

@steveklabnik steveklabnik reopened this Jul 22, 2015

@jkleint

This comment has been minimized.

Copy link

jkleint commented Jul 25, 2015

@chris-morgan aren't Eq and Ord implemented for integers? My example was an integer. :)

@carlosliam

This comment has been minimized.

Copy link
Contributor

carlosliam commented Sep 18, 2015

I've written a POC script that compares a set of ranges with 1 or more exhaustive ranges for the respective type and outputs any ranges that are not covered: https://gist.github.com/aarzee/4395cb34eef31ea4e5c4

I'm interested in implementing this logic in the compiler but looking at check_match.rs I have absolutely no idea how I would do this. Also, this code has opportunities to take advantage of more types in the future, as currently floats aren't supported by it, and specialization and native inclusive ranges have not yet been added to the language.

@jonas-schievink

This comment has been minimized.

Copy link
Member

jonas-schievink commented Sep 24, 2015

Implementing this would cause the following to fail to compile since the last arm is unreachable:

match 0u8 {
    0...255 => {}
    _ => {}
}

Should a warning be emitted first? Is an RFC needed? Should this be ignored without warning, like it is now? Make it a deny-by-default Lint to prevent breaking dependencies? So many questions!

@chris-morgan

This comment has been minimized.

Copy link
Member

chris-morgan commented Sep 24, 2015

To be sure, implementing this will cause certain code that compiles now to cease to compile, but it was bad code to begin with and a bug in the compiler is being fixed. And it is very unlikely to actually affect anyone in practice, because I doubt that anyone has actually written such code. Thus it fits easily inside the stability-retention guidelines. I do not believe any RFC is required, nor any ability to disable the new behaviour.

@jonas-schievink

This comment has been minimized.

Copy link
Member

jonas-schievink commented Sep 24, 2015

I doubt that anyone has actually written such code

Not this exact code, sure. But maybe an opcode dispatch switch that handles all possible byte values? Or one that handles all undefined opcodes with an arm like 0xab ... 0xff => panic!("invalid"), and an additional _ => unreachable!() (because this is currently required).
Maybe some people match on the high bit of a byte like this: 0 ... 127 => "high bit clear", 128 ... 255 => "high bit set", _ => unreachable!()
All of these would stop compiling if this is implemented, so I think the impact should at least be evaluated.

@chris-morgan

This comment has been minimized.

Copy link
Member

chris-morgan commented Sep 25, 2015

Hmm, good point. I myself have just changed the last branch into _, preferring not to put an extra and superfluous unreachable!() into the code, but I can see that someone could easily have gone the other way.

@jonas-schievink

This comment has been minimized.

Copy link
Member

jonas-schievink commented Sep 28, 2015

I wrote a bit of code that at least seems to work semi-correctly for a few test cases: https://play.rust-lang.org/?gist=f3d4e2e9559d84319432&version=stable

It's O(n²) in the number of literal matches (or ranges) for each variable matched against, but I haven't actually measured if there's any impact (there are never too many literal or range patterns for a single variable - 256 seems still useful for matching all u8 values, but I doubt there's much code with more than that).

Feel free to break it play with it using the examples at the bottom!

@petrochenkov

This comment has been minimized.

Copy link
Contributor

petrochenkov commented Sep 28, 2015

For reference, @quantheory wrote an RFC about this - rust-lang/rfcs#880, it was postponed. He wrote some implementation too https://github.com/quantheory/int_range_check.

@comex

This comment has been minimized.

Copy link
Contributor

comex commented Nov 5, 2015

FWIW, GCC can do this: https://goo.gl/4f1i6H (no warning, but a warning appears if you change 255 to something else). Clang can't tell the difference.

@steveklabnik

This comment has been minimized.

Copy link
Member

steveklabnik commented Nov 12, 2015

I'm going to close this in favor of an RFC, as this would be a pretty big change. @quantheory's might be a good starting point!

twe4ked added a commit to twe4ked/rs8080 that referenced this issue Aug 11, 2018

Mark _ as unreachable
Rust's exhaustiveness checking is based entirely on enums and never
values, so this fails because u8 simply isn't an enum type.

    error[E0004]: non-exhaustive patterns: `_` not covered
      --> src/main.rs:39:26
       |
    39 |     let op_bytes = match op_code {
       |                          ^^^^^^^ pattern `_` not covered

rust-lang/rust#12483
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment