@@ -122,11 +122,13 @@ struct ProjectionTyCandidateSet<'tcx> {
122
122
///
123
123
/// for<...> <T as Trait>::U == V
124
124
///
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.
126
127
pub fn poly_project_and_unify_type < ' cx , ' gcx , ' tcx > (
127
128
selcx : & mut SelectionContext < ' cx , ' gcx , ' tcx > ,
128
129
obligation : & PolyProjectionObligation < ' tcx > )
129
- -> Result < Option < Vec < PredicateObligation < ' tcx > > > , MismatchedProjectionTypes < ' tcx > >
130
+ -> Result < Option < Vec < PredicateObligation < ' tcx > > > ,
131
+ MismatchedProjectionTypes < ' tcx > >
130
132
{
131
133
debug ! ( "poly_project_and_unify_type(obligation={:?})" ,
132
134
obligation) ;
@@ -162,7 +164,8 @@ pub fn poly_project_and_unify_type<'cx, 'gcx, 'tcx>(
162
164
fn project_and_unify_type < ' cx , ' gcx , ' tcx > (
163
165
selcx : & mut SelectionContext < ' cx , ' gcx , ' tcx > ,
164
166
obligation : & ProjectionObligation < ' tcx > )
165
- -> Result < Option < Vec < PredicateObligation < ' tcx > > > , MismatchedProjectionTypes < ' tcx > >
167
+ -> Result < Option < Vec < PredicateObligation < ' tcx > > > ,
168
+ MismatchedProjectionTypes < ' tcx > >
166
169
{
167
170
debug ! ( "project_and_unify_type(obligation={:?})" ,
168
171
obligation) ;
@@ -397,6 +400,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
397
400
let infcx = selcx. infcx ( ) ;
398
401
399
402
let projection_ty = infcx. resolve_type_vars_if_possible ( & projection_ty) ;
403
+ let cache_key = ProjectionCacheKey { ty : projection_ty } ;
400
404
401
405
debug ! ( "opt_normalize_projection_type(\
402
406
projection_ty={:?}, \
@@ -412,7 +416,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
412
416
// bounds. It might be the case that we want two distinct caches,
413
417
// or else another kind of cache entry.
414
418
415
- match infcx. projection_cache . borrow_mut ( ) . try_start ( projection_ty ) {
419
+ match infcx. projection_cache . borrow_mut ( ) . try_start ( cache_key ) {
416
420
Ok ( ( ) ) => { }
417
421
Err ( ProjectionCacheEntry :: Ambiguous ) => {
418
422
// If we found ambiguity the last time, that generally
@@ -523,7 +527,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
523
527
obligations,
524
528
}
525
529
} ;
526
- infcx. projection_cache . borrow_mut ( ) . complete ( projection_ty , & result) ;
530
+ infcx. projection_cache . borrow_mut ( ) . insert_ty ( cache_key , & result) ;
527
531
Some ( result)
528
532
}
529
533
Ok ( ProjectedTy :: NoProgress ( projected_ty) ) => {
@@ -534,14 +538,14 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
534
538
value : projected_ty,
535
539
obligations : vec ! [ ]
536
540
} ;
537
- infcx. projection_cache . borrow_mut ( ) . complete ( projection_ty , & result) ;
541
+ infcx. projection_cache . borrow_mut ( ) . insert_ty ( cache_key , & result) ;
538
542
Some ( result)
539
543
}
540
544
Err ( ProjectionTyError :: TooManyCandidates ) => {
541
545
debug ! ( "opt_normalize_projection_type: \
542
546
too many candidates") ;
543
547
infcx. projection_cache . borrow_mut ( )
544
- . ambiguous ( projection_ty ) ;
548
+ . ambiguous ( cache_key ) ;
545
549
None
546
550
}
547
551
Err ( ProjectionTyError :: TraitSelectionError ( _) ) => {
@@ -552,7 +556,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
552
556
// reported later
553
557
554
558
infcx. projection_cache . borrow_mut ( )
555
- . error ( projection_ty ) ;
559
+ . error ( cache_key ) ;
556
560
Some ( normalize_to_error ( selcx, param_env, projection_ty, cause, depth) )
557
561
}
558
562
}
@@ -1381,8 +1385,62 @@ fn assoc_ty_def<'cx, 'gcx, 'tcx>(
1381
1385
1382
1386
// # Cache
1383
1387
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.
1384
1418
pub 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
+ }
1386
1444
}
1387
1445
1388
1446
#[ derive( Clone , Debug ) ]
@@ -1395,7 +1453,7 @@ enum ProjectionCacheEntry<'tcx> {
1395
1453
1396
1454
// NB: intentionally not Clone
1397
1455
pub struct ProjectionCacheSnapshot {
1398
- snapshot : Snapshot
1456
+ snapshot : Snapshot ,
1399
1457
}
1400
1458
1401
1459
impl < ' tcx > ProjectionCache < ' tcx > {
@@ -1414,7 +1472,7 @@ impl<'tcx> ProjectionCache<'tcx> {
1414
1472
}
1415
1473
1416
1474
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 ( ) ) ;
1418
1476
}
1419
1477
1420
1478
pub fn commit ( & mut self , snapshot : ProjectionCacheSnapshot ) {
@@ -1424,7 +1482,7 @@ impl<'tcx> ProjectionCache<'tcx> {
1424
1482
/// Try to start normalize `key`; returns an error if
1425
1483
/// normalization already occurred (this error corresponds to a
1426
1484
/// 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 > )
1428
1486
-> Result < ( ) , ProjectionCacheEntry < ' tcx > > {
1429
1487
if let Some ( entry) = self . map . get ( & key) {
1430
1488
return Err ( entry. clone ( ) ) ;
@@ -1435,25 +1493,51 @@ impl<'tcx> ProjectionCache<'tcx> {
1435
1493
}
1436
1494
1437
1495
/// 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={:?}" ,
1440
1498
key, value) ;
1441
1499
let fresh_key = self . map . insert ( key, ProjectionCacheEntry :: NormalizedTy ( value. clone ( ) ) ) ;
1442
1500
assert ! ( !fresh_key, "never started projecting `{:?}`" , key) ;
1443
1501
}
1444
1502
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
+
1445
1529
/// Indicates that trying to normalize `key` resulted in
1446
1530
/// ambiguity. No point in trying it again then until we gain more
1447
1531
/// type information (in which case, the "fully resolved" key will
1448
1532
/// be different).
1449
- fn ambiguous ( & mut self , key : ty :: ProjectionTy < ' tcx > ) {
1533
+ fn ambiguous ( & mut self , key : ProjectionCacheKey < ' tcx > ) {
1450
1534
let fresh = self . map . insert ( key, ProjectionCacheEntry :: Ambiguous ) ;
1451
1535
assert ! ( !fresh, "never started projecting `{:?}`" , key) ;
1452
1536
}
1453
1537
1454
1538
/// Indicates that trying to normalize `key` resulted in
1455
1539
/// error.
1456
- fn error ( & mut self , key : ty :: ProjectionTy < ' tcx > ) {
1540
+ fn error ( & mut self , key : ProjectionCacheKey < ' tcx > ) {
1457
1541
let fresh = self . map . insert ( key, ProjectionCacheEntry :: Error ) ;
1458
1542
assert ! ( !fresh, "never started projecting `{:?}`" , key) ;
1459
1543
}
0 commit comments