@@ -77,6 +77,7 @@ use rustc_middle::ty::trait_def::TraitSpecializationKind;
77
77
use rustc_middle:: ty:: { self , TyCtxt , TypeFoldable } ;
78
78
use rustc_span:: Span ;
79
79
use rustc_trait_selection:: traits:: { self , translate_substs, wf} ;
80
+ use tracing:: instrument;
80
81
81
82
pub ( super ) fn check_min_specialization ( tcx : TyCtxt < ' _ > , impl_def_id : DefId , span : Span ) {
82
83
if let Some ( node) = parent_specialization_node ( tcx, impl_def_id) {
@@ -102,6 +103,7 @@ fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: DefId) -> Option<No
102
103
}
103
104
104
105
/// Check that `impl1` is a sound specialization
106
+ #[ instrument( level = "debug" , skip( infcx) ) ]
105
107
fn check_always_applicable (
106
108
infcx : & InferCtxt < ' _ , ' _ > ,
107
109
impl1_def_id : DefId ,
@@ -112,10 +114,8 @@ fn check_always_applicable(
112
114
get_impl_substs ( infcx, impl1_def_id, impl2_node, span)
113
115
{
114
116
let impl2_def_id = impl2_node. def_id ( ) ;
115
- debug ! (
116
- "check_always_applicable(\n impl1_def_id={:?},\n impl2_def_id={:?},\n impl2_substs={:?}\n )" ,
117
- impl1_def_id, impl2_def_id, impl2_substs
118
- ) ;
117
+ debug ! ( "impl2_def_id={impl2_def_id:?}" ) ;
118
+ debug ! ( "impl2_substs={impl2_substs:?}" ) ;
119
119
120
120
let tcx = infcx. tcx ;
121
121
@@ -278,10 +278,10 @@ fn check_static_lifetimes<'tcx>(
278
278
/// * global (not reference any parameters)
279
279
/// * `T: Tr` predicate where `Tr` is an always-applicable trait
280
280
/// * on the base `impl impl2`
281
- /// * Currently this check is done using syntactic equality, which is
282
- /// conservative but generally sufficient.
281
+ /// * This check is done using the `trait_predicates_eq` function below.
283
282
/// * a well-formed predicate of a type argument of the trait being implemented,
284
283
/// including the `Self`-type.
284
+ #[ instrument( level = "debug" , skip( infcx) ) ]
285
285
fn check_predicates < ' tcx > (
286
286
infcx : & InferCtxt < ' _ , ' tcx > ,
287
287
impl1_def_id : LocalDefId ,
@@ -313,10 +313,8 @@ fn check_predicates<'tcx>(
313
313
. map ( |obligation| obligation. predicate )
314
314
. collect ( )
315
315
} ;
316
- debug ! (
317
- "check_always_applicable(\n impl1_predicates={:?},\n impl2_predicates={:?}\n )" ,
318
- impl1_predicates, impl2_predicates,
319
- ) ;
316
+ debug ! ( "impl1_predicates={impl1_predicates:?}" ) ;
317
+ debug ! ( "impl2_predicates={impl2_predicates:?}" ) ;
320
318
321
319
// Since impls of always applicable traits don't get to assume anything, we
322
320
// can also assume their supertraits apply.
@@ -362,25 +360,52 @@ fn check_predicates<'tcx>(
362
360
) ;
363
361
364
362
for predicate in impl1_predicates {
365
- if !impl2_predicates. contains ( & predicate) {
363
+ if !impl2_predicates. iter ( ) . any ( |pred2| trait_predicates_eq ( predicate, * pred2 ) ) {
366
364
check_specialization_on ( tcx, predicate, span)
367
365
}
368
366
}
369
367
}
370
368
369
+ /// Checks whether two predicates are the same for the purposes of specialization.
370
+ ///
371
+ /// This is slightly more complicated than simple syntactic equivalence, since
372
+ /// we want to equate `T: Tr` with `T: ~const Tr` so this can work:
373
+ ///
374
+ /// #[rustc_specialization_trait]
375
+ /// trait Specialize { }
376
+ ///
377
+ /// impl<T: ~const Bound> const Tr for T { }
378
+ /// impl<T: Bound + Specialize> Tr for T { }
379
+ fn trait_predicates_eq < ' tcx > (
380
+ predicate1 : ty:: Predicate < ' tcx > ,
381
+ predicate2 : ty:: Predicate < ' tcx > ,
382
+ ) -> bool {
383
+ let predicate_kind_without_constness = |kind : ty:: PredicateKind < ' tcx > | match kind {
384
+ ty:: PredicateKind :: Trait ( ty:: TraitPredicate { trait_ref, constness : _, polarity } ) => {
385
+ ty:: PredicateKind :: Trait ( ty:: TraitPredicate {
386
+ trait_ref,
387
+ constness : ty:: BoundConstness :: NotConst ,
388
+ polarity,
389
+ } )
390
+ }
391
+ _ => kind,
392
+ } ;
393
+
394
+ let pred1_kind_not_const = predicate1. kind ( ) . map_bound ( predicate_kind_without_constness) ;
395
+ let pred2_kind_not_const = predicate2. kind ( ) . map_bound ( predicate_kind_without_constness) ;
396
+
397
+ pred1_kind_not_const == pred2_kind_not_const
398
+ }
399
+
400
+ #[ instrument( level = "debug" , skip( tcx) ) ]
371
401
fn check_specialization_on < ' tcx > ( tcx : TyCtxt < ' tcx > , predicate : ty:: Predicate < ' tcx > , span : Span ) {
372
- debug ! ( "can_specialize_on(predicate = {:?})" , predicate) ;
373
402
match predicate. kind ( ) . skip_binder ( ) {
374
403
// Global predicates are either always true or always false, so we
375
404
// are fine to specialize on.
376
405
_ if predicate. is_global ( ) => ( ) ,
377
406
// We allow specializing on explicitly marked traits with no associated
378
407
// items.
379
- ty:: PredicateKind :: Trait ( ty:: TraitPredicate {
380
- trait_ref,
381
- constness : ty:: BoundConstness :: NotConst ,
382
- polarity : _,
383
- } ) => {
408
+ ty:: PredicateKind :: Trait ( ty:: TraitPredicate { trait_ref, constness : _, polarity : _ } ) => {
384
409
if !matches ! (
385
410
trait_predicate_kind( tcx, predicate) ,
386
411
Some ( TraitSpecializationKind :: Marker )
@@ -409,13 +434,10 @@ fn trait_predicate_kind<'tcx>(
409
434
predicate : ty:: Predicate < ' tcx > ,
410
435
) -> Option < TraitSpecializationKind > {
411
436
match predicate. kind ( ) . skip_binder ( ) {
412
- ty:: PredicateKind :: Trait ( ty:: TraitPredicate {
413
- trait_ref,
414
- constness : ty:: BoundConstness :: NotConst ,
415
- polarity : _,
416
- } ) => Some ( tcx. trait_def ( trait_ref. def_id ) . specialization_kind ) ,
417
- ty:: PredicateKind :: Trait ( _)
418
- | ty:: PredicateKind :: RegionOutlives ( _)
437
+ ty:: PredicateKind :: Trait ( ty:: TraitPredicate { trait_ref, constness : _, polarity : _ } ) => {
438
+ Some ( tcx. trait_def ( trait_ref. def_id ) . specialization_kind )
439
+ }
440
+ ty:: PredicateKind :: RegionOutlives ( _)
419
441
| ty:: PredicateKind :: TypeOutlives ( _)
420
442
| ty:: PredicateKind :: Projection ( _)
421
443
| ty:: PredicateKind :: WellFormed ( _)
0 commit comments