Skip to content

Commit a540ddc

Browse files
committed
Auto merge of #69294 - eddyb:trait-cache-streamline, r=<try>
[WIP] traits/select: use global vs per-infcx caches more uniformly. ~~*Note: this is based on an older `master` to avoid perf interference before #67953 (comment) is resolved.*~~ **EDIT**: sadly not workable due #69294 (comment). So far this PR only has the first couple steps towards making the decision of which cache ("global" vs "per-`InferCtxt`") to use for trait evaluation/selection, only once (at the time of checking the cache). The goal here is to actually make per-`InferCtxt` caches not track `DepNode`s, and maybe even enforce that once `SelectionContext::in_task` is entered, the `InferCtxt` is effectively unused. My assumption is that if you *need* inference variables in your cache key (i.e. `ParamEnv` and/or `PolyTraitPredicate`) to ever end up doing anything "non-global", and you can't get there from a "global cache key" (which would still use `DepNode`s and `in_task` etc.). Perhaps another path forward is to redirect "global cache keys" to a query, but I'm not sure how that would handle cycles (badly?), and it feels like stepping on Chalk's toes. r? @nikomatsakis cc @rust-lang/wg-traits @Zoxc @michaelwoerister
2 parents 7710ae0 + 6a51d66 commit a540ddc

File tree

2 files changed

+87
-89
lines changed

2 files changed

+87
-89
lines changed

src/librustc/traits/select.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_hir::def_id::DefId;
1717
pub struct SelectionCache<'tcx> {
1818
pub hashmap: Lock<
1919
FxHashMap<
20-
ty::ParamEnvAnd<'tcx, ty::TraitRef<'tcx>>,
20+
ty::ParamEnvAnd<'tcx, ty::PolyTraitPredicate<'tcx>>,
2121
WithDepNode<SelectionResult<'tcx, SelectionCandidate<'tcx>>>,
2222
>,
2323
>,
@@ -261,7 +261,10 @@ impl<'tcx> From<OverflowError> for SelectionError<'tcx> {
261261
#[derive(Clone, Default)]
262262
pub struct EvaluationCache<'tcx> {
263263
pub hashmap: Lock<
264-
FxHashMap<ty::ParamEnvAnd<'tcx, ty::PolyTraitRef<'tcx>>, WithDepNode<EvaluationResult>>,
264+
FxHashMap<
265+
ty::ParamEnvAnd<'tcx, ty::PolyTraitPredicate<'tcx>>,
266+
WithDepNode<EvaluationResult>,
267+
>,
265268
>,
266269
}
267270

src/librustc_infer/traits/select.rs

+82-87
Original file line numberDiff line numberDiff line change
@@ -835,18 +835,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
835835
trait_ref: ty::PolyTraitRef<'tcx>,
836836
) -> Option<EvaluationResult> {
837837
let tcx = self.tcx();
838-
if self.can_use_global_caches(param_env) {
839-
let cache = tcx.evaluation_cache.hashmap.borrow();
840-
if let Some(cached) = cache.get(&param_env.and(trait_ref)) {
841-
return Some(cached.get(tcx));
842-
}
843-
}
844-
self.infcx
845-
.evaluation_cache
846-
.hashmap
847-
.borrow()
848-
.get(&param_env.and(trait_ref))
849-
.map(|v| v.get(tcx))
838+
// FIXME(eddyb) pass in the `ty::PolyTraitPredicate` instead.
839+
let predicate = trait_ref.map_bound(|trait_ref| ty::TraitPredicate { trait_ref });
840+
// FIXME(eddyb) reuse the key between checking the cache and inserting.
841+
let cache_key = param_env.and(predicate);
842+
let cache = if self.can_use_global_caches(&cache_key) {
843+
&tcx.evaluation_cache
844+
} else {
845+
&self.infcx.evaluation_cache
846+
};
847+
848+
cache.hashmap.borrow().get(&cache_key).map(|v| v.get(tcx))
850849
}
851850

852851
fn insert_evaluation_cache(
@@ -862,31 +861,26 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
862861
return;
863862
}
864863

