Skip to content

Commit 47bc774

Browse files
committed
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%.
1 parent f778bde commit 47bc774

File tree

5 files changed

+108
-91
lines changed

5 files changed

+108
-91
lines changed

src/librustc/traits/error_reporting.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -202,17 +202,19 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
202202
obligation.cause.span,
203203
infer::LateBoundRegionConversionTime::HigherRankedType,
204204
data);
205-
let normalized = super::normalize_projection_type(
205+
let mut obligations = vec![];
206+
let normalized_ty = super::normalize_projection_type(
206207
&mut selcx,
207208
obligation.param_env,
208209
data.projection_ty,
209210
obligation.cause.clone(),
210-
0
211+
0,
212+
&mut obligations
211213
);
212214
if let Err(error) = self.at(&obligation.cause, obligation.param_env)
213-
.eq(normalized.value, data.ty) {
215+
.eq(normalized_ty, data.ty) {
214216
values = Some(infer::ValuePairs::Types(ExpectedFound {
215-
expected: normalized.value,
217+
expected: normalized_ty,
216218
found: data.ty,
217219
}));
218220
err_buf = error;

src/librustc/traits/fulfill.rs

+12-13
Original file line numberDiff line numberDiff line change
@@ -161,19 +161,18 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
161161
// FIXME(#20304) -- cache
162162

163163
let mut selcx = SelectionContext::new(infcx);
164-
let normalized = project::normalize_projection_type(&mut selcx,
165-
param_env,
166-
projection_ty,
167-
cause,
168-
0);
169-
170-
for obligation in normalized.obligations {
171-
self.register_predicate_obligation(infcx, obligation);
172-
}
173-
174-
debug!("normalize_projection_type: result={:?}", normalized.value);
175-
176-
normalized.value
164+
let mut obligations = vec![];
165+
let normalized_ty = project::normalize_projection_type(&mut selcx,
166+
param_env,
167+
projection_ty,
168+
cause,
169+
0,
170+
&mut obligations);
171+
self.register_predicate_obligations(infcx, obligations);
172+
173+
debug!("normalize_projection_type: result={:?}", normalized_ty);
174+
175+
normalized_ty
177176
}
178177

179178
/// Requires that `ty` must implement the trait with `def_id` in

src/librustc/traits/project.rs

+72-54
Original file line numberDiff line numberDiff line change
@@ -225,12 +225,14 @@ fn project_and_unify_type<'cx, 'gcx, 'tcx>(
225225
debug!("project_and_unify_type(obligation={:?})",
226226
obligation);
227227

228-
let Normalized { value: normalized_ty, mut obligations } =
228+
let mut obligations = vec![];
229+
let normalized_ty =
229230
match opt_normalize_projection_type(selcx,
230231
obligation.param_env,
231232
obligation.predicate.projection_ty,
232233
obligation.cause.clone(),
233-
obligation.recursion_depth) {
234+
obligation.recursion_depth,
235+
&mut obligations) {
234236
Some(n) => n,
235237
None => return Ok(None),
236238
};
@@ -386,16 +388,15 @@ impl<'a, 'b, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for AssociatedTypeNormalizer<'a,
386388
// binder). It would be better to normalize in a
387389
// binding-aware fashion.
388390

389-
let Normalized { value: normalized_ty, obligations } =
390-
normalize_projection_type(self.selcx,
391-
self.param_env,
392-
data.clone(),
393-
self.cause.clone(),
394-
self.depth);
395-
debug!("AssociatedTypeNormalizer: depth={} normalized {:?} to {:?} \
396-
with {} add'l obligations",
397-
self.depth, ty, normalized_ty, obligations.len());
398-
self.obligations.extend(obligations);
391+
let normalized_ty = normalize_projection_type(self.selcx,
392+
self.param_env,
393+
data.clone(),
394+
self.cause.clone(),
395+
self.depth,
396+
&mut self.obligations);
397+
debug!("AssociatedTypeNormalizer: depth={} normalized {:?} to {:?}, \
398+
now with {} obligations",
399+
self.depth, ty, normalized_ty, self.obligations.len());
399400
normalized_ty
400401
}
401402

@@ -471,10 +472,12 @@ pub fn normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
471472
param_env: ty::ParamEnv<'tcx>,
472473
projection_ty: ty::ProjectionTy<'tcx>,
473474
cause: ObligationCause<'tcx>,
474-
depth: usize)
475-
-> NormalizedTy<'tcx>
475+
depth: usize,
476+
obligations: &mut Vec<PredicateObligation<'tcx>>)
477+
-> Ty<'tcx>
476478
{
477-
opt_normalize_projection_type(selcx, param_env, projection_ty.clone(), cause.clone(), depth)
479+
opt_normalize_projection_type(selcx, param_env, projection_ty.clone(), cause.clone(), depth,
480+
obligations)
478481
.unwrap_or_else(move || {
479482
// if we bottom out in ambiguity, create a type variable
480483
// and a deferred predicate to resolve this when more type
@@ -490,24 +493,29 @@ pub fn normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
490493
});
491494
let obligation = Obligation::with_depth(
492495
cause, depth + 1, param_env, projection.to_predicate());
493-
Normalized {
494-
value: ty_var,
495-
obligations: vec![obligation]
496-
}
496+
obligations.push(obligation);
497+
ty_var
497498
})
498499
}
499500

