From e91fc1bc0c05da68d218a01d550c6d12297f5703 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Thu, 21 Nov 2024 20:22:08 +0000
Subject: [PATCH 1/2] Reimplement specialization for const traits

---
 .../src/traits/specialize/mod.rs              | 130 +++++++++++++++---
 ...t-bound-non-const-specialized-bound.stderr |  26 +++-
 .../const-traits/specializing-constness.rs    |   3 +-
 .../specializing-constness.stderr             |   8 +-
 4 files changed, 142 insertions(+), 25 deletions(-)

diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index a9cd705465e..91a0599a3be 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -15,6 +15,7 @@ use rustc_data_structures::fx::FxIndexSet;
 use rustc_errors::codes::*;
 use rustc_errors::{Diag, EmissionGuarantee};
 use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_infer::traits::Obligation;
 use rustc_middle::bug;
 use rustc_middle::query::LocalCrate;
 use rustc_middle::ty::print::PrintTraitRefExt as _;
@@ -230,15 +231,18 @@ pub(super) fn specialization_enabled_in(tcx: TyCtxt<'_>, _: LocalCrate) -> bool
 /// `impl1` specializes `impl2` if it applies to a subset of the types `impl2` applies
 /// to.
 #[instrument(skip(tcx), level = "debug")]
-pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, DefId)) -> bool {
+pub(super) fn specializes(
+    tcx: TyCtxt<'_>,
+    (specializing_impl_def_id, parent_impl_def_id): (DefId, DefId),
+) -> bool {
     // We check that the specializing impl comes from a crate that has specialization enabled,
     // or if the specializing impl is marked with `allow_internal_unstable`.
     //
     // We don't really care if the specialized impl (the parent) is in a crate that has
     // specialization enabled, since it's not being specialized, and it's already been checked
     // for coherence.
-    if !tcx.specialization_enabled_in(impl1_def_id.krate) {
-        let span = tcx.def_span(impl1_def_id);
+    if !tcx.specialization_enabled_in(specializing_impl_def_id.krate) {
+        let span = tcx.def_span(specializing_impl_def_id);
         if !span.allows_unstable(sym::specialization)
             && !span.allows_unstable(sym::min_specialization)
         {
@@ -246,7 +250,7 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId,
         }
     }
 
-    let impl1_trait_header = tcx.impl_trait_header(impl1_def_id).unwrap();
+    let specializing_impl_trait_header = tcx.impl_trait_header(specializing_impl_def_id).unwrap();
 
     // We determine whether there's a subset relationship by:
     //
@@ -261,27 +265,117 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId,
     // See RFC 1210 for more details and justification.
 
     // Currently we do not allow e.g., a negative impl to specialize a positive one
-    if impl1_trait_header.polarity != tcx.impl_polarity(impl2_def_id) {
+    if specializing_impl_trait_header.polarity != tcx.impl_polarity(parent_impl_def_id) {
         return false;
     }
 
-    // create a parameter environment corresponding to an identity instantiation of impl1,
-    // i.e. the most generic instantiation of impl1.
-    let param_env = tcx.param_env(impl1_def_id);
+    // create a parameter environment corresponding to an identity instantiation of the specializing impl,
+    // i.e. the most generic instantiation of the specializing impl.
+    let param_env = tcx.param_env(specializing_impl_def_id);
 
-    // Create an infcx, taking the predicates of impl1 as assumptions:
+    // Create an infcx, taking the predicates of the specializing impl as assumptions:
     let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
 
-    // Attempt to prove that impl2 applies, given all of the above.
-    fulfill_implication(
-        &infcx,
+    let specializing_impl_trait_ref =
+        specializing_impl_trait_header.trait_ref.instantiate_identity();
+    let cause = &ObligationCause::dummy();
+    debug!(
+        "fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)",
+        param_env, specializing_impl_trait_ref, parent_impl_def_id
+    );
+
+    // Attempt to prove that the parent impl applies, given all of the above.
+
+    let ocx = ObligationCtxt::new(&infcx);
+    let specializing_impl_trait_ref = ocx.normalize(cause, param_env, specializing_impl_trait_ref);
+
+    if !ocx.select_all_or_error().is_empty() {
+        infcx.dcx().span_delayed_bug(
+            infcx.tcx.def_span(specializing_impl_def_id),
+            format!("failed to fully normalize {specializing_impl_trait_ref}"),
+        );
+        return false;
+    }
+
+    let parent_args = infcx.fresh_args_for_item(DUMMY_SP, parent_impl_def_id);
+    let parent_impl_trait_ref = ocx.normalize(
+        cause,
         param_env,
-        impl1_trait_header.trait_ref.instantiate_identity(),
-        impl1_def_id,
-        impl2_def_id,
-        &ObligationCause::dummy(),
-    )
-    .is_ok()
+        infcx
+            .tcx
+            .impl_trait_ref(parent_impl_def_id)
+            .expect("expected source impl to be a trait impl")
+            .instantiate(infcx.tcx, parent_args),
+    );
+
+    // do the impls unify? If not, no specialization.
+    let Ok(()) = ocx.eq(cause, param_env, specializing_impl_trait_ref, parent_impl_trait_ref)
+    else {
+        return false;
+    };
+
+    // Now check that the source trait ref satisfies all the where clauses of the target impl.
+    // This is not just for correctness; we also need this to constrain any params that may
+    // only be referenced via projection predicates.
+    let predicates = ocx.normalize(
+        cause,
+        param_env,
+        infcx.tcx.predicates_of(parent_impl_def_id).instantiate(infcx.tcx, parent_args),
+    );
+    let obligations = predicates_for_generics(|_, _| cause.clone(), param_env, predicates);
+    ocx.register_obligations(obligations);
+
+    let errors = ocx.select_all_or_error();
+    if !errors.is_empty() {
+        // no dice!
+        debug!(
+            "fulfill_implication: for impls on {:?} and {:?}, \
+                 could not fulfill: {:?} given {:?}",
+            specializing_impl_trait_ref,
+            parent_impl_trait_ref,
+            errors,
+            param_env.caller_bounds()
+        );
+        return false;
+    }
+
+    // If the parent impl is const, then the specializing impl must be const.
+    if tcx.is_conditionally_const(parent_impl_def_id) {
+        let const_conditions = ocx.normalize(
+            cause,
+            param_env,
+            infcx.tcx.const_conditions(parent_impl_def_id).instantiate(infcx.tcx, parent_args),
+        );
+        ocx.register_obligations(const_conditions.into_iter().map(|(trait_ref, _)| {
+            Obligation::new(
+                infcx.tcx,
+                cause.clone(),
+                param_env,
+                trait_ref.to_host_effect_clause(infcx.tcx, ty::BoundConstness::Maybe),
+            )
+        }));
+
+        let errors = ocx.select_all_or_error();
+        if !errors.is_empty() {
+            // no dice!
+            debug!(
+                "fulfill_implication: for impls on {:?} and {:?}, \
+                 could not fulfill: {:?} given {:?}",
+                specializing_impl_trait_ref,
+                parent_impl_trait_ref,
+                errors,
+                param_env.caller_bounds()
+            );
+            return false;
+        }
+    }
+
+    debug!(
+        "fulfill_implication: an impl for {:?} specializes {:?}",
+        specializing_impl_trait_ref, parent_impl_trait_ref
+    );
+
+    true
 }
 
 /// Query provider for `specialization_graph_of`.
diff --git a/tests/ui/traits/const-traits/specialization/const-default-bound-non-const-specialized-bound.stderr b/tests/ui/traits/const-traits/specialization/const-default-bound-non-const-specialized-bound.stderr
index bffc60c65fc..9166b8ca5d2 100644
--- a/tests/ui/traits/const-traits/specialization/const-default-bound-non-const-specialized-bound.stderr
+++ b/tests/ui/traits/const-traits/specialization/const-default-bound-non-const-specialized-bound.stderr
@@ -1,11 +1,31 @@
-error: cannot specialize on const impl with non-const impl
+error[E0119]: conflicting implementations of trait `Bar`
   --> $DIR/const-default-bound-non-const-specialized-bound.rs:28:1
    |
+LL | / impl<T> const Bar for T
+LL | | where
+LL | |     T: ~const Foo,
+   | |__________________- first implementation here
+...
 LL | / impl<T> Bar for T
 LL | | where
 LL | |     T: Foo, //FIXME ~ ERROR missing `~const` qualifier
 LL | |     T: Specialize,
-   | |__________________^
+   | |__________________^ conflicting implementation
 
-error: aborting due to 1 previous error
+error[E0119]: conflicting implementations of trait `Baz`
+  --> $DIR/const-default-bound-non-const-specialized-bound.rs:48:1
+   |
+LL | / impl<T> const Baz for T
+LL | | where
+LL | |     T: ~const Foo,
+   | |__________________- first implementation here
+...
+LL | / impl<T> const Baz for T //FIXME ~ ERROR conflicting implementations of trait `Baz`
+LL | | where
+LL | |     T: Foo,
+LL | |     T: Specialize,
+   | |__________________^ conflicting implementation
 
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/tests/ui/traits/const-traits/specializing-constness.rs b/tests/ui/traits/const-traits/specializing-constness.rs
index 632121924a6..94b6da7124d 100644
--- a/tests/ui/traits/const-traits/specializing-constness.rs
+++ b/tests/ui/traits/const-traits/specializing-constness.rs
@@ -21,8 +21,7 @@ impl<T: ~const Spec> const A for T {
 }
 
 impl<T: Spec + Sup> A for T {
-//~^ ERROR: cannot specialize
-//FIXME(const_trait_impl) ~| ERROR: missing `~const` qualifier
+    //~^ ERROR conflicting implementations of trait `A`
     fn a() -> u32 {
         3
     }
diff --git a/tests/ui/traits/const-traits/specializing-constness.stderr b/tests/ui/traits/const-traits/specializing-constness.stderr
index 21e21c2cb71..2ca70b53e4e 100644
--- a/tests/ui/traits/const-traits/specializing-constness.stderr
+++ b/tests/ui/traits/const-traits/specializing-constness.stderr
@@ -1,8 +1,12 @@
-error: cannot specialize on const impl with non-const impl
+error[E0119]: conflicting implementations of trait `A`
   --> $DIR/specializing-constness.rs:23:1
    |
+LL | impl<T: ~const Spec> const A for T {
+   | ---------------------------------- first implementation here
+...
 LL | impl<T: Spec + Sup> A for T {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
 
 error: aborting due to 1 previous error
 
+For more information about this error, try `rustc --explain E0119`.

From 9bda88bb58c1c6fa175166a7d5272f483eac38c8 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Sat, 23 Nov 2024 01:00:11 +0000
Subject: [PATCH 2/2] Fix const specialization

---
 compiler/rustc_hir_analysis/messages.ftl      |  2 -
 compiler/rustc_hir_analysis/src/errors.rs     |  7 ----
 .../src/impl_wf_check/min_specialization.rs   | 26 -------------
 .../src/traits/specialize/mod.rs              | 22 ++++++++---
 ...verlap-const-with-nonconst.min_spec.stderr | 14 +++++++
 .../overlap-const-with-nonconst.rs            | 38 +++++++++++++++++++
 .../overlap-const-with-nonconst.spec.stderr   | 24 ++++++++++++
 ...non-const-specialized-impl.min_spec.stderr | 12 ++++++
 ...default-impl-non-const-specialized-impl.rs |  8 +++-
 ...mpl-non-const-specialized-impl.spec.stderr | 22 +++++++++++
 ...ult-impl-non-const-specialized-impl.stderr |  8 ----
 11 files changed, 133 insertions(+), 50 deletions(-)
 create mode 100644 tests/ui/traits/const-traits/overlap-const-with-nonconst.min_spec.stderr
 create mode 100644 tests/ui/traits/const-traits/overlap-const-with-nonconst.rs
 create mode 100644 tests/ui/traits/const-traits/overlap-const-with-nonconst.spec.stderr
 create mode 100644 tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.min_spec.stderr
 create mode 100644 tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.spec.stderr
 delete mode 100644 tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.stderr

diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index cb658111392..070d63b48b7 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -113,8 +113,6 @@ hir_analysis_const_param_ty_impl_on_unsized =
     the trait `ConstParamTy` may not be implemented for this type
     .label = type is not `Sized`
 
-hir_analysis_const_specialize = cannot specialize on const impl with non-const impl
-
 hir_analysis_copy_impl_on_non_adt =
     the trait `Copy` cannot be implemented for this type
     .label = type is not a structure or enumeration
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 51115b11e86..4142dcff226 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -1079,13 +1079,6 @@ pub(crate) struct EmptySpecialization {
     pub base_impl_span: Span,
 }
 
-#[derive(Diagnostic)]
-#[diag(hir_analysis_const_specialize)]
-pub(crate) struct ConstSpecialize {
-    #[primary_span]
-    pub span: Span,
-}
-
 #[derive(Diagnostic)]
 #[diag(hir_analysis_static_specialize)]
 pub(crate) struct StaticSpecialize {
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 246643d8074..ee55e1bc21a 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
@@ -66,7 +66,6 @@
 //! on traits with methods can.
 
 use rustc_data_structures::fx::FxHashSet;
-use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_infer::infer::outlives::env::OutlivesEnvironment;
@@ -134,7 +133,6 @@ fn check_always_applicable(
         unconstrained_parent_impl_args(tcx, impl2_def_id, impl2_args)
     };
 
-    res = res.and(check_constness(tcx, impl1_def_id, impl2_node, span));
     res = res.and(check_static_lifetimes(tcx, &parent_args, span));
     res = res.and(check_duplicate_params(tcx, impl1_args, parent_args, span));
     res = res.and(check_predicates(tcx, impl1_def_id, impl1_args, impl2_node, impl2_args, span));
@@ -157,30 +155,6 @@ fn check_has_items(
     Ok(())
 }
 
-/// Check that the specializing impl `impl1` is at least as const as the base
-/// impl `impl2`
-fn check_constness(
-    tcx: TyCtxt<'_>,
-    impl1_def_id: LocalDefId,
-    impl2_node: Node,
-    span: Span,
-) -> Result<(), ErrorGuaranteed> {
-    if impl2_node.is_from_trait() {
-        // This isn't a specialization
-        return Ok(());
-    }
-
-    let impl1_constness = tcx.constness(impl1_def_id.to_def_id());
-    let impl2_constness = tcx.constness(impl2_node.def_id());
-
-    if let hir::Constness::Const = impl2_constness {
-        if let hir::Constness::NotConst = impl1_constness {
-            return Err(tcx.dcx().emit_err(errors::ConstSpecialize { span }));
-        }
-    }
-    Ok(())
-}
-
 /// Given a specializing impl `impl1`, and the base impl `impl2`, returns two
 /// generic parameters `(S1, S2)` that equate their trait references.
 /// The returned types are expressed in terms of the generics of `impl1`.
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index 91a0599a3be..1430cfae51f 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -225,11 +225,17 @@ pub(super) fn specialization_enabled_in(tcx: TyCtxt<'_>, _: LocalCrate) -> bool
     tcx.features().specialization() || tcx.features().min_specialization()
 }
 
-/// Is `impl1` a specialization of `impl2`?
+/// Is `specializing_impl_def_id` a specialization of `parent_impl_def_id`?
 ///
-/// Specialization is determined by the sets of types to which the impls apply;
-/// `impl1` specializes `impl2` if it applies to a subset of the types `impl2` applies
-/// to.
+/// For every type that could apply to `specializing_impl_def_id`, we prove that
+/// the `parent_impl_def_id` also applies (i.e. it has a valid impl header and
+/// its where-clauses hold).
+///
+/// For the purposes of const traits, we also check that the specializing
+/// impl is not more restrictive than the parent impl. That is, if the
+/// `parent_impl_def_id` is a const impl (conditionally based off of some `~const`
+/// bounds), then `specializing_impl_def_id` must also be const for the same
+/// set of types.
 #[instrument(skip(tcx), level = "debug")]
 pub(super) fn specializes(
     tcx: TyCtxt<'_>,
@@ -339,8 +345,14 @@ pub(super) fn specializes(
         return false;
     }
 
-    // If the parent impl is const, then the specializing impl must be const.
+    // If the parent impl is const, then the specializing impl must be const,
+    // and it must not be *more restrictive* than the parent impl (that is,
+    // it cannot be const in fewer cases than the parent impl).
     if tcx.is_conditionally_const(parent_impl_def_id) {
+        if !tcx.is_conditionally_const(specializing_impl_def_id) {
+            return false;
+        }
+
         let const_conditions = ocx.normalize(
             cause,
             param_env,
diff --git a/tests/ui/traits/const-traits/overlap-const-with-nonconst.min_spec.stderr b/tests/ui/traits/const-traits/overlap-const-with-nonconst.min_spec.stderr
new file mode 100644
index 00000000000..bd822970ad1
--- /dev/null
+++ b/tests/ui/traits/const-traits/overlap-const-with-nonconst.min_spec.stderr
@@ -0,0 +1,14 @@
+error[E0119]: conflicting implementations of trait `Foo` for type `(_,)`
+  --> $DIR/overlap-const-with-nonconst.rs:23:1
+   |
+LL | / impl<T> const Foo for T
+LL | | where
+LL | |     T: ~const Bar,
+   | |__________________- first implementation here
+...
+LL |   impl<T> Foo for (T,) {
+   |   ^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(_,)`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/tests/ui/traits/const-traits/overlap-const-with-nonconst.rs b/tests/ui/traits/const-traits/overlap-const-with-nonconst.rs
new file mode 100644
index 00000000000..eb66d03faa6
--- /dev/null
+++ b/tests/ui/traits/const-traits/overlap-const-with-nonconst.rs
@@ -0,0 +1,38 @@
+//@ revisions: spec min_spec
+
+#![feature(const_trait_impl)]
+#![cfg_attr(spec, feature(specialization))]
+//[spec]~^ WARN the feature `specialization` is incomplete
+#![cfg_attr(min_spec, feature(min_specialization))]
+
+#[const_trait]
+trait Bar {}
+impl<T> const Bar for T {}
+
+#[const_trait]
+trait Foo {
+    fn method(&self);
+}
+impl<T> const Foo for T
+where
+    T: ~const Bar,
+{
+    default fn method(&self) {}
+}
+// specializing impl:
+impl<T> Foo for (T,) {
+//~^ ERROR conflicting implementations
+    fn method(&self) {
+        println!("hi");
+    }
+}
+
+const fn dispatch<T: ~const Bar + Copy>(t: T) {
+    t.method();
+}
+
+fn main() {
+    const {
+        dispatch(((),));
+    }
+}
diff --git a/tests/ui/traits/const-traits/overlap-const-with-nonconst.spec.stderr b/tests/ui/traits/const-traits/overlap-const-with-nonconst.spec.stderr
new file mode 100644
index 00000000000..cbdcb45f6be
--- /dev/null
+++ b/tests/ui/traits/const-traits/overlap-const-with-nonconst.spec.stderr
@@ -0,0 +1,24 @@
+warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/overlap-const-with-nonconst.rs:4:27
+   |
+LL | #![cfg_attr(spec, feature(specialization))]
+   |                           ^^^^^^^^^^^^^^
+   |
+   = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
+   = help: consider using `min_specialization` instead, which is more stable and complete
+   = note: `#[warn(incomplete_features)]` on by default
+
+error[E0119]: conflicting implementations of trait `Foo` for type `(_,)`
+  --> $DIR/overlap-const-with-nonconst.rs:23:1
+   |
+LL | / impl<T> const Foo for T
+LL | | where
+LL | |     T: ~const Bar,
+   | |__________________- first implementation here
+...
+LL |   impl<T> Foo for (T,) {
+   |   ^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(_,)`
+
+error: aborting due to 1 previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.min_spec.stderr b/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.min_spec.stderr
new file mode 100644
index 00000000000..38fc5ddfbef
--- /dev/null
+++ b/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.min_spec.stderr
@@ -0,0 +1,12 @@
+error[E0119]: conflicting implementations of trait `Value` for type `FortyTwo`
+  --> $DIR/const-default-impl-non-const-specialized-impl.rs:22:1
+   |
+LL | impl<T> const Value for T {
+   | ------------------------- first implementation here
+...
+LL | impl Value for FortyTwo {
+   | ^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `FortyTwo`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.rs b/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.rs
index a3bb9b3f93e..acf0a967a88 100644
--- a/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.rs
+++ b/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.rs
@@ -1,7 +1,10 @@
 // Tests that specializing trait impls must be at least as const as the default impl.
+//@ revisions: spec min_spec
 
 #![feature(const_trait_impl)]
-#![feature(min_specialization)]
+#![cfg_attr(spec, feature(specialization))]
+//[spec]~^ WARN the feature `specialization` is incomplete
+#![cfg_attr(min_spec, feature(min_specialization))]
 
 #[const_trait]
 trait Value {
@@ -16,7 +19,8 @@ impl<T> const Value for T {
 
 struct FortyTwo;
 
-impl Value for FortyTwo { //~ ERROR cannot specialize on const impl with non-const impl
+impl Value for FortyTwo {
+    //~^ ERROR conflicting implementations
     fn value() -> u32 {
         println!("You can't do that (constly)");
         42
diff --git a/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.spec.stderr b/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.spec.stderr
new file mode 100644
index 00000000000..b59c42f5189
--- /dev/null
+++ b/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.spec.stderr
@@ -0,0 +1,22 @@
+warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/const-default-impl-non-const-specialized-impl.rs:5:27
+   |
+LL | #![cfg_attr(spec, feature(specialization))]
+   |                           ^^^^^^^^^^^^^^
+   |
+   = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
+   = help: consider using `min_specialization` instead, which is more stable and complete
+   = note: `#[warn(incomplete_features)]` on by default
+
+error[E0119]: conflicting implementations of trait `Value` for type `FortyTwo`
+  --> $DIR/const-default-impl-non-const-specialized-impl.rs:22:1
+   |
+LL | impl<T> const Value for T {
+   | ------------------------- first implementation here
+...
+LL | impl Value for FortyTwo {
+   | ^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `FortyTwo`
+
+error: aborting due to 1 previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.stderr b/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.stderr
deleted file mode 100644
index e356621ba47..00000000000
--- a/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.stderr
+++ /dev/null
@@ -1,8 +0,0 @@
-error: cannot specialize on const impl with non-const impl
-  --> $DIR/const-default-impl-non-const-specialized-impl.rs:19:1
-   |
-LL | impl Value for FortyTwo {
-   | ^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 1 previous error
-