Auto merge of #95031 - compiler-errors:param-env-cache, r=Aaron1011

Do not use `ParamEnv::and` when building a cache key from a param-env and trait eval candidate

Do not use `ParamEnv::and` to cache a param-env with a selection/evaluation candidate.

This is because if the param-env is `RevealAll` mode, and the candidate looks global (i.e. it has erased regions, which can show up when we normalize a projection type under a binder<sup>1</sup>), then when we use `ParamEnv::and` to pair the candidate and the param-env for use as a cache key, we will throw away the param-env's caller bounds, and we'll end up caching a candidate that we inferred from the param-env with a empty param-env, which may cause cache-hit later when we have an empty param-env, and possibly mess with normalization like we see in the referenced issue during codegen.

Not sure how to trigger this with a more structured test, but changing `check-pass` to `build-pass` triggers the case that https://github.com/rust-lang/rust/issues/94903 detected.

<sup>1.</sup> That is, we will replace the late-bound region with a placeholder, which gets canonicalized and turned into an infererence variable, which gets erased during region freshening right before we cache the result. Sorry, it's quite a few steps.

Fixes #94903
r? `@Aaron1011` (or reassign as you see fit)
This commit is contained in:
bors 2022-04-04 04:48:36 +00:00
commit ec667fbcfc
3 changed files with 20 additions and 12 deletions

View file

@ -13,12 +13,19 @@ use rustc_hir::def_id::DefId;
use rustc_query_system::cache::Cache; use rustc_query_system::cache::Cache;
pub type SelectionCache<'tcx> = Cache< pub type SelectionCache<'tcx> = Cache<
ty::ParamEnvAnd<'tcx, ty::TraitPredicate<'tcx>>, // This cache does not use `ParamEnvAnd` in its keys because `ParamEnv::and` can replace
// caller bounds with an empty list if the `TraitPredicate` looks global, which may happen
// after erasing lifetimes from the predicate.
(ty::ParamEnv<'tcx>, ty::TraitPredicate<'tcx>),
SelectionResult<'tcx, SelectionCandidate<'tcx>>, SelectionResult<'tcx, SelectionCandidate<'tcx>>,
>; >;
pub type EvaluationCache<'tcx> = pub type EvaluationCache<'tcx> = Cache<
Cache<ty::ParamEnvAnd<'tcx, ty::PolyTraitPredicate<'tcx>>, EvaluationResult>; // See above: this cache does not use `ParamEnvAnd` in its keys due to sometimes incorrectly
// caching with the wrong `ParamEnv`.
(ty::ParamEnv<'tcx>, ty::PolyTraitPredicate<'tcx>),
EvaluationResult,
>;
/// The selection process begins by considering all impls, where /// The selection process begins by considering all impls, where
/// clauses, and so forth that might resolve an obligation. Sometimes /// clauses, and so forth that might resolve an obligation. Sometimes

View file

@ -1025,11 +1025,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let tcx = self.tcx(); let tcx = self.tcx();
if self.can_use_global_caches(param_env) { if self.can_use_global_caches(param_env) {
if let Some(res) = tcx.evaluation_cache.get(&param_env.and(trait_pred), tcx) { if let Some(res) = tcx.evaluation_cache.get(&(param_env, trait_pred), tcx) {
return Some(res); return Some(res);
} }
} }
self.infcx.evaluation_cache.get(&param_env.and(trait_pred), tcx) self.infcx.evaluation_cache.get(&(param_env, trait_pred), tcx)
} }
fn insert_evaluation_cache( fn insert_evaluation_cache(
@ -1060,13 +1060,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// FIXME: Due to #50507 this overwrites the different values // FIXME: Due to #50507 this overwrites the different values
// This should be changed to use HashMapExt::insert_same // This should be changed to use HashMapExt::insert_same
// when that is fixed // when that is fixed
self.tcx().evaluation_cache.insert(param_env.and(trait_pred), dep_node, result); self.tcx().evaluation_cache.insert((param_env, trait_pred), dep_node, result);
return; return;
} }
} }
debug!(?trait_pred, ?result, "insert_evaluation_cache"); debug!(?trait_pred, ?result, "insert_evaluation_cache");
self.infcx.evaluation_cache.insert(param_env.and(trait_pred), dep_node, result); self.infcx.evaluation_cache.insert((param_env, trait_pred), dep_node, result);
} }
/// For various reasons, it's possible for a subobligation /// For various reasons, it's possible for a subobligation
@ -1275,11 +1275,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
pred.remap_constness(tcx, &mut param_env); pred.remap_constness(tcx, &mut param_env);
if self.can_use_global_caches(param_env) { if self.can_use_global_caches(param_env) {
if let Some(res) = tcx.selection_cache.get(&param_env.and(pred), tcx) { if let Some(res) = tcx.selection_cache.get(&(param_env, pred), tcx) {
return Some(res); return Some(res);
} }
} }
self.infcx.selection_cache.get(&param_env.and(pred), tcx) self.infcx.selection_cache.get(&(param_env, pred), tcx)
} }
/// Determines whether can we safely cache the result /// Determines whether can we safely cache the result
@ -1340,14 +1340,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
if !candidate.needs_infer() { if !candidate.needs_infer() {
debug!(?pred, ?candidate, "insert_candidate_cache global"); debug!(?pred, ?candidate, "insert_candidate_cache global");
// This may overwrite the cache with the same value. // This may overwrite the cache with the same value.
tcx.selection_cache.insert(param_env.and(pred), dep_node, candidate); tcx.selection_cache.insert((param_env, pred), dep_node, candidate);
return; return;
} }
} }
} }
debug!(?pred, ?candidate, "insert_candidate_cache local"); debug!(?pred, ?candidate, "insert_candidate_cache local");
self.infcx.selection_cache.insert(param_env.and(pred), dep_node, candidate); self.infcx.selection_cache.insert((param_env, pred), dep_node, candidate);
} }
/// Matches a predicate against the bounds of its self type. /// Matches a predicate against the bounds of its self type.

View file

@ -1,4 +1,4 @@
// check-pass // build-pass
// edition:2018 // edition:2018
type BoxFuture<T> = std::pin::Pin<Box<dyn std::future::Future<Output=T>>>; type BoxFuture<T> = std::pin::Pin<Box<dyn std::future::Future<Output=T>>>;
@ -65,6 +65,7 @@ async fn run<S>(dep: &str)
where where
S: Storage, S: Storage,
for<'a> SaveUser<'a>: StorageRequest<S>, for<'a> SaveUser<'a>: StorageRequest<S>,
for<'a> SaveUser<'a>: StorageRequestReturnType,
{ {
User { dep }.save().await; User { dep }.save().await;
} }