@@ -25,7 +25,7 @@ use super::VtableImplData;
25
25
use super :: util;
26
26
27
27
use hir:: def_id:: DefId ;
28
- use infer:: InferOk ;
28
+ use infer:: { InferCtxt , InferOk } ;
29
29
use infer:: type_variable:: TypeVariableOrigin ;
30
30
use rustc_data_structures:: snapshot_map:: { Snapshot , SnapshotMap } ;
31
31
use syntax:: ast;
@@ -416,7 +416,8 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
416
416
// bounds. It might be the case that we want two distinct caches,
417
417
// or else another kind of cache entry.
418
418
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 {
420
421
Ok ( ( ) ) => { }
421
422
Err ( ProjectionCacheEntry :: Ambiguous ) => {
422
423
// If we found ambiguity the last time, that generally
@@ -466,7 +467,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
466
467
projection_ty) ;
467
468
selcx. infcx ( ) . report_overflow_error ( & obligation, false ) ;
468
469
}
469
- Err ( ProjectionCacheEntry :: NormalizedTy ( ty) ) => {
470
+ Err ( ProjectionCacheEntry :: NormalizedTy ( mut ty) ) => {
470
471
// If we find the value in the cache, then return it along
471
472
// with the obligations that went along with it. Note
472
473
// that, when using a fulfillment context, these
@@ -479,6 +480,21 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
479
480
debug ! ( "opt_normalize_projection_type: \
480
481
found normalized ty `{:?}`",
481
482
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
+
482
498
return Some ( ty) ;
483
499
}
484
500
Err ( ProjectionCacheEntry :: Error ) => {
@@ -527,7 +543,10 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
527
543
obligations,
528
544
}
529
545
} ;
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
+
531
550
Some ( result)
532
551
}
533
552
Ok ( ProjectedTy :: NoProgress ( projected_ty) ) => {
@@ -538,7 +557,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
538
557
value : projected_ty,
539
558
obligations : vec ! [ ]
540
559
} ;
541
- infcx. projection_cache . borrow_mut ( ) . insert_ty ( cache_key, & result) ;
560
+ infcx. projection_cache . borrow_mut ( ) . insert_ty ( cache_key, result. clone ( ) ) ;
542
561
Some ( result)
543
562
}
544
563
Err ( ProjectionTyError :: TooManyCandidates ) => {
@@ -562,6 +581,82 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
562
581
}
563
582
}
564
583
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
+
565
660
/// If we are projecting `<T as Trait>::Item`, but `T: Trait` does not
566
661
/// hold. In various error cases, we cannot generate a valid
567
662
/// normalized projection. Therefore, we create an inference variable
@@ -1493,10 +1588,10 @@ impl<'tcx> ProjectionCache<'tcx> {
1493
1588
}
1494
1589
1495
1590
/// 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 > ) {
1497
1592
debug ! ( "ProjectionCacheEntry::insert_ty: adding cache entry: key={:?}, value={:?}" ,
1498
1593
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) ) ;
1500
1595
assert ! ( !fresh_key, "never started projecting `{:?}`" , key) ;
1501
1596
}
1502
1597
0 commit comments