@@ -69,6 +69,7 @@ use crate::constrained_generic_params as cgp;
69
69
use crate :: errors:: SubstsOnOverriddenImpl ;
70
70
71
71
use rustc_data_structures:: fx:: FxHashSet ;
72
+ use rustc_hir as hir;
72
73
use rustc_hir:: def_id:: { DefId , LocalDefId } ;
73
74
use rustc_infer:: infer:: outlives:: env:: OutlivesEnvironment ;
74
75
use rustc_infer:: infer:: TyCtxtInferExt ;
@@ -80,6 +81,7 @@ use rustc_span::Span;
80
81
use rustc_trait_selection:: traits:: error_reporting:: TypeErrCtxtExt ;
81
82
use rustc_trait_selection:: traits:: outlives_bounds:: InferCtxtExt as _;
82
83
use rustc_trait_selection:: traits:: { self , translate_substs, wf, ObligationCtxt } ;
84
+ use tracing:: instrument;
83
85
84
86
pub ( super ) fn check_min_specialization ( tcx : TyCtxt < ' _ > , impl_def_id : LocalDefId ) {
85
87
if let Some ( node) = parent_specialization_node ( tcx, impl_def_id) {
@@ -103,13 +105,11 @@ fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId) -> Opti
103
105
}
104
106
105
107
/// Check that `impl1` is a sound specialization
108
+ #[ instrument( level = "debug" , skip( tcx) ) ]
106
109
fn check_always_applicable ( tcx : TyCtxt < ' _ > , impl1_def_id : LocalDefId , impl2_node : Node ) {
107
110
if let Some ( ( impl1_substs, impl2_substs) ) = get_impl_substs ( tcx, impl1_def_id, impl2_node) {
108
111
let impl2_def_id = impl2_node. def_id ( ) ;
109
- debug ! (
110
- "check_always_applicable(\n impl1_def_id={:?},\n impl2_def_id={:?},\n impl2_substs={:?}\n )" ,
111
- impl1_def_id, impl2_def_id, impl2_substs
112
- ) ;
112
+ debug ! ( ?impl2_def_id, ?impl2_substs) ;
113
113
114
114
let parent_substs = if impl2_node. is_from_trait ( ) {
115
115
impl2_substs. to_vec ( )
@@ -118,12 +118,33 @@ fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node
118
118
} ;
119
119
120
120
let span = tcx. def_span ( impl1_def_id) ;
121
+ check_constness ( tcx, impl1_def_id, impl2_node, span) ;
121
122
check_static_lifetimes ( tcx, & parent_substs, span) ;
122
123
check_duplicate_params ( tcx, impl1_substs, & parent_substs, span) ;
123
124
check_predicates ( tcx, impl1_def_id, impl1_substs, impl2_node, impl2_substs, span) ;
124
125
}
125
126
}
126
127
128
+ /// Check that the specializing impl `impl1` is at least as const as the base
129
+ /// impl `impl2`
130
+ fn check_constness ( tcx : TyCtxt < ' _ > , impl1_def_id : LocalDefId , impl2_node : Node , span : Span ) {
131
+ if impl2_node. is_from_trait ( ) {
132
+ // This isn't a specialization
133
+ return ;
134
+ }
135
+
136
+ let impl1_constness = tcx. constness ( impl1_def_id. to_def_id ( ) ) ;
137
+ let impl2_constness = tcx. constness ( impl2_node. def_id ( ) ) ;
138
+
139
+ if let hir:: Constness :: Const = impl2_constness {
140
+ if let hir:: Constness :: NotConst = impl1_constness {
141
+ tcx. sess
142
+ . struct_span_err ( span, "cannot specialize on const impl with non-const impl" )
143
+ . emit ( ) ;
144
+ }
145
+ }
146
+ }
147
+
127
148
/// Given a specializing impl `impl1`, and the base impl `impl2`, returns two
128
149
/// substitutions `(S1, S2)` that equate their trait references. The returned
129
150
/// types are expressed in terms of the generics of `impl1`.
@@ -278,15 +299,15 @@ fn check_static_lifetimes<'tcx>(
278
299
279
300
/// Check whether predicates on the specializing impl (`impl1`) are allowed.
280
301
///
281
- /// Each predicate `P` must be:
302
+ /// Each predicate `P` must be one of :
282
303
///
283
- /// * global (not reference any parameters)
284
- /// * `T: Tr` predicate where `Tr` is an always-applicable trait
285
- /// * on the base `impl impl2`
286
- /// * Currently this check is done using syntactic equality, which is
287
- /// conservative but generally sufficient.
288
- /// * a well-formed predicate of a type argument of the trait being implemented,
304
+ /// * Global (not reference any parameters).
305
+ /// * A `T: Tr` predicate where `Tr` is an always-applicable trait.
306
+ /// * Present on the base impl `impl2`.
307
+ /// * This check is done using the `trait_predicates_eq` function below.
308
+ /// * A well-formed predicate of a type argument of the trait being implemented,
289
309
/// including the `Self`-type.
310
+ #[ instrument( level = "debug" , skip( tcx) ) ]
290
311
fn check_predicates < ' tcx > (
291
312
tcx : TyCtxt < ' tcx > ,
292
313
impl1_def_id : LocalDefId ,
@@ -322,10 +343,7 @@ fn check_predicates<'tcx>(
322
343
. map ( |obligation| obligation. predicate )
323
344
. collect ( )
324
345
} ;
325
- debug ! (
326
- "check_always_applicable(\n impl1_predicates={:?},\n impl2_predicates={:?}\n )" ,
327
- impl1_predicates, impl2_predicates,
328
- ) ;
346
+ debug ! ( ?impl1_predicates, ?impl2_predicates) ;
329
347
330
348
// Since impls of always applicable traits don't get to assume anything, we
331
349
// can also assume their supertraits apply.
@@ -373,25 +391,83 @@ fn check_predicates<'tcx>(
373
391
) ;
374
392
375
393
for ( predicate, span) in impl1_predicates {
376
- if !impl2_predicates. contains ( & predicate) {
394
+ if !impl2_predicates. iter ( ) . any ( |pred2| trait_predicates_eq ( tcx , predicate, * pred2 , span ) ) {
377
395
check_specialization_on ( tcx, predicate, span)
378
396
}
379
397
}
380
398
}
381
399
400
+ /// Checks if some predicate on the specializing impl (`predicate1`) is the same
401
+ /// as some predicate on the base impl (`predicate2`).
402
+ ///
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
+ ///
406
+ /// ```ignore (illustrative)
407
+ /// #[rustc_specialization_trait]
408
+ /// trait Specialize { }
409
+ ///
410
+ /// impl<T: Bound> Tr for T { }
411
+ /// impl<T: ~const Bound + Specialize> const Tr for T { }
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.
423
+ fn trait_predicates_eq < ' tcx > (
424
+ tcx : TyCtxt < ' tcx > ,
425
+ predicate1 : ty:: Predicate < ' tcx > ,
426
+ predicate2 : ty:: Predicate < ' tcx > ,
427
+ span : Span ,
428
+ ) -> bool {
429
+ let pred1_kind = predicate1. kind ( ) . skip_binder ( ) ;
430
+ let pred2_kind = predicate2. kind ( ) . skip_binder ( ) ;
431
+ let ( trait_pred1, trait_pred2) = match ( pred1_kind, pred2_kind) {
432
+ ( ty:: PredicateKind :: Trait ( pred1) , ty:: PredicateKind :: Trait ( pred2) ) => ( pred1, pred2) ,
433
+ // Just use plain syntactic equivalence if either of the predicates aren't
434
+ // trait predicates or have bound vars.
435
+ _ => return predicate1 == predicate2,
436
+ } ;
437
+
438
+ let predicates_equal_modulo_constness = {
439
+ let pred1_unconsted =
440
+ ty:: TraitPredicate { constness : ty:: BoundConstness :: NotConst , ..trait_pred1 } ;
441
+ let pred2_unconsted =
442
+ ty:: TraitPredicate { constness : ty:: BoundConstness :: NotConst , ..trait_pred2 } ;
443
+ pred1_unconsted == pred2_unconsted
444
+ } ;
445
+
446
+ if !predicates_equal_modulo_constness {
447
+ return false ;
448
+ }
449
+
450
+ // Check that the predicate on the specializing impl is at least as const as
451
+ // the one on the base.
452
+ match ( trait_pred2. constness , trait_pred1. constness ) {
453
+ ( ty:: BoundConstness :: ConstIfConst , ty:: BoundConstness :: NotConst ) => {
454
+ tcx. sess . struct_span_err ( span, "missing `~const` qualifier for specialization" ) . emit ( ) ;
455
+ }
456
+ _ => { }
457
+ }
458
+
459
+ true
460
+ }
461
+
462
+ #[ instrument( level = "debug" , skip( tcx) ) ]
382
463
fn check_specialization_on < ' tcx > ( tcx : TyCtxt < ' tcx > , predicate : ty:: Predicate < ' tcx > , span : Span ) {
383
- debug ! ( "can_specialize_on(predicate = {:?})" , predicate) ;
384
464
match predicate. kind ( ) . skip_binder ( ) {
385
465
// Global predicates are either always true or always false, so we
386
466
// are fine to specialize on.
387
467
_ if predicate. is_global ( ) => ( ) ,
388
468
// We allow specializing on explicitly marked traits with no associated
389
469
// items.
390
- ty:: PredicateKind :: Trait ( ty:: TraitPredicate {
391
- trait_ref,
392
- constness : ty:: BoundConstness :: NotConst ,
393
- polarity : _,
394
- } ) => {
470
+ ty:: PredicateKind :: Trait ( ty:: TraitPredicate { trait_ref, constness : _, polarity : _ } ) => {
395
471
if !matches ! (
396
472
trait_predicate_kind( tcx, predicate) ,
397
473
Some ( TraitSpecializationKind :: Marker )
0 commit comments