Avoid allocations in opt_normalize_projection_type
.
This patch changes `opt_normalize_project_type` so it appends obligations to a given obligations vector, instead of returning a new obligations vector. This change avoids lots of allocations. In the most extreme case, for a clean "Check" build of serde it reduces the total number of allocations by 20%.
This commit is contained in:
parent
f778bdefdd
commit
47bc774ab6
5 changed files with 104 additions and 87 deletions
|
@ -202,17 +202,19 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||||
obligation.cause.span,
|
obligation.cause.span,
|
||||||
infer::LateBoundRegionConversionTime::HigherRankedType,
|
infer::LateBoundRegionConversionTime::HigherRankedType,
|
||||||
data);
|
data);
|
||||||
let normalized = super::normalize_projection_type(
|
let mut obligations = vec![];
|
||||||
|
let normalized_ty = super::normalize_projection_type(
|
||||||
&mut selcx,
|
&mut selcx,
|
||||||
obligation.param_env,
|
obligation.param_env,
|
||||||
data.projection_ty,
|
data.projection_ty,
|
||||||
obligation.cause.clone(),
|
obligation.cause.clone(),
|
||||||
0
|
0,
|
||||||
|
&mut obligations
|
||||||
);
|
);
|
||||||
if let Err(error) = self.at(&obligation.cause, obligation.param_env)
|
if let Err(error) = self.at(&obligation.cause, obligation.param_env)
|
||||||
.eq(normalized.value, data.ty) {
|
.eq(normalized_ty, data.ty) {
|
||||||
values = Some(infer::ValuePairs::Types(ExpectedFound {
|
values = Some(infer::ValuePairs::Types(ExpectedFound {
|
||||||
expected: normalized.value,
|
expected: normalized_ty,
|
||||||
found: data.ty,
|
found: data.ty,
|
||||||
}));
|
}));
|
||||||
err_buf = error;
|
err_buf = error;
|
||||||
|
|
|
@ -161,19 +161,18 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
|
||||||
// FIXME(#20304) -- cache
|
// FIXME(#20304) -- cache
|
||||||
|
|
||||||
let mut selcx = SelectionContext::new(infcx);
|
let mut selcx = SelectionContext::new(infcx);
|
||||||
let normalized = project::normalize_projection_type(&mut selcx,
|
let mut obligations = vec![];
|
||||||
param_env,
|
let normalized_ty = project::normalize_projection_type(&mut selcx,
|
||||||
projection_ty,
|
param_env,
|
||||||
cause,
|
projection_ty,
|
||||||
0);
|
cause,
|
||||||
|
0,
|
||||||
|
&mut obligations);
|
||||||
|
self.register_predicate_obligations(infcx, obligations);
|
||||||
|
|
||||||
for obligation in normalized.obligations {
|
debug!("normalize_projection_type: result={:?}", normalized_ty);
|
||||||
self.register_predicate_obligation(infcx, obligation);
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!("normalize_projection_type: result={:?}", normalized.value);
|
normalized_ty
|
||||||
|
|
||||||
normalized.value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Requires that `ty` must implement the trait with `def_id` in
|
/// Requires that `ty` must implement the trait with `def_id` in
|
||||||
|
|
|
@ -225,12 +225,14 @@ fn project_and_unify_type<'cx, 'gcx, 'tcx>(
|
||||||
debug!("project_and_unify_type(obligation={:?})",
|
debug!("project_and_unify_type(obligation={:?})",
|
||||||
obligation);
|
obligation);
|
||||||
|
|
||||||
let Normalized { value: normalized_ty, mut obligations } =
|
let mut obligations = vec![];
|
||||||
|
let normalized_ty =
|
||||||
match opt_normalize_projection_type(selcx,
|
match opt_normalize_projection_type(selcx,
|
||||||
obligation.param_env,
|
obligation.param_env,
|
||||||
obligation.predicate.projection_ty,
|
obligation.predicate.projection_ty,
|
||||||
obligation.cause.clone(),
|
obligation.cause.clone(),
|
||||||
obligation.recursion_depth) {
|
obligation.recursion_depth,
|
||||||
|
&mut obligations) {
|
||||||
Some(n) => n,
|
Some(n) => n,
|
||||||
None => return Ok(None),
|
None => return Ok(None),
|
||||||
};
|
};
|
||||||
|
@ -386,16 +388,15 @@ impl<'a, 'b, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for AssociatedTypeNormalizer<'a,
|
||||||
// binder). It would be better to normalize in a
|
// binder). It would be better to normalize in a
|
||||||
// binding-aware fashion.
|
// binding-aware fashion.
|
||||||
|
|
||||||
let Normalized { value: normalized_ty, obligations } =
|
let normalized_ty = normalize_projection_type(self.selcx,
|
||||||
normalize_projection_type(self.selcx,
|
self.param_env,
|
||||||
self.param_env,
|
data.clone(),
|
||||||
data.clone(),
|
self.cause.clone(),
|
||||||
self.cause.clone(),
|
self.depth,
|
||||||
self.depth);
|
&mut self.obligations);
|
||||||
debug!("AssociatedTypeNormalizer: depth={} normalized {:?} to {:?} \
|
debug!("AssociatedTypeNormalizer: depth={} normalized {:?} to {:?}, \
|
||||||
with {} add'l obligations",
|
now with {} obligations",
|
||||||
self.depth, ty, normalized_ty, obligations.len());
|
self.depth, ty, normalized_ty, self.obligations.len());
|
||||||
self.obligations.extend(obligations);
|
|
||||||
normalized_ty
|
normalized_ty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,10 +472,12 @@ pub fn normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
projection_ty: ty::ProjectionTy<'tcx>,
|
projection_ty: ty::ProjectionTy<'tcx>,
|
||||||
cause: ObligationCause<'tcx>,
|
cause: ObligationCause<'tcx>,
|
||||||
depth: usize)
|
depth: usize,
|
||||||
-> NormalizedTy<'tcx>
|
obligations: &mut Vec<PredicateObligation<'tcx>>)
|
||||||
|
-> Ty<'tcx>
|
||||||
{
|
{
|
||||||
opt_normalize_projection_type(selcx, param_env, projection_ty.clone(), cause.clone(), depth)
|
opt_normalize_projection_type(selcx, param_env, projection_ty.clone(), cause.clone(), depth,
|
||||||
|
obligations)
|
||||||
.unwrap_or_else(move || {
|
.unwrap_or_else(move || {
|
||||||
// if we bottom out in ambiguity, create a type variable
|
// if we bottom out in ambiguity, create a type variable
|
||||||
// and a deferred predicate to resolve this when more type
|
// and a deferred predicate to resolve this when more type
|
||||||
|
@ -490,10 +493,8 @@ pub fn normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
|
||||||
});
|
});
|
||||||
let obligation = Obligation::with_depth(
|
let obligation = Obligation::with_depth(
|
||||||
cause, depth + 1, param_env, projection.to_predicate());
|
cause, depth + 1, param_env, projection.to_predicate());
|
||||||
Normalized {
|
obligations.push(obligation);
|
||||||
value: ty_var,
|
ty_var
|
||||||
obligations: vec![obligation]
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,13 +502,20 @@ pub fn normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
|
||||||
/// as Trait>::Item`. The result is always a type (and possibly
|
/// as Trait>::Item`. The result is always a type (and possibly
|
||||||
/// additional obligations). Returns `None` in the case of ambiguity,
|
/// additional obligations). Returns `None` in the case of ambiguity,
|
||||||
/// which indicates that there are unbound type variables.
|
/// which indicates that there are unbound type variables.
|
||||||
|
///
|
||||||
|
/// This function used to return `Option<NormalizedTy<'tcx>>`, which contains a
|
||||||
|
/// `Ty<'tcx>` and an obligations vector. But that obligation vector was very
|
||||||
|
/// often immediately appended to another obligations vector. So now this
|
||||||
|
/// function takes an obligations vector and appends to it directly, which is
|
||||||
|
/// slightly uglier but avoids the need for an extra short-lived allocation.
|
||||||
fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
|
fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
|
||||||
selcx: &'a mut SelectionContext<'b, 'gcx, 'tcx>,
|
selcx: &'a mut SelectionContext<'b, 'gcx, 'tcx>,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
projection_ty: ty::ProjectionTy<'tcx>,
|
projection_ty: ty::ProjectionTy<'tcx>,
|
||||||
cause: ObligationCause<'tcx>,
|
cause: ObligationCause<'tcx>,
|
||||||
depth: usize)
|
depth: usize,
|
||||||
-> Option<NormalizedTy<'tcx>>
|
obligations: &mut Vec<PredicateObligation<'tcx>>)
|
||||||
|
-> Option<Ty<'tcx>>
|
||||||
{
|
{
|
||||||
let infcx = selcx.infcx();
|
let infcx = selcx.infcx();
|
||||||
|
|
||||||
|
@ -579,7 +587,9 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
|
||||||
projection_ty);
|
projection_ty);
|
||||||
selcx.infcx().report_overflow_error(&obligation, false);
|
selcx.infcx().report_overflow_error(&obligation, false);
|
||||||
}
|
}
|
||||||
Err(ProjectionCacheEntry::NormalizedTy(mut ty)) => {
|
Err(ProjectionCacheEntry::NormalizedTy(ty)) => {
|
||||||
|
// This is the hottest path in this function.
|
||||||
|
//
|
||||||
// If we find the value in the cache, then return it along
|
// If we find the value in the cache, then return it along
|
||||||
// with the obligations that went along with it. Note
|
// with the obligations that went along with it. Note
|
||||||
// that, when using a fulfillment context, these
|
// that, when using a fulfillment context, these
|
||||||
|
@ -597,28 +607,31 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
|
||||||
// can ignore the `obligations` from that point on.
|
// can ignore the `obligations` from that point on.
|
||||||
if !infcx.any_unresolved_type_vars(&ty.value) {
|
if !infcx.any_unresolved_type_vars(&ty.value) {
|
||||||
infcx.projection_cache.borrow_mut().complete_normalized(cache_key, &ty);
|
infcx.projection_cache.borrow_mut().complete_normalized(cache_key, &ty);
|
||||||
ty.obligations = vec![];
|
// No need to extend `obligations`.
|
||||||
|
} else {
|
||||||
|
obligations.extend(ty.obligations);
|
||||||
}
|
}
|
||||||
|
|
||||||
push_paranoid_cache_value_obligation(infcx,
|
obligations.push(get_paranoid_cache_value_obligation(infcx,
|
||||||
param_env,
|
param_env,
|
||||||
projection_ty,
|
projection_ty,
|
||||||
cause,
|
cause,
|
||||||
depth,
|
depth));
|
||||||
&mut ty);
|
return Some(ty.value);
|
||||||
|
|
||||||
return Some(ty);
|
|
||||||
}
|
}
|
||||||
Err(ProjectionCacheEntry::Error) => {
|
Err(ProjectionCacheEntry::Error) => {
|
||||||
debug!("opt_normalize_projection_type: \
|
debug!("opt_normalize_projection_type: \
|
||||||
found error");
|
found error");
|
||||||
return Some(normalize_to_error(selcx, param_env, projection_ty, cause, depth));
|
let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth);
|
||||||
|
obligations.extend(result.obligations);
|
||||||
|
return Some(result.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let obligation = Obligation::with_depth(cause.clone(), depth, param_env, projection_ty);
|
let obligation = Obligation::with_depth(cause.clone(), depth, param_env, projection_ty);
|
||||||
match project_type(selcx, &obligation) {
|
match project_type(selcx, &obligation) {
|
||||||
Ok(ProjectedTy::Progress(Progress { ty: projected_ty, mut obligations })) => {
|
Ok(ProjectedTy::Progress(Progress { ty: projected_ty,
|
||||||
|
obligations: mut projected_obligations })) => {
|
||||||
// if projection succeeded, then what we get out of this
|
// if projection succeeded, then what we get out of this
|
||||||
// is also non-normalized (consider: it was derived from
|
// is also non-normalized (consider: it was derived from
|
||||||
// an impl, where-clause etc) and hence we must
|
// an impl, where-clause etc) and hence we must
|
||||||
|
@ -627,10 +640,10 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
|
||||||
debug!("opt_normalize_projection_type: \
|
debug!("opt_normalize_projection_type: \
|
||||||
projected_ty={:?} \
|
projected_ty={:?} \
|
||||||
depth={} \
|
depth={} \
|
||||||
obligations={:?}",
|
projected_obligations={:?}",
|
||||||
projected_ty,
|
projected_ty,
|
||||||
depth,
|
depth,
|
||||||
obligations);
|
projected_obligations);
|
||||||
|
|
||||||
let result = if projected_ty.has_projections() {
|
let result = if projected_ty.has_projections() {
|
||||||
let mut normalizer = AssociatedTypeNormalizer::new(selcx,
|
let mut normalizer = AssociatedTypeNormalizer::new(selcx,
|
||||||
|
@ -644,22 +657,22 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
|
||||||
normalized_ty,
|
normalized_ty,
|
||||||
depth);
|
depth);
|
||||||
|
|
||||||
obligations.extend(normalizer.obligations);
|
projected_obligations.extend(normalizer.obligations);
|
||||||
Normalized {
|
Normalized {
|
||||||
value: normalized_ty,
|
value: normalized_ty,
|
||||||
obligations,
|
obligations: projected_obligations,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Normalized {
|
Normalized {
|
||||||
value: projected_ty,
|
value: projected_ty,
|
||||||
obligations,
|
obligations: projected_obligations,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let cache_value = prune_cache_value_obligations(infcx, &result);
|
let cache_value = prune_cache_value_obligations(infcx, &result);
|
||||||
infcx.projection_cache.borrow_mut().insert_ty(cache_key, cache_value);
|
infcx.projection_cache.borrow_mut().insert_ty(cache_key, cache_value);
|
||||||
|
obligations.extend(result.obligations);
|
||||||
Some(result)
|
Some(result.value)
|
||||||
}
|
}
|
||||||
Ok(ProjectedTy::NoProgress(projected_ty)) => {
|
Ok(ProjectedTy::NoProgress(projected_ty)) => {
|
||||||
debug!("opt_normalize_projection_type: \
|
debug!("opt_normalize_projection_type: \
|
||||||
|
@ -670,7 +683,8 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
|
||||||
obligations: vec![]
|
obligations: vec![]
|
||||||
};
|
};
|
||||||
infcx.projection_cache.borrow_mut().insert_ty(cache_key, result.clone());
|
infcx.projection_cache.borrow_mut().insert_ty(cache_key, result.clone());
|
||||||
Some(result)
|
// No need to extend `obligations`.
|
||||||
|
Some(result.value)
|
||||||
}
|
}
|
||||||
Err(ProjectionTyError::TooManyCandidates) => {
|
Err(ProjectionTyError::TooManyCandidates) => {
|
||||||
debug!("opt_normalize_projection_type: \
|
debug!("opt_normalize_projection_type: \
|
||||||
|
@ -688,7 +702,9 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
|
||||||
|
|
||||||
infcx.projection_cache.borrow_mut()
|
infcx.projection_cache.borrow_mut()
|
||||||
.error(cache_key);
|
.error(cache_key);
|
||||||
Some(normalize_to_error(selcx, param_env, projection_ty, cause, depth))
|
let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth);
|
||||||
|
obligations.extend(result.obligations);
|
||||||
|
Some(result.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -737,7 +753,7 @@ fn prune_cache_value_obligations<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx,
|
||||||
/// may or may not be necessary -- in principle, all the obligations
|
/// may or may not be necessary -- in principle, all the obligations
|
||||||
/// that must be proven to show that `T: Trait` were also returned
|
/// that must be proven to show that `T: Trait` were also returned
|
||||||
/// when the cache was first populated. But there are some vague concerns,
|
/// when the cache was first populated. But there are some vague concerns,
|
||||||
/// and so we take the precatuionary measure of including `T: Trait` in
|
/// and so we take the precautionary measure of including `T: Trait` in
|
||||||
/// the result:
|
/// the result:
|
||||||
///
|
///
|
||||||
/// Concern #1. The current setup is fragile. Perhaps someone could
|
/// Concern #1. The current setup is fragile. Perhaps someone could
|
||||||
|
@ -754,19 +770,21 @@ fn prune_cache_value_obligations<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx,
|
||||||
/// that may yet turn out to be wrong. This *may* lead to some sort
|
/// that may yet turn out to be wrong. This *may* lead to some sort
|
||||||
/// of trouble, though we don't have a concrete example of how that
|
/// of trouble, though we don't have a concrete example of how that
|
||||||
/// can occur yet. But it seems risky at best.
|
/// can occur yet. But it seems risky at best.
|
||||||
fn push_paranoid_cache_value_obligation<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
|
fn get_paranoid_cache_value_obligation<'a, 'gcx, 'tcx>(
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
|
||||||
projection_ty: ty::ProjectionTy<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
cause: ObligationCause<'tcx>,
|
projection_ty: ty::ProjectionTy<'tcx>,
|
||||||
depth: usize,
|
cause: ObligationCause<'tcx>,
|
||||||
result: &mut NormalizedTy<'tcx>)
|
depth: usize)
|
||||||
|
-> PredicateObligation<'tcx>
|
||||||
{
|
{
|
||||||
let trait_ref = projection_ty.trait_ref(infcx.tcx).to_poly_trait_ref();
|
let trait_ref = projection_ty.trait_ref(infcx.tcx).to_poly_trait_ref();
|
||||||
let trait_obligation = Obligation { cause,
|
Obligation {
|
||||||
recursion_depth: depth,
|
cause,
|
||||||
param_env,
|
recursion_depth: depth,
|
||||||
predicate: trait_ref.to_predicate() };
|
param_env,
|
||||||
result.obligations.push(trait_obligation);
|
predicate: trait_ref.to_predicate(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If we are projecting `<T as Trait>::Item`, but `T: Trait` does not
|
/// If we are projecting `<T as Trait>::Item`, but `T: Trait` does not
|
||||||
|
|
|
@ -9,8 +9,7 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use rustc::infer::canonical::{Canonical, QueryResult};
|
use rustc::infer::canonical::{Canonical, QueryResult};
|
||||||
use rustc::traits::{self, FulfillmentContext, Normalized, ObligationCause,
|
use rustc::traits::{self, FulfillmentContext, ObligationCause, SelectionContext};
|
||||||
SelectionContext};
|
|
||||||
use rustc::traits::query::{CanonicalProjectionGoal, NoSolution, normalize::NormalizationResult};
|
use rustc::traits::query::{CanonicalProjectionGoal, NoSolution, normalize::NormalizationResult};
|
||||||
use rustc::ty::{ParamEnvAnd, TyCtxt};
|
use rustc::ty::{ParamEnvAnd, TyCtxt};
|
||||||
use rustc_data_structures::sync::Lrc;
|
use rustc_data_structures::sync::Lrc;
|
||||||
|
@ -37,10 +36,9 @@ crate fn normalize_projection_ty<'tcx>(
|
||||||
let fulfill_cx = &mut FulfillmentContext::new();
|
let fulfill_cx = &mut FulfillmentContext::new();
|
||||||
let selcx = &mut SelectionContext::new(infcx);
|
let selcx = &mut SelectionContext::new(infcx);
|
||||||
let cause = ObligationCause::misc(DUMMY_SP, DUMMY_NODE_ID);
|
let cause = ObligationCause::misc(DUMMY_SP, DUMMY_NODE_ID);
|
||||||
let Normalized {
|
let mut obligations = vec![];
|
||||||
value: answer,
|
let answer =
|
||||||
obligations,
|
traits::normalize_projection_type(selcx, param_env, goal, cause, 0, &mut obligations);
|
||||||
} = traits::normalize_projection_type(selcx, param_env, goal, cause, 0);
|
|
||||||
fulfill_cx.register_predicate_obligations(infcx, obligations);
|
fulfill_cx.register_predicate_obligations(infcx, obligations);
|
||||||
|
|
||||||
// Now that we have fulfilled as much as we can, create a solution
|
// Now that we have fulfilled as much as we can, create a solution
|
||||||
|
|
|
@ -129,20 +129,20 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut selcx = traits::SelectionContext::new(self.fcx);
|
let mut selcx = traits::SelectionContext::new(self.fcx);
|
||||||
let normalized = traits::normalize_projection_type(&mut selcx,
|
let normalized_ty = traits::normalize_projection_type(&mut selcx,
|
||||||
self.fcx.param_env,
|
self.fcx.param_env,
|
||||||
ty::ProjectionTy::from_ref_and_name(
|
ty::ProjectionTy::from_ref_and_name(
|
||||||
tcx,
|
tcx,
|
||||||
trait_ref,
|
trait_ref,
|
||||||
Symbol::intern("Target"),
|
Symbol::intern("Target"),
|
||||||
),
|
),
|
||||||
cause,
|
cause,
|
||||||
0);
|
0,
|
||||||
|
&mut self.obligations);
|
||||||
|
|
||||||
debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized);
|
debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized_ty);
|
||||||
self.obligations.extend(normalized.obligations);
|
|
||||||
|
|
||||||
Some(self.fcx.resolve_type_vars_if_possible(&normalized.value))
|
Some(self.fcx.resolve_type_vars_if_possible(&normalized_ty))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the final type, generating an error if it is an
|
/// Returns the final type, generating an error if it is an
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue