Skip to content

Commit

Permalink
Reduce Vec allocations in normalization by passing &mut Vec
Browse files Browse the repository at this point in the history
  • Loading branch information
Marwes authored and Markus Westerlind committed Feb 8, 2020
1 parent 1f8df25 commit 51b891a
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 61 deletions.
4 changes: 3 additions & 1 deletion src/librustc/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ pub use self::object_safety::MethodViolationCode;
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;
Expand Down
81 changes: 59 additions & 22 deletions src/librustc/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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",
Expand All @@ -240,14 +271,14 @@ where
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,
}

Expand All @@ -257,8 +288,9 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
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 {
Expand Down Expand Up @@ -343,7 +375,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
);
debug!(
"AssocTypeNormalizer: depth={} normalized {:?} to {:?}, \
now with {} obligations",
now with {} obligations",
self.depth,
ty,
normalized_ty,
Expand Down Expand Up @@ -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
);

Expand All @@ -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;
Expand Down Expand Up @@ -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:
Expand All @@ -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
);

Expand All @@ -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);
Expand All @@ -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 }
Expand All @@ -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![] };
Expand All @@ -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
Expand Down Expand Up @@ -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
);

Expand Down Expand Up @@ -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());
Expand Down
56 changes: 28 additions & 28 deletions src/librustc/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use self::SelectionCandidate::*;

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;
Expand Down Expand Up @@ -1019,7 +1021,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
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(
Expand Down Expand Up @@ -1317,7 +1319,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
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;
Expand Down Expand Up @@ -3484,25 +3486,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// 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
Expand All @@ -3513,20 +3513,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// 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
}
}

Expand Down
20 changes: 10 additions & 10 deletions src/librustc/traits/wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use rustc_hir as hir;
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
Expand All @@ -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)
Expand Down Expand Up @@ -143,15 +143,15 @@ impl<'a, 'tcx> WfPredicates<'a, '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`.
Expand Down

0 comments on commit 51b891a

Please sign in to comment.