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

Incorrect "unreachable pattern" warning with opaque constant #78057

Closed
Nadrieril opened this issue Oct 17, 2020 · 15 comments · Fixed by #115937
Closed

Incorrect "unreachable pattern" warning with opaque constant #78057

Nadrieril opened this issue Oct 17, 2020 · 15 comments · Fixed by #115937
Labels
A-exhaustiveness-checking Relating to exhaustiveness / usefulness checking of patterns C-bug Category: This is a bug.

Comments

@Nadrieril
Copy link
Member

Nadrieril commented Oct 17, 2020

I tried this code (playground link):

#[derive(PartialEq)]
struct Opaque(i32);

impl Eq for Opaque {}

const FOO: Opaque = Opaque(42);

fn foo() {
    match Opaque(0) {
        FOO => {},
        _ => {}
    }
}

This emits an "unreachable pattern" warning on the second branch of the match, even though it is clearly reachable. This gives the same warning for current nightly and beta, as well as stable 1.47.0 and 1.40.0.

This was uncovered while discussing #77390.

Meta

rustc --version --verbose:

rustc 1.47.0 (18bf6b4f0 2020-10-07)
binary: rustc
commit-hash: 18bf6b4f01a6feaf7259ba7cdae58031af1b7b39
commit-date: 2020-10-07
host: x86_64-unknown-linux-gnu
release: 1.47.0
LLVM version: 11.0
@Nadrieril Nadrieril added the C-bug Category: This is a bug. label Oct 17, 2020
@Nadrieril
Copy link
Member Author

@rustbot modify labels: +A-exhaustiveness-checking

@rustbot rustbot added the A-exhaustiveness-checking Relating to exhaustiveness / usefulness checking of patterns label Oct 17, 2020
@bugadani
Copy link
Contributor

How is the second branch reachable?

@Nadrieril
Copy link
Member Author

Nadrieril commented Oct 17, 2020

A value like Opaque(0) would reach it. I do realize that using FOO as the match scrutinee is confusing, I'll change that

@bugadani
Copy link
Contributor

bugadani commented Oct 17, 2020

With this example, I get an error: error: to use a constant of type `Opaque` in a pattern, `Opaque` must be annotated with `#[derive(PartialEq, Eq)]

I believe the warning is raised because the compiler then assumes you use FOO as a catch-all name instead?

@Nadrieril
Copy link
Member Author

Oh, that's an interesting hypothesis. That would mean some part of the code sees this as a const pattern (hence the error) and some other part as a variable binding (hence the warning). I would find that quite surprising honestly.

@Nadrieril
Copy link
Member Author

Nah, the bit that emits the error is in rustc_mir_build/src/thir/pattern/const_to_pat.rs, and the one that emits the warning is rustc_mir_build/src/thir/pattern/check_match.rs. I'm mostly sure that those two take the same inputs and in particular would not reclassify the const as a variable binding

@bugadani
Copy link
Contributor

bugadani commented Oct 17, 2020

But if I derive Eq instead of implementing it like you did, it's all good: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c3d83bafde050c32204321a21cf44689

@Nadrieril
Copy link
Member Author

Yeah, something happens when the const is considered opaque which triggers this behavior. I'm not sure what exactly, @oli-obk do you know? Maybe returning Err after emitting the error in const-to-pat somehow transforms the pattern into a wildcard?

@Nadrieril
Copy link
Member Author

Nadrieril commented Oct 17, 2020

Oh wait that's exactly what happens :D

let msg = format!(
"to use a constant of type `{}` in a pattern, \
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
path, path,
);
self.saw_const_match_error.set(true);
if self.include_lint_checks {
tcx.sess.span_err(span, &msg);
} else {
tcx.sess.delay_span_bug(span, &msg)
}
PatKind::Wild

@oli-obk I believe to solve that particular issue we'd need to return something else than PatKind::Wild here

Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Oct 22, 2020
…, r=varkor

Cleanup constant matching in exhaustiveness checking

This supercedes rust-lang#77390. I made the `Opaque` constructor work.
I have opened two issues rust-lang#78071 and rust-lang#78057 from the discussion we had on the previous PR. They are not regressions nor directly related to the current PR so I thought we'd deal with them separately.

I left a FIXME somewhere because I didn't know how to compare string constants for equality. There might even be some unicode things that need to happen there. In the meantime I preserved previous behavior.

EDIT: I accidentally fixed rust-lang#78071
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Oct 22, 2020
…, r=varkor

Cleanup constant matching in exhaustiveness checking

This supercedes rust-lang#77390. I made the `Opaque` constructor work.
I have opened two issues rust-lang#78071 and rust-lang#78057 from the discussion we had on the previous PR. They are not regressions nor directly related to the current PR so I thought we'd deal with them separately.

I left a FIXME somewhere because I didn't know how to compare string constants for equality. There might even be some unicode things that need to happen there. In the meantime I preserved previous behavior.

EDIT: I accidentally fixed rust-lang#78071
@Nadrieril
Copy link
Member Author

I gave it more thought, and the correct approach seems instead to not even start exhaustiveness checking if const_to_pat emits an unrecoverable error.

@oli-obk
Copy link
Contributor

oli-obk commented Dec 7, 2020

How about we introduce a PatKind::Error to make these hacky PatKind::Wild situations obvious? Then exhaustiveness can just bail out and assume an error has already been reported.

@Nadrieril
Copy link
Member Author

There's already code in check_match to track errors and skip exhaustiveness, we could reuse that:

// Bail out early if lowering failed.

I'll add that to my queue of things to do

@Nadrieril
Copy link
Member Author

Nadrieril commented Dec 20, 2020

yeah ok there's a complicated path from const_to_pat to check_match. PatKind::Error sounds much more feasible.
EDIT: ugh maybe not, it's a mess

@Dylan-DPC
Copy link
Member

The code no longer emits this warning for this case.

@Dylan-DPC Dylan-DPC closed this as not planned Won't fix, can't repro, duplicate, stale Apr 7, 2023
@Nadrieril
Copy link
Member Author

Nadrieril commented Apr 7, 2023

It does, I just tested with the same playground link. But it also emits an error so maybe the warning doesn't matter too much. I just think it's a bit confusing.

Full output:

error: to use a constant of type `Opaque` in a pattern, `Opaque` must be annotated with `#[derive(PartialEq, Eq)]`
  --> src/lib.rs:10:9
   |
10 |         FOO => {},
   |         ^^^

warning: unreachable pattern
  --> src/lib.rs:11:9
   |
10 |         FOO => {},
   |         --- matches any value
11 |         _ => {}
   |         ^ unreachable pattern
   |
   = note: `#[warn(unreachable_patterns)]` on by default

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-exhaustiveness-checking Relating to exhaustiveness / usefulness checking of patterns C-bug Category: This is a bug.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants