@@ -69,6 +69,7 @@ use crate::constrained_generic_params as cgp;
6969use crate :: errors:: SubstsOnOverriddenImpl ;
7070
7171use rustc_data_structures:: fx:: FxHashSet ;
72+ use rustc_hir as hir;
7273use rustc_hir:: def_id:: { DefId , LocalDefId } ;
7374use rustc_infer:: infer:: outlives:: env:: OutlivesEnvironment ;
7475use rustc_infer:: infer:: TyCtxtInferExt ;
@@ -80,6 +81,7 @@ use rustc_span::Span;
8081use rustc_trait_selection:: traits:: error_reporting:: TypeErrCtxtExt ;
8182use rustc_trait_selection:: traits:: outlives_bounds:: InferCtxtExt as _;
8283use rustc_trait_selection:: traits:: { self , translate_substs, wf, ObligationCtxt } ;
84+ use tracing:: instrument;
8385
8486pub ( super ) fn check_min_specialization ( tcx : TyCtxt < ' _ > , impl_def_id : LocalDefId ) {
8587 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
103105}
104106
105107/// Check that `impl1` is a sound specialization
108+ #[ instrument( level = "debug" , skip( tcx) ) ]
106109fn check_always_applicable ( tcx : TyCtxt < ' _ > , impl1_def_id : LocalDefId , impl2_node : Node ) {
107110 if let Some ( ( impl1_substs, impl2_substs) ) = get_impl_substs ( tcx, impl1_def_id, impl2_node) {
108111 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) ;
113113
114114 let parent_substs = if impl2_node. is_from_trait ( ) {
115115 impl2_substs. to_vec ( )
@@ -118,12 +118,33 @@ fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node
118118 } ;
119119
120120 let span = tcx. def_span ( impl1_def_id) ;
121+ check_constness ( tcx, impl1_def_id, impl2_node, span) ;
121122 check_static_lifetimes ( tcx, & parent_substs, span) ;
122123 check_duplicate_params ( tcx, impl1_substs, & parent_substs, span) ;
123124 check_predicates ( tcx, impl1_def_id, impl1_substs, impl2_node, impl2_substs, span) ;
124125 }
125126}
126127
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+
127148/// Given a specializing impl `impl1`, and the base impl `impl2`, returns two
128149/// substitutions `(S1, S2)` that equate their trait references. The returned
129150/// types are expressed in terms of the generics of `impl1`.
@@ -278,15 +299,15 @@ fn check_static_lifetimes<'tcx>(
278299
279300/// Check whether predicates on the specializing impl (`impl1`) are allowed.
280301///
281- /// Each predicate `P` must be:
302+ /// Each predicate `P` must be one of :
282303///
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,
289309/// including the `Self`-type.
310+ #[ instrument( level = "debug" , skip( tcx) ) ]
290311fn check_predicates < ' tcx > (
291312 tcx : TyCtxt < ' tcx > ,
292313 impl1_def_id : LocalDefId ,
@@ -322,10 +343,7 @@ fn check_predicates<'tcx>(
322343 . map ( |obligation| obligation. predicate )
323344 . collect ( )
324345 } ;
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) ;
329347
330348 // Since impls of always applicable traits don't get to assume anything, we
331349 // can also assume their supertraits apply.
@@ -373,25 +391,83 @@ fn check_predicates<'tcx>(
373391 ) ;
374392
375393 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 ) ) {
377395 check_specialization_on ( tcx, predicate, span)
378396 }
379397 }
380398}
381399
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) ) ]
382463fn check_specialization_on < ' tcx > ( tcx : TyCtxt < ' tcx > , predicate : ty:: Predicate < ' tcx > , span : Span ) {
383- debug ! ( "can_specialize_on(predicate = {:?})" , predicate) ;
384464 match predicate. kind ( ) . skip_binder ( ) {
385465 // Global predicates are either always true or always false, so we
386466 // are fine to specialize on.
387467 _ if predicate. is_global ( ) => ( ) ,
388468 // We allow specializing on explicitly marked traits with no associated
389469 // 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 : _ } ) => {
395471 if !matches ! (
396472 trait_predicate_kind( tcx, predicate) ,
397473 Some ( TraitSpecializationKind :: Marker )
0 commit comments