diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 50068b89687..25bb54033f1 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -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; diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index fffcf66075f..a1d785cf444 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -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>, +) -> 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>, +) -> 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 @@ 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>, + obligations: &'a mut Vec>, depth: usize, } @@ -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>, ) -> AssocTypeNormalizer<'a, 'b, 'tcx> { - AssocTypeNormalizer { selcx, param_env, cause, obligations: vec![], depth } + AssocTypeNormalizer { selcx, param_env, cause, obligations, depth } } fn fold>(&mut self, value: &T) -> T { @@ -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, @@ -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()); diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index e4ef68c167f..26f2a4ddb38 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -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; @@ -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( @@ -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; @@ -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 @@ -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 } } diff --git a/src/librustc/traits/wf.rs b/src/librustc/traits/wf.rs index 9fa3c874779..fbcb77a4031 100644 --- a/src/librustc/traits/wf.rs +++ b/src/librustc/traits/wf.rs @@ -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 @@ -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 @@ 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`.