Rollup merge of #68857 - Marwes:allocations, r=matthewjasper
perf: Reduce Vec allocations in normalization by passing &mut Vec Complicates the code a bit but allocation/freeing were a few percent of the overall runtime in trait heavy code.
This commit is contained in:
commit
664d87f9b1
@ -50,7 +50,9 @@
|
||||
pub use self::object_safety::ObjectSafetyViolation;
|
||||
pub use self::on_unimplemented::{OnUnimplementedDirective, OnUnimplementedNote};
|
||||
pub use self::project::MismatchedProjectionTypes;
|
||||
pub use self::project::{normalize, normalize_projection_type, poly_project_and_unify_type};
|
||||
pub use self::project::{
|
||||
normalize, normalize_projection_type, normalize_to, poly_project_and_unify_type,
|
||||
};
|
||||
pub use self::project::{Normalized, ProjectionCache, ProjectionCacheSnapshot};
|
||||
pub use self::select::{IntercrateAmbiguityCause, SelectionContext};
|
||||
pub use self::specialize::find_associated_item;
|
||||
|
@ -216,7 +216,22 @@ pub fn normalize<'a, 'b, 'tcx, T>(
|
||||
where
|
||||
T: TypeFoldable<'tcx>,
|
||||
{
|
||||
normalize_with_depth(selcx, param_env, cause, 0, value)
|
||||
let mut obligations = Vec::new();
|
||||
let value = normalize_to(selcx, param_env, cause, value, &mut obligations);
|
||||
Normalized { value, obligations }
|
||||
}
|
||||
|
||||
pub fn normalize_to<'a, 'b, 'tcx, T>(
|
||||
selcx: &'a mut SelectionContext<'b, 'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
cause: ObligationCause<'tcx>,
|
||||
value: &T,
|
||||
obligations: &mut Vec<PredicateObligation<'tcx>>,
|
||||
) -> T
|
||||
where
|
||||
T: TypeFoldable<'tcx>,
|
||||
{
|
||||
normalize_with_depth_to(selcx, param_env, cause, 0, value, obligations)
|
||||
}
|
||||
|
||||
/// As `normalize`, but with a custom depth.
|
||||
@ -227,11 +242,27 @@ pub fn normalize_with_depth<'a, 'b, 'tcx, T>(
|
||||
depth: usize,
|
||||
value: &T,
|
||||
) -> Normalized<'tcx, T>
|
||||
where
|
||||
T: TypeFoldable<'tcx>,
|
||||
{
|
||||
let mut obligations = Vec::new();
|
||||
let value = normalize_with_depth_to(selcx, param_env, cause, depth, value, &mut obligations);
|
||||
Normalized { value, obligations }
|
||||
}
|
||||
|
||||
pub fn normalize_with_depth_to<'a, 'b, 'tcx, T>(
|
||||
selcx: &'a mut SelectionContext<'b, 'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
cause: ObligationCause<'tcx>,
|
||||
depth: usize,
|
||||
value: &T,
|
||||
obligations: &mut Vec<PredicateObligation<'tcx>>,
|
||||
) -> T
|
||||
where
|
||||
T: TypeFoldable<'tcx>,
|
||||
{
|
||||
debug!("normalize_with_depth(depth={}, value={:?})", depth, value);
|
||||
let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth);
|
||||
let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth, obligations);
|
||||
let result = normalizer.fold(value);
|
||||
debug!(
|
||||
"normalize_with_depth: depth={} result={:?} with {} obligations",
|
||||
@ -240,14 +271,14 @@ pub fn normalize_with_depth<'a, 'b, 'tcx, T>(
|
||||
normalizer.obligations.len()
|
||||
);
|
||||
debug!("normalize_with_depth: depth={} obligations={:?}", depth, normalizer.obligations);
|
||||
Normalized { value: result, obligations: normalizer.obligations }
|
||||
result
|
||||
}
|
||||
|
||||
struct AssocTypeNormalizer<'a, 'b, 'tcx> {
|
||||
selcx: &'a mut SelectionContext<'b, 'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
cause: ObligationCause<'tcx>,
|
||||
obligations: Vec<PredicateObligation<'tcx>>,
|
||||
obligations: &'a mut Vec<PredicateObligation<'tcx>>,
|
||||
depth: usize,
|
||||
}
|
||||
|
||||
@ -257,8 +288,9 @@ fn new(
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
cause: ObligationCause<'tcx>,
|
||||
depth: usize,
|
||||
obligations: &'a mut Vec<PredicateObligation<'tcx>>,
|
||||
) -> AssocTypeNormalizer<'a, 'b, 'tcx> {
|
||||
AssocTypeNormalizer { selcx, param_env, cause, obligations: vec![], depth }
|
||||
AssocTypeNormalizer { selcx, param_env, cause, obligations, depth }
|
||||
}
|
||||
|
||||
fn fold<T: TypeFoldable<'tcx>>(&mut self, value: &T) -> T {
|
||||
@ -343,7 +375,7 @@ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
);
|
||||
debug!(
|
||||
"AssocTypeNormalizer: depth={} normalized {:?} to {:?}, \
|
||||
now with {} obligations",
|
||||
now with {} obligations",
|
||||
self.depth,
|
||||
ty,
|
||||
normalized_ty,
|
||||
@ -441,8 +473,8 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
|
||||
|
||||
debug!(
|
||||
"opt_normalize_projection_type(\
|
||||
projection_ty={:?}, \
|
||||
depth={})",
|
||||
projection_ty={:?}, \
|
||||
depth={})",
|
||||
projection_ty, depth
|
||||
);
|
||||
|
||||
@ -469,7 +501,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
|
||||
// changes
|
||||
debug!(
|
||||
"opt_normalize_projection_type: \
|
||||
found cache entry: ambiguous"
|
||||
found cache entry: ambiguous"
|
||||
);
|
||||
if !projection_ty.has_closure_types() {
|
||||
return None;
|
||||
@ -498,7 +530,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
|
||||
|
||||
debug!(
|
||||
"opt_normalize_projection_type: \
|
||||
found cache entry: in-progress"
|
||||
found cache entry: in-progress"
|
||||
);
|
||||
|
||||
// But for now, let's classify this as an overflow:
|
||||
@ -521,7 +553,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
|
||||
// evaluations can causes ICEs (e.g., #43132).
|
||||
debug!(
|
||||
"opt_normalize_projection_type: \
|
||||
found normalized ty `{:?}`",
|
||||
found normalized ty `{:?}`",
|
||||
ty
|
||||
);
|
||||
|
||||
@ -546,7 +578,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
|
||||
Err(ProjectionCacheEntry::Error) => {
|
||||
debug!(
|
||||
"opt_normalize_projection_type: \
|
||||
found error"
|
||||
found error"
|
||||
);
|
||||
let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth);
|
||||
obligations.extend(result.obligations);
|
||||
@ -567,23 +599,28 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
|
||||
|
||||
debug!(
|
||||
"opt_normalize_projection_type: \
|
||||
projected_ty={:?} \
|
||||
depth={} \
|
||||
projected_obligations={:?}",
|
||||
projected_ty={:?} \
|
||||
depth={} \
|
||||
projected_obligations={:?}",
|
||||
projected_ty, depth, projected_obligations
|
||||
);
|
||||
|
||||
let result = if projected_ty.has_projections() {
|
||||
let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth + 1);
|
||||
let mut normalizer = AssocTypeNormalizer::new(
|
||||
selcx,
|
||||
param_env,
|
||||
cause,
|
||||
depth + 1,
|
||||
&mut projected_obligations,
|
||||
);
|
||||
let normalized_ty = normalizer.fold(&projected_ty);
|
||||
|
||||
debug!(
|
||||
"opt_normalize_projection_type: \
|
||||
normalized_ty={:?} depth={}",
|
||||
normalized_ty={:?} depth={}",
|
||||
normalized_ty, depth
|
||||
);
|
||||
|
||||
projected_obligations.extend(normalizer.obligations);
|
||||
Normalized { value: normalized_ty, obligations: projected_obligations }
|
||||
} else {
|
||||
Normalized { value: projected_ty, obligations: projected_obligations }
|
||||
@ -597,7 +634,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
|
||||
Ok(ProjectedTy::NoProgress(projected_ty)) => {
|
||||
debug!(
|
||||
"opt_normalize_projection_type: \
|
||||
projected_ty={:?} no progress",
|
||||
projected_ty={:?} no progress",
|
||||
projected_ty
|
||||
);
|
||||
let result = Normalized { value: projected_ty, obligations: vec![] };
|
||||
@ -608,7 +645,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
|
||||
Err(ProjectionTyError::TooManyCandidates) => {
|
||||
debug!(
|
||||
"opt_normalize_projection_type: \
|
||||
too many candidates"
|
||||
too many candidates"
|
||||
);
|
||||
infcx.projection_cache.borrow_mut().ambiguous(cache_key);
|
||||
None
|
||||
@ -930,7 +967,7 @@ fn assemble_candidates_from_predicates<'cx, 'tcx, I>(
|
||||
|
||||
debug!(
|
||||
"assemble_candidates_from_predicates: candidate={:?} \
|
||||
is_match={} same_def_id={}",
|
||||
is_match={} same_def_id={}",
|
||||
data, is_match, same_def_id
|
||||
);
|
||||
|
||||
@ -1192,7 +1229,7 @@ fn confirm_object_candidate<'cx, 'tcx>(
|
||||
None => {
|
||||
debug!(
|
||||
"confirm_object_candidate: no env-predicate \
|
||||
found in object type `{:?}`; ill-formed",
|
||||
found in object type `{:?}`; ill-formed",
|
||||
object_ty
|
||||
);
|
||||
return Progress::error(selcx.tcx());
|
||||
|
@ -9,7 +9,9 @@
|
||||
|
||||
use super::coherence::{self, Conflict};
|
||||
use super::project;
|
||||
use super::project::{normalize_with_depth, Normalized, ProjectionCacheKey};
|
||||
use super::project::{
|
||||
normalize_with_depth, normalize_with_depth_to, Normalized, ProjectionCacheKey,
|
||||
};
|
||||
use super::util;
|
||||
use super::util::{closure_trait_ref_and_return_type, predicate_for_trait_def};
|
||||
use super::wf;
|
||||
@ -1019,7 +1021,7 @@ fn filter_negative_and_reservation_impls(
|
||||
if let Some(value) = value {
|
||||
debug!(
|
||||
"filter_negative_and_reservation_impls: \
|
||||
reservation impl ambiguity on {:?}",
|
||||
reservation impl ambiguity on {:?}",
|
||||
def_id
|
||||
);
|
||||
intercrate_ambiguity_clauses.push(
|
||||
@ -1317,7 +1319,7 @@ fn insert_candidate_cache(
|
||||
if !self.can_cache_candidate(&candidate) {
|
||||
debug!(
|
||||
"insert_candidate_cache(trait_ref={:?}, candidate={:?} -\
|
||||
candidate is not cacheable",
|
||||
candidate is not cacheable",
|
||||
trait_ref, candidate
|
||||
);
|
||||
return;
|
||||
@ -3484,25 +3486,23 @@ fn impl_or_trait_obligations(
|
||||
// that order.
|
||||
let predicates = tcx.predicates_of(def_id);
|
||||
assert_eq!(predicates.parent, None);
|
||||
let mut predicates: Vec<_> = predicates
|
||||
.predicates
|
||||
.iter()
|
||||
.flat_map(|(predicate, _)| {
|
||||
let predicate = normalize_with_depth(
|
||||
self,
|
||||
param_env,
|
||||
cause.clone(),
|
||||
recursion_depth,
|
||||
&predicate.subst(tcx, substs),
|
||||
);
|
||||
predicate.obligations.into_iter().chain(Some(Obligation {
|
||||
cause: cause.clone(),
|
||||
recursion_depth,
|
||||
param_env,
|
||||
predicate: predicate.value,
|
||||
}))
|
||||
})
|
||||
.collect();
|
||||
let mut obligations = Vec::new();
|
||||
for (predicate, _) in predicates.predicates {
|
||||
let predicate = normalize_with_depth_to(
|
||||
self,
|
||||
param_env,
|
||||
cause.clone(),
|
||||
recursion_depth,
|
||||
&predicate.subst(tcx, substs),
|
||||
&mut obligations,
|
||||
);
|
||||
obligations.push(Obligation {
|
||||
cause: cause.clone(),
|
||||
recursion_depth,
|
||||
param_env,
|
||||
predicate,
|
||||
});
|
||||
}
|
||||
|
||||
// We are performing deduplication here to avoid exponential blowups
|
||||
// (#38528) from happening, but the real cause of the duplication is
|
||||
@ -3513,20 +3513,20 @@ fn impl_or_trait_obligations(
|
||||
// This code is hot enough that it's worth avoiding the allocation
|
||||
// required for the FxHashSet when possible. Special-casing lengths 0,
|
||||
// 1 and 2 covers roughly 75-80% of the cases.
|
||||
if predicates.len() <= 1 {
|
||||
if obligations.len() <= 1 {
|
||||
// No possibility of duplicates.
|
||||
} else if predicates.len() == 2 {
|
||||
} else if obligations.len() == 2 {
|
||||
// Only two elements. Drop the second if they are equal.
|
||||
if predicates[0] == predicates[1] {
|
||||
predicates.truncate(1);
|
||||
if obligations[0] == obligations[1] {
|
||||
obligations.truncate(1);
|
||||
}
|
||||
} else {
|
||||
// Three or more elements. Use a general deduplication process.
|
||||
let mut seen = FxHashSet::default();
|
||||
predicates.retain(|i| seen.insert(i.clone()));
|
||||
obligations.retain(|i| seen.insert(i.clone()));
|
||||
}
|
||||
|
||||
predicates
|
||||
obligations
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,6 @@
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_span::symbol::{kw, Ident};
|
||||
use rustc_span::Span;
|
||||
use std::iter::once;
|
||||
|
||||
/// Returns the set of obligations needed to make `ty` well-formed.
|
||||
/// If `ty` contains unresolved inference variables, this may include
|
||||
@ -26,6 +25,7 @@ pub fn obligations<'a, 'tcx>(
|
||||
let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None };
|
||||
if wf.compute(ty) {
|
||||
debug!("wf::obligations({:?}, body_id={:?}) = {:?}", ty, body_id, wf.out);
|
||||
|
||||
let result = wf.normalize();
|
||||
debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", ty, body_id, result);
|
||||
Some(result)
|
||||
@ -143,15 +143,15 @@ fn normalize(&mut self) -> Vec<traits::PredicateObligation<'tcx>> {
|
||||
let cause = self.cause(traits::MiscObligation);
|
||||
let infcx = &mut self.infcx;
|
||||
let param_env = self.param_env;
|
||||
self.out
|
||||
.iter()
|
||||
.inspect(|pred| assert!(!pred.has_escaping_bound_vars()))
|
||||
.flat_map(|pred| {
|
||||
let mut selcx = traits::SelectionContext::new(infcx);
|
||||
let pred = traits::normalize(&mut selcx, param_env, cause.clone(), pred);
|
||||
once(pred.value).chain(pred.obligations)
|
||||
})
|
||||
.collect()
|
||||
let mut obligations = Vec::new();
|
||||
self.out.iter().inspect(|pred| assert!(!pred.has_escaping_bound_vars())).for_each(|pred| {
|
||||
let mut selcx = traits::SelectionContext::new(infcx);
|
||||
let i = obligations.len();
|
||||
let value =
|
||||
traits::normalize_to(&mut selcx, param_env, cause.clone(), pred, &mut obligations);
|
||||
obligations.insert(i, value);
|
||||
});
|
||||
obligations
|
||||
}
|
||||
|
||||
/// Pushes the obligations required for `trait_ref` to be WF into `self.out`.
|
||||
|
Loading…
Reference in New Issue
Block a user