Skip to content

Commit 70cae78

Browse files
committed
resolve: already-bound-check: account for or-patterns.
Also document `ast::Pat::walk`.
1 parent dc91e22 commit 70cae78

File tree

3 files changed

+88
-59
lines changed

3 files changed

+88
-59
lines changed

src/librustc_resolve/late.rs

Lines changed: 73 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use rustc::hir::def::Namespace::{self, *};
1919
use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX};
2020
use rustc::hir::TraitCandidate;
2121
use rustc::util::nodemap::FxHashMap;
22+
use rustc_data_structures::fx::FxIndexMap;
2223
use smallvec::{smallvec, SmallVec};
2324
use syntax::{unwrap_or, walk_list};
2425
use syntax::ast::*;
@@ -408,7 +409,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LateResolutionVisitor<'a, '_> {
408409
visit::walk_foreign_item(this, foreign_item);
409410
});
410411
}
411-
fn visit_fn(&mut self, fn_kind: FnKind<'tcx>, declaration: &'tcx FnDecl, _: Span, _: NodeId) {
412+
fn visit_fn(&mut self, fn_kind: FnKind<'tcx>, declaration: &'tcx FnDecl, _: Span, id: NodeId) {
412413
debug!("(resolving function) entering function");
413414
let rib_kind = match fn_kind {
414415
FnKind::ItemFn(..) => FnItemRibKind,
@@ -420,7 +421,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LateResolutionVisitor<'a, '_> {
420421
// Create a label rib for the function.
421422
this.with_label_rib(rib_kind, |this| {
422423
// Add each argument to the rib.
423-
this.resolve_params(&declaration.inputs);
424+
this.resolve_params(&declaration.inputs, id);
424425

425426
visit::walk_fn_ret_ty(this, &declaration.output);
426427

@@ -1108,11 +1109,11 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
11081109
}
11091110
}
11101111

1111-
fn resolve_params(&mut self, params: &[Arg]) {
1112-
let mut bindings_list = FxHashMap::default();
1113-
for param in params {
1114-
self.resolve_pattern(&param.pat, PatternSource::FnParam, &mut bindings_list);
1115-
self.visit_ty(&param.ty);
1112+
fn resolve_params(&mut self, params: &[Param], id: NodeId) {
1113+
let mut bindings = FxIndexMap::default();
1114+
for Param { pat, ty, .. } in params {
1115+
self.resolve_pattern(pat, PatternSource::FnParam, &mut smallvec![id], &mut bindings);
1116+
self.visit_ty(ty);
11161117
debug!("(resolving function / closure) recorded parameter");
11171118
}
11181119
}
@@ -1125,7 +1126,7 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
11251126
walk_list!(self, visit_expr, &local.init);
11261127

11271128
// Resolve the pattern.
1128-
self.resolve_pattern(&local.pat, PatternSource::Let, &mut FxHashMap::default());
1129+
self.resolve_pattern_top(&local.pat, PatternSource::Let);
11291130
}
11301131

11311132
// build a map from pattern identifiers to binding-info's.
@@ -1219,25 +1220,39 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
12191220

12201221
/// Arising from `source`, resolve a sequence of patterns (top level or-patterns).
12211222
fn resolve_pats(&mut self, pats: &[P<Pat>], source: PatternSource) {
1222-
let mut bindings_list = FxHashMap::default();
1223+
let mut bindings_list = FxIndexMap::default();
12231224
for pat in pats {
1224-
self.resolve_pattern(pat, source, &mut bindings_list);
1225+
self.resolve_pattern(pat, source, &mut smallvec![pat.id], &mut bindings_list);
12251226
}
12261227
// This has to happen *after* we determine which pat_idents are variants
12271228
if pats.len() > 1 {
12281229
self.check_consistent_bindings(pats);
12291230
}
12301231
}
12311232

1233+
fn resolve_pattern_top(&mut self, pat: &Pat, pat_src: PatternSource) {
1234+
self.resolve_pattern(pat, pat_src, &mut smallvec![pat.id], &mut FxIndexMap::default());
1235+
}
1236+
12321237
fn resolve_pattern(
12331238
&mut self,
12341239
pat: &Pat,
12351240
pat_src: PatternSource,
1236-
// Maps idents to the node ID for the outermost pattern that binds them.
1237-
bindings: &mut IdentMap<NodeId>,
1241+
prod_ids: &mut SmallVec<[NodeId; 1]>,
1242+
bindings: &mut FxIndexMap<Ident, NodeId>,
1243+
) {
1244+
self.resolve_pattern_inner(pat, pat_src, prod_ids, bindings);
1245+
visit::walk_pat(self, pat);
1246+
}
1247+
1248+
fn resolve_pattern_inner(
1249+
&mut self,
1250+
pat: &Pat,
1251+
pat_src: PatternSource,
1252+
prod_ids: &mut SmallVec<[NodeId; 1]>,
1253+
bindings: &mut FxIndexMap<Ident, NodeId>,
12381254
) {
12391255
// Visit all direct subpatterns of this pattern.
1240-
let outer_pat_id = pat.id;
12411256
pat.walk(&mut |pat| {
12421257
debug!("resolve_pattern pat={:?} node={:?}", pat, pat.node);
12431258
match pat.node {
@@ -1247,7 +1262,7 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
12471262
let has_sub = sub.is_some();
12481263
let res = self.try_resolve_as_non_binding(pat_src, pat, bmode, ident, has_sub)
12491264
.unwrap_or_else(|| {
1250-
self.fresh_binding(ident, pat.id, outer_pat_id, pat_src, bindings)
1265+
self.fresh_binding(ident, pat.id, pat_src, prod_ids, bindings)
12511266
});
12521267
self.r.record_partial_res(pat.id, PartialRes::new(res));
12531268
}
@@ -1260,59 +1275,70 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
12601275
PatKind::Struct(ref path, ..) => {
12611276
self.smart_resolve_path(pat.id, None, path, PathSource::Struct);
12621277
}
1278+
PatKind::Or(ref ps) => {
1279+
let len_before = bindings.len();
1280+
for p in ps {
1281+
// We need to change `prod_ids.last()` at this point so that overlapping
1282+
// bindings across the summands in the or-pattern do not result in an error.
1283+
// The idea is that in `V1(a) | V2(a)`, the `a` in `V1` will be inserted
1284+
// with a different id than the one in `V2`. As a result, `V1(a) | V2(a)`
1285+
// compiles as it should. We will later check or-patterns for consistency.
1286+
prod_ids.push(p.id);
1287+
self.resolve_pattern_inner(p, pat_src, prod_ids, bindings);
1288+
prod_ids.pop();
1289+
}
1290+
1291+
// We've rejected overlap in each product in the sum.
1292+
// Now we must account for the possibility that the or-pattern is a factor
1293+
// in a product. A basic case to reject here is `(V1(a) | V2(a), a)`.
1294+
let last_id = *prod_ids.last().unwrap();
1295+
bindings.values_mut().skip(len_before).for_each(|val| *val = last_id);
1296+
1297+
// Prevent visiting `ps` as we've already done so above.
1298+
return false;
1299+
}
12631300
_ => {}
12641301
}
12651302
true
12661303
});
1267-
1268-
visit::walk_pat(self, pat);
12691304
}
12701305

12711306
fn fresh_binding(
12721307
&mut self,
12731308
ident: Ident,
12741309
pat_id: NodeId,
1275-
outer_pat_id: NodeId,
12761310
pat_src: PatternSource,
1277-
bindings: &mut IdentMap<NodeId>,
1311+
prod_ids: &[NodeId],
1312+
bindings: &mut FxIndexMap<Ident, NodeId>,
12781313
) -> Res {
12791314
// Add the binding to the local ribs, if it doesn't already exist in the bindings map.
12801315
// (We must not add it if it's in the bindings map because that breaks the assumptions
12811316
// later passes make about or-patterns.)
12821317
let ident = ident.modern_and_legacy();
1283-
let mut res = Res::Local(pat_id);
1318+
let res = Res::Local(pat_id);
12841319
match bindings.get(&ident).cloned() {
1285-
Some(id) if id == outer_pat_id => {
1286-
// `Variant(a, a)`, error
1287-
self.r.report_error(
1288-
ident.span,
1289-
ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(&ident.as_str()),
1290-
);
1291-
}
1292-
Some(..) if pat_src == PatternSource::FnParam => {
1293-
// `fn f(a: u8, a: u8)`, error
1294-
self.r.report_error(
1295-
ident.span,
1296-
ResolutionError::IdentifierBoundMoreThanOnceInParameterList(&ident.as_str()),
1297-
);
1320+
Some(id) if prod_ids.contains(&id) => {
1321+
// We have some overlap in a product pattern, e.g. `(a, a)` which is not allowed.
1322+
use ResolutionError::*;
1323+
let error = match pat_src {
1324+
// `fn f(a: u8, a: u8)`:
1325+
PatternSource::FnParam => IdentifierBoundMoreThanOnceInParameterList,
1326+
// `Variant(a, a)`:
1327+
_ => IdentifierBoundMoreThanOnceInSamePattern,
1328+
};
1329+
self.r.report_error(ident.span, error(&ident.as_str()));
12981330
}
1299-
Some(..) if pat_src == PatternSource::Match ||
1300-
pat_src == PatternSource::Let => {
1331+
Some(..) => {
13011332
// `Variant1(a) | Variant2(a)`, ok
13021333
// Reuse definition from the first `a`.
1303-
res = self.innermost_rib_bindings(ValueNS)[&ident];
1334+
return self.innermost_rib_bindings(ValueNS)[&ident];
13041335
}
1305-
Some(..) => {
1306-
span_bug!(ident.span, "two bindings with the same name from \
1307-
unexpected pattern source {:?}", pat_src);
1308-
}
1309-
None => {
1310-
// A completely fresh binding, add to the lists if it's valid.
1311-
if ident.name != kw::Invalid {
1312-
bindings.insert(ident, outer_pat_id);
1313-
self.innermost_rib_bindings(ValueNS).insert(ident, res);
1314-
}
1336+
// A completely fresh binding, add to the lists if it's valid.
1337+
None if ident.name != kw::Invalid => {
1338+
bindings.insert(ident, *prod_ids.last().unwrap());
1339+
self.innermost_rib_bindings(ValueNS).insert(ident, res);
13151340
}
1341+
None => {}
13161342
}
13171343

13181344
res
@@ -1810,7 +1836,7 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
18101836
ExprKind::ForLoop(ref pat, ref iter_expr, ref block, label) => {
18111837
self.visit_expr(iter_expr);
18121838
self.with_rib(ValueNS, NormalRibKind, |this| {
1813-
this.resolve_pattern(pat, PatternSource::For, &mut FxHashMap::default());
1839+
this.resolve_pattern_top(pat, PatternSource::For);
18141840
this.resolve_labeled_block(label, expr.id, block);
18151841
});
18161842
}
@@ -1847,7 +1873,7 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
18471873
ExprKind::Closure(_, IsAsync::Async { .. }, _, ref fn_decl, ref body, _span) => {
18481874
self.with_rib(ValueNS, NormalRibKind, |this| {
18491875
// Resolve arguments:
1850-
this.resolve_params(&fn_decl.inputs);
1876+
this.resolve_params(&fn_decl.inputs, expr.id);
18511877
// No need to resolve return type --
18521878
// the outer closure return type is `FunctionRetTy::Default`.
18531879

src/libsyntax/ast.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -561,29 +561,31 @@ impl Pat {
561561
}))
562562
}
563563

564-
pub fn walk<F>(&self, it: &mut F) -> bool
565-
where
566-
F: FnMut(&Pat) -> bool,
567-
{
564+
/// Walk top-down and call `it` in each place where a pattern occurs
565+
/// starting with the root pattern `walk` is called on. If `it` returns
566+
/// false then we will decend no further but siblings will be processed.
567+
pub fn walk(&self, it: &mut impl FnMut(&Pat) -> bool) {
568568
if !it(self) {
569-
return false;
569+
return;
570570
}
571571

572572
match &self.node {
573573
PatKind::Ident(_, _, Some(p)) => p.walk(it),
574-
PatKind::Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk(it)),
574+
PatKind::Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk(it)),
575575
PatKind::TupleStruct(_, s)
576576
| PatKind::Tuple(s)
577577
| PatKind::Slice(s)
578-
| PatKind::Or(s) => s.iter().all(|p| p.walk(it)),
579-
PatKind::Box(s) | PatKind::Ref(s, _) | PatKind::Paren(s) => s.walk(it),
578+
| PatKind::Or(s) => s.iter().for_each(|p| p.walk(it)),
579+
PatKind::Box(s)
580+
| PatKind::Ref(s, _)
581+
| PatKind::Paren(s) => s.walk(it),
580582
PatKind::Wild
581583
| PatKind::Rest
582584
| PatKind::Lit(_)
583585
| PatKind::Range(..)
584586
| PatKind::Ident(..)
585587
| PatKind::Path(..)
586-
| PatKind::Mac(_) => true,
588+
| PatKind::Mac(_) => {},
587589
}
588590
}
589591

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error[E0416]: identifier `a` is bound more than once in the same pattern
1+
error[E0415]: identifier `a` is bound more than once in this parameter list
22
--> $DIR/shadowing-in-the-same-pattern.rs:3:10
33
|
44
LL | fn f((a, a): (isize, isize)) {}
5-
| ^ used in a pattern more than once
5+
| ^ used as parameter more than once
66

77
error[E0416]: identifier `a` is bound more than once in the same pattern
88
--> $DIR/shadowing-in-the-same-pattern.rs:6:13
@@ -12,4 +12,5 @@ LL | let (a, a) = (1, 1);
1212

1313
error: aborting due to 2 previous errors
1414

15-
For more information about this error, try `rustc --explain E0416`.
15+
Some errors have detailed explanations: E0415, E0416.
16+
For more information about an error, try `rustc --explain E0415`.

0 commit comments

Comments
 (0)