-
Notifications
You must be signed in to change notification settings - Fork 67
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
Non exhaustiveness of patterns #1318
base: main
Are you sure you want to change the base?
Non exhaustiveness of patterns #1318
Conversation
…ed on rustc usefulness
…function. Some tests still failing
Ah nice! I asked because I was not sure if the other function is maybe different. One small note: I think currently an enum variant is always marked as refutable, but with this checker, I think we should be more exact: It is irrefutable if and only if it is the only variant of the enum. |
Cool! Just to confirm:
is represented as Btw, I need to check/fix an edge cases with respect to the use of elipsis inside arrays, and I think this PR will be ready to be reviewed :) |
Not sure I understood correctly but given that enum declaration, we could use it in a pattern like this:
The second parameter of If you consider the following enum definition:
Then we could do the following because the pattern is irrefutable:
Of course we still need to check that the inner patterns are all irrefutable, so it would not work for |
Thanks for your comments @chriseth. Last night I found a small bug when specializing patterns with internal parameters (like arrays and tuples). I'll fix that and take care of all your comments. It should be ready by tonight, sorry if it's taking longer than expected. |
No worries, take your time, it's not easy navigating in a new codebase. |
…rns inside tuples, arrays, etc
Co-authored-by: chriseth <chris@ethereum.org>
…tiveness_of_patterns
|
||
use super::{asm::SymbolPath, visitor::Children}; | ||
|
||
/// When a pattern is specialised, in some cases the algorithm will return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it makes sense to move this a bit down so that Pattern is the first element in this file?
|
||
/// Check for irrefutable patterns in pattern tuples. Since pattern tuples can contain | ||
/// internal patterns, irrefutability only applies in cases of unique patterns (len == 1). | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this work fine if you have an empty line without comment?
/// If (_, _) was obtained from specialization, it is not true that the pattern is irrefutable | ||
/// because having more than one element implies that the evaluation is performed on internal patterns. | ||
/// If, on the other hand, the pattern tuple is `(Pattern::Tuple(_,_), )``, irrefutability applies in the standard way. | ||
pub fn is_irrefutable(&self) -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I cannot find any use of this function...
/// If the constructor is shared with the pattern, specialize the pattern removing | ||
/// "one layer" (the constructor) and returning internal patterns. | ||
/// Return None if the pattern can't be specialized with the constructor or if the pattern is empty. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still don't really understand this routine, but maybe adding the following items to the docstring helps:
We are only interested in the outermost type-constructor of the constructor
pattern and ignore the fields and the inner patterns.
And maybe also a variant of the following:
matches!(c(v_1, .., v_n), p)
<=> specialize(c, p) returns something
&& matches!((v_1, .., v_n), specialize(c, p))
} | ||
} | ||
|
||
impl Default for PatternTuple { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't that the same as derive(Default)
?
/// If the constructor is shared with the pattern, specialize the pattern removing | ||
/// "one layer" (the constructor) and returning internal patterns. | ||
/// Return None if the pattern can't be specialized using the constructor. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I think this part I understand now. Let me rephrase it in my own words and then we can see how we want to combine the docs.
/// If the constructor is shared with the pattern, specialize the pattern removing | |
/// "one layer" (the constructor) and returning internal patterns. | |
/// Return None if the pattern can't be specialized using the constructor. | |
/// We are only interested in the type constructor part of `constructor` and ignore the inner patterns and fields. | |
/// If the constructor does not match the pattern, returns None. | |
/// If it matches, returns a vector of the inner fields of the pattern. |
And then maybe add some formal statement like on https://doc.rust-lang.org/nightly/nightly-rustc/rustc_pattern_analysis/usefulness/index.html#specialization
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok it seems I still have not understood it :)
The returned patterns correspond to the sub-items of the constructor, not of the pattern, right?
But they are taken from the pattern.
pub fn specialize(&self, constructor: &Self) -> Option<PatternTuple> { | ||
match (constructor, self) { | ||
(_, Pattern::CatchAll) => Some(PatternTuple { patterns: vec![] }), | ||
(Pattern::CatchAll, _) => None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering, shouldn't this panic? CatchAll is not a constructor, is it?
}), | ||
|
||
(Pattern::Enum(path1, patterns1), Pattern::Enum(path2, patterns2)) => { | ||
if path1 != path2 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be pulled into the match pattern
if path1 != path2 { | ||
return None; | ||
} | ||
match (patterns1, patterns2) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
match (patterns1, patterns2) { | |
match (cons_patterns, patterns) { |
let pats = patterns2.as_ref().unwrap(); | ||
Some(PatternTuple { | ||
patterns: pats.clone(), | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let pats = patterns2.as_ref().unwrap(); | |
Some(PatternTuple { | |
patterns: pats.clone(), | |
}) | |
Some(PatternTuple { | |
patterns: patterns2.as_ref().unwrap().clone(), | |
}) |
(or maybe even .cloned()
cold work here?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh wait, why don't you use Some(patterns)
instead of Some(_)
one line above?
(Pattern::Array(cons_patterns), Pattern::Array(patterns)) => { | ||
let max_len = cons_patterns.len(); | ||
let mut constructors = Vec::new(); | ||
if patterns.contains(&Pattern::Ellipsis) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if patterns.contains(&Pattern::Ellipsis) { | |
if let Some(ellipsis_inde) = patterns.iter().position(|p| p ==&Pattern::Ellipsis) { |
}) | ||
} | ||
(Pattern::Array(cons_patterns), Pattern::Array(patterns)) => { | ||
let max_len = cons_patterns.len(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cons_len? Also please pull it into the block where it is used.
(Pattern::Array(cons_patterns), Pattern::Array(patterns)) => { | ||
let max_len = cons_patterns.len(); | ||
let mut constructors = Vec::new(); | ||
if patterns.contains(&Pattern::Ellipsis) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we assert that the constructor does not contain an ellipsis?
let mut constructors = Vec::new(); | ||
if patterns.contains(&Pattern::Ellipsis) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let mut constructors = Vec::new(); | |
if patterns.contains(&Pattern::Ellipsis) { | |
let constructorns = if patterns.contains(&Pattern::Ellipsis) { |
@@ -381,6 +423,61 @@ impl PILAnalyzer { | |||
fn driver(&self) -> Driver { | |||
Driver(self) | |||
} | |||
|
|||
/// Check if a new pattern is useful in a set of patterns. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the new pattern here?
This PR implements non-exhaustiveness check for matchs (issue #1287). Based on the
usefulness
implementation proposed in the rustc_pattern_analysis[1] module of the Rust compiler. A match is exhaustive iff the wildcard_
pattern is not useful with respect to the patterns in the match. The values returned by usefulness are used to tell the user which values are missing ([] where the match is exhaustive).Still in draft until I finish analyzing some edge cases.
[1] https://doc.rust-lang.org/nightly/nightly-rustc/rustc_pattern_analysis/usefulness/index.html