-
Notifications
You must be signed in to change notification settings - Fork 13k
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
unreachable pattern lint should report some (or all) of the earlier match arms that subsume the unreachable one #127870
Comments
In truth my original intent was to make an example that would motivate showing multiple arms that are collectively causing the unreachable arm to become unreachable. But I got the logic backward and gave an example that showed the opposite of that (i.e. multiple arms where each independently makes the problematic one unreachable). Here's an example of what I mean by a collective set of arms making a later one unreachable (playground): enum Sym { A(i32, i32), B, C }
fn foo(s: (Sym, bool)) -> &'static str {
match s {
(Sym::B, _) => "B",
(Sym::A(_, 0), true) => "Am,0",
(Sym::A(0, _), false) => "A0,n",
/* imagine a *lot* of other match arms inserted in here */
(Sym::C, _) => "C",
(Sym::A(0, 0), _) => "A0,0",
(Sym::A(_, _), _) => "Am,n",
}
}
fn main() {
foo((Sym::A(0, 0), true));
foo((Sym::A(1, 1), false));
foo((Sym::B, true));
foo((Sym::C, false));
} This is the scenario where I think the compiler can be most helpful in reporting multiple arms, because their cumulative effect is not trivial for a human to immediately see. So I would like in this case to see output like:
|
@Nadrieril could probably weigh in on the feasibility of recording this information (or possibly re-deriving it on error). Seems hard to do the former, from scanning and being vaguely familiar with it from reviewing Nadrieril's changes some months ago, but perhaps it's easier to redo on the error path. |
This sounds doable actually! We track intersection data today: we know for a given branch which branches above it match values in common. Unfortunately we only compute an underapproximation to prevent complexity blowups, but I'd guess it's good enough for diagnostics. The report might look like "such and such branch intersect this one, they contribute to making it unreachable" |
Yeah, incomplete context is better than no context. All of the subtle cases I could think of are actually situations in which naïve output would be sufficient for the user to understand the issue. |
…try> match exhaustiveness: Expand or-patterns as a separate step To compute exhaustiveness, we must expand or-patterns. Previously, we expanded them at the same time that we pushed patterns into the matrix. This made it harder to track pattern reachability, because the or-pattern itself would never show up in the matrix so we had to recover missing information. This PR changes that: we no longer expand or-patterns as we push them into the matrix. Instead, if we find an or-pattern in the matrix we expand them in a step very much like the specialization we already do. This simplifies a bunch of things, and will greatly simplify the implementation of rust-lang#127870. r? `@compiler-errors`
match ... {
(true, _, _) => {}
//^ note: this may match some of the same values
(_, true, 0..10) => {}
//^ note: this may match some of the same values
(_, true, 10..) => {}
//^ note: this may match some of the same values
(_, true, 3) => {}
//^ unreachable pattern
}
|
After giving this more thought, it appears that the under-approximation is good enough that it still always contains a set of rows that cover the one we care about. Hence the lint is always able to list a set of arms that cause the unreachability. |
exhaustiveness: Explain why a given pattern is considered unreachable This PR tells the user why a given pattern is considered unreachable. I reused the intersection information we were already computing; even though it's incomplete I convinced myself that it is sufficient to always get a set of patterns that cover the unreachable one. I'm not a fan of the diagnostic messages I came up with, I'm open to suggestions. Fixes rust-lang#127870. This is also the other one of the two diagnostic improvements I wanted to do before rust-lang#122792. Note: the first commit is rust-lang#128015, the second is an unrelated drive-by tweak. r? `@compiler-errors`
…ompiler-errors match exhaustiveness: Expand or-patterns as a separate step To compute exhaustiveness, we must expand or-patterns. Previously, we expanded them at the same time that we pushed patterns into the matrix. This made it harder to track pattern reachability, because the or-pattern itself would never show up in the matrix so we had to recover missing information. This PR changes that: we no longer expand or-patterns as we push them into the matrix. Instead, if we find an or-pattern in the matrix we expand them in a step very much like the specialization we already do. This simplifies a bunch of things, and should greatly simplify the implementation of rust-lang#127870. r? `@compiler-errors`
exhaustiveness: Explain why a given pattern is considered unreachable This PR tells the user why a given pattern is considered unreachable. I reused the intersection information we were already computing; even though it's incomplete I convinced myself that it is sufficient to always get a set of patterns that cover the unreachable one. I'm not a fan of the diagnostic messages I came up with, I'm open to suggestions. Fixes rust-lang/rust#127870. This is also the other one of the two diagnostic improvements I wanted to do before rust-lang/rust#122792. Note: the first commit is an unrelated drive-by tweak. r? `@compiler-errors`
…rrors match exhaustiveness: Expand or-patterns as a separate step To compute exhaustiveness, we must expand or-patterns. Previously, we expanded them at the same time that we pushed patterns into the matrix. This made it harder to track pattern reachability, because the or-pattern itself would never show up in the matrix so we had to recover missing information. This PR changes that: we no longer expand or-patterns as we push them into the matrix. Instead, if we find an or-pattern in the matrix we expand them in a step very much like the specialization we already do. This simplifies a bunch of things, and should greatly simplify the implementation of rust-lang/rust#127870. r? `@compiler-errors`
Code
Current output
Desired output
Rationale and extra context
I was recently adding a new enum variant that had a ton of other enum variants. I had to update an associated match expression to include it. Then, at some later point, I forgot that I had updated that match expression, redundantly added new match arms for the same variant, and got the above diangostic (which was set to be denied in my project and so it was a hard error).
I spent a little while puzzling over how the pattern could possibly be unreachable before I realized my mistake, in part because the list was so long that I had inserted the new variants at different points in the (unsorted) series of match arms. It was relatively trivial for me to diagnose in my own case, but I think my example above helps illustrate hypothetical scenarios where the compiler could do this mechanical work for us.
(Another options might be to report all the arms that would each independently subsume the unreachable one, as I have two such arms in my example above. But I'm not sure it would be worth the effort of computing that set ... its probably good enough to give the user the first counter-example and let them iteratively invoke the compiler if that's the usage pattern they want to follow.)
Other cases
No response
Rust Version
Anything else?
No response
The text was updated successfully, but these errors were encountered: