Auto merge of #119610 - Nadrieril:never_pattern_bindings, r=compiler-errors
never patterns: Check bindings wrt never patterns Never patterns: - Shouldn't contain bindings since they never match anything; - Don't count when checking that or-patterns have consistent bindings. r? `@compiler-errors`
This commit is contained in:
@@ -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)]
|
||||
@@ -3246,12 +3248,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 {
|
||||
@@ -3263,18 +3284,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 {
|
||||
@@ -3284,33 +3314,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
|
||||
.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));
|
||||
.flat_map(|(map, _)| map);
|
||||
|
||||
let inners = inners.collect::<Vec<_>>();
|
||||
|
||||
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 =
|
||||
@@ -3351,19 +3400,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) {
|
||||
@@ -3392,7 +3454,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`.
|
||||
|
||||
Reference in New Issue
Block a user