@@ -391,7 +391,7 @@ fn check_predicates<'tcx>(
391
391
) ;
392
392
393
393
for ( predicate, span) in impl1_predicates {
394
- if !impl2_predicates. iter ( ) . any ( |pred2| trait_predicates_eq ( predicate, * pred2) ) {
394
+ if !impl2_predicates. iter ( ) . any ( |pred2| trait_predicates_eq ( tcx , predicate, * pred2, span ) ) {
395
395
check_specialization_on ( tcx, predicate, span)
396
396
}
397
397
}
@@ -400,8 +400,8 @@ fn check_predicates<'tcx>(
400
400
/// Checks if some predicate on the specializing impl (`predicate1`) is the same
401
401
/// as some predicate on the base impl (`predicate2`).
402
402
///
403
- /// This is slightly more complicated than simple syntactic equivalence, since
404
- /// we want to equate `T: Tr` with `T: ~const Tr` so this can work:
403
+ /// This basically just checks syntactic equivalence, but is a little more
404
+ /// forgiving since we want to equate `T: Tr` with `T: ~const Tr` so this can work:
405
405
///
406
406
/// ```ignore (illustrative)
407
407
/// #[rustc_specialization_trait]
@@ -410,27 +410,54 @@ fn check_predicates<'tcx>(
410
410
/// impl<T: Bound> Tr for T { }
411
411
/// impl<T: ~const Bound + Specialize> const Tr for T { }
412
412
/// ```
413
+ ///
414
+ /// However, we *don't* want to allow the reverse, i.e., when the bound on the
415
+ /// specializing impl is not as const as the bound on the base impl:
416
+ ///
417
+ /// ```ignore (illustrative)
418
+ /// impl<T: ~const Bound> const Tr for T { }
419
+ /// impl<T: Bound + Specialize> const Tr for T { } // should be T: ~const Bound
420
+ /// ```
421
+ ///
422
+ /// So we make that check in this function and try to raise a helpful error message.
413
423
fn trait_predicates_eq < ' tcx > (
424
+ tcx : TyCtxt < ' tcx > ,
414
425
predicate1 : ty:: Predicate < ' tcx > ,
415
426
predicate2 : ty:: Predicate < ' tcx > ,
427
+ span : Span ,
416
428
) -> bool {
417
- let predicate_kind_without_constness = |kind : ty:: PredicateKind < ' tcx > | match kind {
418
- ty:: PredicateKind :: Trait ( ty:: TraitPredicate { trait_ref, constness : _, polarity } ) => {
419
- ty:: PredicateKind :: Trait ( ty:: TraitPredicate {
420
- trait_ref,
421
- constness : ty:: BoundConstness :: NotConst ,
422
- polarity,
423
- } )
429
+ let pred1_kind = predicate1. kind ( ) . no_bound_vars ( ) ;
430
+ let pred2_kind = predicate2. kind ( ) . no_bound_vars ( ) ;
431
+ let ( trait_pred1, trait_pred2) = match ( pred1_kind, pred2_kind) {
432
+ ( Some ( ty:: PredicateKind :: Trait ( pred1) ) , Some ( ty:: PredicateKind :: Trait ( pred2) ) ) => {
433
+ ( pred1, pred2)
424
434
}
425
- _ => kind,
435
+ // Just use plain syntactic equivalence if either of the predicates aren't
436
+ // trait predicates or have bound vars.
437
+ _ => return pred1_kind == pred2_kind,
438
+ } ;
439
+
440
+ let predicates_equal_modulo_constness = {
441
+ let pred1_unconsted =
442
+ ty:: TraitPredicate { constness : ty:: BoundConstness :: NotConst , ..trait_pred1 } ;
443
+ let pred2_unconsted =
444
+ ty:: TraitPredicate { constness : ty:: BoundConstness :: NotConst , ..trait_pred2 } ;
445
+ pred1_unconsted == pred2_unconsted
426
446
} ;
427
447
428
- // We rely on `check_constness` above to ensure that pred1 is const if pred2
429
- // is const.
430
- let pred1_kind_not_const = predicate1. kind ( ) . map_bound ( predicate_kind_without_constness) ;
431
- let pred2_kind_not_const = predicate2. kind ( ) . map_bound ( predicate_kind_without_constness) ;
448
+ if !predicates_equal_modulo_constness {
449
+ return false ;
450
+ }
451
+
452
+ // Check that the predicate on the specializing impl is at least as const as
453
+ // the one on the base.
454
+ if trait_pred2. constness == ty:: BoundConstness :: ConstIfConst
455
+ && trait_pred1. constness == ty:: BoundConstness :: NotConst
456
+ {
457
+ tcx. sess . struct_span_err ( span, "missing `~const` qualifier" ) . emit ( ) ;
458
+ }
432
459
433
- pred1_kind_not_const == pred2_kind_not_const
460
+ true
434
461
}
435
462
436
463
#[ instrument( level = "debug" , skip( tcx) ) ]
0 commit comments