Skip to content

Commit d85ff16

Browse files
committed
Treat builtin bounds like all other kinds of trait matches. Introduce a simple hashset in the fulfillment context to catch cases where we register the exact same obligation twice. This helps prevent duplicate error reports but also handles the recursive obligations created by builtin bounds.
1 parent 3a325c6 commit d85ff16

File tree

2 files changed

+29
-25
lines changed

2 files changed

+29
-25
lines changed

src/librustc/middle/traits/fulfill.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
use middle::mem_categorization::Typer;
1212
use middle::ty;
1313
use middle::typeck::infer::InferCtxt;
14+
use std::collections::HashSet;
15+
use std::rc::Rc;
1416
use util::ppaux::Repr;
1517

1618
use super::CodeAmbiguity;
@@ -30,6 +32,13 @@ use super::select::SelectionContext;
3032
/// method `select_all_or_error` can be used to report any remaining
3133
/// ambiguous cases as errors.
3234
pub struct FulfillmentContext<'tcx> {
35+
// a simple cache that aims to cache *exact duplicate obligations*
36+
// and avoid adding them twice. This serves a different purpose
37+
// than the `SelectionCache`: it avoids duplicate errors and
38+
// permits recursive obligations, which are often generated from
39+
// traits like `Send` et al.
40+
duplicate_set: HashSet<Rc<ty::TraitRef<'tcx>>>,
41+
3342
// A list of all obligations that have been registered with this
3443
// fulfillment context.
3544
trait_obligations: Vec<Obligation<'tcx>>,
@@ -43,6 +52,7 @@ pub struct FulfillmentContext<'tcx> {
4352
impl<'tcx> FulfillmentContext<'tcx> {
4453
pub fn new() -> FulfillmentContext<'tcx> {
4554
FulfillmentContext {
55+
duplicate_set: HashSet::new(),
4656
trait_obligations: Vec::new(),
4757
attempted_mark: 0,
4858
}
@@ -52,9 +62,13 @@ impl<'tcx> FulfillmentContext<'tcx> {
5262
tcx: &ty::ctxt<'tcx>,
5363
obligation: Obligation<'tcx>)
5464
{
55-
debug!("register_obligation({})", obligation.repr(tcx));
56-
assert!(!obligation.trait_ref.has_escaping_regions());
57-
self.trait_obligations.push(obligation);
65+
if self.duplicate_set.insert(obligation.trait_ref.clone()) {
66+
debug!("register_obligation({})", obligation.repr(tcx));
67+
assert!(!obligation.trait_ref.has_escaping_regions());
68+
self.trait_obligations.push(obligation);
69+
} else {
70+
debug!("register_obligation({}) -- already seen, skip", obligation.repr(tcx));
71+
}
5872
}
5973

6074
pub fn select_all_or_error<'a>(&mut self,

src/librustc/middle/traits/select.rs

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ pub enum MethodMatchedData {
113113
/// candidate is one that might match or might not, depending on how
114114
/// type variables wind up being resolved. This only occurs during inference.
115115
///
116-
/// For selection to suceed, there must be exactly one non-ambiguous
116+
/// For selection to succeed, there must be exactly one non-ambiguous
117117
/// candidate. Usually, it is not possible to have more than one
118118
/// definitive candidate, due to the coherence rules. However, there is
119119
/// one case where it could occur: if there is a blanket impl for a
@@ -1149,24 +1149,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
11491149
candidates: &mut CandidateSet<'tcx>)
11501150
-> Result<(),SelectionError<'tcx>>
11511151
{
1152-
// FIXME -- To be more like a normal impl, we should just
1153-
// ignore the nested cases here, and instead generate nested
1154-
// obligations in `confirm_candidate`. However, this doesn't
1155-
// work because we require handling the recursive cases to
1156-
// avoid infinite cycles (that is, with recursive types,
1157-
// sometimes `Foo : Copy` only holds if `Foo : Copy`).
1158-
11591152
match self.builtin_bound(bound, stack.obligation.self_ty()) {
1160-
Ok(If(nested)) => {
1161-
debug!("builtin_bound: bound={} nested={}",
1162-
bound.repr(self.tcx()),
1163-
nested.repr(self.tcx()));
1164-
let data = self.vtable_builtin_data(stack.obligation, bound, nested);
1165-
match self.winnow_selection(Some(stack), VtableBuiltin(data)) {
1166-
EvaluatedToOk => { Ok(candidates.vec.push(BuiltinCandidate(bound))) }
1167-
EvaluatedToAmbig => { Ok(candidates.ambiguous = true) }
1168-
EvaluatedToErr => { Err(Unimplemented) }
1169-
}
1153+
Ok(If(_)) => {
1154+
debug!("builtin_bound: bound={}",
1155+
bound.repr(self.tcx()));
1156+
candidates.vec.push(BuiltinCandidate(bound));
1157+
Ok(())
11701158
}
11711159
Ok(ParameterBuiltin) => { Ok(()) }
11721160
Ok(AmbiguousBuiltin) => { Ok(candidates.ambiguous = true) }
@@ -1539,8 +1527,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
15391527
candidate.repr(self.tcx()));
15401528

15411529
match candidate {
1542-
// FIXME -- see assemble_builtin_bound_candidates()
1543-
BuiltinCandidate(_) |
1530+
BuiltinCandidate(builtin_bound) => {
1531+
Ok(VtableBuiltin(
1532+
try!(self.confirm_builtin_candidate(obligation, builtin_bound))))
1533+
}
1534+
15441535
ErrorCandidate => {
15451536
Ok(VtableBuiltin(VtableBuiltinData { nested: VecPerParamSpace::empty() }))
15461537
}
@@ -1590,8 +1581,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
15901581

15911582
match try!(self.builtin_bound(bound, obligation.self_ty())) {
15921583
If(nested) => Ok(self.vtable_builtin_data(obligation, bound, nested)),
1593-
AmbiguousBuiltin |
1594-
ParameterBuiltin => {
1584+
AmbiguousBuiltin | ParameterBuiltin => {
15951585
self.tcx().sess.span_bug(
15961586
obligation.cause.span,
15971587
format!("builtin bound for {} was ambig",

0 commit comments

Comments
 (0)