Skip to content

Commit 166a558

Browse files
committed
resolve: revamp already-bound check -- fix some bugs.
1 parent 70cae78 commit 166a558

File tree

1 file changed

+79
-59
lines changed

1 file changed

+79
-59
lines changed

src/librustc_resolve/late.rs

Lines changed: 79 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ use rustc::hir::def::{self, PartialRes, DefKind, CtorKind, PerNS};
1818
use rustc::hir::def::Namespace::{self, *};
1919
use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX};
2020
use rustc::hir::TraitCandidate;
21-
use rustc::util::nodemap::FxHashMap;
22-
use rustc_data_structures::fx::FxIndexMap;
21+
use rustc::util::nodemap::{FxHashMap, FxHashSet};
2322
use smallvec::{smallvec, SmallVec};
2423
use syntax::{unwrap_or, walk_list};
2524
use syntax::ast::*;
@@ -409,7 +408,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LateResolutionVisitor<'a, '_> {
409408
visit::walk_foreign_item(this, foreign_item);
410409
});
411410
}
412-
fn visit_fn(&mut self, fn_kind: FnKind<'tcx>, declaration: &'tcx FnDecl, _: Span, id: NodeId) {
411+
fn visit_fn(&mut self, fn_kind: FnKind<'tcx>, declaration: &'tcx FnDecl, _: Span, _: NodeId) {
413412
debug!("(resolving function) entering function");
414413
let rib_kind = match fn_kind {
415414
FnKind::ItemFn(..) => FnItemRibKind,
@@ -421,7 +420,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LateResolutionVisitor<'a, '_> {
421420
// Create a label rib for the function.
422421
this.with_label_rib(rib_kind, |this| {
423422
// Add each argument to the rib.
424-
this.resolve_params(&declaration.inputs, id);
423+
this.resolve_params(&declaration.inputs);
425424

426425
visit::walk_fn_ret_ty(this, &declaration.output);
427426

@@ -1109,10 +1108,10 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
11091108
}
11101109
}
11111110

1112-
fn resolve_params(&mut self, params: &[Param], id: NodeId) {
1113-
let mut bindings = FxIndexMap::default();
1111+
fn resolve_params(&mut self, params: &[Param]) {
1112+
let mut bindings = smallvec![(false, <_>::default())];
11141113
for Param { pat, ty, .. } in params {
1115-
self.resolve_pattern(pat, PatternSource::FnParam, &mut smallvec![id], &mut bindings);
1114+
self.resolve_pattern(pat, PatternSource::FnParam, &mut bindings);
11161115
self.visit_ty(ty);
11171116
debug!("(resolving function / closure) recorded parameter");
11181117
}
@@ -1220,9 +1219,12 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
12201219

12211220
/// Arising from `source`, resolve a sequence of patterns (top level or-patterns).
12221221
fn resolve_pats(&mut self, pats: &[P<Pat>], source: PatternSource) {
1223-
let mut bindings_list = FxIndexMap::default();
1222+
let mut bindings = smallvec![(true, <_>::default())];
12241223
for pat in pats {
1225-
self.resolve_pattern(pat, source, &mut smallvec![pat.id], &mut bindings_list);
1224+
bindings.push((false, <_>::default()));
1225+
self.resolve_pattern(pat, source, &mut bindings);
1226+
let collected = bindings.pop().unwrap().1;
1227+
bindings.last_mut().unwrap().1.extend(collected);
12261228
}
12271229
// This has to happen *after* we determine which pat_idents are variants
12281230
if pats.len() > 1 {
@@ -1231,26 +1233,24 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
12311233
}
12321234

12331235
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());
1236+
self.resolve_pattern(pat, pat_src, &mut smallvec![(false, <_>::default())]);
12351237
}
12361238

12371239
fn resolve_pattern(
12381240
&mut self,
12391241
pat: &Pat,
12401242
pat_src: PatternSource,
1241-
prod_ids: &mut SmallVec<[NodeId; 1]>,
1242-
bindings: &mut FxIndexMap<Ident, NodeId>,
1243+
bindings: &mut SmallVec<[(bool, FxHashSet<Ident>); 1]>,
12431244
) {
1244-
self.resolve_pattern_inner(pat, pat_src, prod_ids, bindings);
1245+
self.resolve_pattern_inner(pat, pat_src, bindings);
12451246
visit::walk_pat(self, pat);
12461247
}
12471248

12481249
fn resolve_pattern_inner(
12491250
&mut self,
12501251
pat: &Pat,
12511252
pat_src: PatternSource,
1252-
prod_ids: &mut SmallVec<[NodeId; 1]>,
1253-
bindings: &mut FxIndexMap<Ident, NodeId>,
1253+
bindings: &mut SmallVec<[(bool, FxHashSet<Ident>); 1]>,
12541254
) {
12551255
// Visit all direct subpatterns of this pattern.
12561256
pat.walk(&mut |pat| {
@@ -1261,9 +1261,7 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
12611261
// then fall back to a fresh binding.
12621262
let has_sub = sub.is_some();
12631263
let res = self.try_resolve_as_non_binding(pat_src, pat, bmode, ident, has_sub)
1264-
.unwrap_or_else(|| {
1265-
self.fresh_binding(ident, pat.id, pat_src, prod_ids, bindings)
1266-
});
1264+
.unwrap_or_else(|| self.fresh_binding(ident, pat.id, pat_src, bindings));
12671265
self.r.record_partial_res(pat.id, PartialRes::new(res));
12681266
}
12691267
PatKind::TupleStruct(ref path, ..) => {
@@ -1276,23 +1274,26 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
12761274
self.smart_resolve_path(pat.id, None, path, PathSource::Struct);
12771275
}
12781276
PatKind::Or(ref ps) => {
1279-
let len_before = bindings.len();
1277+
// Add a new set of bindings to the stack. `true` here records that when a
1278+
// binding already exists in this set, it should not result in an error because
1279+
// `V1(a) | V2(a)` must be allowed and are checked for consistency later.
1280+
bindings.push((true, <_>::default()));
12801281
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();
1282+
// Now we need to switch back to a product context so that each
1283+
// part of the or-pattern internally rejects already bound names.
1284+
// For example, `V1(a) | V2(a, a)` and `V1(a, a) | V2(a)` are bad.
1285+
bindings.push((false, <_>::default()));
1286+
self.resolve_pattern_inner(p, pat_src, bindings);
1287+
// Move up the non-overlapping bindings to the or-pattern.
1288+
// Existing bindings just get "merged".
1289+
let collected = bindings.pop().unwrap().1;
1290+
bindings.last_mut().unwrap().1.extend(collected);
12891291
}
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);
1292+
// This or-pattern itself can itself be part of a product,
1293+
// e.g. `(V1(a) | V2(a), a)` or `(a, V1(a) | V2(a))`.
1294+
// Both cases bind `a` again in a product pattern and must be rejected.
1295+
let collected = bindings.pop().unwrap().1;
1296+
bindings.last_mut().unwrap().1.extend(collected);
12961297

12971298
// Prevent visiting `ps` as we've already done so above.
12981299
return false;
@@ -1308,40 +1309,59 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
13081309
ident: Ident,
13091310
pat_id: NodeId,
13101311
pat_src: PatternSource,
1311-
prod_ids: &[NodeId],
1312-
bindings: &mut FxIndexMap<Ident, NodeId>,
1312+
bindings: &mut SmallVec<[(bool, FxHashSet<Ident>); 1]>,
13131313
) -> Res {
13141314
// Add the binding to the local ribs, if it doesn't already exist in the bindings map.
13151315
// (We must not add it if it's in the bindings map because that breaks the assumptions
13161316
// later passes make about or-patterns.)
13171317
let ident = ident.modern_and_legacy();
1318-
let res = Res::Local(pat_id);
1319-
match bindings.get(&ident).cloned() {
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()));
1330-
}
1331-
Some(..) => {
1332-
// `Variant1(a) | Variant2(a)`, ok
1333-
// Reuse definition from the first `a`.
1334-
return self.innermost_rib_bindings(ValueNS)[&ident];
1318+
1319+
// Walk outwards the stack of products / or-patterns and
1320+
// find out if the identifier has been bound in any of these.
1321+
let mut already_bound_and = false;
1322+
let mut already_bound_or = false;
1323+
for (is_sum, set) in bindings.iter_mut().rev() {
1324+
match (is_sum, set.get(&ident).cloned()) {
1325+
// Already bound in a product pattern, e.g. `(a, a)` which is not allowed.
1326+
(false, Some(..)) => already_bound_and = true,
1327+
// Already bound in an or-pattern, e.g. `V1(a) | V2(a)`.
1328+
// This is *required* for consistency which is checked later.
1329+
(true, Some(..)) => already_bound_or = true,
1330+
// Not already bound here.
1331+
_ => {}
13351332
}
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());
1333+
}
1334+
1335+
if already_bound_and {
1336+
// Overlap in a product pattern somewhere; report an error.
1337+
use ResolutionError::*;
1338+
let error = match pat_src {
1339+
// `fn f(a: u8, a: u8)`:
1340+
PatternSource::FnParam => IdentifierBoundMoreThanOnceInParameterList,
1341+
// `Variant(a, a)`:
1342+
_ => IdentifierBoundMoreThanOnceInSamePattern,
1343+
};
1344+
self.r.report_error(ident.span, error(&ident.as_str()));
1345+
}
1346+
1347+
// Record as bound if it's valid:
1348+
let ident_valid = ident.name != kw::Invalid;
1349+
if ident_valid {
1350+
bindings.last_mut().unwrap().1.insert(ident);
1351+
}
1352+
1353+
if already_bound_or {
1354+
// `Variant1(a) | Variant2(a)`, ok
1355+
// Reuse definition from the first `a`.
1356+
self.innermost_rib_bindings(ValueNS)[&ident]
1357+
} else {
1358+
let res = Res::Local(pat_id);
1359+
if ident_valid {
1360+
// A completely fresh binding add to the set if it's valid.
13391361
self.innermost_rib_bindings(ValueNS).insert(ident, res);
13401362
}
1341-
None => {}
1363+
res
13421364
}
1343-
1344-
res
13451365
}
13461366

13471367
fn innermost_rib_bindings(&mut self, ns: Namespace) -> &mut IdentMap<Res> {
@@ -1873,7 +1893,7 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
18731893
ExprKind::Closure(_, IsAsync::Async { .. }, _, ref fn_decl, ref body, _span) => {
18741894
self.with_rib(ValueNS, NormalRibKind, |this| {
18751895
// Resolve arguments:
1876-
this.resolve_params(&fn_decl.inputs, expr.id);
1896+
this.resolve_params(&fn_decl.inputs);
18771897
// No need to resolve return type --
18781898
// the outer closure return type is `FunctionRetTy::Default`.
18791899

0 commit comments

Comments
 (0)