Skip to content

Commit 8f36038

Browse files
committed
Auto merge of #31087 - nikomatsakis:incr-comp-fulfillment-cache, r=arielb1
This is a fix for #30741. It simplifies dep-graph tracking for trait matching. I was experimenting with having a greater resolution here, but decided to pare back to just have one dep node for "trait resolutions on trait `Foo`", which means that adding an impl to the trait `Foo` will invalidate all fns that had to do any trait matching at all on `Foo`. This seems like a reasonable starting place. Independently, I realized I had neglected to record a dependency from trans on typeck -- this is obviously needed, since trans consumes a bunch of data structures that typeck produces (but which are not currently individually tracked) -- and because trans assumes that typeck has been done. Eventually those are going to go away and be replaced with MIR, which will be tracked, so this edge would presumably be derived automatically then, but it's an obvious enough thing to want for now. r? @arielb1 cc @michaelwoerister -- this might indirectly fix the problem you observed with the trans cache, though it'd be nice to try and craft an independent test case for that.
2 parents e1cb0a3 + 56c73e5 commit 8f36038

File tree

9 files changed

+139
-46
lines changed

9 files changed

+139
-46
lines changed

Diff for: src/librustc/dep_graph/mod.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
use self::thread::{DepGraphThreadData, DepMessage};
1212
use middle::def_id::DefId;
1313
use middle::ty;
14-
use middle::ty::fast_reject::SimplifiedType;
1514
use rustc_front::hir;
1615
use rustc_front::intravisit::Visitor;
1716
use std::rc::Rc;
@@ -102,7 +101,7 @@ pub enum DepNode {
102101
// which would yield an overly conservative dep-graph.
103102
TraitItems(DefId),
104103
ReprHints(DefId),
105-
TraitSelect(DefId, Option<SimplifiedType>),
104+
TraitSelect(DefId),
106105
}
107106

108107
#[derive(Clone)]

Diff for: 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>

Diff for: src/librustc/middle/traits/mod.rs

+1-15
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,10 @@ 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;
2221
use middle::ty::{self, Ty, TypeFoldable};
23-
use middle::ty::fast_reject;
2422
use middle::infer::{self, fixup_err_to_string, InferCtxt};
2523

2624
use std::rc::Rc;
@@ -37,7 +35,7 @@ pub use self::error_reporting::report_object_safety_error;
3735
pub use self::coherence::orphan_check;
3836
pub use self::coherence::overlapping_impls;
3937
pub use self::coherence::OrphanCheckErr;
40-
pub use self::fulfill::{FulfillmentContext, FulfilledPredicates, RegionObligation};
38+
pub use self::fulfill::{FulfillmentContext, GlobalFulfilledPredicates, RegionObligation};
4139
pub use self::project::MismatchedProjectionTypes;
4240
pub use self::project::normalize;
4341
pub use self::project::Normalized;
@@ -617,18 +615,6 @@ impl<'tcx> FulfillmentError<'tcx> {
617615
}
618616

619617
impl<'tcx> TraitObligation<'tcx> {
620-
/// Creates the dep-node for selecting/evaluating this trait reference.
621-
fn dep_node(&self, tcx: &ty::ctxt<'tcx>) -> DepNode {
622-
let simplified_ty =
623-
fast_reject::simplify_type(tcx,
624-
self.predicate.skip_binder().self_ty(), // (*)
625-
true);
626-
627-
// (*) skip_binder is ok because `simplify_type` doesn't care about regions
628-
629-
DepNode::TraitSelect(self.predicate.def_id(), simplified_ty)
630-
}
631-
632618
fn self_ty(&self) -> ty::Binder<Ty<'tcx>> {
633619
ty::Binder(self.predicate.skip_binder().self_ty())
634620
}

Diff for: 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(self.tcx());
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

Diff for: 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())),

Diff for: 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)]

Diff for: src/librustc_trans/trans/base.rs

+8
Original file line numberDiff line numberDiff line change
@@ -3323,6 +3323,14 @@ impl<'a, 'tcx, 'v> Visitor<'v> for TransItemsWithinModVisitor<'a, 'tcx> {
33233323
// giving `trans_item` access to this item, so also record a read.
33243324
tcx.dep_graph.with_task(DepNode::TransCrateItem(def_id), || {
33253325
tcx.dep_graph.read(DepNode::Hir(def_id));
3326+
3327+
// We are going to be accessing various tables
3328+
// generated by TypeckItemBody; we also assume
3329+
// that the body passes type check. These tables
3330+
// are not individually tracked, so just register
3331+
// a read here.
3332+
tcx.dep_graph.read(DepNode::TypeckItemBody(def_id));
3333+
33263334
trans_item(self.ccx, i);
33273335
});
33283336

Diff for: src/test/compile-fail/dep-graph-assoc-type-trans.rs

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that when a trait impl changes, fns whose body uses that trait
12+
// must also be recompiled.
13+
14+
// compile-flags: -Z incr-comp
15+
16+
#![feature(rustc_attrs)]
17+
#![allow(warnings)]
18+
19+
fn main() { }
20+
21+
pub trait Foo: Sized {
22+
type T;
23+
fn method(self) { }
24+
}
25+
26+
mod x {
27+
use Foo;
28+
29+
#[rustc_if_this_changed]
30+
impl Foo for char { type T = char; }
31+
32+
impl Foo for u32 { type T = u32; }
33+
}
34+
35+
mod y {
36+
use Foo;
37+
38+
#[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR OK
39+
#[rustc_then_this_would_need(TransCrateItem)] //~ ERROR OK
40+
pub fn use_char_assoc() {
41+
// Careful here: in the representation, <char as Foo>::T gets
42+
// normalized away, so at a certain point we had no edge to
43+
// trans. (But now trans just depends on typeck.)
44+
let x: <char as Foo>::T = 'a';
45+
}
46+
47+
pub fn take_foo<T:Foo>(t: T) { }
48+
}

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

+4-6
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,8 @@ mod y {
4040
char::method('a');
4141
}
4242

43-
// FIXME(#30741) tcx fulfillment cache not tracked
44-
#[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path
45-
#[rustc_then_this_would_need(TransCrateItem)] //~ ERROR no path
43+
#[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR OK
44+
#[rustc_then_this_would_need(TransCrateItem)] //~ ERROR OK
4645
pub fn take_foo_with_char() {
4746
take_foo::<char>('a');
4847
}
@@ -53,9 +52,8 @@ mod y {
5352
u32::method(22);
5453
}
5554

56-
// FIXME(#30741) tcx fulfillment cache not tracked
57-
#[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path
58-
#[rustc_then_this_would_need(TransCrateItem)] //~ ERROR no path
55+
#[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR OK
56+
#[rustc_then_this_would_need(TransCrateItem)] //~ ERROR OK
5957
pub fn take_foo_with_u32() {
6058
take_foo::<u32>(22);
6159
}

0 commit comments

Comments
 (0)