diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 25be4a2ff5c..e10379ea176 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -202,17 +202,19 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { obligation.cause.span, infer::LateBoundRegionConversionTime::HigherRankedType, data); - let normalized = super::normalize_projection_type( + let mut obligations = vec![]; + let normalized_ty = super::normalize_projection_type( &mut selcx, obligation.param_env, data.projection_ty, obligation.cause.clone(), - 0 + 0, + &mut obligations ); 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 { - expected: normalized.value, + expected: normalized_ty, found: data.ty, })); err_buf = error; diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index 6e201507181..faf77af5981 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -161,19 +161,18 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { // FIXME(#20304) -- cache let mut selcx = SelectionContext::new(infcx); - let normalized = project::normalize_projection_type(&mut selcx, - param_env, - projection_ty, - cause, - 0); + let mut obligations = vec![]; + let normalized_ty = project::normalize_projection_type(&mut selcx, + param_env, + projection_ty, + cause, + 0, + &mut obligations); + self.register_predicate_obligations(infcx, obligations); - for obligation in normalized.obligations { - self.register_predicate_obligation(infcx, obligation); - } + debug!("normalize_projection_type: result={:?}", normalized_ty); - debug!("normalize_projection_type: result={:?}", normalized.value); - - normalized.value + normalized_ty } /// Requires that `ty` must implement the trait with `def_id` in diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index da8f086b9c5..8a089dff153 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -225,12 +225,14 @@ fn project_and_unify_type<'cx, 'gcx, 'tcx>( debug!("project_and_unify_type(obligation={:?})", obligation); - let Normalized { value: normalized_ty, mut obligations } = + let mut obligations = vec![]; + let normalized_ty = match opt_normalize_projection_type(selcx, obligation.param_env, obligation.predicate.projection_ty, obligation.cause.clone(), - obligation.recursion_depth) { + obligation.recursion_depth, + &mut obligations) { Some(n) => n, 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 // binding-aware fashion. - let Normalized { value: normalized_ty, obligations } = - normalize_projection_type(self.selcx, - self.param_env, - data.clone(), - self.cause.clone(), - self.depth); - debug!("AssociatedTypeNormalizer: depth={} normalized {:?} to {:?} \ - with {} add'l obligations", - self.depth, ty, normalized_ty, obligations.len()); - self.obligations.extend(obligations); + let normalized_ty = normalize_projection_type(self.selcx, + self.param_env, + data.clone(), + self.cause.clone(), + self.depth, + &mut self.obligations); + debug!("AssociatedTypeNormalizer: depth={} normalized {:?} to {:?}, \ + now with {} obligations", + self.depth, ty, normalized_ty, self.obligations.len()); normalized_ty } @@ -471,10 +472,12 @@ pub fn normalize_projection_type<'a, 'b, 'gcx, 'tcx>( param_env: ty::ParamEnv<'tcx>, projection_ty: ty::ProjectionTy<'tcx>, cause: ObligationCause<'tcx>, - depth: usize) - -> NormalizedTy<'tcx> + depth: usize, + obligations: &mut Vec>) + -> 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 || { // if we bottom out in ambiguity, create a type variable // 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( cause, depth + 1, param_env, projection.to_predicate()); - Normalized { - value: ty_var, - obligations: vec![obligation] - } + obligations.push(obligation); + ty_var }) } @@ -501,13 +502,20 @@ pub fn normalize_projection_type<'a, 'b, 'gcx, 'tcx>( /// as Trait>::Item`. The result is always a type (and possibly /// additional obligations). Returns `None` in the case of ambiguity, /// which indicates that there are unbound type variables. +/// +/// This function used to return `Option>`, 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>( selcx: &'a mut SelectionContext<'b, 'gcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, projection_ty: ty::ProjectionTy<'tcx>, cause: ObligationCause<'tcx>, - depth: usize) - -> Option> + depth: usize, + obligations: &mut Vec>) + -> Option> { let infcx = selcx.infcx(); @@ -579,7 +587,9 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( projection_ty); 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 // with the obligations that went along with it. Note // 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. if !infcx.any_unresolved_type_vars(&ty.value) { 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, - param_env, - projection_ty, - cause, - depth, - &mut ty); - - return Some(ty); + obligations.push(get_paranoid_cache_value_obligation(infcx, + param_env, + projection_ty, + cause, + depth)); + return Some(ty.value); } Err(ProjectionCacheEntry::Error) => { debug!("opt_normalize_projection_type: \ 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); 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 // is also non-normalized (consider: it was derived from // 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: \ projected_ty={:?} \ depth={} \ - obligations={:?}", + projected_obligations={:?}", projected_ty, depth, - obligations); + projected_obligations); let result = if projected_ty.has_projections() { let mut normalizer = AssociatedTypeNormalizer::new(selcx, @@ -644,22 +657,22 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( normalized_ty, depth); - obligations.extend(normalizer.obligations); + projected_obligations.extend(normalizer.obligations); Normalized { value: normalized_ty, - obligations, + obligations: projected_obligations, } } else { Normalized { value: projected_ty, - obligations, + obligations: projected_obligations, } }; let cache_value = prune_cache_value_obligations(infcx, &result); infcx.projection_cache.borrow_mut().insert_ty(cache_key, cache_value); - - Some(result) + obligations.extend(result.obligations); + Some(result.value) } Ok(ProjectedTy::NoProgress(projected_ty)) => { debug!("opt_normalize_projection_type: \ @@ -670,7 +683,8 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( obligations: vec![] }; infcx.projection_cache.borrow_mut().insert_ty(cache_key, result.clone()); - Some(result) + // No need to extend `obligations`. + Some(result.value) } Err(ProjectionTyError::TooManyCandidates) => { debug!("opt_normalize_projection_type: \ @@ -688,7 +702,9 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>( infcx.projection_cache.borrow_mut() .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 /// that must be proven to show that `T: Trait` were also returned /// 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: /// /// 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 /// of trouble, though we don't have a concrete example of how that /// can occur yet. But it seems risky at best. -fn push_paranoid_cache_value_obligation<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - projection_ty: ty::ProjectionTy<'tcx>, - cause: ObligationCause<'tcx>, - depth: usize, - result: &mut NormalizedTy<'tcx>) +fn get_paranoid_cache_value_obligation<'a, 'gcx, 'tcx>( + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize) + -> PredicateObligation<'tcx> { let trait_ref = projection_ty.trait_ref(infcx.tcx).to_poly_trait_ref(); - let trait_obligation = Obligation { cause, - recursion_depth: depth, - param_env, - predicate: trait_ref.to_predicate() }; - result.obligations.push(trait_obligation); + Obligation { + cause, + recursion_depth: depth, + param_env, + predicate: trait_ref.to_predicate(), + } } /// If we are projecting `::Item`, but `T: Trait` does not diff --git a/src/librustc_traits/normalize_projection_ty.rs b/src/librustc_traits/normalize_projection_ty.rs index 8fc00c937e6..a9ac53972e4 100644 --- a/src/librustc_traits/normalize_projection_ty.rs +++ b/src/librustc_traits/normalize_projection_ty.rs @@ -9,8 +9,7 @@ // except according to those terms. use rustc::infer::canonical::{Canonical, QueryResult}; -use rustc::traits::{self, FulfillmentContext, Normalized, ObligationCause, - SelectionContext}; +use rustc::traits::{self, FulfillmentContext, ObligationCause, SelectionContext}; use rustc::traits::query::{CanonicalProjectionGoal, NoSolution, normalize::NormalizationResult}; use rustc::ty::{ParamEnvAnd, TyCtxt}; use rustc_data_structures::sync::Lrc; @@ -37,10 +36,9 @@ crate fn normalize_projection_ty<'tcx>( let fulfill_cx = &mut FulfillmentContext::new(); let selcx = &mut SelectionContext::new(infcx); let cause = ObligationCause::misc(DUMMY_SP, DUMMY_NODE_ID); - let Normalized { - value: answer, - obligations, - } = traits::normalize_projection_type(selcx, param_env, goal, cause, 0); + let mut obligations = vec![]; + let answer = + traits::normalize_projection_type(selcx, param_env, goal, cause, 0, &mut obligations); fulfill_cx.register_predicate_obligations(infcx, obligations); // Now that we have fulfilled as much as we can, create a solution diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs index e1ce6073ce4..4274e5c1e1f 100644 --- a/src/librustc_typeck/check/autoderef.rs +++ b/src/librustc_typeck/check/autoderef.rs @@ -129,20 +129,20 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> { } let mut selcx = traits::SelectionContext::new(self.fcx); - let normalized = traits::normalize_projection_type(&mut selcx, - self.fcx.param_env, - ty::ProjectionTy::from_ref_and_name( - tcx, - trait_ref, - Symbol::intern("Target"), - ), - cause, - 0); + let normalized_ty = traits::normalize_projection_type(&mut selcx, + self.fcx.param_env, + ty::ProjectionTy::from_ref_and_name( + tcx, + trait_ref, + Symbol::intern("Target"), + ), + cause, + 0, + &mut self.obligations); - debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized); - self.obligations.extend(normalized.obligations); + debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized_ty); - 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