500501
/// The guts of `normalize`: normalize a specific projection like `<T
501502
/// as Trait>::Item`. The result is always a type (and possibly
502503
/// additional obligations). Returns `None` in the case of ambiguity,
503504
/// which indicates that there are unbound type variables.
505+
///
506+
/// This function used to return `Option<NormalizedTy<'tcx>>`, which contains a
507+
/// `Ty<'tcx>` and an obligations vector. But that obligation vector was very
508+
/// often immediately appended to another obligations vector. So now this
509+
/// function takes an obligations vector and appends to it directly, which is
510+
/// slightly uglier but avoids the need for an extra short-lived allocation.
504511
fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
505512
selcx: &'a mut SelectionContext<'b, 'gcx, 'tcx>,
506513
param_env: ty::ParamEnv<'tcx>,
507514
projection_ty: ty::ProjectionTy<'tcx>,
508515
cause: ObligationCause<'tcx>,
509-
depth: usize)
510-
-> Option<NormalizedTy<'tcx>>
516+
depth: usize,
517+
obligations: &mut Vec<PredicateObligation<'tcx>>)
518+
-> Option<Ty<'tcx>>
511519
{
512520
let infcx = selcx.infcx();
513521

@@ -579,7 +587,9 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
579587
projection_ty);
580588
selcx.infcx().report_overflow_error(&obligation, false);
581589
}
582-
Err(ProjectionCacheEntry::NormalizedTy(mut ty)) => {
590+
Err(ProjectionCacheEntry::NormalizedTy(ty)) => {
591+
// This is the hottest path in this function.
592+
//
583593
// If we find the value in the cache, then return it along
584594
// with the obligations that went along with it. Note
585595
// that, when using a fulfillment context, these
@@ -597,28 +607,31 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
597607
// can ignore the `obligations` from that point on.
598608
if !infcx.any_unresolved_type_vars(&ty.value) {
599609
infcx.projection_cache.borrow_mut().complete_normalized(cache_key, &ty);
600-
ty.obligations = vec![];
610+
// No need to extend `obligations`.
611+
} else {
612+
obligations.extend(ty.obligations);
601613
}
602614

603-
push_paranoid_cache_value_obligation(infcx,
604-
param_env,
605-
projection_ty,
606-
cause,
607-
depth,
608-
&mut ty);
609-
610-
return Some(ty);
615+
obligations.push(get_paranoid_cache_value_obligation(infcx,
616+
param_env,
617+
projection_ty,
618+
cause,
619+
depth));
620+
return Some(ty.value);
611621
}
612622
Err(ProjectionCacheEntry::Error) => {
613623
debug!("opt_normalize_projection_type: \
614624
found error");
615-
return Some(normalize_to_error(selcx, param_env, projection_ty, cause, depth));
625+
let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth);
626+
obligations.extend(result.obligations);
627+
return Some(result.value)
616628
}
617629
}
618630

619631
let obligation = Obligation::with_depth(cause.clone(), depth, param_env, projection_ty);
620632
match project_type(selcx, &obligation) {
621-
Ok(ProjectedTy::Progress(Progress { ty: projected_ty, mut obligations })) => {
633+
Ok(ProjectedTy::Progress(Progress { ty: projected_ty,
634+
obligations: mut projected_obligations })) => {
622635
// if projection succeeded, then what we get out of this
623636
// is also non-normalized (consider: it was derived from
624637
// an impl, where-clause etc) and hence we must
@@ -627,10 +640,10 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
627640
debug!("opt_normalize_projection_type: \
628641
projected_ty={:?} \
629642
depth={} \
630-
obligations={:?}",
643+
projected_obligations={:?}",
631644
projected_ty,
632645
depth,
633-
obligations);
646+
projected_obligations);
634647

635648
let result = if projected_ty.has_projections() {
636649
let mut normalizer = AssociatedTypeNormalizer::new(selcx,
@@ -644,22 +657,22 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
644657
normalized_ty,
645658
depth);
646659

647-
obligations.extend(normalizer.obligations);
660+
projected_obligations.extend(normalizer.obligations);
648661
Normalized {
649662
value: normalized_ty,
650-
obligations,
663+
obligations: projected_obligations,
651664
}
652665
} else {
653666
Normalized {
654667
value: projected_ty,
655-
obligations,
668+
obligations: projected_obligations,
656669
}
657670
};
658671

659672
let cache_value = prune_cache_value_obligations(infcx, &result);
660673
infcx.projection_cache.borrow_mut().insert_ty(cache_key, cache_value);
661-
662-
Some(result)
674+
obligations.extend(result.obligations);
675+
Some(result.value)
663676
}
664677
Ok(ProjectedTy::NoProgress(projected_ty)) => {
665678
debug!("opt_normalize_projection_type: \
@@ -670,7 +683,8 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
670683
obligations: vec![]
671684
};
672685
infcx.projection_cache.borrow_mut().insert_ty(cache_key, result.clone());
673-
Some(result)
686+
// No need to extend `obligations`.
687+
Some(result.value)
674688
}
675689
Err(ProjectionTyError::TooManyCandidates) => {
676690
debug!("opt_normalize_projection_type: \
@@ -688,7 +702,9 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
688702

689703
infcx.projection_cache.borrow_mut()
690704
.error(cache_key);
691-
Some(normalize_to_error(selcx, param_env, projection_ty, cause, depth))
705+
let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth);
706+
obligations.extend(result.obligations);
707+
Some(result.value)
692708
}
693709
}
694710
}
@@ -737,7 +753,7 @@ fn prune_cache_value_obligations<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx,
737753
/// may or may not be necessary -- in principle, all the obligations
738754
/// that must be proven to show that `T: Trait` were also returned
739755
/// when the cache was first populated. But there are some vague concerns,
740-
/// and so we take the precatuionary measure of including `T: Trait` in
756+
/// and so we take the precautionary measure of including `T: Trait` in
741757
/// the result:
742758
///
743759
/// 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,
754770
/// that may yet turn out to be wrong. This *may* lead to some sort
755771
/// of trouble, though we don't have a concrete example of how that
756772
/// can occur yet. But it seems risky at best.
757-
fn push_paranoid_cache_value_obligation<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
758-
param_env: ty::ParamEnv<'tcx>,
759-
projection_ty: ty::ProjectionTy<'tcx>,
760-
cause: ObligationCause<'tcx>,
761-
depth: usize,
762-
result: &mut NormalizedTy<'tcx>)
773+
fn get_paranoid_cache_value_obligation<'a, 'gcx, 'tcx>(
774+
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
775+
param_env: ty::ParamEnv<'tcx>,
776+
projection_ty: ty::ProjectionTy<'tcx>,
777+
cause: ObligationCause<'tcx>,
778+
depth: usize)
779+
-> PredicateObligation<'tcx>
763780
{
764781
let trait_ref = projection_ty.trait_ref(infcx.tcx).to_poly_trait_ref();
765-
let trait_obligation = Obligation { cause,
766-
recursion_depth: depth,
767-
param_env,
768-
predicate: trait_ref.to_predicate() };
769-
result.obligations.push(trait_obligation);
782+
Obligation {
783+
cause,
784+
recursion_depth: depth,
785+
param_env,
786+
predicate: trait_ref.to_predicate(),
787+
}
770788
}
771789