865-
if self.can_use_global_caches(param_env) {
866-
if !trait_ref.has_local_value() {
867-
debug!(
868-
"insert_evaluation_cache(trait_ref={:?}, candidate={:?}) global",
869-
trait_ref, result,
870-
);
871-
// This may overwrite the cache with the same value
872-
// FIXME: Due to #50507 this overwrites the different values
873-
// This should be changed to use HashMapExt::insert_same
874-
// when that is fixed
875-
self.tcx()
876-
.evaluation_cache
877-
.hashmap
878-
.borrow_mut()
879-
.insert(param_env.and(trait_ref), WithDepNode::new(dep_node, result));
880-
return;
881-
}
882-
}
864+
// FIXME(eddyb) pass in the `ty::PolyTraitPredicate` instead.
865+
let predicate = trait_ref.map_bound(|trait_ref| ty::TraitPredicate { trait_ref });
866+
// FIXME(eddyb) reuse the key between checking the cache and inserting.
867+
let cache_key = param_env.and(predicate);
868+
let cache = if self.can_use_global_caches(&cache_key) {
869+
debug!(
870+
"insert_evaluation_cache(trait_ref={:?}, candidate={:?}) global",
871+
trait_ref, result,
872+
);
873+
// This may overwrite the cache with the same value
874+
// FIXME: Due to #50507 this overwrites the different values
875+
// This should be changed to use HashMapExt::insert_same
876+
// when that is fixed
877+
&self.tcx().evaluation_cache
878+
} else {
879+
debug!("insert_evaluation_cache(trait_ref={:?}, candidate={:?})", trait_ref, result,);
880+
&self.infcx.evaluation_cache
881+
};
883882

884-
debug!("insert_evaluation_cache(trait_ref={:?}, candidate={:?})", trait_ref, result,);
885-
self.infcx
886-
.evaluation_cache
887-
.hashmap
888-
.borrow_mut()
889-
.insert(param_env.and(trait_ref), WithDepNode::new(dep_node, result));
883+
cache.hashmap.borrow_mut().insert(cache_key, WithDepNode::new(dep_node, result));
890884
}
891885

892886
/// For various reasons, it's possible for a subobligation
@@ -961,7 +955,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
961955
debug_assert!(!stack.obligation.predicate.has_escaping_bound_vars());
962956

963957
if let Some(c) =
964-
self.check_candidate_cache(stack.obligation.param_env, &cache_fresh_trait_pred)
958+
self.check_candidate_cache(stack.obligation.param_env, cache_fresh_trait_pred)
965959
{
966960
debug!("CACHE HIT: SELECT({:?})={:?}", cache_fresh_trait_pred, c);
967961
return c;
@@ -982,6 +976,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
982976
cache_fresh_trait_pred,
983977
dep_node,
984978
candidate.clone(),
979+
stack.obligation.cause.span,
985980
);
986981
candidate
987982
}
@@ -1218,13 +1213,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12181213
}
12191214

