Preserve most sub-obligations in the projection cache
This commit is contained in:
parent
64929313f5
commit
f2d9ee9c34
6 changed files with 41 additions and 82 deletions
|
@ -10,6 +10,7 @@ use super::PredicateObligation;
|
|||
use super::Selection;
|
||||
use super::SelectionContext;
|
||||
use super::SelectionError;
|
||||
use super::TraitQueryMode;
|
||||
use super::{
|
||||
ImplSourceClosureData, ImplSourceDiscriminantKindData, ImplSourceFnPointerData,
|
||||
ImplSourceGeneratorData, ImplSourcePointeeData, ImplSourceUserDefinedData,
|
||||
|
@ -18,7 +19,7 @@ use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey};
|
|||
|
||||
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime};
|
||||
use crate::traits::error_reporting::InferCtxtExt;
|
||||
use crate::traits::error_reporting::InferCtxtExt as _;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_errors::ErrorReported;
|
||||
use rustc_hir::def_id::DefId;
|
||||
|
@ -912,6 +913,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
|
|||
}
|
||||
|
||||
let obligation = Obligation::with_depth(cause.clone(), depth, param_env, projection_ty);
|
||||
|
||||
match project_type(selcx, &obligation) {
|
||||
Ok(ProjectedTy::Progress(Progress {
|
||||
ty: projected_ty,
|
||||
|
@ -925,7 +927,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
|
|||
let projected_ty = selcx.infcx().resolve_vars_if_possible(projected_ty);
|
||||
debug!(?projected_ty, ?depth, ?projected_obligations);
|
||||
|
||||
let result = if projected_ty.has_projections() {
|
||||
let mut result = if projected_ty.has_projections() {
|
||||
let mut normalizer = AssocTypeNormalizer::new(
|
||||
selcx,
|
||||
param_env,
|
||||
|
@ -942,8 +944,26 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
|
|||
Normalized { value: projected_ty, obligations: projected_obligations }
|
||||
};
|
||||
|
||||
let cache_value = prune_cache_value_obligations(infcx, &result);
|
||||
infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, cache_value);
|
||||
let mut canonical =
|
||||
SelectionContext::with_query_mode(selcx.infcx(), TraitQueryMode::Canonical);
|
||||
result.obligations.drain_filter(|projected_obligation| {
|
||||
// If any global obligations always apply, considering regions, then we don't
|
||||
// need to include them. The `is_global` check rules out inference variables,
|
||||
// so there's no need for the caller of `opt_normalize_projection_type`
|
||||
// to evaluate them.
|
||||
// Note that we do *not* discard obligations that evaluate to
|
||||
// `EvaluatedtoOkModuloRegions`. Evaluating these obligations
|
||||
// inside of a query (e.g. `evaluate_obligation`) can change
|
||||
// the result to `EvaluatedToOkModuloRegions`, while an
|
||||
// `EvaluatedToOk` obligation will never change the result.
|
||||
// See #85360 for more details
|
||||
projected_obligation.is_global(canonical.tcx())
|
||||
&& canonical
|
||||
.evaluate_root_obligation(projected_obligation)
|
||||
.map_or(false, |res| res.must_apply_considering_regions())
|
||||
});
|
||||
|
||||
infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, result.clone());
|
||||
obligations.extend(result.obligations);
|
||||
Ok(Some(result.value))
|
||||
}
|
||||
|
@ -974,49 +994,6 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
/// If there are unresolved type variables, then we need to include
|
||||
/// any subobligations that bind them, at least until those type
|
||||
/// variables are fully resolved.
|
||||
fn prune_cache_value_obligations<'a, 'tcx>(
|
||||
infcx: &'a InferCtxt<'a, 'tcx>,
|
||||
result: &NormalizedTy<'tcx>,
|
||||
) -> NormalizedTy<'tcx> {
|
||||
if infcx.unresolved_type_vars(&result.value).is_none() {
|
||||
return NormalizedTy { value: result.value, obligations: vec![] };
|
||||
}
|
||||
|
||||
let mut obligations: Vec<_> = result
|
||||
.obligations
|
||||
.iter()
|
||||
.filter(|obligation| {
|
||||
let bound_predicate = obligation.predicate.kind();
|
||||
match bound_predicate.skip_binder() {
|
||||
// We found a `T: Foo<X = U>` predicate, let's check
|
||||
// if `U` references any unresolved type
|
||||
// variables. In principle, we only care if this
|
||||
// projection can help resolve any of the type
|
||||
// variables found in `result.value` -- but we just
|
||||
// check for any type variables here, for fear of
|
||||
// indirect obligations (e.g., we project to `?0`,
|
||||
// but we have `T: Foo<X = ?1>` and `?1: Bar<X =
|
||||
// ?0>`).
|
||||
ty::PredicateKind::Projection(data) => {
|
||||
infcx.unresolved_type_vars(&bound_predicate.rebind(data.ty)).is_some()
|
||||
}
|
||||
|
||||
// We are only interested in `T: Foo<X = U>` predicates, whre
|
||||
// `U` references one of `unresolved_type_vars`. =)
|
||||
_ => false,
|
||||
}
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
obligations.shrink_to_fit();
|
||||
|
||||
NormalizedTy { value: result.value, obligations }
|
||||
}
|
||||
|
||||
/// If we are projecting `<T as Trait>::Item`, but `T: Trait` does not
|
||||
/// hold. In various error cases, we cannot generate a valid
|
||||
/// normalized projection. Therefore, we create an inference variable
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue