diff options
8 files changed, 137 insertions, 24 deletions
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index 9c7150b7924..f3cb558ef70 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -391,7 +391,7 @@ fn check_predicates<'tcx>( ); for (predicate, span) in impl1_predicates { - if !impl2_predicates.iter().any(|pred2| trait_predicates_eq(predicate, *pred2)) { + if !impl2_predicates.iter().any(|pred2| trait_predicates_eq(tcx, predicate, *pred2, span)) { check_specialization_on(tcx, predicate, span) } } @@ -400,8 +400,8 @@ fn check_predicates<'tcx>( /// Checks if some predicate on the specializing impl (`predicate1`) is the same /// as some predicate on the base impl (`predicate2`). /// -/// This is slightly more complicated than simple syntactic equivalence, since -/// we want to equate `T: Tr` with `T: ~const Tr` so this can work: +/// This basically just checks syntactic equivalence, but is a little more +/// forgiving since we want to equate `T: Tr` with `T: ~const Tr` so this can work: /// /// ```ignore (illustrative) /// #[rustc_specialization_trait] @@ -410,27 +410,54 @@ fn check_predicates<'tcx>( /// impl<T: Bound> Tr for T { } /// impl<T: ~const Bound + Specialize> const Tr for T { } /// ``` +/// +/// However, we *don't* want to allow the reverse, i.e., when the bound on the +/// specializing impl is not as const as the bound on the base impl: +/// +/// ```ignore (illustrative) +/// impl<T: ~const Bound> const Tr for T { } +/// impl<T: Bound + Specialize> const Tr for T { } // should be T: ~const Bound +/// ``` +/// +/// So we make that check in this function and try to raise a helpful error message. fn trait_predicates_eq<'tcx>( + tcx: TyCtxt<'tcx>, predicate1: ty::Predicate<'tcx>, predicate2: ty::Predicate<'tcx>, + span: Span, ) -> bool { - let predicate_kind_without_constness = |kind: ty::PredicateKind<'tcx>| match kind { - ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: _, polarity }) => { - ty::PredicateKind::Trait(ty::TraitPredicate { - trait_ref, - constness: ty::BoundConstness::NotConst, - polarity, - }) + let pred1_kind = predicate1.kind().no_bound_vars(); + let pred2_kind = predicate2.kind().no_bound_vars(); + let (trait_pred1, trait_pred2) = match (pred1_kind, pred2_kind) { + (Some(ty::PredicateKind::Trait(pred1)), Some(ty::PredicateKind::Trait(pred2))) => { + (pred1, pred2) } - _ => kind, + // Just use plain syntactic equivalence if either of the predicates aren't + // trait predicates or have bound vars. + _ => return pred1_kind == pred2_kind, + }; + + let predicates_equal_modulo_constness = { + let pred1_unconsted = + ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..trait_pred1 }; + let pred2_unconsted = + ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..trait_pred2 }; + pred1_unconsted == pred2_unconsted }; - // We rely on `check_constness` above to ensure that pred1 is const if pred2 - // is const. - let pred1_kind_not_const = predicate1.kind().map_bound(predicate_kind_without_constness); - let pred2_kind_not_const = predicate2.kind().map_bound(predicate_kind_without_constness); + if !predicates_equal_modulo_constness { + return false; + } + + // Check that the predicate on the specializing impl is at least as const as + // the one on the base. + if trait_pred2.constness == ty::BoundConstness::ConstIfConst + && trait_pred1.constness == ty::BoundConstness::NotConst + { + tcx.sess.struct_span_err(span, "missing `~const` qualifier").emit(); + } - pred1_kind_not_const == pred2_kind_not_const + true } #[instrument(level = "debug", skip(tcx))] diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.rs new file mode 100644 index 00000000000..3ac90992486 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.rs @@ -0,0 +1,46 @@ +// Tests that trait bounds on specializing trait impls must be `~const` if the +// same bound is present on the default impl and is `~const` there. + +#![feature(const_trait_impl)] +#![feature(rustc_attrs)] +#![feature(min_specialization)] + +#[rustc_specialization_trait] +trait Specialize {} + +#[const_trait] +trait Foo {} + +#[const_trait] +trait Bar {} + +// bgr360: I was only able to exercise the code path that raises the +// "missing ~const qualifier" error by making this base impl non-const, even +// though that doesn't really make sense to do. As seen below, if the base impl +// is made const, rustc fails earlier with an overlapping impl failure. +impl<T> Bar for T +where + T: ~const Foo, +{} + +impl<T> Bar for T +where + T: Foo, //~ ERROR missing `~const` qualifier + T: Specialize, +{} + +#[const_trait] +trait Baz {} + +impl<T> const Baz for T +where + T: ~const Foo, +{} + +impl<T> const Baz for T //~ ERROR conflicting implementations of trait `Baz` +where + T: Foo, + T: Specialize, +{} + +fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr new file mode 100644 index 00000000000..583c4cec77f --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr @@ -0,0 +1,18 @@ +error: missing `~const` qualifier + --> $DIR/const-default-bound-non-const-specialized-bound.rs:28:8 + | +LL | T: Foo, + | ^^^ + +error[E0119]: conflicting implementations of trait `Baz` + --> $DIR/const-default-bound-non-const-specialized-bound.rs:40:1 + | +LL | impl<T> const Baz for T + | ----------------------- first implementation here +... +LL | impl<T> const Baz for T + | ^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0119`. diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.rs index 79dd4950af3..a3bb9b3f93e 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.rs @@ -1,5 +1,4 @@ -// Tests that a const default trait impl cannot be specialized by a non-const -// trait impl. +// Tests that specializing trait impls must be at least as const as the default impl. #![feature(const_trait_impl)] #![feature(min_specialization)] diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.stderr b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.stderr index 5232a8609cd..24766804708 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.stderr @@ -1,5 +1,5 @@ error: cannot specialize on const impl with non-const impl - --> $DIR/const-default-non-const-specialized.rs:20:1 + --> $DIR/const-default-impl-non-const-specialized-impl.rs:19:1 | LL | impl Value for FortyTwo { | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs index 3370aebf063..1e6b1c6513b 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs @@ -1,5 +1,6 @@ -// Tests that `T: Foo` and `T: ~const Foo` are treated as equivalent for the -// purposes of min_specialization. +// Tests that `T: ~const Foo` in a specializing impl is treated as equivalent to +// `T: Foo` in the default impl for the purposes of specialization (i.e., it +// does not think that the user is attempting to specialize on trait `Foo`). // check-pass @@ -27,4 +28,18 @@ where T: Specialize, {} +#[const_trait] +trait Baz {} + +impl<T> const Baz for T +where + T: Foo, +{} + +impl<T> const Baz for T +where + T: ~const Foo, + T: Specialize, +{} + fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/specializing-constness.rs b/src/test/ui/rfc-2632-const-trait-impl/specializing-constness.rs index ff0cd489d47..9ab170f0920 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specializing-constness.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/specializing-constness.rs @@ -17,7 +17,9 @@ impl<T: ~const Default> const A for T { } } -impl<T: Default + Sup> A for T { //~ ERROR: cannot specialize +impl<T: Default + Sup> A for T { +//~^ ERROR: cannot specialize +//~| ERROR: missing `~const` qualifier fn a() -> u32 { 3 } diff --git a/src/test/ui/rfc-2632-const-trait-impl/specializing-constness.stderr b/src/test/ui/rfc-2632-const-trait-impl/specializing-constness.stderr index 3296c109c4e..281ba82d644 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specializing-constness.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/specializing-constness.stderr @@ -1,8 +1,14 @@ -error: cannot specialize on trait `Default` +error: cannot specialize on const impl with non-const impl + --> $DIR/specializing-constness.rs:20:1 + | +LL | impl<T: Default + Sup> A for T { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing `~const` qualifier --> $DIR/specializing-constness.rs:20:9 | LL | impl<T: Default + Sup> A for T { | ^^^^^^^ -error: aborting due to previous error +error: aborting due to 2 previous errors |
