@@ -25,7 +25,7 @@ use super::VtableImplData;
2525use super :: util;
2626
2727use hir:: def_id:: DefId ;
28- use infer:: InferOk ;
28+ use infer:: { InferCtxt , InferOk } ;
2929use infer:: type_variable:: TypeVariableOrigin ;
3030use rustc_data_structures:: snapshot_map:: { Snapshot , SnapshotMap } ;
3131use syntax:: ast;
@@ -416,7 +416,8 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
416416 // bounds. It might be the case that we want two distinct caches,
417417 // or else another kind of cache entry.
418418
419- match infcx. projection_cache . borrow_mut ( ) . try_start ( cache_key) {
419+ let cache_result = infcx. projection_cache . borrow_mut ( ) . try_start ( cache_key) ;
420+ match cache_result {
420421 Ok ( ( ) ) => { }
421422 Err ( ProjectionCacheEntry :: Ambiguous ) => {
422423 // If we found ambiguity the last time, that generally
@@ -466,7 +467,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
466467 projection_ty) ;
467468 selcx. infcx ( ) . report_overflow_error ( & obligation, false ) ;
468469 }
469- Err ( ProjectionCacheEntry :: NormalizedTy ( ty) ) => {
470+ Err ( ProjectionCacheEntry :: NormalizedTy ( mut ty) ) => {
470471 // If we find the value in the cache, then return it along
471472 // with the obligations that went along with it. Note
472473 // that, when using a fulfillment context, these
@@ -479,6 +480,21 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
479480 debug ! ( "opt_normalize_projection_type: \
480481 found normalized ty `{:?}`",
481482 ty) ;
483+
484+ // Once we have inferred everything we need to know, we
485+ // can ignore the `obligations` from that point on.
486+ if !infcx. any_unresolved_type_vars ( & ty. value ) {
487+ infcx. projection_cache . borrow_mut ( ) . complete ( cache_key) ;
488+ ty. obligations = vec ! [ ] ;
489+ }
490+
491+ push_paranoid_cache_value_obligation ( infcx,
492+ param_env,
493+ projection_ty,
494+ cause,
495+ depth,
496+ & mut ty) ;
497+
482498 return Some ( ty) ;
483499 }
484500 Err ( ProjectionCacheEntry :: Error ) => {
@@ -527,7 +543,10 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
527543 obligations,
528544 }
529545 } ;
530- infcx. projection_cache . borrow_mut ( ) . insert_ty ( cache_key, & result) ;
546+
547+ let cache_value = prune_cache_value_obligations ( infcx, & result) ;
548+ infcx. projection_cache . borrow_mut ( ) . insert_ty ( cache_key, cache_value) ;
549+
531550 Some ( result)
532551 }
533552 Ok ( ProjectedTy :: NoProgress ( projected_ty) ) => {
@@ -538,7 +557,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
538557 value : projected_ty,
539558 obligations : vec ! [ ]
540559 } ;
541- infcx. projection_cache . borrow_mut ( ) . insert_ty ( cache_key, & result) ;
560+ infcx. projection_cache . borrow_mut ( ) . insert_ty ( cache_key, result. clone ( ) ) ;
542561 Some ( result)
543562 }
544563 Err ( ProjectionTyError :: TooManyCandidates ) => {
@@ -562,6 +581,82 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
562581 }
563582}
564583
584+ /// If there are unresolved type variables, then we need to include
585+ /// any subobligations that bind them, at least until those type
586+ /// variables are fully resolved.
587+ fn prune_cache_value_obligations < ' a , ' gcx , ' tcx > ( infcx : & ' a InferCtxt < ' a , ' gcx , ' tcx > ,
588+ result : & NormalizedTy < ' tcx > )
589+ -> NormalizedTy < ' tcx > {
590+ if !infcx. any_unresolved_type_vars ( & result. value ) {
591+ return NormalizedTy { value : result. value , obligations : vec ! [ ] } ;
592+ }
593+
594+ let mut obligations: Vec < _ > =
595+ result. obligations
596+ . iter ( )
597+ . filter ( |obligation| match obligation. predicate {
598+ // We found a `T: Foo<X = U>` predicate, let's check
599+ // if `U` references any unresolved type
600+ // variables. In principle, we only care if this
601+ // projection can help resolve any of the type
602+ // variables found in `result.value` -- but we just
603+ // check for any type variables here, for fear of
604+ // indirect obligations (e.g., we project to `?0`,
605+ // but we have `T: Foo<X = ?1>` and `?1: Bar<X =
606+ // ?0>`).
607+ ty:: Predicate :: Projection ( ref data) =>
608+ !infcx. any_unresolved_type_vars ( & data. ty ( ) ) ,
609+
610+ // We are only interested in `T: Foo<X = U>` predicates, whre
611+ // `U` references one of `unresolved_type_vars`. =)
612+ _ => false ,
613+ } )
614+ . cloned ( )
615+ . collect ( ) ;
616+
617+ obligations. shrink_to_fit ( ) ;
618+
619+ NormalizedTy { value : result. value , obligations }
620+ }
621+
622+ /// Whenever we give back a cache result for a projection like `<T as
623+ /// Trait>::Item ==> X`, we *always* include the obligation to prove
624+ /// that `T: Trait` (we may also include some other obligations). This
625+ /// may or may not be necessary -- in principle, all the obligations
626+ /// that must be proven to show that `T: Trait` were also returned
627+ /// when the cache was first populated. But there are some vague concerns,
628+ /// and so we take the precatuionary measure of including `T: Trait` in
629+ /// the result:
630+ ///
631+ /// Concern #1. The current setup is fragile. Perhaps someone could
632+ /// have failed to prove the concerns from when the cache was
633+ /// populated, but also not have used a snapshot, in which case the
634+ /// cache could remain populated even though `T: Trait` has not been
635+ /// shown. In this case, the "other code" is at fault -- when you
636+ /// project something, you are supposed to either have a snapshot or
637+ /// else prove all the resulting obligations -- but it's still easy to
638+ /// get wrong.
639+ ///
640+ /// Concern #2. Even within the snapshot, if those original
641+ /// obligations are not yet proven, then we are able to do projections
642+ /// that may yet turn out to be wrong. This *may* lead to some sort
643+ /// of trouble, though we don't have a concrete example of how that
644+ /// can occur yet. But it seems risky at best.
645+ fn push_paranoid_cache_value_obligation < ' a , ' gcx , ' tcx > ( infcx : & ' a InferCtxt < ' a , ' gcx , ' tcx > ,
646+ param_env : ty:: ParamEnv < ' tcx > ,
647+ projection_ty : ty:: ProjectionTy < ' tcx > ,
648+ cause : ObligationCause < ' tcx > ,
649+ depth : usize ,
650+ result : & mut NormalizedTy < ' tcx > )
651+ {
652+ let trait_ref = projection_ty. trait_ref ( infcx. tcx ) . to_poly_trait_ref ( ) ;
653+ let trait_obligation = Obligation { cause,
654+ recursion_depth : depth,
655+ param_env,
656+ predicate : trait_ref. to_predicate ( ) } ;
657+ result. obligations . push ( trait_obligation) ;
658+ }
659+
565660/// If we are projecting `<T as Trait>::Item`, but `T: Trait` does not
566661/// hold. In various error cases, we cannot generate a valid
567662/// normalized projection. Therefore, we create an inference variable
@@ -1493,10 +1588,10 @@ impl<'tcx> ProjectionCache<'tcx> {
14931588 }
14941589
14951590 /// Indicates that `key` was normalized to `value`.
1496- fn insert_ty ( & mut self , key : ProjectionCacheKey < ' tcx > , value : & NormalizedTy < ' tcx > ) {
1591+ fn insert_ty ( & mut self , key : ProjectionCacheKey < ' tcx > , value : NormalizedTy < ' tcx > ) {
14971592 debug ! ( "ProjectionCacheEntry::insert_ty: adding cache entry: key={:?}, value={:?}" ,
14981593 key, value) ;
1499- let fresh_key = self . map . insert ( key, ProjectionCacheEntry :: NormalizedTy ( value. clone ( ) ) ) ;
1594+ let fresh_key = self . map . insert ( key, ProjectionCacheEntry :: NormalizedTy ( value) ) ;
15001595 assert ! ( !fresh_key, "never started projecting `{:?}`" , key) ;
15011596 }
15021597
0 commit comments