Re-introduce concept of projection cache 'completion'
Instead of clearing out the cache entirely, we store the intermediate evaluation result into the cache entry. This accomplishes several things: * We avoid the performance hit associated with re-evaluating the sub-obligations * We avoid causing issues with incremental compilation, since the final evaluation result is always the same * We avoid affecting other uses of the same `InferCtxt` which might care about 'side effects' from processing the sub-obligations (e,g. region constraints). Only code that is specifically aware of the new 'complete' code is affected
This commit is contained in:
@@ -25,6 +25,8 @@ use super::{ObligationCause, PredicateObligation, TraitObligation};
|
||||
|
||||
use crate::infer::{InferCtxt, InferOk, TypeFreshener};
|
||||
use crate::traits::error_reporting::InferCtxtExt;
|
||||
use crate::traits::project::ProjectionCacheKeyExt;
|
||||
use crate::traits::ProjectionCacheKey;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
@@ -550,8 +552,54 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
let project_obligation = obligation.with(data);
|
||||
match project::poly_project_and_unify_type(self, &project_obligation) {
|
||||
Ok(Ok(Some(mut subobligations))) => {
|
||||
self.add_depth(subobligations.iter_mut(), obligation.recursion_depth);
|
||||
self.evaluate_predicates_recursively(previous_stack, subobligations)
|
||||
'compute_res: {
|
||||
// If we've previously marked this projection as 'complete', thne
|
||||
// use the final cached result (either `EvaluatedToOk` or
|
||||
// `EvaluatedToOkModuloRegions`), and skip re-evaluating the
|
||||
// sub-obligations.
|
||||
if let Some(key) =
|
||||
ProjectionCacheKey::from_poly_projection_predicate(self, data)
|
||||
{
|
||||
if let Some(cached_res) = self
|
||||
.infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.projection_cache()
|
||||
.is_complete(key)
|
||||
{
|
||||
break 'compute_res Ok(cached_res);
|
||||
}
|
||||
}
|
||||
|
||||
self.add_depth(
|
||||
subobligations.iter_mut(),
|
||||
obligation.recursion_depth,
|
||||
);
|
||||
let res = self.evaluate_predicates_recursively(
|
||||
previous_stack,
|
||||
subobligations,
|
||||
);
|
||||
if let Ok(res) = res {
|
||||
if res == EvaluatedToOk || res == EvaluatedToOkModuloRegions {
|
||||
if let Some(key) =
|
||||
ProjectionCacheKey::from_poly_projection_predicate(
|
||||
self, data,
|
||||
)
|
||||
{
|
||||
// If the result is something that we can cache, then mark this
|
||||
// entry as 'complete'. This will allow us to skip evaluating the
|
||||
// suboligations at all the next time we evaluate the projection
|
||||
// predicate.
|
||||
self.infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.projection_cache()
|
||||
.complete(key, res);
|
||||
}
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
Ok(Ok(None)) => Ok(EvaluatedToAmbig),
|
||||
Ok(Err(project::InProgress)) => Ok(EvaluatedToRecur),
|
||||
|
||||
Reference in New Issue
Block a user