Auto merge of #66821 - eddyb:global-trait-caching, r=nikomatsakis
rustc: allow non-empty ParamEnv's in global trait select/eval caches. *Based on #66963* This appears to alleviate the symptoms of #65510 locally (without fixing WF directly), and is potentially easier to validate as sound (since it's a more ad-hoc version of queries we already have). I'm opening this PR primarily to test the effects on perf. r? @nikomatsakis cc @rust-lang/wg-traits
This commit is contained in:
commit
90b957a17c
@ -1079,12 +1079,10 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
|
|||||||
if !is_default {
|
if !is_default {
|
||||||
true
|
true
|
||||||
} else if obligation.param_env.reveal == Reveal::All {
|
} else if obligation.param_env.reveal == Reveal::All {
|
||||||
debug_assert!(!poly_trait_ref.needs_infer());
|
// NOTE(eddyb) inference variables can resolve to parameters, so
|
||||||
if !poly_trait_ref.needs_subst() {
|
// assume `poly_trait_ref` isn't monomorphic, if it contains any.
|
||||||
true
|
let poly_trait_ref = selcx.infcx().resolve_vars_if_possible(&poly_trait_ref);
|
||||||
} else {
|
!poly_trait_ref.needs_infer() && !poly_trait_ref.needs_subst()
|
||||||
false
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -204,7 +204,10 @@ struct TraitObligationStack<'prev, 'tcx> {
|
|||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct SelectionCache<'tcx> {
|
pub struct SelectionCache<'tcx> {
|
||||||
hashmap: Lock<
|
hashmap: Lock<
|
||||||
FxHashMap<ty::TraitRef<'tcx>, WithDepNode<SelectionResult<'tcx, SelectionCandidate<'tcx>>>>,
|
FxHashMap<
|
||||||
|
ty::ParamEnvAnd<'tcx, ty::TraitRef<'tcx>>,
|
||||||
|
WithDepNode<SelectionResult<'tcx, SelectionCandidate<'tcx>>>,
|
||||||
|
>,
|
||||||
>,
|
>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -490,7 +493,9 @@ fn from(OverflowError: OverflowError) -> SelectionError<'tcx> {
|
|||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct EvaluationCache<'tcx> {
|
pub struct EvaluationCache<'tcx> {
|
||||||
hashmap: Lock<FxHashMap<ty::PolyTraitRef<'tcx>, WithDepNode<EvaluationResult>>>,
|
hashmap: Lock<
|
||||||
|
FxHashMap<ty::ParamEnvAnd<'tcx, ty::PolyTraitRef<'tcx>>, WithDepNode<EvaluationResult>>,
|
||||||
|
>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
@ -1143,7 +1148,7 @@ fn check_evaluation_cache(
|
|||||||
let tcx = self.tcx();
|
let tcx = self.tcx();
|
||||||
if self.can_use_global_caches(param_env) {
|
if self.can_use_global_caches(param_env) {
|
||||||
let cache = tcx.evaluation_cache.hashmap.borrow();
|
let cache = tcx.evaluation_cache.hashmap.borrow();
|
||||||
if let Some(cached) = cache.get(&trait_ref) {
|
if let Some(cached) = cache.get(¶m_env.and(trait_ref)) {
|
||||||
return Some(cached.get(tcx));
|
return Some(cached.get(tcx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1151,7 +1156,7 @@ fn check_evaluation_cache(
|
|||||||
.evaluation_cache
|
.evaluation_cache
|
||||||
.hashmap
|
.hashmap
|
||||||
.borrow()
|
.borrow()
|
||||||
.get(&trait_ref)
|
.get(¶m_env.and(trait_ref))
|
||||||
.map(|v| v.get(tcx))
|
.map(|v| v.get(tcx))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1182,7 +1187,7 @@ fn insert_evaluation_cache(
|
|||||||
.evaluation_cache
|
.evaluation_cache
|
||||||
.hashmap
|
.hashmap
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert(trait_ref, WithDepNode::new(dep_node, result));
|
.insert(param_env.and(trait_ref), WithDepNode::new(dep_node, result));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1195,7 +1200,7 @@ fn insert_evaluation_cache(
|
|||||||
.evaluation_cache
|
.evaluation_cache
|
||||||
.hashmap
|
.hashmap
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert(trait_ref, WithDepNode::new(dep_node, result));
|
.insert(param_env.and(trait_ref), WithDepNode::new(dep_node, result));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For various reasons, it's possible for a subobligation
|
/// For various reasons, it's possible for a subobligation
|
||||||
@ -1567,14 +1572,10 @@ fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Option<
|
|||||||
/// Do note that if the type itself is not in the
|
/// Do note that if the type itself is not in the
|
||||||
/// global tcx, the local caches will be used.
|
/// global tcx, the local caches will be used.
|
||||||
fn can_use_global_caches(&self, param_env: ty::ParamEnv<'tcx>) -> bool {
|
fn can_use_global_caches(&self, param_env: ty::ParamEnv<'tcx>) -> bool {
|
||||||
// If there are any where-clauses in scope, then we always use
|
// If there are any e.g. inference variables in the `ParamEnv`, then we
|
||||||
// a cache local to this particular scope. Otherwise, we
|
// always use a cache local to this particular scope. Otherwise, we
|
||||||
// switch to a global cache. We used to try and draw
|
// switch to a global cache.
|
||||||
// finer-grained distinctions, but that led to a serious of
|
if param_env.has_local_value() {
|
||||||
// annoying and weird bugs like #22019 and #18290. This simple
|
|
||||||
// rule seems to be pretty clearly safe and also still retains
|
|
||||||
// a very high hit rate (~95% when compiling rustc).
|
|
||||||
if !param_env.caller_bounds.is_empty() {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1602,7 +1603,7 @@ fn check_candidate_cache(
|
|||||||
let trait_ref = &cache_fresh_trait_pred.skip_binder().trait_ref;
|
let trait_ref = &cache_fresh_trait_pred.skip_binder().trait_ref;
|
||||||
if self.can_use_global_caches(param_env) {
|
if self.can_use_global_caches(param_env) {
|
||||||
let cache = tcx.selection_cache.hashmap.borrow();
|
let cache = tcx.selection_cache.hashmap.borrow();
|
||||||
if let Some(cached) = cache.get(&trait_ref) {
|
if let Some(cached) = cache.get(¶m_env.and(*trait_ref)) {
|
||||||
return Some(cached.get(tcx));
|
return Some(cached.get(tcx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1610,7 +1611,7 @@ fn check_candidate_cache(
|
|||||||
.selection_cache
|
.selection_cache
|
||||||
.hashmap
|
.hashmap
|
||||||
.borrow()
|
.borrow()
|
||||||
.get(trait_ref)
|
.get(¶m_env.and(*trait_ref))
|
||||||
.map(|v| v.get(tcx))
|
.map(|v| v.get(tcx))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1671,7 +1672,7 @@ fn insert_candidate_cache(
|
|||||||
tcx.selection_cache
|
tcx.selection_cache
|
||||||
.hashmap
|
.hashmap
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert(trait_ref, WithDepNode::new(dep_node, candidate));
|
.insert(param_env.and(trait_ref), WithDepNode::new(dep_node, candidate));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1685,7 +1686,7 @@ fn insert_candidate_cache(
|
|||||||
.selection_cache
|
.selection_cache
|
||||||
.hashmap
|
.hashmap
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert(trait_ref, WithDepNode::new(dep_node, candidate));
|
.insert(param_env.and(trait_ref), WithDepNode::new(dep_node, candidate));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assemble_candidates<'o>(
|
fn assemble_candidates<'o>(
|
||||||
|
@ -9,6 +9,7 @@ trait TraitWithAssoc {
|
|||||||
|
|
||||||
type Foo<V> = impl Trait<V>;
|
type Foo<V> = impl Trait<V>;
|
||||||
//~^ ERROR could not find defining uses
|
//~^ ERROR could not find defining uses
|
||||||
|
//~| ERROR the trait bound `T: TraitWithAssoc` is not satisfied
|
||||||
|
|
||||||
trait Trait<U> {}
|
trait Trait<U> {}
|
||||||
|
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
|
error[E0277]: the trait bound `T: TraitWithAssoc` is not satisfied
|
||||||
|
--> $DIR/bound_reduction2.rs:10:1
|
||||||
|
|
|
||||||
|
LL | type Foo<V> = impl Trait<V>;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TraitWithAssoc` is not implemented for `T`
|
||||||
|
...
|
||||||
|
LL | fn foo_desugared<T: TraitWithAssoc>(_: T) -> Foo<T::Assoc> {
|
||||||
|
| -- help: consider further restricting this bound: `T: TraitWithAssoc +`
|
||||||
|
|
||||||
error: defining opaque type use does not fully define opaque type: generic parameter `V` is specified as concrete type `<T as TraitWithAssoc>::Assoc`
|
error: defining opaque type use does not fully define opaque type: generic parameter `V` is specified as concrete type `<T as TraitWithAssoc>::Assoc`
|
||||||
--> $DIR/bound_reduction2.rs:17:1
|
--> $DIR/bound_reduction2.rs:18:1
|
||||||
|
|
|
|
||||||
LL | / fn foo_desugared<T: TraitWithAssoc>(_: T) -> Foo<T::Assoc> {
|
LL | / fn foo_desugared<T: TraitWithAssoc>(_: T) -> Foo<T::Assoc> {
|
||||||
LL | | ()
|
LL | | ()
|
||||||
@ -12,5 +21,6 @@ error: could not find defining uses
|
|||||||
LL | type Foo<V> = impl Trait<V>;
|
LL | type Foo<V> = impl Trait<V>;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
||||||
|
Loading…
Reference in New Issue
Block a user