Skip to content

Commit 896a1c7

Browse files
committed
resolve: account for general or-patterns in consistency checking.
1 parent 498ec59 commit 896a1c7

File tree

3 files changed

+93
-49
lines changed

3 files changed

+93
-49
lines changed

src/librustc_resolve/late.rs

Lines changed: 82 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,6 +1112,7 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
11121112
let mut bindings = smallvec![(false, <_>::default())];
11131113
for Param { pat, ty, .. } in params {
11141114
self.resolve_pattern(pat, PatternSource::FnParam, &mut bindings);
1115+
self.check_consistent_bindings_top(pat);
11151116
self.visit_ty(ty);
11161117
debug!("(resolving function / closure) recorded parameter");
11171118
}
@@ -1128,69 +1129,90 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
11281129
self.resolve_pattern_top(&local.pat, PatternSource::Let);
11291130
}
11301131

1131-
// build a map from pattern identifiers to binding-info's.
1132-
// this is done hygienically. This could arise for a macro
1133-
// that expands into an or-pattern where one 'x' was from the
1134-
// user and one 'x' came from the macro.
1132+
/// build a map from pattern identifiers to binding-info's.
1133+
/// this is done hygienically. This could arise for a macro
1134+
/// that expands into an or-pattern where one 'x' was from the
1135+
/// user and one 'x' came from the macro.
11351136
fn binding_mode_map(&mut self, pat: &Pat) -> BindingMap {
11361137
let mut binding_map = FxHashMap::default();
11371138

11381139
pat.walk(&mut |pat| {
1139-
if let PatKind::Ident(binding_mode, ident, ref sub_pat) = pat.node {
1140-
if sub_pat.is_some() || match self.r.partial_res_map.get(&pat.id)
1141-
.map(|res| res.base_res()) {
1142-
Some(Res::Local(..)) => true,
1143-
_ => false,
1144-
} {
1145-
let binding_info = BindingInfo { span: ident.span, binding_mode: binding_mode };
1146-
binding_map.insert(ident, binding_info);
1140+
match pat.node {
1141+
PatKind::Ident(binding_mode, ident, ref sub_pat)
1142+
if sub_pat.is_some() || self.is_base_res_local(pat.id) =>
1143+
{
1144+
binding_map.insert(ident, BindingInfo { span: ident.span, binding_mode });
1145+
}
1146+
PatKind::Or(ref ps) => {
1147+
// Check the consistency of this or-pattern and
1148+
// then add all bindings to the larger map.
1149+
for bm in self.check_consistent_bindings(ps) {
1150+
binding_map.extend(bm);
1151+
}
1152+
return false;
11471153
}
1154+
_ => {}
11481155
}
1156+
11491157
true
11501158
});
11511159

11521160
binding_map
11531161
}
11541162

1155-
// Checks that all of the arms in an or-pattern have exactly the
1156-
// same set of bindings, with the same binding modes for each.
1157-
fn check_consistent_bindings(&mut self, pats: &[P<Pat>]) {
1163+
fn is_base_res_local(&self, nid: NodeId) -> bool {
1164+
match self.r.partial_res_map.get(&nid).map(|res| res.base_res()) {
1165+
Some(Res::Local(..)) => true,
1166+
_ => false,
1167+
}
1168+
}
1169+
1170+
/// Checks that all of the arms in an or-pattern have exactly the
1171+
/// same set of bindings, with the same binding modes for each.
1172+
fn check_consistent_bindings(&mut self, pats: &[P<Pat>]) -> Vec<BindingMap> {
11581173
let mut missing_vars = FxHashMap::default();
11591174
let mut inconsistent_vars = FxHashMap::default();
11601175

1161-
for pat_outer in pats.iter() {
1162-
let map_outer = self.binding_mode_map(&pat_outer);
1163-
1164-
for pat_inner in pats.iter().filter(|pat| pat.id != pat_outer.id) {
1165-
let map_inner = self.binding_mode_map(&pat_inner);
1166-
1167-
for (&key_inner, &binding_inner) in map_inner.iter() {
1168-
match map_outer.get(&key_inner) {
1169-
None => { // missing binding
1170-
let binding_error = missing_vars
1171-
.entry(key_inner.name)
1172-
.or_insert(BindingError {
1173-
name: key_inner.name,
1174-
origin: BTreeSet::new(),
1175-
target: BTreeSet::new(),
1176-
could_be_path:
1177-
key_inner.name.as_str().starts_with(char::is_uppercase)
1178-
});
1179-
binding_error.origin.insert(binding_inner.span);
1180-
binding_error.target.insert(pat_outer.span);
1181-
}
1182-
Some(binding_outer) => { // check consistent binding
1183-
if binding_outer.binding_mode != binding_inner.binding_mode {
1184-
inconsistent_vars
1185-
.entry(key_inner.name)
1186-
.or_insert((binding_inner.span, binding_outer.span));
1187-
}
1176+
// 1) Compute the binding maps of all arms.
1177+
let maps = pats.iter()
1178+
.map(|pat| self.binding_mode_map(pat))
1179+
.collect::<Vec<_>>();
1180+
1181+
// 2) Record any missing bindings or binding mode inconsistencies.
1182+
for (map_outer, pat_outer) in pats.iter().enumerate().map(|(idx, pat)| (&maps[idx], pat)) {
1183+
// Check against all arms except for the same pattern which is always self-consistent.
1184+
let inners = pats.iter().enumerate()
1185+
.filter(|(_, pat)| pat.id != pat_outer.id)
1186+
.flat_map(|(idx, _)| maps[idx].iter())
1187+
.map(|(key, binding)| (key.name, map_outer.get(&key), binding));
1188+
1189+
for (name, info, &binding_inner) in inners {
1190+
match info {
1191+
None => { // The inner binding is missing in the outer.
1192+
let binding_error = missing_vars
1193+
.entry(name)
1194+
.or_insert_with(|| BindingError {
1195+
name,
1196+
origin: BTreeSet::new(),
1197+
target: BTreeSet::new(),
1198+
could_be_path: name.as_str().starts_with(char::is_uppercase),
1199+
});
1200+
binding_error.origin.insert(binding_inner.span);
1201+
binding_error.target.insert(pat_outer.span);
1202+
}
1203+
Some(binding_outer) => {
1204+
if binding_outer.binding_mode != binding_inner.binding_mode {
1205+
// The binding modes in the outer and inner bindings differ.
1206+
inconsistent_vars
1207+
.entry(name)
1208+
.or_insert((binding_inner.span, binding_outer.span));
11881209
}
11891210
}
11901211
}
11911212
}
11921213
}
11931214

1215+
// 3) Report all missing variables we found.
11941216
let mut missing_vars = missing_vars.iter_mut().collect::<Vec<_>>();
11951217
missing_vars.sort();
11961218
for (name, mut v) in missing_vars {
@@ -1202,11 +1224,26 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
12021224
ResolutionError::VariableNotBoundInPattern(v));
12031225
}
12041226

1227+
// 4) Report all inconsistencies in binding modes we found.
12051228
let mut inconsistent_vars = inconsistent_vars.iter().collect::<Vec<_>>();
12061229
inconsistent_vars.sort();
12071230
for (name, v) in inconsistent_vars {
12081231
self.r.report_error(v.0, ResolutionError::VariableBoundWithDifferentMode(*name, v.1));
12091232
}
1233+
1234+
// 5) Finally bubble up all the binding maps.
1235+
maps
1236+
}
1237+
1238+
/// Check the consistency of the outermost or-patterns.
1239+
fn check_consistent_bindings_top(&mut self, pat: &Pat) {
1240+
pat.walk(&mut |pat| match pat.node {
1241+
PatKind::Or(ref ps) => {
1242+
self.check_consistent_bindings(ps);
1243+
false
1244+
},
1245+
_ => true,
1246+
})
12101247
}
12111248

12121249
fn resolve_arm(&mut self, arm: &Arm) {
@@ -1227,13 +1264,13 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
12271264
bindings.last_mut().unwrap().1.extend(collected);
12281265
}
12291266
// This has to happen *after* we determine which pat_idents are variants
1230-
if pats.len() > 1 {
1231-
self.check_consistent_bindings(pats);
1232-
}
1267+
self.check_consistent_bindings(pats);
12331268
}
12341269

12351270
fn resolve_pattern_top(&mut self, pat: &Pat, pat_src: PatternSource) {
12361271
self.resolve_pattern(pat, pat_src, &mut smallvec![(false, <_>::default())]);
1272+
// This has to happen *after* we determine which pat_idents are variants:
1273+
self.check_consistent_bindings_top(pat);
12371274
}
12381275

12391276
fn resolve_pattern(

src/test/ui/or-patterns/already-bound-name.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ fn main() {
3838
let B(_) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1));
3939
//~^ ERROR identifier `a` is bound more than once in the same pattern
4040
//~| ERROR identifier `a` is bound more than once in the same pattern
41+
//~| ERROR variable `a` is not bound in all patterns
4142

4243
let B(A(a, _) | B(a)) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1));
4344
//~^ ERROR identifier `a` is bound more than once in the same pattern

src/test/ui/or-patterns/already-bound-name.stderr

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,20 @@ error[E0416]: identifier `a` is bound more than once in the same pattern
6464
LL | let B(_) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1));
6565
| ^ used in a pattern more than once
6666

67+
error[E0408]: variable `a` is not bound in all patterns
68+
--> $DIR/already-bound-name.rs:38:9
69+
|
70+
LL | let B(_) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1));
71+
| ^^^^ pattern doesn't bind `a` - variable not in all patterns
72+
6773
error[E0416]: identifier `a` is bound more than once in the same pattern
68-
--> $DIR/already-bound-name.rs:42:49
74+
--> $DIR/already-bound-name.rs:43:49
6975
|
7076
LL | let B(A(a, _) | B(a)) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1));
7177
| ^ used in a pattern more than once
7278

7379
error[E0416]: identifier `a` is bound more than once in the same pattern
74-
--> $DIR/already-bound-name.rs:42:59
80+
--> $DIR/already-bound-name.rs:43:59
7581
|
7682
LL | let B(A(a, _) | B(a)) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1));
7783
| ^ used in a pattern more than once
@@ -85,7 +91,7 @@ LL | let B(A(a, _) | B(a)) | A(a, A(a, _) | B(a)) = B(B(1));
8591
= note: expected type `{integer}`
8692
found type `E<{integer}>`
8793

88-
error: aborting due to 14 previous errors
94+
error: aborting due to 15 previous errors
8995

90-
Some errors have detailed explanations: E0308, E0416.
96+
Some errors have detailed explanations: E0308, E0408, E0416.
9197
For more information about an error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)