772790
/// If we are projecting `<T as Trait>::Item`, but `T: Trait` does not

src/librustc_traits/normalize_projection_ty.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
// except according to those terms.
1010

1111
use rustc::infer::canonical::{Canonical, QueryResult};
12-
use rustc::traits::{self, FulfillmentContext, Normalized, ObligationCause,
13-
SelectionContext};
12+
use rustc::traits::{self, FulfillmentContext, ObligationCause, SelectionContext};
1413
use rustc::traits::query::{CanonicalProjectionGoal, NoSolution, normalize::NormalizationResult};
1514
use rustc::ty::{ParamEnvAnd, TyCtxt};
1615
use rustc_data_structures::sync::Lrc;
@@ -37,10 +36,9 @@ crate fn normalize_projection_ty<'tcx>(
3736
let fulfill_cx = &mut FulfillmentContext::new();
3837
let selcx = &mut SelectionContext::new(infcx);
3938
let cause = ObligationCause::misc(DUMMY_SP, DUMMY_NODE_ID);
40-
let Normalized {
41-
value: answer,
42-
obligations,
43-
} = traits::normalize_projection_type(selcx, param_env, goal, cause, 0);
39+
let mut obligations = vec![];
40+
let answer =
41+
traits::normalize_projection_type(selcx, param_env, goal, cause, 0, &mut obligations);
4442
fulfill_cx.register_predicate_obligations(infcx, obligations);
4543

4644
// Now that we have fulfilled as much as we can, create a solution

src/librustc_typeck/check/autoderef.rs

+14-14
Original file line numberDiff line numberDiff line change
@@ -129,20 +129,20 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
129129
}
130130

131131
let mut selcx = traits::SelectionContext::new(self.fcx);
132-
let normalized = traits::normalize_projection_type(&mut selcx,
133-
self.fcx.param_env,
134-
ty::ProjectionTy::from_ref_and_name(
135-
tcx,
136-
trait_ref,
137-
Symbol::intern("Target"),
138-
),
139-
cause,
140-
0);
141-
142-
debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized);
143-
self.obligations.extend(normalized.obligations);
144-
145-
Some(self.fcx.resolve_type_vars_if_possible(&normalized.value))
132+
let normalized_ty = traits::normalize_projection_type(&mut selcx,
133+
self.fcx.param_env,
134+
ty::ProjectionTy::from_ref_and_name(
135+
tcx,
136+
trait_ref,
137+
Symbol::intern("Target"),
138+
),
139+
cause,
140+
0,
141+
&mut self.obligations);
142+
143+
debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized_ty);
144+
145+
Some(self.fcx.resolve_type_vars_if_possible(&normalized_ty))
146146
}
147147

148148
/// Returns the final type, generating an error if it is an

0 commit comments

Comments
 (0)