12201215
/// Returns `true` if the global caches can be used.
1221-
/// Do note that if the type itself is not in the
1222-
/// global tcx, the local caches will be used.
1223-
fn can_use_global_caches(&self, param_env: ty::ParamEnv<'tcx>) -> bool {
1224-
// If there are any e.g. inference variables in the `ParamEnv`, then we
1225-
// always use a cache local to this particular scope. Otherwise, we
1226-
// switch to a global cache.
1227-
if param_env.has_local_value() {
1216+
fn can_use_global_caches(
1217+
&self,
1218+
cache_key: &ty::ParamEnvAnd<'tcx, ty::PolyTraitPredicate<'tcx>>,
1219+
) -> bool {
1220+
// If there are any e.g. inference variables in the `ParamEnv` or predicate,
1221+
// then we always use a cache local to this particular inference context.
1222+
// Otherwise, we switch to a global cache.
1223+
if cache_key.has_local_value() {
12281224
return false;
12291225
}
12301226

@@ -1246,22 +1242,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12461242
fn check_candidate_cache(
12471243
&mut self,
12481244
param_env: ty::ParamEnv<'tcx>,
1249-
cache_fresh_trait_pred: &ty::PolyTraitPredicate<'tcx>,
1245+
cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>,
12501246
) -> Option<SelectionResult<'tcx, SelectionCandidate<'tcx>>> {
12511247
let tcx = self.tcx();
1252-
let trait_ref = &cache_fresh_trait_pred.skip_binder().trait_ref;
1253-
if self.can_use_global_caches(param_env) {
1254-
let cache = tcx.selection_cache.hashmap.borrow();
1255-
if let Some(cached) = cache.get(&param_env.and(*trait_ref)) {
1256-
return Some(cached.get(tcx));
1257-
}
1258-
}
1259-
self.infcx
1260-
.selection_cache
1261-
.hashmap
1262-
.borrow()
1263-
.get(&param_env.and(*trait_ref))
1264-
.map(|v| v.get(tcx))
1248+
// FIXME(eddyb) reuse the key between checking the cache and inserting.
1249+
let cache_key = param_env.and(cache_fresh_trait_pred);
1250+
let cache = if self.can_use_global_caches(&cache_key) {
1251+
&tcx.selection_cache
1252+
} else {
1253+
&self.infcx.selection_cache
1254+
};
1255+
1256+
cache.hashmap.borrow().get(&cache_key).map(|v| v.get(tcx))
12651257
}
12661258

12671259
/// Determines whether can we safely cache the result
@@ -1298,47 +1290,50 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12981290
cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>,
12991291
dep_node: DepNodeIndex,
13001292
candidate: SelectionResult<'tcx, SelectionCandidate<'tcx>>,
1293+
span: rustc_span::Span,
13011294
) {
13021295
let tcx = self.tcx();
1303-
let trait_ref = cache_fresh_trait_pred.skip_binder().trait_ref;
13041296

13051297
if !self.can_cache_candidate(&candidate) {
13061298
debug!(
1307-
"insert_candidate_cache(trait_ref={:?}, candidate={:?} -\
1299+
"insert_candidate_cache(predicate={:?}, candidate={:?} -\
13081300
candidate is not cacheable",
1309-
trait_ref, candidate
1301+
cache_fresh_trait_pred, candidate
13101302
);
13111303
return;
13121304
}
13131305

1314-
if self.can_use_global_caches(param_env) {
1315-
if let Err(Overflow) = candidate {
1316-
// Don't cache overflow globally; we only produce this in certain modes.
1317-
} else if !trait_ref.has_local_value() {
1318-
if !candidate.has_local_value() {
1319-
debug!(
1320-
"insert_candidate_cache(trait_ref={:?}, candidate={:?}) global",
1321-
trait_ref, candidate,
1322-
);
1323-
// This may overwrite the cache with the same value.
1324-
tcx.selection_cache
1325-
.hashmap
1326-
.borrow_mut()
1327-
.insert(param_env.and(trait_ref), WithDepNode::new(dep_node, candidate));
1328-
return;
1329-
}
1330-
}
1306+
// HACK(eddyb) never cache overflow (this check used to be global-only).
1307+
if let Err(Overflow) = candidate {
1308+
return;
13311309
}
13321310

1333-
debug!(
1334-
"insert_candidate_cache(trait_ref={:?}, candidate={:?}) local",
1335-
trait_ref, candidate,
1336-
);
1337-
self.infcx
1338-
.selection_cache
1339-
.hashmap
1340-
.borrow_mut()
1341-
.insert(param_env.and(trait_ref), WithDepNode::new(dep_node, candidate));
1311+
// FIXME(eddyb) reuse the key between checking the cache and inserting.
1312+
let cache_key = param_env.and(cache_fresh_trait_pred);
1313+
let cache = if self.can_use_global_caches(&cache_key) {
1314+
if candidate.has_local_value() {
1315+
span_bug!(
1316+
span,
1317+
"selecting inference-free `{:?}` resulted in `{:?}`?!",
1318+
cache_fresh_trait_pred,
1319+
candidate,
1320+
);
1321+
}
1322+
debug!(
1323+
"insert_candidate_cache(predicate={:?}, candidate={:?}) global",
1324+
cache_fresh_trait_pred, candidate,
1325+
);
1326+
// This may overwrite the cache with the same value.
1327+
&tcx.selection_cache
1328+
} else {
1329+
debug!(
1330+
"insert_candidate_cache(predicate={:?}, candidate={:?}) local",
1331+
cache_fresh_trait_pred, candidate,
1332+
);
1333+
&self.infcx.selection_cache
1334+
};
1335+
1336+
cache.hashmap.borrow_mut().insert(cache_key, WithDepNode::new(dep_node, candidate));
13421337
}
13431338

13441339
fn assemble_candidates<'o>(

0 commit comments

Comments
 (0)