Skip to content

Commit 87a1181

Browse files
arielb1nikomatsakis
authored andcommitted
use the evaluation cache instead of the global fulfillment cache
The evaluation cache already exists, and it can handle differing parameter environments natively. Fixes #39970. Fixes #42796.
1 parent 16d1700 commit 87a1181

File tree

6 files changed

+99
-106
lines changed

6 files changed

+99
-106
lines changed

src/librustc/traits/fulfill.rs

+18-77
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,14 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use dep_graph::DepGraph;
1211
use infer::{InferCtxt, InferOk};
13-
use ty::{self, Ty, TypeFoldable, ToPolyTraitRef, TyCtxt, ToPredicate};
12+
use ty::{self, Ty, TypeFoldable, ToPolyTraitRef, ToPredicate};
1413
use ty::error::ExpectedFound;
1514
use rustc_data_structures::obligation_forest::{ObligationForest, Error};
1615
use rustc_data_structures::obligation_forest::{ForestObligation, ObligationProcessor};
1716
use std::marker::PhantomData;
1817
use syntax::ast;
19-
use util::nodemap::{FxHashSet, NodeMap};
18+
use util::nodemap::NodeMap;
2019
use hir::def_id::DefId;
2120

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

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

184178
assert!(!infcx.is_in_snapshot());
185179

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-
193180
self.predicates.register_obligation(PendingPredicateObligation {
194181
obligation,
195182
stalled_on: vec![]
@@ -264,13 +251,6 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
264251
});
265252
debug!("select: outcome={:?}", outcome);
266253

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-
274254
errors.extend(
275255
outcome.errors.into_iter()
276256
.map(|e| to_fulfillment_error(e)));
@@ -375,21 +355,31 @@ fn process_predicate<'a, 'gcx, 'tcx>(
375355

376356
match obligation.predicate {
377357
ty::Predicate::Trait(ref data) => {
378-
let tcx = selcx.tcx();
379-
if tcx.fulfilled_predicates.borrow().check_duplicate_trait(tcx, data) {
380-
return Ok(Some(vec![]));
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+
}
381372
}
382373

383-
let trait_obligation = obligation.with(data.clone());
384374
match selcx.select(&trait_obligation) {
385375
Ok(Some(vtable)) => {
386376
debug!("selecting trait `{:?}` at depth {} yielded Ok(Some)",
387-
data, obligation.recursion_depth);
377+
data, obligation.recursion_depth);
388378
Ok(Some(vtable.nested_obligations()))
389379
}
390380
Ok(None) => {
391381
debug!("selecting trait `{:?}` at depth {} yielded Ok(None)",
392-
data, obligation.recursion_depth);
382+
data, obligation.recursion_depth);
393383

394384
// This is a bit subtle: for the most part, the
395385
// only reason we can fail to make progress on
@@ -568,55 +558,6 @@ fn register_region_obligation<'tcx>(t_a: Ty<'tcx>,
568558

569559
}
570560

