@@ -391,7 +391,7 @@ fn check_predicates<'tcx>(
391391 ) ;
392392
393393 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 ) ) {
395395 check_specialization_on ( tcx, predicate, span)
396396 }
397397 }
@@ -400,8 +400,8 @@ fn check_predicates<'tcx>(
400400/// Checks if some predicate on the specializing impl (`predicate1`) is the same
401401/// as some predicate on the base impl (`predicate2`).
402402///
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:
405405///
406406/// ```ignore (illustrative)
407407/// #[rustc_specialization_trait]
@@ -410,27 +410,54 @@ fn check_predicates<'tcx>(
410410/// impl<T: Bound> Tr for T { }
411411/// impl<T: ~const Bound + Specialize> const Tr for T { }
412412/// ```
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.
413423fn trait_predicates_eq < ' tcx > (
424+ tcx : TyCtxt < ' tcx > ,
414425 predicate1 : ty:: Predicate < ' tcx > ,
415426 predicate2 : ty:: Predicate < ' tcx > ,
427+ span : Span ,
416428) -> 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)
424434 }
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
426446 } ;
427447
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+ }
432459
433- pred1_kind_not_const == pred2_kind_not_const
460+ true
434461}
435462
436463#[ instrument( level = "debug" , skip( tcx) ) ]
0 commit comments