@@ -122,11 +122,13 @@ struct ProjectionTyCandidateSet<'tcx> {
122122///
123123/// for<...> <T as Trait>::U == V
124124///
125- /// If successful, this may result in additional obligations.
125+ /// If successful, this may result in additional obligations. Also returns
126+ /// the projection cache key used to track these additional obligations.
126127pub fn poly_project_and_unify_type < ' cx , ' gcx , ' tcx > (
127128 selcx : & mut SelectionContext < ' cx , ' gcx , ' tcx > ,
128129 obligation : & PolyProjectionObligation < ' tcx > )
129- -> Result < Option < Vec < PredicateObligation < ' tcx > > > , MismatchedProjectionTypes < ' tcx > >
130+ -> Result < Option < Vec < PredicateObligation < ' tcx > > > ,
131+ MismatchedProjectionTypes < ' tcx > >
130132{
131133 debug ! ( "poly_project_and_unify_type(obligation={:?})" ,
132134 obligation) ;
@@ -162,7 +164,8 @@ pub fn poly_project_and_unify_type<'cx, 'gcx, 'tcx>(
162164fn project_and_unify_type < ' cx , ' gcx , ' tcx > (
163165 selcx : & mut SelectionContext < ' cx , ' gcx , ' tcx > ,
164166 obligation : & ProjectionObligation < ' tcx > )
165- -> Result < Option < Vec < PredicateObligation < ' tcx > > > , MismatchedProjectionTypes < ' tcx > >
167+ -> Result < Option < Vec < PredicateObligation < ' tcx > > > ,
168+ MismatchedProjectionTypes < ' tcx > >
166169{
167170 debug ! ( "project_and_unify_type(obligation={:?})" ,
168171 obligation) ;
@@ -397,6 +400,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
397400 let infcx = selcx. infcx ( ) ;
398401
399402 let projection_ty = infcx. resolve_type_vars_if_possible ( & projection_ty) ;
403+ let cache_key = ProjectionCacheKey { ty : projection_ty } ;
400404
401405 debug ! ( "opt_normalize_projection_type(\
402406 projection_ty={:?}, \
@@ -412,7 +416,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
412416 // bounds. It might be the case that we want two distinct caches,
413417 // or else another kind of cache entry.
414418
415- match infcx. projection_cache . borrow_mut ( ) . try_start ( projection_ty ) {
419+ match infcx. projection_cache . borrow_mut ( ) . try_start ( cache_key ) {
416420 Ok ( ( ) ) => { }
417421 Err ( ProjectionCacheEntry :: Ambiguous ) => {
418422 // If we found ambiguity the last time, that generally
@@ -523,7 +527,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
523527 obligations,
524528 }
525529 } ;
526- infcx. projection_cache . borrow_mut ( ) . complete ( projection_ty , & result) ;
530+ infcx. projection_cache . borrow_mut ( ) . insert_ty ( cache_key , & result) ;
527531 Some ( result)
528532 }
529533 Ok ( ProjectedTy :: NoProgress ( projected_ty) ) => {
@@ -534,14 +538,14 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
534538 value : projected_ty,
535539 obligations : vec ! [ ]
536540 } ;
537- infcx. projection_cache . borrow_mut ( ) . complete ( projection_ty , & result) ;
541+ infcx. projection_cache . borrow_mut ( ) . insert_ty ( cache_key , & result) ;
538542 Some ( result)
539543 }
540544 Err ( ProjectionTyError :: TooManyCandidates ) => {
541545 debug ! ( "opt_normalize_projection_type: \
542546 too many candidates") ;
543547 infcx. projection_cache . borrow_mut ( )
544- . ambiguous ( projection_ty ) ;
548+ . ambiguous ( cache_key ) ;
545549 None
546550 }
547551 Err ( ProjectionTyError :: TraitSelectionError ( _) ) => {
@@ -552,7 +556,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
552556 // reported later
553557
554558 infcx. projection_cache . borrow_mut ( )
555- . error ( projection_ty ) ;
559+ . error ( cache_key ) ;
556560 Some ( normalize_to_error ( selcx, param_env, projection_ty, cause, depth) )
557561 }
558562 }
@@ -1381,8 +1385,62 @@ fn assoc_ty_def<'cx, 'gcx, 'tcx>(
13811385
13821386// # Cache
13831387
1388+ /// The projection cache. Unlike the standard caches, this can
1389+ /// include infcx-dependent type variables - therefore, we have to roll
1390+ /// the cache back each time we roll a snapshot back, to avoid assumptions
1391+ /// on yet-unresolved inference variables. Types with skolemized regions
1392+ /// also have to be removed when the respective snapshot ends.
1393+ ///
1394+ /// Because of that, projection cache entries can be "stranded" and left
1395+ /// inaccessible when type variables inside the key are resolved. We make no
1396+ /// attempt to recover or remove "stranded" entries, but rather let them be
1397+ /// (for the lifetime of the infcx).
1398+ ///
1399+ /// Entries in the projection cache might contain inference variables
1400+ /// that will be resolved by obligations on the projection cache entry - e.g.
1401+ /// when a type parameter in the associated type is constrained through
1402+ /// an "RFC 447" projection on the impl.
1403+ ///
1404+ /// When working with a fulfillment context, the derived obligations of each
1405+ /// projection cache entry will be registered on the fulfillcx, so any users
1406+ /// that can wait for a fulfillcx fixed point need not care about this. However,
1407+ /// users that don't wait for a fixed point (e.g. trait evaluation) have to
1408+ /// resolve the obligations themselves to make sure the projected result is
1409+ /// ok and avoid issues like #43132.
1410+ ///
1411+ /// If that is done, after evaluation the obligations, it is a good idea to
1412+ /// call `ProjectionCache::complete` to make sure the obligations won't be
1413+ /// re-evaluated and avoid an exponential worst-case.
1414+ ///
1415+ /// FIXME: we probably also want some sort of cross-infcx cache here to
1416+ /// reduce the amount of duplication. Let's see what we get with the Chalk
1417+ /// reforms.
13841418pub struct ProjectionCache < ' tcx > {
1385- map : SnapshotMap < ty:: ProjectionTy < ' tcx > , ProjectionCacheEntry < ' tcx > > ,
1419+ map : SnapshotMap < ProjectionCacheKey < ' tcx > , ProjectionCacheEntry < ' tcx > > ,
1420+ }
1421+
1422+ #[ derive( Copy , Clone , Debug , Hash , PartialEq , Eq ) ]
1423+ pub struct ProjectionCacheKey < ' tcx > {
1424+ ty : ty:: ProjectionTy < ' tcx >
1425+ }
1426+
1427+ impl < ' cx , ' gcx , ' tcx > ProjectionCacheKey < ' tcx > {
1428+ pub fn from_poly_projection_predicate ( selcx : & mut SelectionContext < ' cx , ' gcx , ' tcx > ,
1429+ predicate : & ty:: PolyProjectionPredicate < ' tcx > )
1430+ -> Option < Self >
1431+ {
1432+ let infcx = selcx. infcx ( ) ;
1433+ // We don't do cross-snapshot caching of obligations with escaping regions,
1434+ // so there's no cache key to use
1435+ infcx. tcx . no_late_bound_regions ( & predicate)
1436+ . map ( |predicate| ProjectionCacheKey {
1437+ // We don't attempt to match up with a specific type-variable state
1438+ // from a specific call to `opt_normalize_projection_type` - if
1439+ // there's no precise match, the original cache entry is "stranded"
1440+ // anyway.
1441+ ty : infcx. resolve_type_vars_if_possible ( & predicate. projection_ty )
1442+ } )
1443+ }
13861444}
13871445
13881446#[ derive( Clone , Debug ) ]
@@ -1395,7 +1453,7 @@ enum ProjectionCacheEntry<'tcx> {
13951453
13961454// NB: intentionally not Clone
13971455pub struct ProjectionCacheSnapshot {
1398- snapshot : Snapshot
1456+ snapshot : Snapshot ,
13991457}
14001458
14011459impl < ' tcx > ProjectionCache < ' tcx > {
@@ -1414,7 +1472,7 @@ impl<'tcx> ProjectionCache<'tcx> {
14141472 }
14151473
14161474 pub fn rollback_skolemized ( & mut self , snapshot : & ProjectionCacheSnapshot ) {
1417- self . map . partial_rollback ( & snapshot. snapshot , & |k| k. has_re_skol ( ) ) ;
1475+ self . map . partial_rollback ( & snapshot. snapshot , & |k| k. ty . has_re_skol ( ) ) ;
14181476 }
14191477
14201478 pub fn commit ( & mut self , snapshot : ProjectionCacheSnapshot ) {
@@ -1424,7 +1482,7 @@ impl<'tcx> ProjectionCache<'tcx> {
14241482 /// Try to start normalize `key`; returns an error if
14251483 /// normalization already occurred (this error corresponds to a
14261484 /// cache hit, so it's actually a good thing).
1427- fn try_start ( & mut self , key : ty :: ProjectionTy < ' tcx > )
1485+ fn try_start ( & mut self , key : ProjectionCacheKey < ' tcx > )
14281486 -> Result < ( ) , ProjectionCacheEntry < ' tcx > > {
14291487 if let Some ( entry) = self . map . get ( & key) {
14301488 return Err ( entry. clone ( ) ) ;
@@ -1435,25 +1493,51 @@ impl<'tcx> ProjectionCache<'tcx> {
14351493 }
14361494
14371495 /// Indicates that `key` was normalized to `value`.
1438- fn complete ( & mut self , key : ty :: ProjectionTy < ' tcx > , value : & NormalizedTy < ' tcx > ) {
1439- debug ! ( "ProjectionCacheEntry::complete : adding cache entry: key={:?}, value={:?}" ,
1496+ fn insert_ty ( & mut self , key : ProjectionCacheKey < ' tcx > , value : & NormalizedTy < ' tcx > ) {
1497+ debug ! ( "ProjectionCacheEntry::insert_ty : adding cache entry: key={:?}, value={:?}" ,
14401498 key, value) ;
14411499 let fresh_key = self . map . insert ( key, ProjectionCacheEntry :: NormalizedTy ( value. clone ( ) ) ) ;
14421500 assert ! ( !fresh_key, "never started projecting `{:?}`" , key) ;
14431501 }
14441502
1503+ /// Mark the relevant projection cache key as having its derived obligations
1504+ /// complete, so they won't have to be re-computed (this is OK to do in a
1505+ /// snapshot - if the snapshot is rolled back, the obligations will be
1506+ /// marked as incomplete again).
1507+ pub fn complete ( & mut self , key : ProjectionCacheKey < ' tcx > ) {
1508+ let ty = match self . map . get ( & key) {
1509+ Some ( & ProjectionCacheEntry :: NormalizedTy ( ref ty) ) => {
1510+ debug ! ( "ProjectionCacheEntry::complete({:?}) - completing {:?}" ,
1511+ key, ty) ;
1512+ ty. value
1513+ }
1514+ ref value => {
1515+ // Type inference could "strand behind" old cache entries. Leave
1516+ // them alone for now.
1517+ debug ! ( "ProjectionCacheEntry::complete({:?}) - ignoring {:?}" ,
1518+ key, value) ;
1519+ return
1520+ }
1521+ } ;
1522+
1523+ self . map . insert ( key, ProjectionCacheEntry :: NormalizedTy ( Normalized {
1524+ value : ty,
1525+ obligations : vec ! [ ]
1526+ } ) ) ;
1527+ }
1528+
14451529 /// Indicates that trying to normalize `key` resulted in
14461530 /// ambiguity. No point in trying it again then until we gain more
14471531 /// type information (in which case, the "fully resolved" key will
14481532 /// be different).
1449- fn ambiguous ( & mut self , key : ty :: ProjectionTy < ' tcx > ) {
1533+ fn ambiguous ( & mut self , key : ProjectionCacheKey < ' tcx > ) {
14501534 let fresh = self . map . insert ( key, ProjectionCacheEntry :: Ambiguous ) ;
14511535 assert ! ( !fresh, "never started projecting `{:?}`" , key) ;
14521536 }
14531537
14541538 /// Indicates that trying to normalize `key` resulted in
14551539 /// error.
1456- fn error ( & mut self , key : ty :: ProjectionTy < ' tcx > ) {
1540+ fn error ( & mut self , key : ProjectionCacheKey < ' tcx > ) {
14571541 let fresh = self . map . insert ( key, ProjectionCacheEntry :: Error ) ;
14581542 assert ! ( !fresh, "never started projecting `{:?}`" , key) ;
14591543 }
0 commit comments