-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[WIP] traits/select: use global vs per-infcx caches more uniformly. #69294
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -835,18 +835,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | |
trait_ref: ty::PolyTraitRef<'tcx>, | ||
) -> Option<EvaluationResult> { | ||
let tcx = self.tcx(); | ||
if self.can_use_global_caches(param_env) { | ||
let cache = tcx.evaluation_cache.hashmap.borrow(); | ||
if let Some(cached) = cache.get(¶m_env.and(trait_ref)) { | ||
return Some(cached.get(tcx)); | ||
} | ||
} | ||
self.infcx | ||
.evaluation_cache | ||
.hashmap | ||
.borrow() | ||
.get(¶m_env.and(trait_ref)) | ||
.map(|v| v.get(tcx)) | ||
// FIXME(eddyb) pass in the `ty::PolyTraitPredicate` instead. | ||
let predicate = trait_ref.map_bound(|trait_ref| ty::TraitPredicate { trait_ref }); | ||
// FIXME(eddyb) reuse the key between checking the cache and inserting. | ||
let cache_key = param_env.and(predicate); | ||
let cache = if self.can_use_global_caches(&cache_key) { | ||
&tcx.evaluation_cache | ||
} else { | ||
&self.infcx.evaluation_cache | ||
}; | ||
|
||
cache.hashmap.borrow().get(&cache_key).map(|v| v.get(tcx)) | ||
} | ||
|
||
fn insert_evaluation_cache( | ||
|
@@ -862,31 +861,26 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | |
return; | ||
} | ||
|
||
if self.can_use_global_caches(param_env) { | ||
if !trait_ref.has_local_value() { | ||
debug!( | ||
"insert_evaluation_cache(trait_ref={:?}, candidate={:?}) global", | ||
trait_ref, result, | ||
); | ||
// This may overwrite the cache with the same value | ||
// FIXME: Due to #50507 this overwrites the different values | ||
// This should be changed to use HashMapExt::insert_same | ||
// when that is fixed | ||
self.tcx() | ||
.evaluation_cache | ||
.hashmap | ||
.borrow_mut() | ||
.insert(param_env.and(trait_ref), WithDepNode::new(dep_node, result)); | ||
return; | ||
} | ||
} | ||
// FIXME(eddyb) pass in the `ty::PolyTraitPredicate` instead. | ||
let predicate = trait_ref.map_bound(|trait_ref| ty::TraitPredicate { trait_ref }); | ||
// FIXME(eddyb) reuse the key between checking the cache and inserting. | ||
let cache_key = param_env.and(predicate); | ||
let cache = if self.can_use_global_caches(&cache_key) { | ||
debug!( | ||
"insert_evaluation_cache(trait_ref={:?}, candidate={:?}) global", | ||
trait_ref, result, | ||
); | ||
// This may overwrite the cache with the same value | ||
// FIXME: Due to #50507 this overwrites the different values | ||
// This should be changed to use HashMapExt::insert_same | ||
// when that is fixed | ||
&self.tcx().evaluation_cache | ||
} else { | ||
debug!("insert_evaluation_cache(trait_ref={:?}, candidate={:?})", trait_ref, result,); | ||
&self.infcx.evaluation_cache | ||
}; | ||
|
||
debug!("insert_evaluation_cache(trait_ref={:?}, candidate={:?})", trait_ref, result,); | ||
self.infcx | ||
.evaluation_cache | ||
.hashmap | ||
.borrow_mut() | ||
.insert(param_env.and(trait_ref), WithDepNode::new(dep_node, result)); | ||
cache.hashmap.borrow_mut().insert(cache_key, WithDepNode::new(dep_node, result)); | ||
} | ||
|
||
/// For various reasons, it's possible for a subobligation | ||
|
@@ -961,7 +955,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | |
debug_assert!(!stack.obligation.predicate.has_escaping_bound_vars()); | ||
|
||
if let Some(c) = | ||
self.check_candidate_cache(stack.obligation.param_env, &cache_fresh_trait_pred) | ||
self.check_candidate_cache(stack.obligation.param_env, cache_fresh_trait_pred) | ||
{ | ||
debug!("CACHE HIT: SELECT({:?})={:?}", cache_fresh_trait_pred, c); | ||
return c; | ||
|
@@ -982,6 +976,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | |
cache_fresh_trait_pred, | ||
dep_node, | ||
candidate.clone(), | ||
stack.obligation.cause.span, | ||
); | ||
candidate | ||
} | ||
|
@@ -1218,13 +1213,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | |
} | ||
|
||
/// Returns `true` if the global caches can be used. | ||
/// Do note that if the type itself is not in the | ||
/// global tcx, the local caches will be used. | ||
fn can_use_global_caches(&self, param_env: ty::ParamEnv<'tcx>) -> bool { | ||
// If there are any e.g. inference variables in the `ParamEnv`, then we | ||
// always use a cache local to this particular scope. Otherwise, we | ||
// switch to a global cache. | ||
if param_env.has_local_value() { | ||
fn can_use_global_caches( | ||
&self, | ||
cache_key: &ty::ParamEnvAnd<'tcx, ty::PolyTraitPredicate<'tcx>>, | ||
) -> bool { | ||
// If there are any e.g. inference variables in the `ParamEnv` or predicate, | ||
// then we always use a cache local to this particular inference context. | ||
// Otherwise, we switch to a global cache. | ||
if cache_key.has_local_value() { | ||
return false; | ||
} | ||
|
||
|
@@ -1246,22 +1242,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | |
fn check_candidate_cache( | ||
&mut self, | ||
param_env: ty::ParamEnv<'tcx>, | ||
cache_fresh_trait_pred: &ty::PolyTraitPredicate<'tcx>, | ||
cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, | ||
) -> Option<SelectionResult<'tcx, SelectionCandidate<'tcx>>> { | ||
let tcx = self.tcx(); | ||
let trait_ref = &cache_fresh_trait_pred.skip_binder().trait_ref; | ||
if self.can_use_global_caches(param_env) { | ||
let cache = tcx.selection_cache.hashmap.borrow(); | ||
if let Some(cached) = cache.get(¶m_env.and(*trait_ref)) { | ||
return Some(cached.get(tcx)); | ||
} | ||
} | ||
self.infcx | ||
.selection_cache | ||
.hashmap | ||
.borrow() | ||
.get(¶m_env.and(*trait_ref)) | ||
.map(|v| v.get(tcx)) | ||
// FIXME(eddyb) reuse the key between checking the cache and inserting. | ||
let cache_key = param_env.and(cache_fresh_trait_pred); | ||
let cache = if self.can_use_global_caches(&cache_key) { | ||
&tcx.selection_cache | ||
} else { | ||
&self.infcx.selection_cache | ||
}; | ||
|
||
cache.hashmap.borrow().get(&cache_key).map(|v| v.get(tcx)) | ||
} | ||
|
||
/// Determines whether can we safely cache the result | ||
|
@@ -1285,9 +1277,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | |
result: &SelectionResult<'tcx, SelectionCandidate<'tcx>>, | ||
) -> bool { | ||
match result { | ||
Ok(Some(SelectionCandidate::ParamCandidate(trait_ref))) => { | ||
!trait_ref.skip_binder().input_types().any(|t| t.walk().any(|t_| t_.is_ty_infer())) | ||
} | ||
Ok(Some(SelectionCandidate::ParamCandidate(trait_ref))) => !trait_ref.needs_infer(), | ||
_ => true, | ||
} | ||
} | ||
|
@@ -1298,47 +1288,50 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | |
cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, | ||
dep_node: DepNodeIndex, | ||
candidate: SelectionResult<'tcx, SelectionCandidate<'tcx>>, | ||
span: rustc_span::Span, | ||
) { | ||
let tcx = self.tcx(); | ||
let trait_ref = cache_fresh_trait_pred.skip_binder().trait_ref; | ||
|
||
if !self.can_cache_candidate(&candidate) { | ||
debug!( | ||
"insert_candidate_cache(trait_ref={:?}, candidate={:?} -\ | ||
"insert_candidate_cache(predicate={:?}, candidate={:?} -\ | ||
candidate is not cacheable", | ||
trait_ref, candidate | ||
cache_fresh_trait_pred, candidate | ||
); | ||
return; | ||
} | ||
|
||
if self.can_use_global_caches(param_env) { | ||
if let Err(Overflow) = candidate { | ||
// Don't cache overflow globally; we only produce this in certain modes. | ||
} else if !trait_ref.has_local_value() { | ||
if !candidate.has_local_value() { | ||
debug!( | ||
"insert_candidate_cache(trait_ref={:?}, candidate={:?}) global", | ||
trait_ref, candidate, | ||
); | ||
// This may overwrite the cache with the same value. | ||
tcx.selection_cache | ||
.hashmap | ||
.borrow_mut() | ||
.insert(param_env.and(trait_ref), WithDepNode::new(dep_node, candidate)); | ||
return; | ||
} | ||
} | ||
// HACK(eddyb) never cache overflow (this check used to be global-only). | ||
if let Err(Overflow) = candidate { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. with the exception of this tiny logic change, I guess There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't really think why we would want to cache overflow locally anyhow, that seems like a bug... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pfft, that's nothing, here's something more fun: I don't think we ever use the local caches except for the So we could put |
||
return; | ||
} | ||
|
||
debug!( | ||
"insert_candidate_cache(trait_ref={:?}, candidate={:?}) local", | ||
trait_ref, candidate, | ||
); | ||
self.infcx | ||
.selection_cache | ||
.hashmap | ||
.borrow_mut() | ||
.insert(param_env.and(trait_ref), WithDepNode::new(dep_node, candidate)); | ||
// FIXME(eddyb) reuse the key between checking the cache and inserting. | ||
let cache_key = param_env.and(cache_fresh_trait_pred); | ||
let cache = if self.can_use_global_caches(&cache_key) { | ||
if candidate.has_local_value() { | ||
span_bug!( | ||
span, | ||
"selecting inference-free `{:?}` resulted in `{:?}`?!", | ||
cache_fresh_trait_pred, | ||
candidate, | ||
); | ||
} | ||
debug!( | ||
"insert_candidate_cache(predicate={:?}, candidate={:?}) global", | ||
cache_fresh_trait_pred, candidate, | ||
); | ||
// This may overwrite the cache with the same value. | ||
&tcx.selection_cache | ||
} else { | ||
debug!( | ||
"insert_candidate_cache(predicate={:?}, candidate={:?}) local", | ||
cache_fresh_trait_pred, candidate, | ||
); | ||
&self.infcx.selection_cache | ||
}; | ||
|
||
cache.hashmap.borrow_mut().insert(cache_key, WithDepNode::new(dep_node, candidate)); | ||
} | ||
|
||
fn assemble_candidates<'o>( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a fix for #55258 (comment), I should make a separate commit but I didn't want to lose track of this.