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

Exhaustive match of number treated as non-exhaustive #12483

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

Exhaustive match of number treated as non-exhaustive #12483

ebiggers opened this issue Feb 23, 2014 · 17 comments
Labels
C-enhancement Category: An issue proposing an enhancement or a PR with one.

Comments

@ebiggers
Copy link
Contributor

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
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
Copy link

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
Copy link
Contributor

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
Copy link
Member

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
Copy link
Member

@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
Copy link
Member

@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
Copy link
Member

@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
Copy link

jkleint commented Jul 25, 2015

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

@steveklabnik steveklabnik added the C-enhancement Category: An issue proposing an enhancement or a PR with one. label Sep 3, 2015
@carloslbello
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Member

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
Copy link
Contributor

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
Copy link
Member

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
Copy link
Contributor

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
Copy link
Contributor

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
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
Copy link
Member

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
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
bors added a commit to rust-lang-ci/rust that referenced this issue Jul 25, 2022
fix methods in pub trait generated by macro cannot be completed

Fix rust-lang#12483

Check if the container is trait and inherit the visibility to associate items during collection.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-enhancement Category: An issue proposing an enhancement or a PR with one.
Projects
None yet
Development

No branches or pull requests

10 participants