@@ -121,11 +121,13 @@ struct ProjectionTyCandidateSet<'tcx> {
121121///
122122/// for<...> <T as Trait>::U == V
123123///
124- /// If successful, this may result in additional obligations.
124+ /// If successful, this may result in additional obligations. Also returns
125+ /// the projection cache key used to track these additional obligations.
125126pub fn poly_project_and_unify_type < ' cx , ' gcx , ' tcx > (
126127 selcx : & mut SelectionContext < ' cx , ' gcx , ' tcx > ,
127128 obligation : & PolyProjectionObligation < ' tcx > )
128- -> Result < Option < Vec < PredicateObligation < ' tcx > > > , MismatchedProjectionTypes < ' tcx > >
129+ -> Result < Option < Vec < PredicateObligation < ' tcx > > > ,
130+ MismatchedProjectionTypes < ' tcx > >
129131{
130132 debug ! ( "poly_project_and_unify_type(obligation={:?})" ,
131133 obligation) ;
@@ -161,7 +163,8 @@ pub fn poly_project_and_unify_type<'cx, 'gcx, 'tcx>(
161163fn project_and_unify_type < ' cx , ' gcx , ' tcx > (
162164 selcx : & mut SelectionContext < ' cx , ' gcx , ' tcx > ,
163165 obligation : & ProjectionObligation < ' tcx > )
164- -> Result < Option < Vec < PredicateObligation < ' tcx > > > , MismatchedProjectionTypes < ' tcx > >
166+ -> Result < Option < Vec < PredicateObligation < ' tcx > > > ,
167+ MismatchedProjectionTypes < ' tcx > >
165168{
166169 debug ! ( "project_and_unify_type(obligation={:?})" ,
167170 obligation) ;
@@ -396,6 +399,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
396399 let infcx = selcx. infcx ( ) ;
397400
398401 let projection_ty = infcx. resolve_type_vars_if_possible ( & projection_ty) ;
402+ let cache_key = ProjectionCacheKey { ty : projection_ty } ;
399403
400404 debug ! ( "opt_normalize_projection_type(\
401405 projection_ty={:?}, \
@@ -411,7 +415,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
411415 // bounds. It might be the case that we want two distinct caches,
412416 // or else another kind of cache entry.
413417
414- match infcx. projection_cache . borrow_mut ( ) . try_start ( projection_ty ) {
418+ match infcx. projection_cache . borrow_mut ( ) . try_start ( cache_key ) {
415419 Ok ( ( ) ) => { }
416420 Err ( ProjectionCacheEntry :: Ambiguous ) => {
417421 // If we found ambiguity the last time, that generally
@@ -522,7 +526,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
522526 obligations,
523527 }
524528 } ;
525- infcx. projection_cache . borrow_mut ( ) . complete ( projection_ty , & result) ;
529+ infcx. projection_cache . borrow_mut ( ) . insert_ty ( cache_key , & result) ;
526530 Some ( result)
527531 }
528532 Ok ( ProjectedTy :: NoProgress ( projected_ty) ) => {
@@ -533,14 +537,14 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
533537 value : projected_ty,
534538 obligations : vec ! [ ]
535539 } ;
536- infcx. projection_cache . borrow_mut ( ) . complete ( projection_ty , & result) ;
540+ infcx. projection_cache . borrow_mut ( ) . insert_ty ( cache_key , & result) ;
537541 Some ( result)
538542 }
539543 Err ( ProjectionTyError :: TooManyCandidates ) => {
540544 debug ! ( "opt_normalize_projection_type: \
541545 too many candidates") ;
542546 infcx. projection_cache . borrow_mut ( )
543- . ambiguous ( projection_ty ) ;
547+ . ambiguous ( cache_key ) ;
544548 None
545549 }
546550 Err ( ProjectionTyError :: TraitSelectionError ( _) ) => {
@@ -551,7 +555,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
551555 // reported later
552556
553557 infcx. projection_cache . borrow_mut ( )
554- . error ( projection_ty ) ;
558+ . error ( cache_key ) ;
555559 Some ( normalize_to_error ( selcx, param_env, projection_ty, cause, depth) )
556560 }
557561 }
@@ -1323,8 +1327,62 @@ fn assoc_ty_def<'cx, 'gcx, 'tcx>(
13231327
13241328// # Cache
13251329
1330+ /// The projection cache. Unlike the standard caches, this can
1331+ /// include infcx-dependent type variables - therefore, we have to roll
1332+ /// the cache back each time we roll a snapshot back, to avoid assumptions
1333+ /// on yet-unresolved inference variables. Types with skolemized regions
1334+ /// also have to be removed when the respective snapshot ends.
1335+ ///
1336+ /// Because of that, projection cache entries can be "stranded" and left
1337+ /// inaccessible when type variables inside the key are resolved. We make no
1338+ /// attempt to recover or remove "stranded" entries, but rather let them be
1339+ /// (for the lifetime of the infcx).
1340+ ///
1341+ /// Entries in the projection cache might contain inference variables
1342+ /// that will be resolved by obligations on the projection cache entry - e.g.
1343+ /// when a type parameter in the associated type is constrained through
1344+ /// an "RFC 447" projection on the impl.
1345+ ///
1346+ /// When working with a fulfillment context, the derived obligations of each
1347+ /// projection cache entry will be registered on the fulfillcx, so any users
1348+ /// that can wait for a fulfillcx fixed point need not care about this. However,
1349+ /// users that don't wait for a fixed point (e.g. trait evaluation) have to
1350+ /// resolve the obligations themselves to make sure the projected result is
1351+ /// ok and avoid issues like #43132.
1352+ ///
1353+ /// If that is done, after evaluation the obligations, it is a good idea to
1354+ /// call `ProjectionCache::complete` to make sure the obligations won't be
1355+ /// re-evaluated and avoid an exponential worst-case.
1356+ ///
1357+ /// FIXME: we probably also want some sort of cross-infcx cache here to
1358+ /// reduce the amount of duplication. Let's see what we get with the Chalk
1359+ /// reforms.
13261360pub struct ProjectionCache < ' tcx > {
1327- map : SnapshotMap < ty:: ProjectionTy < ' tcx > , ProjectionCacheEntry < ' tcx > > ,
1361+ map : SnapshotMap < ProjectionCacheKey < ' tcx > , ProjectionCacheEntry < ' tcx > > ,
1362+ }
1363+
1364+ #[ derive( Copy , Clone , Debug , Hash , PartialEq , Eq ) ]
1365+ pub struct ProjectionCacheKey < ' tcx > {
1366+ ty : ty:: ProjectionTy < ' tcx >
1367+ }
1368+
1369+ impl < ' cx , ' gcx , ' tcx > ProjectionCacheKey < ' tcx > {
1370+ pub fn from_poly_projection_predicate ( selcx : & mut SelectionContext < ' cx , ' gcx , ' tcx > ,
1371+ predicate : & ty:: PolyProjectionPredicate < ' tcx > )
1372+ -> Option < Self >
1373+ {
1374+ let infcx = selcx. infcx ( ) ;
1375+ // We don't do cross-snapshot caching of obligations with escaping regions,
1376+ // so there's no cache key to use
1377+ infcx. tcx . no_late_bound_regions ( & predicate)
1378+ . map ( |predicate| ProjectionCacheKey {
1379+ // We don't attempt to match up with a specific type-variable state
1380+ // from a specific call to `opt_normalize_projection_type` - if
1381+ // there's no precise match, the original cache entry is "stranded"
1382+ // anyway.
1383+ ty : infcx. resolve_type_vars_if_possible ( & predicate. projection_ty )
1384+ } )
1385+ }
13281386}
13291387
13301388#[ derive( Clone , Debug ) ]
@@ -1337,7 +1395,7 @@ enum ProjectionCacheEntry<'tcx> {
13371395
13381396// NB: intentionally not Clone
13391397pub struct ProjectionCacheSnapshot {
1340- snapshot : Snapshot
1398+ snapshot : Snapshot ,
13411399}
13421400
13431401impl < ' tcx > ProjectionCache < ' tcx > {
@@ -1356,7 +1414,7 @@ impl<'tcx> ProjectionCache<'tcx> {
13561414 }
13571415
13581416 pub fn rollback_skolemized ( & mut self , snapshot : & ProjectionCacheSnapshot ) {
1359- self . map . partial_rollback ( & snapshot. snapshot , & |k| k. has_re_skol ( ) ) ;
1417+ self . map . partial_rollback ( & snapshot. snapshot , & |k| k. ty . has_re_skol ( ) ) ;
13601418 }
13611419
13621420 pub fn commit ( & mut self , snapshot : ProjectionCacheSnapshot ) {
@@ -1366,7 +1424,7 @@ impl<'tcx> ProjectionCache<'tcx> {
13661424 /// Try to start normalize `key`; returns an error if
13671425 /// normalization already occurred (this error corresponds to a
13681426 /// cache hit, so it's actually a good thing).
1369- fn try_start ( & mut self , key : ty :: ProjectionTy < ' tcx > )
1427+ fn try_start ( & mut self , key : ProjectionCacheKey < ' tcx > )
13701428 -> Result < ( ) , ProjectionCacheEntry < ' tcx > > {
13711429 if let Some ( entry) = self . map . get ( & key) {
13721430 return Err ( entry. clone ( ) ) ;
@@ -1377,25 +1435,51 @@ impl<'tcx> ProjectionCache<'tcx> {
13771435 }
13781436
13791437 /// Indicates that `key` was normalized to `value`.
1380- fn complete ( & mut self , key : ty :: ProjectionTy < ' tcx > , value : & NormalizedTy < ' tcx > ) {
1381- debug ! ( "ProjectionCacheEntry::complete : adding cache entry: key={:?}, value={:?}" ,
1438+ fn insert_ty ( & mut self , key : ProjectionCacheKey < ' tcx > , value : & NormalizedTy < ' tcx > ) {
1439+ debug ! ( "ProjectionCacheEntry::insert_ty : adding cache entry: key={:?}, value={:?}" ,
13821440 key, value) ;
13831441 let fresh_key = self . map . insert ( key, ProjectionCacheEntry :: NormalizedTy ( value. clone ( ) ) ) ;
13841442 assert ! ( !fresh_key, "never started projecting `{:?}`" , key) ;
13851443 }
13861444
1445+ /// Mark the relevant projection cache key as having its derived obligations
1446+ /// complete, so they won't have to be re-computed (this is OK to do in a
1447+ /// snapshot - if the snapshot is rolled back, the obligations will be
1448+ /// marked as incomplete again).
1449+ pub fn complete ( & mut self , key : ProjectionCacheKey < ' tcx > ) {
1450+ let ty = match self . map . get ( & key) {
1451+ Some ( & ProjectionCacheEntry :: NormalizedTy ( ref ty) ) => {
1452+ debug ! ( "ProjectionCacheEntry::complete({:?}) - completing {:?}" ,
1453+ key, ty) ;
1454+ ty. value
1455+ }
1456+ ref value => {
1457+ // Type inference could "strand behind" old cache entries. Leave
1458+ // them alone for now.
1459+ debug ! ( "ProjectionCacheEntry::complete({:?}) - ignoring {:?}" ,
1460+ key, value) ;
1461+ return
1462+ }
1463+ } ;
1464+
1465+ self . map . insert ( key, ProjectionCacheEntry :: NormalizedTy ( Normalized {
1466+ value : ty,
1467+ obligations : vec ! [ ]
1468+ } ) ) ;
1469+ }
1470+
13871471 /// Indicates that trying to normalize `key` resulted in
13881472 /// ambiguity. No point in trying it again then until we gain more
13891473 /// type information (in which case, the "fully resolved" key will
13901474 /// be different).
1391- fn ambiguous ( & mut self , key : ty :: ProjectionTy < ' tcx > ) {
1475+ fn ambiguous ( & mut self , key : ProjectionCacheKey < ' tcx > ) {
13921476 let fresh = self . map . insert ( key, ProjectionCacheEntry :: Ambiguous ) ;
13931477 assert ! ( !fresh, "never started projecting `{:?}`" , key) ;
13941478 }
13951479
13961480 /// Indicates that trying to normalize `key` resulted in
13971481 /// error.
1398- fn error ( & mut self , key : ty :: ProjectionTy < ' tcx > ) {
1482+ fn error ( & mut self , key : ProjectionCacheKey < ' tcx > ) {
13991483 let fresh = self . map . insert ( key, ProjectionCacheEntry :: Error ) ;
14001484 assert ! ( !fresh, "never started projecting `{:?}`" , key) ;
14011485 }
0 commit comments