@@ -358,7 +358,8 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
358
358
& Err ( SelectionError :: Unimplemented ) => {
359
359
if self . is_of_param ( pred. skip_binder ( ) . trait_ref . substs ) {
360
360
already_visited. remove ( & pred) ;
361
- user_computed_preds. insert ( ty:: Predicate :: Trait ( pred. clone ( ) ) ) ;
361
+ self . add_user_pred ( & mut user_computed_preds,
362
+ ty:: Predicate :: Trait ( pred. clone ( ) ) ) ;
362
363
predicates. push_back ( pred) ;
363
364
} else {
364
365
debug ! (
@@ -393,6 +394,92 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
393
394
return Some ( ( new_env, final_user_env) ) ;
394
395
}
395
396
397
+ // This method is designed to work around the following issue:
398
+ // When we compute auto trait bounds, we repeatedly call SelectionContext.select,
399
+ // progressively building a ParamEnv based on the results we get.
400
+ // However, our usage of SelectionContext differs from its normal use within the compiler,
401
+ // in that we capture and re-reprocess predicates from Unimplemented errors.
402
+ //
403
+ // This can lead to a corner case when dealing with region parameters.
404
+ // During our selection loop in evaluate_predicates, we might end up with
405
+ // two trait predicates that differ only in their region parameters:
406
+ // one containing a HRTB lifetime parameter, and one containing a 'normal'
407
+ // lifetime parameter. For example:
408
+ //
409
+ // T as MyTrait<'a>
410
+ // T as MyTrait<'static>
411
+ //
412
+ // If we put both of these predicates in our computed ParamEnv, we'll
413
+ // confuse SelectionContext, since it will (correctly) view both as being applicable.
414
+ //
415
+ // To solve this, we pick the 'more strict' lifetime bound - i.e. the HRTB
416
+ // Our end goal is to generate a user-visible description of the conditions
417
+ // under which a type implements an auto trait. A trait predicate involving
418
+ // a HRTB means that the type needs to work with any choice of lifetime,
419
+ // not just one specific lifetime (e.g. 'static).
420
+ fn add_user_pred < ' c > ( & self , user_computed_preds : & mut FxHashSet < ty:: Predicate < ' c > > ,
421
+ new_pred : ty:: Predicate < ' c > ) {
422
+ let mut should_add_new = true ;
423
+ user_computed_preds. retain ( |& old_pred| {
424
+ match ( & new_pred, old_pred) {
425
+ ( & ty:: Predicate :: Trait ( new_trait) , ty:: Predicate :: Trait ( old_trait) ) => {
426
+ if new_trait. def_id ( ) == old_trait. def_id ( ) {
427
+ let new_substs = new_trait. skip_binder ( ) . trait_ref . substs ;
428
+ let old_substs = old_trait. skip_binder ( ) . trait_ref . substs ;
429
+ if !new_substs. types ( ) . eq ( old_substs. types ( ) ) {
430
+ // We can't compare lifetimes if the types are different,
431
+ // so skip checking old_pred
432
+ return true
433
+ }
434
+
435
+ for ( new_region, old_region) in new_substs
436
+ . regions ( )
437
+ . zip ( old_substs. regions ( ) ) {
438
+
439
+ match ( new_region, old_region) {
440
+ // If both predicates have an 'ReLateBound' (a HRTB) in the
441
+ // same spot, we do nothing
442
+ (
443
+ ty:: RegionKind :: ReLateBound ( _, _) ,
444
+ ty:: RegionKind :: ReLateBound ( _, _)
445
+ ) => { } ,
446
+
447
+ ( ty:: RegionKind :: ReLateBound ( _, _) , _) => {
448
+ // The new predicate has a HRTB in a spot where the old
449
+ // predicate does not (if they both had a HRTB, the previous
450
+ // match arm would have executed).
451
+ //
452
+ // The means we want to remove the older predicate from
453
+ // user_computed_preds, since having both it and the new
454
+ // predicate in a ParamEnv would confuse SelectionContext
455
+ // We're currently in the predicate passed to 'retain',
456
+ // so we return 'false' to remove the old predicate from
457
+ // user_computed_preds
458
+ return false ;
459
+ } ,
460
+ ( _, ty:: RegionKind :: ReLateBound ( _, _) ) => {
461
+ // This is the opposite situation as the previous arm - the
462
+ // old predicate has a HRTB lifetime in a place where the
463
+ // new predicate does not. We want to leave the old
464
+ // predicate in user_computed_preds, and skip adding
465
+ // new_pred to user_computed_params.
466
+ should_add_new = false
467
+ }
468
+ _ => { }
469
+ }
470
+ }
471
+ }
472
+ } ,
473
+ _ => { }
474
+ }
475
+ return true
476
+ } ) ;
477
+
478
+ if should_add_new {
479
+ user_computed_preds. insert ( new_pred) ;
480
+ }
481
+ }
482
+
396
483
pub fn region_name ( & self , region : Region ) -> Option < String > {
397
484
match region {
398
485
& ty:: ReEarlyBound ( r) => Some ( r. name . to_string ( ) ) ,
@@ -555,15 +642,15 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
555
642
let substs = & p. skip_binder ( ) . trait_ref . substs ;
556
643
557
644
if self . is_of_param ( substs) && !only_projections && is_new_pred {
558
- computed_preds . insert ( predicate) ;
645
+ self . add_user_pred ( computed_preds , predicate) ;
559
646
}
560
647
predicates. push_back ( p. clone ( ) ) ;
561
648
}
562
649
& ty:: Predicate :: Projection ( p) => {
563
650
// If the projection isn't all type vars, then
564
651
// we don't want to add it as a bound
565
652
if self . is_of_param ( p. skip_binder ( ) . projection_ty . substs ) && is_new_pred {
566
- computed_preds . insert ( predicate) ;
653
+ self . add_user_pred ( computed_preds , predicate) ;
567
654
} else {
568
655
match poly_project_and_unify_type ( select, & obligation. with ( p. clone ( ) ) ) {
569
656
Err ( e) => {
0 commit comments