Skip to content

Commit 3b91a39

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 dd5ce5a commit 3b91a39

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;
@@ -32,6 +34,13 @@ use super::select::SelectionContext;
3234
* ambiguous cases as errors.
3335
*/
3436
pub struct FulfillmentContext<'tcx> {
37+
// a simple cache that aims to cache *exact duplicate obligations*
38+
// and avoid adding them twice. This serves a different purpose
39+
// than the `SelectionCache`: it avoids duplicate errors and
40+
// permits recursive obligations, which are often generated from
41+
// traits like `Send` et al.
42+
duplicate_set: HashSet<Rc<ty::TraitRef<'tcx>>>,
43+
3544
// A list of all obligations that have been registered with this
3645
// fulfillment context.
3746
trait_obligations: Vec<Obligation<'tcx>>,
@@ -45,6 +54,7 @@ pub struct FulfillmentContext<'tcx> {
4554
impl<'tcx> FulfillmentContext<'tcx> {
4655
pub fn new() -> FulfillmentContext<'tcx> {
4756
FulfillmentContext {
57+
duplicate_set: HashSet::new(),
4858
trait_obligations: Vec::new(),
4959
attempted_mark: 0,
5060
}
@@ -54,9 +64,13 @@ impl<'tcx> FulfillmentContext<'tcx> {
5464
tcx: &ty::ctxt<'tcx>,
5565
obligation: Obligation<'tcx>)
5666
{
57-
debug!("register_obligation({})", obligation.repr(tcx));
58-
assert!(!obligation.trait_ref.has_escaping_regions());
59-
self.trait_obligations.push(obligation);
67+
if self.duplicate_set.insert(obligation.trait_ref.clone()) {
68+
debug!("register_obligation({})", obligation.repr(tcx));
69+
assert!(!obligation.trait_ref.has_escaping_regions());
70+
self.trait_obligations.push(obligation);
71+
} else {
72+
debug!("register_obligation({}) -- already seen, skip", obligation.repr(tcx));
73+
}
6074
}
6175

6276
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
@@ -114,7 +114,7 @@ pub enum MethodMatchedData {
114114
* candidate is one that might match or might not, depending on how
115115
* type variables wind up being resolved. This only occurs during inference.
116116
*
117-
* For selection to suceed, there must be exactly one non-ambiguous
117+
* For selection to succeed, there must be exactly one non-ambiguous
118118
* candidate. Usually, it is not possible to have more than one
119119
* definitive candidate, due to the coherence rules. However, there is
120120
* one case where it could occur: if there is a blanket impl for a
@@ -1202,24 +1202,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12021202
candidates: &mut CandidateSet<'tcx>)
12031203
-> Result<(),SelectionError<'tcx>>
12041204
{
1205-
// FIXME -- To be more like a normal impl, we should just
1206-
// ignore the nested cases here, and instead generate nested
1207-
// obligations in `confirm_candidate`. However, this doesn't
1208-
// work because we require handling the recursive cases to
1209-
// avoid infinite cycles (that is, with recursive types,
1210-
// sometimes `Foo : Copy` only holds if `Foo : Copy`).
1211-
12121205
match self.builtin_bound(bound, stack.obligation.self_ty()) {
1213-
Ok(If(nested)) => {
1214-
debug!("builtin_bound: bound={} nested={}",
1215-
bound.repr(self.tcx()),
1216-
nested.repr(self.tcx()));
1217-
let data = self.vtable_builtin_data(stack.obligation, bound, nested);
1218-
match self.winnow_selection(Some(stack), VtableBuiltin(data)) {
1219-
EvaluatedToOk => { Ok(candidates.vec.push(BuiltinCandidate(bound))) }
1220-
EvaluatedToAmbig => { Ok(candidates.ambiguous = true) }
1221-
EvaluatedToErr => { Err(Unimplemented) }
1222-
}
1206+
Ok(If(_)) => {
1207+
debug!("builtin_bound: bound={}",
1208+
bound.repr(self.tcx()));
1209+
candidates.vec.push(BuiltinCandidate(bound));
1210+
Ok(())
12231211
}
12241212
Ok(ParameterBuiltin) => { Ok(()) }
12251213
Ok(AmbiguousBuiltin) => { Ok(candidates.ambiguous = true) }
@@ -1575,8 +1563,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
15751563
candidate.repr(self.tcx()));
15761564

15771565
match candidate {
1578-
// FIXME -- see assemble_builtin_bound_candidates()
1579-
BuiltinCandidate(_) |
1566+
BuiltinCandidate(builtin_bound) => {
1567+
Ok(VtableBuiltin(
1568+
try!(self.confirm_builtin_candidate(obligation, builtin_bound))))
1569+
}
1570+
15801571
ErrorCandidate => {
15811572
Ok(VtableBuiltin(VtableBuiltinData { nested: VecPerParamSpace::empty() }))
15821573
}
@@ -1626,8 +1617,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
16261617

16271618
match try!(self.builtin_bound(bound, obligation.self_ty())) {
16281619
If(nested) => Ok(self.vtable_builtin_data(obligation, bound, nested)),
1629-
AmbiguousBuiltin |
1630-
ParameterBuiltin => {
1620+
AmbiguousBuiltin | ParameterBuiltin => {
16311621
self.tcx().sess.span_bug(
16321622
obligation.cause.span,
16331623
format!("builtin bound for {} was ambig",

0 commit comments

Comments
 (0)