-
Notifications
You must be signed in to change notification settings - Fork 13.4k
never patterns: Check bindings wrt never patterns #119610
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
Changes from all commits
342ea15
b31735a
560beb1
807d618
223cda4
68a13bf
5ccd29d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -65,6 +65,8 @@ enum IsRepeatExpr { | |
Yes, | ||
} | ||
|
||
struct IsNeverPattern; | ||
|
||
/// Describes whether an `AnonConst` is a type level const arg or | ||
/// some other form of anon const (i.e. inline consts or enum discriminants) | ||
#[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
|
@@ -3190,12 +3192,31 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { | |
self.resolve_pattern_top(&local.pat, PatternSource::Let); | ||
} | ||
|
||
/// build a map from pattern identifiers to binding-info's. | ||
/// this is done hygienically. This could arise for a macro | ||
/// that expands into an or-pattern where one 'x' was from the | ||
/// user and one 'x' came from the macro. | ||
fn binding_mode_map(&mut self, pat: &Pat) -> FxIndexMap<Ident, BindingInfo> { | ||
/// Build a map from pattern identifiers to binding-info's, and check the bindings are | ||
/// consistent when encountering or-patterns and never patterns. | ||
/// This is done hygienically: this could arise for a macro that expands into an or-pattern | ||
/// where one 'x' was from the user and one 'x' came from the macro. | ||
/// | ||
/// A never pattern by definition indicates an unreachable case. For example, matching on | ||
/// `Result<T, &!>` could look like: | ||
/// ```rust | ||
/// # #![feature(never_type)] | ||
/// # #![feature(never_patterns)] | ||
/// # fn bar(_x: u32) {} | ||
/// let foo: Result<u32, &!> = Ok(0); | ||
/// match foo { | ||
/// Ok(x) => bar(x), | ||
/// Err(&!), | ||
/// } | ||
/// ``` | ||
/// This extends to product types: `(x, !)` is likewise unreachable. So it doesn't make sense to | ||
/// have a binding here, and we tell the user to use `_` instead. | ||
fn compute_and_check_binding_map( | ||
&mut self, | ||
pat: &Pat, | ||
) -> Result<FxIndexMap<Ident, BindingInfo>, IsNeverPattern> { | ||
let mut binding_map = FxIndexMap::default(); | ||
let mut is_never_pat = false; | ||
|
||
pat.walk(&mut |pat| { | ||
match pat.kind { | ||
|
@@ -3207,18 +3228,27 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { | |
PatKind::Or(ref ps) => { | ||
// Check the consistency of this or-pattern and | ||
// then add all bindings to the larger map. | ||
for bm in self.check_consistent_bindings(ps) { | ||
binding_map.extend(bm); | ||
match self.compute_and_check_or_pat_binding_map(ps) { | ||
Ok(bm) => binding_map.extend(bm), | ||
Err(IsNeverPattern) => is_never_pat = true, | ||
} | ||
return false; | ||
} | ||
PatKind::Never => is_never_pat = true, | ||
_ => {} | ||
} | ||
|
||
true | ||
}); | ||
|
||
binding_map | ||
if is_never_pat { | ||
for (_, binding) in binding_map { | ||
self.report_error(binding.span, ResolutionError::BindingInNeverPattern); | ||
} | ||
Err(IsNeverPattern) | ||
} else { | ||
Ok(binding_map) | ||
} | ||
} | ||
|
||
fn is_base_res_local(&self, nid: NodeId) -> bool { | ||
|
@@ -3228,33 +3258,52 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { | |
) | ||
} | ||
|
||
/// Checks that all of the arms in an or-pattern have exactly the | ||
/// same set of bindings, with the same binding modes for each. | ||
fn check_consistent_bindings( | ||
/// Compute the binding map for an or-pattern. Checks that all of the arms in the or-pattern | ||
/// have exactly the same set of bindings, with the same binding modes for each. | ||
/// Returns the computed binding map and a boolean indicating whether the pattern is a never | ||
/// pattern. | ||
/// | ||
/// A never pattern by definition indicates an unreachable case. For example, destructuring a | ||
/// `Result<T, &!>` could look like: | ||
/// ```rust | ||
/// # #![feature(never_type)] | ||
/// # #![feature(never_patterns)] | ||
/// # fn foo() -> Result<bool, &'static !> { Ok(true) } | ||
/// let (Ok(x) | Err(&!)) = foo(); | ||
/// # let _ = x; | ||
/// ``` | ||
/// Because the `Err(&!)` branch is never reached, it does not need to have the same bindings as | ||
/// the other branches of the or-pattern. So we must ignore never pattern when checking the | ||
/// bindings of an or-pattern. | ||
/// Moreover, if all the subpatterns are never patterns (e.g. `Ok(!) | Err(!)`), then the | ||
/// pattern as a whole counts as a never pattern (since it's definitionallly unreachable). | ||
fn compute_and_check_or_pat_binding_map( | ||
&mut self, | ||
pats: &[P<Pat>], | ||
) -> Vec<FxIndexMap<Ident, BindingInfo>> { | ||
// pats is consistent. | ||
) -> Result<FxIndexMap<Ident, BindingInfo>, IsNeverPattern> { | ||
let mut missing_vars = FxIndexMap::default(); | ||
let mut inconsistent_vars = FxIndexMap::default(); | ||
|
||
// 1) Compute the binding maps of all arms. | ||
let maps = pats.iter().map(|pat| self.binding_mode_map(pat)).collect::<Vec<_>>(); | ||
// 1) Compute the binding maps of all arms; we must ignore never patterns here. | ||
let not_never_pats = pats | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we should use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What would we do with them? Just store them in a |
||
.iter() | ||
.filter_map(|pat| { | ||
let binding_map = self.compute_and_check_binding_map(pat).ok()?; | ||
Some((binding_map, pat)) | ||
}) | ||
.collect::<Vec<_>>(); | ||
|
||
// 2) Record any missing bindings or binding mode inconsistencies. | ||
for (map_outer, pat_outer) in maps.iter().zip(pats.iter()) { | ||
for (map_outer, pat_outer) in not_never_pats.iter() { | ||
// Check against all arms except for the same pattern which is always self-consistent. | ||
let inners = maps | ||
let inners = not_never_pats | ||
.iter() | ||
.zip(pats.iter()) | ||
.filter(|(_, pat)| pat.id != pat_outer.id) | ||
.flat_map(|(map, _)| map) | ||
.map(|(key, binding)| (key.name, map_outer.get(key), binding)); | ||
|
||
let inners = inners.collect::<Vec<_>>(); | ||
.flat_map(|(map, _)| map); | ||
|
||
for (name, info, &binding_inner) in inners { | ||
match info { | ||
for (key, binding_inner) in inners { | ||
let name = key.name; | ||
match map_outer.get(key) { | ||
None => { | ||
// The inner binding is missing in the outer. | ||
let binding_error = | ||
|
@@ -3295,19 +3344,32 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { | |
self.report_error(v.0, ResolutionError::VariableBoundWithDifferentMode(name, v.1)); | ||
} | ||
|
||
// 5) Finally bubble up all the binding maps. | ||
maps | ||
// 5) Bubble up the final binding map. | ||
if not_never_pats.is_empty() { | ||
// All the patterns are never patterns, so the whole or-pattern is one too. | ||
Err(IsNeverPattern) | ||
} else { | ||
let mut binding_map = FxIndexMap::default(); | ||
for (bm, _) in not_never_pats { | ||
binding_map.extend(bm); | ||
} | ||
Ok(binding_map) | ||
} | ||
} | ||
|
||
/// Check the consistency of the outermost or-patterns. | ||
fn check_consistent_bindings_top(&mut self, pat: &'ast Pat) { | ||
/// Check the consistency of bindings wrt or-patterns and never patterns. | ||
fn check_consistent_bindings(&mut self, pat: &'ast Pat) { | ||
let mut is_or_or_never = false; | ||
pat.walk(&mut |pat| match pat.kind { | ||
PatKind::Or(ref ps) => { | ||
self.check_consistent_bindings(ps); | ||
PatKind::Or(..) | PatKind::Never => { | ||
is_or_or_never = true; | ||
false | ||
} | ||
_ => true, | ||
}) | ||
}); | ||
if is_or_or_never { | ||
let _ = self.compute_and_check_binding_map(pat); | ||
} | ||
} | ||
|
||
fn resolve_arm(&mut self, arm: &'ast Arm) { | ||
|
@@ -3336,7 +3398,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { | |
visit::walk_pat(self, pat); | ||
self.resolve_pattern_inner(pat, pat_src, bindings); | ||
// This has to happen *after* we determine which pat_idents are variants: | ||
self.check_consistent_bindings_top(pat); | ||
self.check_consistent_bindings(pat); | ||
} | ||
|
||
/// Resolve bindings in a pattern. This is a helper to `resolve_pattern`. | ||
|
Uh oh!
There was an error while loading. Please reload this page.