Skip to content

Commit b212a3f

Browse files
committedAug 19, 2017
Auto merge of #43952 - arielb1:the-big-backout, r=nikomatsakis
[beta] back out #42480 and its dependents #42480 makes the ICE in #43132 worse, and the "safe" fix to that causes the #43787 exponential worst-case. Let's back everything out for beta to avoid regressions, and get the permafix in nightly.
2 parents 54279df + 97e9c7e commit b212a3f

File tree

13 files changed

+287
-400
lines changed

13 files changed

+287
-400
lines changed
 

‎src/librustc/dep_graph/dep_node.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,7 @@ define_dep_nodes!( <'tcx>
495495
// imprecision in our dep-graph tracking. The important thing is
496496
// that for any given trait-ref, we always map to the **same**
497497
// trait-select node.
498-
[anon] TraitSelect,
498+
[] TraitSelect { trait_def_id: DefId, input_def_id: DefId },
499499

500500
// For proj. cache, we just keep a list of all def-ids, since it is
501501
// not a hotspot.

‎src/librustc/dep_graph/dep_tracking_map.rs

+31-9
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,24 @@ use rustc_data_structures::fx::FxHashMap;
1212
use std::cell::RefCell;
1313
use std::hash::Hash;
1414
use std::marker::PhantomData;
15+
use ty::TyCtxt;
1516
use util::common::MemoizationMap;
1617

17-
use super::{DepKind, DepNodeIndex, DepGraph};
18+
use super::{DepNode, DepGraph};
1819

1920
/// A DepTrackingMap offers a subset of the `Map` API and ensures that
2021
/// we make calls to `read` and `write` as appropriate. We key the
2122
/// maps with a unique type for brevity.
2223
pub struct DepTrackingMap<M: DepTrackingMapConfig> {
2324
phantom: PhantomData<M>,
2425
graph: DepGraph,
25-
map: FxHashMap<M::Key, (M::Value, DepNodeIndex)>,
26+
map: FxHashMap<M::Key, M::Value>,
2627
}
2728

2829
pub trait DepTrackingMapConfig {
2930
type Key: Eq + Hash + Clone;
3031
type Value: Clone;
31-
fn to_dep_kind() -> DepKind;
32+
fn to_dep_node(tcx: TyCtxt, key: &Self::Key) -> DepNode;
3233
}
3334

3435
impl<M: DepTrackingMapConfig> DepTrackingMap<M> {
@@ -39,6 +40,27 @@ impl<M: DepTrackingMapConfig> DepTrackingMap<M> {
3940
map: FxHashMap(),
4041
}
4142
}
43+
44+
/// Registers a (synthetic) read from the key `k`. Usually this
45+
/// is invoked automatically by `get`.
46+
fn read(&self, tcx: TyCtxt, k: &M::Key) {
47+
let dep_node = M::to_dep_node(tcx, k);
48+
self.graph.read(dep_node);
49+
}
50+
51+
pub fn get(&self, tcx: TyCtxt, k: &M::Key) -> Option<&M::Value> {
52+
self.read(tcx, k);
53+
self.map.get(k)
54+
}
55+
56+
pub fn contains_key(&self, tcx: TyCtxt, k: &M::Key) -> bool {
57+
self.read(tcx, k);
58+
self.map.contains_key(k)
59+
}
60+
61+
pub fn keys(&self) -> Vec<M::Key> {
62+
self.map.keys().cloned().collect()
63+
}
4264
}
4365