571-
impl<'a, 'gcx, 'tcx> GlobalFulfilledPredicates<'gcx> {
572-
pub fn new(dep_graph: DepGraph) -> GlobalFulfilledPredicates<'gcx> {
573-
GlobalFulfilledPredicates {
574-
set: FxHashSet(),
575-
dep_graph,
576-
}
577-
}
578-
579-
pub fn check_duplicate(&self, tcx: TyCtxt, key: &ty::Predicate<'tcx>) -> bool {
580-
if let ty::Predicate::Trait(ref data) = *key {
581-
self.check_duplicate_trait(tcx, data)
582-
} else {
583-
false
584-
}
585-
}
586-
587-
pub fn check_duplicate_trait(&self, tcx: TyCtxt, data: &ty::PolyTraitPredicate<'tcx>) -> bool {
588-
// For the global predicate registry, when we find a match, it
589-
// may have been computed by some other task, so we want to
590-
// add a read from the node corresponding to the predicate
591-
// processing to make sure we get the transitive dependencies.
592-
if self.set.contains(data) {
593-
debug_assert!(data.is_global());
594-
self.dep_graph.read(data.dep_node(tcx));
595-
debug!("check_duplicate: global predicate `{:?}` already proved elsewhere", data);
596-
597-
true
598-
} else {
599-
false
600-
}
601-
}
602-
603-
fn add_if_global(&mut self, tcx: TyCtxt<'a, 'gcx, 'tcx>, key: &ty::Predicate<'tcx>) {
604-
if let ty::Predicate::Trait(ref data) = *key {
605-
// We only add things to the global predicate registry
606-
// after the current task has proved them, and hence
607-
// already has the required read edges, so we don't need
608-
// to add any more edges here.
609-
if data.is_global() {
610-
if let Some(data) = tcx.lift_to_global(data) {
611-
if self.set.insert(data.clone()) {
612-
debug!("add_if_global: global predicate `{:?}` added", data);
613-
}
614-
}
615-
}
616-
}
617-
}
618-
}
619-
620561
fn to_fulfillment_error<'tcx>(
621562
error: Error<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>>)
622563
-> 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, GlobalFulfilledPredicates, RegionObligation};
34+
pub use self::fulfill::{FulfillmentContext, 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/select.rs

+20-20
Original file line numberDiff line numberDiff line change
@@ -514,21 +514,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
514514
debug!("evaluate_predicate_recursively({:?})",
515515
obligation);
516516

517-
let tcx = self.tcx();
518-
519-
// Check the cache from the tcx of predicates that we know
520-
// have been proven elsewhere. This cache only contains
521-
// predicates that are global in scope and hence unaffected by
522-
// the current environment.
523-
if tcx.fulfilled_predicates.borrow().check_duplicate(tcx, &obligation.predicate) {
524-
return EvaluatedToOk;
525-
}
526-
527517
match obligation.predicate {
528518
ty::Predicate::Trait(ref t) => {
529519
assert!(!t.has_escaping_regions());
530520
let obligation = obligation.with(t.clone());
531-
self.evaluate_obligation_recursively(previous_stack, &obligation)
521+
self.evaluate_trait_predicate_recursively(previous_stack, obligation)
532522
}
533523

534524
ty::Predicate::Equate(ref p) => {
@@ -613,15 +603,23 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
613603
}
614604
}
615605

616-
fn evaluate_obligation_recursively<'o>(&mut self,
617-
previous_stack: TraitObligationStackList<'o, 'tcx>,
618-
obligation: &TraitObligation<'tcx>)
619-
-> EvaluationResult
606+
fn evaluate_trait_predicate_recursively<'o>(&mut self,
607+
previous_stack: TraitObligationStackList<'o, 'tcx>,
608+
mut obligation: TraitObligation<'tcx>)
609+
-> EvaluationResult
620610
{
621-
debug!("evaluate_obligation_recursively({:?})",
611+
debug!("evaluate_trait_predicate_recursively({:?})",
622612
obligation);
623613

624-
let stack = self.push_stack(previous_stack, obligation);
614+
if !self.intercrate && obligation.is_global() {
615+
// If a param env is consistent, global obligations do not depend on its particular
616+
// value in order to work, so we can clear out the param env and get better
617+
// caching. (If the current param env is inconsistent, we don't care what happens).
618+
debug!("evaluate_trait_predicate_recursively({:?}) - in global", obligation);
619+
obligation.param_env = ty::ParamEnv::empty(obligation.param_env.reveal);
620+
}
621+
622+
let stack = self.push_stack(previous_stack, &obligation);
625623
let fresh_trait_ref = stack.fresh_trait_ref;
626624
if let Some(result) = self.check_evaluation_cache(obligation.param_env, fresh_trait_ref) {
627625
debug!("CACHE HIT: EVAL({:?})={:?}",
@@ -676,8 +674,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
676674
}
677675
if unbound_input_types &&
678676
stack.iter().skip(1).any(
679-
|prev| self.match_fresh_trait_refs(&stack.fresh_trait_ref,
680-
&prev.fresh_trait_ref))
677+
|prev| stack.obligation.param_env == prev.obligation.param_env &&
678+
self.match_fresh_trait_refs(&stack.fresh_trait_ref,
679+
&prev.fresh_trait_ref))
681680
{
682681
debug!("evaluate_stack({:?}) --> unbound argument, recursive --> giving up",
683682
stack.fresh_trait_ref);
@@ -706,7 +705,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
706705
if let Some(rec_index) =
707706
stack.iter()
708707
.skip(1) // skip top-most frame
709-
.position(|prev| stack.fresh_trait_ref == prev.fresh_trait_ref)
708+
.position(|prev| stack.obligation.param_env == prev.obligation.param_env &&
709+
stack.fresh_trait_ref == prev.fresh_trait_ref)
710710
{
711711
debug!("evaluate_stack({:?}) --> recursive",
712712
stack.fresh_trait_ref);

src/librustc/ty/context.rs

-8
Original file line numberDiff line numberDiff line change
@@ -507,12 +507,6 @@ pub struct GlobalCtxt<'tcx> {
507507
/// Merge this with `selection_cache`?
508508
pub evaluation_cache: traits::EvaluationCache<'tcx>,
509509

510-
/// A set of predicates that have been fulfilled *somewhere*.
511-
/// This is used to avoid duplicate work. Predicates are only
512-
/// added to this set when they mention only "global" names
513-
/// (i.e., no type or lifetime parameters).
514-
pub fulfilled_predicates: RefCell<traits::GlobalFulfilledPredicates<'tcx>>,
515-
516510
/// Maps Expr NodeId's to `true` iff `&expr` can have 'static lifetime.
517511
pub rvalue_promotable_to_static: RefCell<NodeMap<bool>>,
518512

@@ -686,7 +680,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
686680
let interners = CtxtInterners::new(arena);
687681
let common_types = CommonTypes::new(&interners);
688682
let dep_graph = hir.dep_graph.clone();
689-
let fulfilled_predicates = traits::GlobalFulfilledPredicates::new(dep_graph.clone());
690683
let max_cnum = s.cstore.crates().iter().map(|c| c.as_usize()).max().unwrap_or(0);
691684
let mut providers = IndexVec::from_elem_n(extern_providers, max_cnum + 1);
692685
providers[LOCAL_CRATE] = local_providers;
@@ -735,7 +728,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
735728
named_region_map,
736729
trait_map: resolutions.trait_map,
737730
export_map: resolutions.export_map,
738-
fulfilled_predicates: RefCell::new(fulfilled_predicates),
739731
hir,
740732
def_path_hash_to_def_id,
741733
maps: maps::Maps::new(providers),

src/test/compile-fail/issue-39970.rs

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2017 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+
trait Array<'a> {
12+
type Element: 'a;
13+
}
14+
15+
trait Visit {
16+
fn visit() {}
17+
}
18+
19+
impl<'a> Array<'a> for () {
20+
type Element = &'a ();
21+
}
22+
23+
impl Visit for () where
24+
//(): for<'a> Array<'a, Element=&'a ()>, // No ICE
25+
(): for<'a> Array<'a, Element=()>, // ICE
26+
{}
27+
28+
fn main() {
29+
<() as Visit>::visit();
30+
//~^ ERROR type mismatch resolving `for<'a> <() as Array<'a>>::Element == ()`
31+
}

src/test/compile-fail/issue-42796.rs

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2017 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+
pub trait Mirror<Smoke> {
12+
type Image;
13+
}
14+
15+
impl<T, Smoke> Mirror<Smoke> for T {
16+
type Image = T;
17+
}
18+
19+
pub fn poison<S>(victim: String) where <String as Mirror<S>>::Image: Copy {
20+
loop { drop(victim); } //~ ERROR use of moved value
21+
}
22+
23+
fn main() {
24+
let s = "Hello!".to_owned();
25+
let mut s_copy = s;
26+
s_copy.push_str("World!");
27+
"0wned!".to_owned();
28+
println!("{}", s); //~ ERROR use of moved value
29+
}

0 commit comments

Comments
 (0)