Skip to content

Commit 37815fd

Browse files
committed
Add a notion of "per-tree" state
1 parent c22cb53 commit 37815fd

File tree

5 files changed

+237
-114
lines changed

5 files changed

+237
-114
lines changed

src/librustc/middle/traits/fulfill.rs

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ pub struct FulfillmentContext<'tcx> {
6666

6767
// A list of all obligations that have been registered with this
6868
// fulfillment context.
69-
predicates: ObligationForest<PendingPredicateObligation<'tcx>>,
69+
predicates: ObligationForest<PendingPredicateObligation<'tcx>, ()>,
7070

7171
// A set of constraints that regionck must validate. Each
7272
// constraint has the form `T:'a`, meaning "some type `T` must
@@ -192,7 +192,7 @@ impl<'tcx> FulfillmentContext<'tcx> {
192192
obligation: obligation,
193193
stalled_on: vec![]
194194
};
195-
self.predicates.push_root(obligation);
195+
self.predicates.push_tree(obligation, ());
196196
}
197197

198198
pub fn region_obligations(&self,
@@ -278,10 +278,10 @@ impl<'tcx> FulfillmentContext<'tcx> {
278278
let outcome = {
279279
let region_obligations = &mut self.region_obligations;
280280
self.predicates.process_obligations(
281-
|obligation, backtrace| process_predicate(selcx,
282-
obligation,
283-
backtrace,
284-
region_obligations))
281+
|obligation, _tree, backtrace| process_predicate(selcx,
282+
obligation,
283+
backtrace,
284+
region_obligations))
285285
};
286286

287287
debug!("select_where_possible: outcome={:?}", outcome);
@@ -405,7 +405,7 @@ fn process_predicate1<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
405405
pending_obligation.stalled_on = vec![];
406406
}
407407

408-
let obligation = &pending_obligation.obligation;
408+
let obligation = &mut pending_obligation.obligation;
409409

410410
// If we exceed the recursion limit, take a moment to look for a
411411
// cycle so we can give a better error report from here, where we
@@ -417,18 +417,31 @@ fn process_predicate1<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
417417
}
418418
}
419419

420+
if obligation.predicate.has_infer_types() {
421+
obligation.predicate = selcx.infcx().resolve_type_vars_if_possible(&obligation.predicate);
422+
}
423+
420424
match obligation.predicate {
421425
ty::Predicate::Trait(ref data) => {
426+
if selcx.tcx().fulfilled_predicates.borrow().check_duplicate_trait(data) {
427+
return Ok(Some(vec![]));
428+
}
429+
422430
if coinductive_match(selcx, obligation, data, &backtrace) {
423431
return Ok(Some(vec![]));
424432
}
425433

426434
let trait_obligation = obligation.with(data.clone());
427435
match selcx.select(&trait_obligation) {
428436
Ok(Some(vtable)) => {
437+
info!("selecting trait `{:?}` at depth {} yielded Ok(Some)",
438+
data, obligation.recursion_depth);
429439
Ok(Some(vtable.nested_obligations()))
430440
}
431441
Ok(None) => {
442+
info!("selecting trait `{:?}` at depth {} yielded Ok(None)",
443+
data, obligation.recursion_depth);
444+
432445
// This is a bit subtle: for the most part, the
433446
// only reason we can fail to make progress on
434447
// trait selection is because we don't have enough
@@ -457,6 +470,8 @@ fn process_predicate1<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
457470
Ok(None)
458471
}
459472
Err(selection_err) => {
473+
info!("selecting trait `{:?}` at depth {} yielded Err",
474+
data, obligation.recursion_depth);
460475
Err(CodeSelectionError(selection_err))
461476
}
462477
}
@@ -642,18 +657,28 @@ impl<'tcx> GlobalFulfilledPredicates<'tcx> {
642657

643658
pub fn check_duplicate(&self, key: &ty::Predicate<'tcx>) -> bool {
644659
if let ty::Predicate::Trait(ref data) = *key {
645-
// For the global predicate registry, when we find a match, it
646-
// may have been computed by some other task, so we want to
647-
// add a read from the node corresponding to the predicate
648-
// processing to make sure we get the transitive dependencies.
649-
if self.set.contains(data) {
650-
debug_assert!(data.is_global());
651-
self.dep_graph.read(data.dep_node());
652-
return true;
653-
}
660+
self.check_duplicate_trait(data)
661+
} else {
662+
false
654663
}
664+
}
665+
666+
pub fn check_duplicate_trait(&self, data: &ty::PolyTraitPredicate<'tcx>) -> bool {
667+
// For the global predicate registry, when we find a match, it
668+
// may have been computed by some other task, so we want to
669+
// add a read from the node corresponding to the predicate
670+
// processing to make sure we get the transitive dependencies.
671+
if self.set.contains(data) {
672+
debug_assert!(data.is_global());
673+
self.dep_graph.read(data.dep_node());
674+
debug!("check_duplicate: global predicate `{:?}` already proved elsewhere", data);
675+
676+
info!("check_duplicate_trait hit: `{:?}`", data);
655677

656-
return false;
678+
true
679+
} else {
680+
false
681+
}
657682
}
658683

659684
fn add_if_global(&mut self, key: &ty::Predicate<'tcx>) {
@@ -663,7 +688,10 @@ impl<'tcx> GlobalFulfilledPredicates<'tcx> {
663688
// already has the required read edges, so we don't need
664689
// to add any more edges here.
665690
if data.is_global() {
666-
self.set.insert(data.clone());
691+
if self.set.insert(data.clone()) {
692+
debug!("add_if_global: global predicate `{:?}` added", data);
693+
info!("check_duplicate_trait entry: `{:?}`", data);
694+
}
667695
}
668696
}
669697
}

src/librustc_data_structures/obligation_forest/README.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,18 @@ place).
99
`ObligationForest` supports two main public operations (there are a
1010
few others not discussed here):
1111

12-
1. Add a new root obligation (`push_root`).
12+
1. Add a new root obligations (`push_tree`).
1313
2. Process the pending obligations (`process_obligations`).
1414

1515
When a new obligation `N` is added, it becomes the root of an
16-
obligation tree. This tree is a singleton to start, so `N` is both the
17-
root and the only leaf. Each time the `process_obligations` method is
18-
called, it will invoke its callback with every pending obligation (so
19-
that will include `N`, the first time). The callback shoud process the
20-
obligation `O` that it is given and return one of three results:
16+
obligation tree. This tree can also carry some per-tree state `T`,
17+
which is given at the same time. This tree is a singleton to start, so
18+
`N` is both the root and the only leaf. Each time the
19+
`process_obligations` method is called, it will invoke its callback
20+
with every pending obligation (so that will include `N`, the first
21+
time). The callback also receives a (mutable) reference to the
22+
per-tree state `T`. The callback should process the obligation `O`
23+
that it is given and return one of three results:
2124

2225
- `Ok(None)` -> ambiguous result. Obligation was neither a success
2326
nor a failure. It is assumed that further attempts to process the

0 commit comments

Comments
 (0)