Skip to content

Commit 0bdefd7

Browse files
committed
Revamp the fulfillment cache tracking to only cache trait-refs, which
was the major use-case, and to update the dep-graph. Other kinds of predicates are now excluded from the cache because there is no easy way to make a good dep-graph node for them, and because they are not believed to be that useful. :) Fixes #30741. (However, the test still gives wrong result for trans, for an independent reason which is fixed in the next commit.)
1 parent b5f85cf commit 0bdefd7

File tree

6 files changed

+80
-32
lines changed

6 files changed

+80
-32
lines changed

src/librustc/middle/traits/fulfill.rs

+61-19
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use dep_graph::DepGraph;
1112
use middle::infer::InferCtxt;
1213
use middle::ty::{self, Ty, TypeFoldable};
1314
use rustc_data_structures::obligation_forest::{Backtrace, ObligationForest, Error};
@@ -30,7 +31,12 @@ use super::select::SelectionContext;
3031
use super::Unimplemented;
3132
use super::util::predicate_for_builtin_bound;
3233

33-
pub struct FulfilledPredicates<'tcx> {
34+
pub struct GlobalFulfilledPredicates<'tcx> {
35+
set: FnvHashSet<ty::PolyTraitPredicate<'tcx>>,
36+
dep_graph: DepGraph,
37+
}
38+
39+
pub struct LocalFulfilledPredicates<'tcx> {
3440
set: FnvHashSet<ty::Predicate<'tcx>>
3541
}
3642

@@ -56,7 +62,7 @@ pub struct FulfillmentContext<'tcx> {
5662
// initially-distinct type variables are unified after being
5763
// inserted. Deduplicating the predicate set on selection had a
5864
// significant performance cost the last time I checked.
59-
duplicate_set: FulfilledPredicates<'tcx>,
65+
duplicate_set: LocalFulfilledPredicates<'tcx>,
6066