4466
impl<M: DepTrackingMapConfig> MemoizationMap for RefCell<DepTrackingMap<M>> {
@@ -76,22 +98,22 @@ impl<M: DepTrackingMapConfig> MemoizationMap for RefCell<DepTrackingMap<M>> {
7698
/// The key is the line marked `(*)`: the closure implicitly
7799
/// accesses the body of the item `item`, so we register a read
78100
/// from `Hir(item_def_id)`.
79-
fn memoize<OP>(&self, key: M::Key, op: OP) -> M::Value
101+
fn memoize<OP>(&self, tcx: TyCtxt, key: M::Key, op: OP) -> M::Value
80102
where OP: FnOnce() -> M::Value
81103
{
82104
let graph;
83105
{
84106
let this = self.borrow();
85-
if let Some(&(ref result, dep_node)) = this.map.get(&key) {
86-
this.graph.read_index(dep_node);
107+
if let Some(result) = this.map.get(&key) {
108+
this.read(tcx, &key);
87109
return result.clone();
88110
}
89111
graph = this.graph.clone();
90112
}
91113

92-
let (result, dep_node) = graph.with_anon_task(M::to_dep_kind(), op);
93-
self.borrow_mut().map.insert(key, (result.clone(), dep_node));
94-
graph.read_index(dep_node);
114+
let _task = graph.in_task(M::to_dep_node(tcx, &key));
115+
let result = op();
116+
self.borrow_mut().map.insert(key, result.clone());
95117
result
96118
}
97119
}

‎src/librustc/traits/fulfill.rs

+112-19
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@
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 infer::{InferCtxt, InferOk};
12-
use ty::{self, Ty, TypeFoldable, ToPolyTraitRef, ToPredicate};
13+
use ty::{self, Ty, TypeFoldable, ToPolyTraitRef, TyCtxt, ToPredicate};
1314
use ty::error::ExpectedFound;
1415
use rustc_data_structures::obligation_forest::{ObligationForest, Error};
1516
use rustc_data_structures::obligation_forest::{ForestObligation, ObligationProcessor};
1617
use std::marker::PhantomData;
1718
use syntax::ast;
18-
use util::nodemap::NodeMap;
19+
use util::nodemap::{FxHashSet, NodeMap};
1920
use hir::def_id::DefId;
2021

2122
use super::CodeAmbiguity;
@@ -33,6 +34,11 @@ impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> {
3334
fn as_predicate(&self) -> &Self::Predicate { &self.obligation.predicate }
3435
}
3536

37+
pub struct GlobalFulfilledPredicates<'tcx> {
38+
set: FxHashSet<ty::PolyTraitPredicate<'tcx>>,
39+
dep_graph: DepGraph,
40+
}
41+
3642
/// The fulfillment context is used to drive trait resolution. It
3743
/// consists of a list of obligations that must be (eventually)
3844
/// satisfied. The job is to track which are satisfied, which yielded
@@ -177,6 +183,13 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
177183

178184
assert!(!infcx.is_in_snapshot());
179185

186+
let tcx = infcx.tcx;
187+
188+
if tcx.fulfilled_predicates.borrow().check_duplicate(tcx, &obligation.predicate) {
189+
debug!("register_predicate_obligation: duplicate");
190+
return
191+
}
192+
180193
self.predicates.register_obligation(PendingPredicateObligation {
181194
obligation,
182195
stalled_on: vec![]
@@ -251,6 +264,13 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
251264
});
252265
debug!("select: outcome={:?}", outcome);
253266

267+
// these are obligations that were proven to be true.
268+
for pending_obligation in outcome.completed {
269+
let predicate = &pending_obligation.obligation.predicate;
270+
selcx.tcx().fulfilled_predicates.borrow_mut()
271+
.add_if_global(selcx.tcx(), predicate);
272+
}
273+
254274
errors.extend(
255275
outcome.errors.into_iter()
256276
.map(|e| to_fulfillment_error(e)));
@@ -298,7 +318,7 @@ impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx,
298318
_marker: PhantomData<&'c PendingPredicateObligation<'tcx>>)
299319
where I: Clone + Iterator<Item=&'c PendingPredicateObligation<'tcx>>,
300320
{
301-
if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) {
321+
if coinductive_match(self.selcx, cycle.clone()) {
302322
debug!("process_child_obligations: coinductive match");
303323
} else {
304324
let cycle : Vec<_> = cycle.map(|c| c.obligation.clone()).collect();
@@ -355,31 +375,21 @@ fn process_predicate<'a, 'gcx, 'tcx>(
355375

356376
match obligation.predicate {
357377
ty::Predicate::Trait(ref data) => {
358-
let trait_obligation = obligation.with(data.clone());
359-
360-
if data.is_global() {
361-
// no type variables present, can use evaluation for better caching.
362-
// FIXME: consider caching errors too.
363-
if
364-
// make defaulted unit go through the slow path for better warnings,
365-
// please remove this when the warnings are removed.
366-
!trait_obligation.predicate.skip_binder().self_ty().is_defaulted_unit() &&
367-
selcx.evaluate_obligation_conservatively(&obligation) {
368-
debug!("selecting trait `{:?}` at depth {} evaluated to holds",
369-
data, obligation.recursion_depth);
370-
return Ok(Some(vec![]))
371-
}
378+
let tcx = selcx.tcx();
379+
if tcx.fulfilled_predicates.borrow().check_duplicate_trait(tcx, data) {
380+
return Ok(Some(vec![]));
372381
}
373382

383+
let trait_obligation = obligation.with(data.clone());
374384
match selcx.select(&trait_obligation) {
375385
Ok(Some(vtable)) => {
376386
debug!("selecting trait `{:?}` at depth {} yielded Ok(Some)",
377-
data, obligation.recursion_depth);
387+
data, obligation.recursion_depth);
378388
Ok(Some(vtable.nested_obligations()))
379389
}
380390
Ok(None) => {
381391
debug!("selecting trait `{:?}` at depth {} yielded Ok(None)",
382-
data, obligation.recursion_depth);
392+
data, obligation.recursion_depth);
383393

384394
// This is a bit subtle: for the most part, the
385395
// only reason we can fail to make progress on
@@ -540,6 +550,40 @@ fn process_predicate<'a, 'gcx, 'tcx>(
540550
}
541551
}
542552

553+
/// For defaulted traits, we use a co-inductive strategy to solve, so
554+
/// that recursion is ok. This routine returns true if the top of the
555+
/// stack (`cycle[0]`):
556+
/// - is a defaulted trait, and
557+
/// - it also appears in the backtrace at some position `X`; and,
558+
/// - all the predicates at positions `X..` between `X` an the top are
559+
/// also defaulted traits.
560+
fn coinductive_match<'a,'c,'gcx,'tcx,I>(selcx: &mut SelectionContext<'a,'gcx,'tcx>,
561+
cycle: I) -> bool
562+
where I: Iterator<Item=&'c PendingPredicateObligation<'tcx>>,
563+
'tcx: 'c
564+
{
565+
let mut cycle = cycle;
566+
cycle
567+
.all(|bt_obligation| {
568+
let result = coinductive_obligation(selcx, &bt_obligation.obligation);
569+
debug!("coinductive_match: bt_obligation={:?} coinductive={}",
570+
bt_obligation, result);
571+
result
572+
})
573+
}
574+
575+
fn coinductive_obligation<'a,'gcx,'tcx>(selcx: &SelectionContext<'a,'gcx,'tcx>,
576+
obligation: &PredicateObligation<'tcx>)
577+
-> bool {
578+
match obligation.predicate {
579+
ty::Predicate::Trait(ref data) => {
580+
selcx.tcx().trait_has_default_impl(data.def_id())
581+
}
582+
_ => {
583+
false
584+
}
585+
}
586+
}
543587

544588
fn register_region_obligation<'tcx>(t_a: Ty<'tcx>,
545589
r_b: ty::Region<'tcx>,
@@ -559,6 +603,55 @@ fn register_region_obligation<'tcx>(t_a: Ty<'tcx>,
559603

560604
}
561605

606+
impl<'a, 'gcx, 'tcx> GlobalFulfilledPredicates<'gcx> {
607+
pub fn new(dep_graph: DepGraph) -> GlobalFulfilledPredicates<'gcx> {
608+
GlobalFulfilledPredicates {
609+
set: FxHashSet(),
610+
dep_graph,
611+
}
612+
}
613+
614+
pub fn check_duplicate(&self, tcx: TyCtxt, key: &ty::Predicate<'tcx>) -> bool {
615+
if let ty::Predicate::Trait(ref data) = *key {
616+
self.check_duplicate_trait(tcx, data)
617+
} else {
618+
false
619+
}
620+
}
621+
622+
pub fn check_duplicate_trait(&self, tcx: TyCtxt, data: &ty::PolyTraitPredicate<'tcx>) -> bool {
623+
// For the global predicate registry, when we find a match, it
624+
// may have been computed by some other task, so we want to
625+
// add a read from the node corresponding to the predicate
626+
// processing to make sure we get the transitive dependencies.
627+
if self.set.contains(data) {
628+
debug_assert!(data.is_global());
629+
self.dep_graph.read(data.dep_node(tcx));
630+
debug!("check_duplicate: global predicate `{:?}` already proved elsewhere", data);
631+
632+
true
633+
} else {
634+
false
635+
}
636+
}
637+
638+
fn add_if_global(&mut self, tcx: TyCtxt<'a, 'gcx, 'tcx>, key: &ty::Predicate<'tcx>) {
639+
if let ty::Predicate::Trait(ref data) = *key {
640+
// We only add things to the global predicate registry
641+
// after the current task has proved them, and hence
642+
// already has the required read edges, so we don't need
643+
// to add any more edges here.
644+
if data.is_global() {
645+
if let Some(data) = tcx.lift_to_global(data) {
646+
if self.set.insert(data.clone()) {
647+
debug!("add_if_global: global predicate `{:?}` added", data);
648+
}
649+
}
650+
}
651+
}
652+
}
653+
}
654+
562655
fn to_fulfillment_error<'tcx>(
563656
error: Error<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>>)
564657
-> FulfillmentError<'tcx>

