@@ -121,11 +121,13 @@ struct ProjectionTyCandidateSet<'tcx> {
121
121
///
122
122
/// for<...> <T as Trait>::U == V
123
123
///
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.
125
126
pub fn poly_project_and_unify_type < ' cx , ' gcx , ' tcx > (
126
127
selcx : & mut SelectionContext < ' cx , ' gcx , ' tcx > ,
127
128
obligation : & PolyProjectionObligation < ' tcx > )
128
- -> Result < Option < Vec < PredicateObligation < ' tcx > > > , MismatchedProjectionTypes < ' tcx > >
129
+ -> Result < Option < Vec < PredicateObligation < ' tcx > > > ,
130
+ MismatchedProjectionTypes < ' tcx > >
129
131
{
130
132
debug ! ( "poly_project_and_unify_type(obligation={:?})" ,
131
133
obligation) ;
@@ -161,7 +163,8 @@ pub fn poly_project_and_unify_type<'cx, 'gcx, 'tcx>(
161
163
fn project_and_unify_type < ' cx , ' gcx , ' tcx > (
162
164
selcx : & mut SelectionContext < ' cx , ' gcx , ' tcx > ,
163
165
obligation : & ProjectionObligation < ' tcx > )
164
- -> Result < Option < Vec < PredicateObligation < ' tcx > > > , MismatchedProjectionTypes < ' tcx > >
166
+ -> Result < Option < Vec < PredicateObligation < ' tcx > > > ,
167
+ MismatchedProjectionTypes < ' tcx > >
165
168
{
166
169
debug ! ( "project_and_unify_type(obligation={:?})" ,
167
170
obligation) ;
@@ -396,6 +399,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
396
399
let infcx = selcx. infcx ( ) ;
397
400
398
401
let projection_ty = infcx. resolve_type_vars_if_possible ( & projection_ty) ;
402
+ let cache_key = ProjectionCacheKey { ty : projection_ty } ;
399
403
400
404
debug ! ( "opt_normalize_projection_type(\
401
405
projection_ty={:?}, \
@@ -411,7 +415,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
411
415
// bounds. It might be the case that we want two distinct caches,
412
416
// or else another kind of cache entry.
413
417
414
- match infcx. projection_cache . borrow_mut ( ) . try_start ( projection_ty ) {
418
+ match infcx. projection_cache . borrow_mut ( ) . try_start ( cache_key ) {
415
419
Ok ( ( ) ) => { }
416
420
Err ( ProjectionCacheEntry :: Ambiguous ) => {
417
421
// If we found ambiguity the last time, that generally
@@ -522,7 +526,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
522
526
obligations,
523
527
}
524
528
} ;
525
- infcx. projection_cache . borrow_mut ( ) . complete ( projection_ty , & result) ;
529
+ infcx. projection_cache . borrow_mut ( ) . insert_ty ( cache_key , & result) ;
526
530
Some ( result)
527
531
}
528
532
Ok ( ProjectedTy :: NoProgress ( projected_ty) ) => {
@@ -533,14 +537,14 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
533
537
value : projected_ty,
534
538
obligations : vec ! [ ]
535
539
} ;
536
- infcx. projection_cache . borrow_mut ( ) . complete ( projection_ty , & result) ;
540
+ infcx. projection_cache . borrow_mut ( ) . insert_ty ( cache_key , & result) ;
537
541
Some ( result)
538
542
}
539
543
Err ( ProjectionTyError :: TooManyCandidates ) => {
540
544
debug ! ( "opt_normalize_projection_type: \
541
545
too many candidates") ;
542
546
infcx. projection_cache . borrow_mut ( )
543
- . ambiguous ( projection_ty ) ;
547
+ . ambiguous ( cache_key ) ;
544
548
None
545
549
}
546
550
Err ( ProjectionTyError :: TraitSelectionError ( _) ) => {
@@ -551,7 +555,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
551
555
// reported later
552
556
553
557
infcx. projection_cache . borrow_mut ( )
554
- . error ( projection_ty ) ;
558
+ . error ( cache_key ) ;
555
559
Some ( normalize_to_error ( selcx, param_env, projection_ty, cause, depth) )
556
560
}
557
561
}
@@ -1323,8 +1327,62 @@ fn assoc_ty_def<'cx, 'gcx, 'tcx>(
1323
1327
1324
1328
// # Cache
1325
1329
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.
1326
1360
pub 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
+ }
1328
1386
}
1329
1387
1330
1388
#[ derive( Clone , Debug ) ]
@@ -1337,7 +1395,7 @@ enum ProjectionCacheEntry<'tcx> {
1337
1395
1338
1396
// NB: intentionally not Clone
1339
1397
pub struct ProjectionCacheSnapshot {
1340
- snapshot : Snapshot
1398
+ snapshot : Snapshot ,
1341
1399
}
1342
1400
1343
1401
impl < ' tcx > ProjectionCache < ' tcx > {
@@ -1356,7 +1414,7 @@ impl<'tcx> ProjectionCache<'tcx> {
1356
1414
}
1357
1415
1358
1416
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 ( ) ) ;
1360
1418
}
1361
1419
1362
1420
pub fn commit ( & mut self , snapshot : ProjectionCacheSnapshot ) {
@@ -1366,7 +1424,7 @@ impl<'tcx> ProjectionCache<'tcx> {
1366
1424
/// Try to start normalize `key`; returns an error if
1367
1425
/// normalization already occurred (this error corresponds to a
1368
1426
/// 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 > )
1370
1428
-> Result < ( ) , ProjectionCacheEntry < ' tcx > > {
1371
1429
if let Some ( entry) = self . map . get ( & key) {
1372
1430
return Err ( entry. clone ( ) ) ;
@@ -1377,25 +1435,51 @@ impl<'tcx> ProjectionCache<'tcx> {
1377
1435
}
1378
1436
1379
1437
/// 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={:?}" ,
1382
1440
key, value) ;
1383
1441
let fresh_key = self . map . insert ( key, ProjectionCacheEntry :: NormalizedTy ( value. clone ( ) ) ) ;
1384
1442
assert ! ( !fresh_key, "never started projecting `{:?}`" , key) ;
1385
1443
}
1386
1444
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
+
1387
1471
/// Indicates that trying to normalize `key` resulted in
1388
1472
/// ambiguity. No point in trying it again then until we gain more
1389
1473
/// type information (in which case, the "fully resolved" key will
1390
1474
/// be different).
1391
- fn ambiguous ( & mut self , key : ty :: ProjectionTy < ' tcx > ) {
1475
+ fn ambiguous ( & mut self , key : ProjectionCacheKey < ' tcx > ) {
1392
1476
let fresh = self . map . insert ( key, ProjectionCacheEntry :: Ambiguous ) ;
1393
1477
assert ! ( !fresh, "never started projecting `{:?}`" , key) ;
1394
1478
}
1395
1479
1396
1480
/// Indicates that trying to normalize `key` resulted in
1397
1481
/// error.
1398
- fn error ( & mut self , key : ty :: ProjectionTy < ' tcx > ) {
1482
+ fn error ( & mut self , key : ProjectionCacheKey < ' tcx > ) {
1399
1483
let fresh = self . map . insert ( key, ProjectionCacheEntry :: Error ) ;
1400
1484
assert ! ( !fresh, "never started projecting `{:?}`" , key) ;
1401
1485
}
0 commit comments