6167
// A list of all obligations that have been registered with this
6268
// fulfillment context.
@@ -106,7 +112,7 @@ impl<'tcx> FulfillmentContext<'tcx> {
106112
/// Creates a new fulfillment context.
107113
pub fn new() -> FulfillmentContext<'tcx> {
108114
FulfillmentContext {
109-
duplicate_set: FulfilledPredicates::new(),
115+
duplicate_set: LocalFulfilledPredicates::new(),
110116
predicates: ObligationForest::new(),
111117
region_obligations: NodeMap(),
112118
}
@@ -240,7 +246,7 @@ impl<'tcx> FulfillmentContext<'tcx> {
240246
// local cache). This is because the tcx cache maintains the
241247
// invariant that it only contains things that have been
242248
// proven, and we have not yet proven that `predicate` holds.
243-
if predicate.is_global() && tcx.fulfilled_predicates.borrow().is_duplicate(predicate) {
249+
if tcx.fulfilled_predicates.borrow().check_duplicate(predicate) {
244250
return true;
245251
}
246252

@@ -283,10 +289,7 @@ impl<'tcx> FulfillmentContext<'tcx> {
283289
// these are obligations that were proven to be true.
284290
for pending_obligation in outcome.completed {
285291
let predicate = &pending_obligation.obligation.predicate;
286-
if predicate.is_global() {
287-
selcx.tcx().fulfilled_predicates.borrow_mut()
288-
.is_duplicate_or_add(predicate);
289-
}
292+
selcx.tcx().fulfilled_predicates.borrow_mut().add_if_global(predicate);
290293
}
291294

292295
errors.extend(
@@ -329,17 +332,16 @@ fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
329332
// However, this is a touch tricky, so I'm doing something
330333
// a bit hackier for now so that the `huge-struct.rs` passes.
331334

335+
let tcx = selcx.tcx();
336+
332337
let retain_vec: Vec<_> = {
333338
let mut dedup = FnvHashSet();
334339
v.iter()
335340
.map(|o| {
336341
// Screen out obligations that we know globally
337342
// are true. This should really be the DAG check
338343
// mentioned above.
339-
if
340-
o.predicate.is_global() &&
341-
selcx.tcx().fulfilled_predicates.borrow().is_duplicate(&o.predicate)
342-
{
344+
if tcx.fulfilled_predicates.borrow().check_duplicate(&o.predicate) {
343345
return false;
344346
}
345347

@@ -611,22 +613,62 @@ fn register_region_obligation<'tcx>(t_a: Ty<'tcx>,
611613

612614
}
613615

614-
impl<'tcx> FulfilledPredicates<'tcx> {
615-
pub fn new() -> FulfilledPredicates<'tcx> {
616-
FulfilledPredicates {
616+
impl<'tcx> LocalFulfilledPredicates<'tcx> {
617+
pub fn new() -> LocalFulfilledPredicates<'tcx> {
618+
LocalFulfilledPredicates {
617619
set: FnvHashSet()
618620
}
619621
}
620622

621-
pub fn is_duplicate(&self, key: &ty::Predicate<'tcx>) -> bool {
622-
self.set.contains(key)
623-
}
624-
625623
fn is_duplicate_or_add(&mut self, key: &ty::Predicate<'tcx>) -> bool {
624+
// For a `LocalFulfilledPredicates`, if we find a match, we
625+
// don't need to add a read edge to the dep-graph. This is
626+
// because it means that the predicate has already been
627+
// considered by this `FulfillmentContext`, and hence the
628+
// containing task will already have an edge. (Here we are
629+
// assuming each `FulfillmentContext` only gets used from one
630+
// task; but to do otherwise makes no sense)
626631
!self.set.insert(key.clone())
627632
}
628633
}
629634

635+
impl<'tcx> GlobalFulfilledPredicates<'tcx> {
636+
pub fn new(dep_graph: DepGraph) -> GlobalFulfilledPredicates<'tcx> {
637+
GlobalFulfilledPredicates {
638+
set: FnvHashSet(),
639+
dep_graph: dep_graph,
640+
}
641+
}
642+
643+
pub fn check_duplicate(&self, key: &ty::Predicate<'tcx>) -> bool {
644+
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+
}
654+
}
655+
656+
return false;
657+
}
658+
659+
fn add_if_global(&mut self, key: &ty::Predicate<'tcx>) {
660+
if let ty::Predicate::Trait(ref data) = *key {
661+
// We only add things to the global predicate registry
662+
// after the current task has proved them, and hence
663+
// already has the required read edges, so we don't need
664+
// to add any more edges here.
665+
if data.is_global() {
666+
self.set.insert(data.clone());
667+
}
668+
}
669+
}
670+
}
671+
630672
fn to_fulfillment_error<'tcx>(
631673
error: Error<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>>)
632674
-> FulfillmentError<'tcx>

src/librustc/middle/traits/mod.rs

+1-7
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ pub use self::FulfillmentErrorCode::*;
1515
pub use self::Vtable::*;
1616
pub use self::ObligationCauseCode::*;
1717

18-
use dep_graph::DepNode;
1918
use middle::def_id::DefId;
2019
use middle::free_region::FreeRegionMap;
2120
use middle::subst;
@@ -36,7 +35,7 @@ pub use self::error_reporting::report_object_safety_error;
3635
pub use self::coherence::orphan_check;
3736
pub use self::coherence::overlapping_impls;
3837
pub use self::coherence::OrphanCheckErr;
39-
pub use self::fulfill::{FulfillmentContext, FulfilledPredicates, RegionObligation};
38+
pub use self::fulfill::{FulfillmentContext, GlobalFulfilledPredicates, RegionObligation};
4039
pub use self::project::MismatchedProjectionTypes;
4140
pub use self::project::normalize;
4241
pub use self::project::Normalized;
@@ -616,11 +615,6 @@ impl<'tcx> FulfillmentError<'tcx> {
616615
}
617616

618617
impl<'tcx> TraitObligation<'tcx> {
619-
/// Creates the dep-node for selecting/evaluating this trait reference.
620-
fn dep_node(&self) -> DepNode {
621-
DepNode::TraitSelect(self.predicate.def_id())
622-
}
623-
624618
fn self_ty(&self) -> ty::Binder<Ty<'tcx>> {
625619
ty::Binder(self.predicate.skip_binder().self_ty())
626620
}

src/librustc/middle/traits/select.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
307307
debug!("select({:?})", obligation);
308308
assert!(!obligation.predicate.has_escaping_regions());
309309

310-
let dep_node = obligation.dep_node();
310+
let dep_node = obligation.predicate.dep_node();
311311
let _task = self.tcx().dep_graph.in_task(dep_node);
312312

313313
let stack = self.push_stack(TraitObligationStackList::empty(), obligation);
@@ -462,7 +462,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
462462
// have been proven elsewhere. This cache only contains
463463
// predicates that are global in scope and hence unaffected by
464464
// the current environment.
465-
if self.tcx().fulfilled_predicates.borrow().is_duplicate(&obligation.predicate) {
465+
if self.tcx().fulfilled_predicates.borrow().check_duplicate(&obligation.predicate) {
466466
return EvaluatedToOk;
467467
}
468468

src/librustc/middle/ty/context.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ pub struct ctxt<'tcx> {
367367
/// This is used to avoid duplicate work. Predicates are only
368368
/// added to this set when they mention only "global" names
369369
/// (i.e., no type or lifetime parameters).
370-
pub fulfilled_predicates: RefCell<traits::FulfilledPredicates<'tcx>>,
370+
pub fulfilled_predicates: RefCell<traits::GlobalFulfilledPredicates<'tcx>>,
371371

372372
/// Caches the representation hints for struct definitions.
373373
repr_hint_cache: RefCell<DepTrackingMap<maps::ReprHints<'tcx>>>,
@@ -510,6 +510,7 @@ impl<'tcx> ctxt<'tcx> {
510510
let interner = RefCell::new(FnvHashMap());
511511
let common_types = CommonTypes::new(&arenas.type_, &interner);
512512
let dep_graph = DepGraph::new(s.opts.incremental_compilation);
513+
let fulfilled_predicates = traits::GlobalFulfilledPredicates::new(dep_graph.clone());
513514
tls::enter(ctxt {
514515
arenas: arenas,
515516
interner: interner,
@@ -532,7 +533,7 @@ impl<'tcx> ctxt<'tcx> {
532533
adt_defs: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
533534
predicates: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
534535
super_predicates: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
535-
fulfilled_predicates: RefCell::new(traits::FulfilledPredicates::new()),
536+
fulfilled_predicates: RefCell::new(fulfilled_predicates),
536537
map: map,
537538
freevars: RefCell::new(freevars),
538539
tcache: RefCell::new(DepTrackingMap::new(dep_graph.clone())),

src/librustc/middle/ty/mod.rs

+11
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,11 @@ impl<'tcx> TraitPredicate<'tcx> {
838838
self.trait_ref.def_id
839839
}
840840

841+
/// Creates the dep-node for selecting/evaluating this trait reference.
842+
fn dep_node(&self) -> DepNode {
843+
DepNode::TraitSelect(self.def_id())
844+
}
845+
841846
pub fn input_types(&self) -> &[Ty<'tcx>] {
842847
self.trait_ref.substs.types.as_slice()
843848
}
@@ -849,8 +854,14 @@ impl<'tcx> TraitPredicate<'tcx> {
849854

850855
impl<'tcx> PolyTraitPredicate<'tcx> {
851856
pub fn def_id(&self) -> DefId {
857+
// ok to skip binder since trait def-id does not care about regions
852858
self.0.def_id()
853859
}
860+
861+
pub fn dep_node(&self) -> DepNode {
862+
// ok to skip binder since depnode does not care about regions
863+
self.0.dep_node()
864+
}
854865
}
855866

856867
#[derive(Clone, PartialEq, Eq, Hash, Debug)]

src/test/compile-fail/dep-graph-trait-impl.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ mod y {
4141
}
4242

4343
// FIXME(#30741) tcx fulfillment cache not tracked
44-
#[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path
44+
#[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR OK
4545
#[rustc_then_this_would_need(TransCrateItem)] //~ ERROR no path
4646
pub fn take_foo_with_char() {
4747
take_foo::<char>('a');
@@ -54,7 +54,7 @@ mod y {
5454
}
5555

5656
// FIXME(#30741) tcx fulfillment cache not tracked
57-
#[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path
57+
#[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR OK
5858
#[rustc_then_this_would_need(TransCrateItem)] //~ ERROR no path
5959
pub fn take_foo_with_u32() {
6060
take_foo::<u32>(22);

0 commit comments

Comments
 (0)