‎src/librustc/traits/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use syntax_pos::{Span, DUMMY_SP};
3131
pub use self::coherence::orphan_check;
3232
pub use self::coherence::overlapping_impls;
3333
pub use self::coherence::OrphanCheckErr;
34-
pub use self::fulfill::{FulfillmentContext, RegionObligation};
34+
pub use self::fulfill::{FulfillmentContext, GlobalFulfilledPredicates, RegionObligation};
3535
pub use self::project::MismatchedProjectionTypes;
3636
pub use self::project::{normalize, normalize_projection_type, Normalized};
3737
pub use self::project::{ProjectionCache, ProjectionCacheSnapshot, Reveal};

‎src/librustc/traits/project.rs

+6-12
Original file line numberDiff line numberDiff line change
@@ -462,19 +462,13 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
462462
selcx.infcx().report_overflow_error(&obligation, false);
463463
}
464464
Err(ProjectionCacheEntry::NormalizedTy(ty)) => {
465-
// If we find the value in the cache, then return it along
466-
// with the obligations that went along with it. Note
467-
// that, when using a fulfillment context, these
468-
// obligations could in principle be ignored: they have
469-
// already been registered when the cache entry was
470-
// created (and hence the new ones will quickly be
471-
// discarded as duplicated). But when doing trait
472-
// evaluation this is not the case, and dropping the trait
473-
// evaluations can causes ICEs (e.g. #43132).
465+
// If we find the value in the cache, then the obligations
466+
// have already been returned from the previous entry (and
467+
// should therefore have been honored).
474468
debug!("opt_normalize_projection_type: \
475469
found normalized ty `{:?}`",
476470
ty);
477-
return Some(ty);
471+
return Some(NormalizedTy { value: ty, obligations: vec![] });
478472
}
479473
Err(ProjectionCacheEntry::Error) => {
480474
debug!("opt_normalize_projection_type: \
@@ -1342,7 +1336,7 @@ enum ProjectionCacheEntry<'tcx> {
13421336
InProgress,
13431337
Ambiguous,
13441338
Error,
1345-
NormalizedTy(NormalizedTy<'tcx>),
1339+
NormalizedTy(Ty<'tcx>),
13461340
}
13471341

13481342
// NB: intentionally not Clone
@@ -1395,7 +1389,7 @@ impl<'tcx> ProjectionCache<'tcx> {
13951389
let fresh_key = if cacheable {
13961390
debug!("ProjectionCacheEntry::complete: adding cache entry: key={:?}, value={:?}",
13971391
key, value);
1398-
self.map.insert(key, ProjectionCacheEntry::NormalizedTy(value.clone()))
1392+
self.map.insert(key, ProjectionCacheEntry::NormalizedTy(value.value))
13991393
} else {
14001394
debug!("ProjectionCacheEntry::complete: cannot cache: key={:?}, value={:?}",
14011395
key, value);

0 commit comments

Comments
 (0)
Please sign in to comment.