From 2743df848b0970ad9d97b6ce8222d4fd64898b61 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Sun, 12 Jan 2025 20:52:30 +0000
Subject: [PATCH] Enforce syntactical stability of const traits in HIR

---
 compiler/rustc_middle/src/middle/stability.rs | 89 ++++++++++++++++++-
 compiler/rustc_passes/src/stability.rs        | 36 +++++++-
 compiler/rustc_resolve/src/macros.rs          |  1 +
 library/core/src/intrinsics/fallback.rs       |  1 +
 library/core/src/marker.rs                    |  1 +
 library/core/src/ops/arith.rs                 |  1 +
 library/core/src/ops/deref.rs                 |  2 +
 library/core/src/ops/drop.rs                  |  1 +
 tests/ui/consts/promoted-const-drop.rs        |  2 +-
 tests/ui/consts/promoted_const_call.rs        | 16 ++--
 tests/ui/consts/promoted_const_call.stderr    | 39 ++++----
 .../missing-const-stability.rs                |  1 +
 .../missing-const-stability.stderr            | 14 ++-
 .../const-traits/auxiliary/staged-api.rs      |  1 +
 .../call-const-trait-method-pass.rs           |  2 +-
 .../const-traits/const-and-non-const-impl.rs  |  2 +-
 tests/ui/traits/const-traits/generic-bound.rs |  2 +-
 .../const-traits/syntactical-unstable.rs      | 34 +++++++
 .../const-traits/syntactical-unstable.stderr  | 67 ++++++++++++++
 .../trait-default-body-stability.rs           |  1 +
 .../trait-default-body-stability.stderr       |  4 +-
 21 files changed, 279 insertions(+), 38 deletions(-)
 create mode 100644 tests/ui/traits/const-traits/syntactical-unstable.rs
 create mode 100644 tests/ui/traits/const-traits/syntactical-unstable.stderr

diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs
index 84c3c2eb49e..75ca289056e 100644
--- a/compiler/rustc_middle/src/middle/stability.rs
+++ b/compiler/rustc_middle/src/middle/stability.rs
@@ -30,6 +30,14 @@ pub enum StabilityLevel {
     Stable,
 }
 
+#[derive(Copy, Clone)]
+pub enum UnstableKind {
+    /// Enforcing regular stability of an item
+    Regular,
+    /// Enforcing const stability of an item
+    Const(Span),
+}
+
 /// An entry in the `depr_map`.
 #[derive(Copy, Clone, HashStable, Debug, Encodable, Decodable)]
 pub struct DeprecationEntry {
@@ -108,10 +116,16 @@ pub fn report_unstable(
     is_soft: bool,
     span: Span,
     soft_handler: impl FnOnce(&'static Lint, Span, String),
+    kind: UnstableKind,
 ) {
+    let qual = match kind {
+        UnstableKind::Regular => "",
+        UnstableKind::Const(_) => " const",
+    };
+
     let msg = match reason {
-        Some(r) => format!("use of unstable library feature `{feature}`: {r}"),
-        None => format!("use of unstable library feature `{feature}`"),
+        Some(r) => format!("use of unstable{qual} library feature `{feature}`: {r}"),
+        None => format!("use of unstable{qual} library feature `{feature}`"),
     };
 
     if is_soft {
@@ -121,6 +135,9 @@ pub fn report_unstable(
         if let Some((inner_types, msg, sugg, applicability)) = suggestion {
             err.span_suggestion(inner_types, msg, sugg, applicability);
         }
+        if let UnstableKind::Const(kw) = kind {
+            err.span_label(kw, "trait is not stable as const yet");
+        }
         err.emit();
     }
 }
@@ -587,6 +604,7 @@ impl<'tcx> TyCtxt<'tcx> {
                 is_soft,
                 span,
                 soft_handler,
+                UnstableKind::Regular,
             ),
             EvalResult::Unmarked => unmarked(span, def_id),
         }
@@ -594,6 +612,73 @@ impl<'tcx> TyCtxt<'tcx> {
         is_allowed
     }
 
+    /// This function is analogous to `check_optional_stability` but with the logic in
+    /// `eval_stability_allow_unstable` inlined, and which operating on const stability
+    /// instead of regular stability.
+    ///
+    /// This enforces *syntactical* const stability of const traits. In other words,
+    /// it enforces the ability to name `~const`/`const` traits in trait bounds in various
+    /// syntax positions in HIR (including in the trait of an impl header).
+    pub fn check_const_stability(self, def_id: DefId, span: Span, const_kw_span: Span) {
+        let is_staged_api = self.lookup_stability(def_id.krate.as_def_id()).is_some();
+        if !is_staged_api {
+            return;
+        }
+
+        // Only the cross-crate scenario matters when checking unstable APIs
+        let cross_crate = !def_id.is_local();
+        if !cross_crate {
+            return;
+        }
+
+        let stability = self.lookup_const_stability(def_id);
+        debug!(
+            "stability: \
+                inspecting def_id={:?} span={:?} of stability={:?}",
+            def_id, span, stability
+        );
+
+        match stability {
+            Some(ConstStability {
+                level: attr::StabilityLevel::Unstable { reason, issue, is_soft, implied_by, .. },
+                feature,
+                ..
+            }) => {
+                assert!(!is_soft);
+
+                if span.allows_unstable(feature) {
+                    debug!("body stability: skipping span={:?} since it is internal", span);
+                    return;
+                }
+                if self.features().enabled(feature) {
+                    return;
+                }
+
+                // If this item was previously part of a now-stabilized feature which is still
+                // enabled (i.e. the user hasn't removed the attribute for the stabilized feature
+                // yet) then allow use of this item.
+                if let Some(implied_by) = implied_by
+                    && self.features().enabled(implied_by)
+                {
+                    return;
+                }
+
+                report_unstable(
+                    self.sess,
+                    feature,
+                    reason.to_opt_reason(),
+                    issue,
+                    None,
+                    false,
+                    span,
+                    |_, _, _| {},
+                    UnstableKind::Const(const_kw_span),
+                );
+            }
+            Some(_) | None => {}
+        }
+    }
+
     pub fn lookup_deprecation(self, id: DefId) -> Option<Deprecation> {
         self.lookup_deprecation_entry(id).map(|depr| depr.attr)
     }
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 30f9e698521..aabc33b015e 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -593,9 +593,11 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> {
     }
 
     fn check_missing_const_stability(&self, def_id: LocalDefId, span: Span) {
-        let is_const = self.tcx.is_const_fn(def_id.to_def_id());
+        let is_const = self.tcx.is_const_fn(def_id.to_def_id())
+            || (self.tcx.def_kind(def_id.to_def_id()) == DefKind::Trait
+                && self.tcx.is_const_trait(def_id.to_def_id()));
 
-        // Reachable const fn must have a stability attribute.
+        // Reachable const fn/trait must have a stability attribute.
         if is_const
             && self.effective_visibilities.is_reachable(def_id)
             && self.tcx.lookup_const_stability(def_id).is_none()
@@ -772,7 +774,13 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
             // For implementations of traits, check the stability of each item
             // individually as it's possible to have a stable trait with unstable
             // items.
-            hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref t), self_ty, items, .. }) => {
+            hir::ItemKind::Impl(hir::Impl {
+                of_trait: Some(ref t),
+                self_ty,
+                items,
+                constness,
+                ..
+            }) => {
                 let features = self.tcx.features();
                 if features.staged_api() {
                     let attrs = self.tcx.hir().attrs(item.hir_id());
@@ -814,6 +822,16 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
                     }
                 }
 
+                match constness {
+                    rustc_hir::Constness::Const => {
+                        if let Some(def_id) = t.trait_def_id() {
+                            // FIXME(const_trait_impl): Improve the span here.
+                            self.tcx.check_const_stability(def_id, t.path.span, t.path.span);
+                        }
+                    }
+                    rustc_hir::Constness::NotConst => {}
+                }
+
                 for impl_item_ref in *items {
                     let impl_item = self.tcx.associated_item(impl_item_ref.id.owner_id);
 
@@ -829,6 +847,18 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
         intravisit::walk_item(self, item);
     }
 
+    fn visit_poly_trait_ref(&mut self, t: &'tcx hir::PolyTraitRef<'tcx>) {
+        match t.modifiers.constness {
+            hir::BoundConstness::Always(span) | hir::BoundConstness::Maybe(span) => {
+                if let Some(def_id) = t.trait_ref.trait_def_id() {
+                    self.tcx.check_const_stability(def_id, t.trait_ref.path.span, span);
+                }
+            }
+            hir::BoundConstness::Never => {}
+        }
+        intravisit::walk_poly_trait_ref(self, t);
+    }
+
     fn visit_path(&mut self, path: &hir::Path<'tcx>, id: hir::HirId) {
         if let Some(def_id) = path.res.opt_def_id() {
             let method_span = path.segments.last().map(|s| s.ident.span);
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index b24e343c58d..25e35fead7e 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -1031,6 +1031,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                         is_soft,
                         span,
                         soft_handler,
+                        stability::UnstableKind::Regular,
                     );
                 }
             }
diff --git a/library/core/src/intrinsics/fallback.rs b/library/core/src/intrinsics/fallback.rs
index 1779126b180..70484e4d0f2 100644
--- a/library/core/src/intrinsics/fallback.rs
+++ b/library/core/src/intrinsics/fallback.rs
@@ -8,6 +8,7 @@
 #![allow(missing_docs)]
 
 #[const_trait]
+#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
 pub trait CarryingMulAdd: Copy + 'static {
     type Unsigned: Copy + 'static;
     fn carrying_mul_add(
diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index 4af9b666e54..01af964a83e 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -952,6 +952,7 @@ marker_impls! {
 /// This should be used for `~const` bounds,
 /// as non-const bounds will always hold for every type.
 #[unstable(feature = "const_destruct", issue = "133214")]
+#[rustc_const_unstable(feature = "const_destruct", issue = "133214")]
 #[lang = "destruct"]
 #[rustc_on_unimplemented(message = "can't drop `{Self}`", append_const_msg)]
 #[rustc_deny_explicit_impl]
diff --git a/library/core/src/ops/arith.rs b/library/core/src/ops/arith.rs
index 810b906b871..fe7ff2d9ede 100644
--- a/library/core/src/ops/arith.rs
+++ b/library/core/src/ops/arith.rs
@@ -65,6 +65,7 @@
 /// ```
 #[lang = "add"]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[rustc_const_unstable(feature = "const_ops", issue = "90080")]
 #[rustc_on_unimplemented(
     on(all(_Self = "{integer}", Rhs = "{float}"), message = "cannot add a float to an integer",),
     on(all(_Self = "{float}", Rhs = "{integer}"), message = "cannot add an integer to a float",),
diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs
index ed0d30a0f50..11490ea2bfc 100644
--- a/library/core/src/ops/deref.rs
+++ b/library/core/src/ops/deref.rs
@@ -134,6 +134,7 @@
 #[stable(feature = "rust1", since = "1.0.0")]
 #[rustc_diagnostic_item = "Deref"]
 #[const_trait]
+#[rustc_const_unstable(feature = "const_deref", issue = "88955")]
 pub trait Deref {
     /// The resulting type after dereferencing.
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -263,6 +264,7 @@ impl<T: ?Sized> const Deref for &mut T {
 #[doc(alias = "*")]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[const_trait]
+#[rustc_const_unstable(feature = "const_deref", issue = "88955")]
 pub trait DerefMut: ~const Deref {
     /// Mutably dereferences the value.
     #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/core/src/ops/drop.rs b/library/core/src/ops/drop.rs
index 78b5252195f..e024b7fb4d3 100644
--- a/library/core/src/ops/drop.rs
+++ b/library/core/src/ops/drop.rs
@@ -204,6 +204,7 @@
 #[lang = "drop"]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[const_trait]
+#[rustc_const_unstable(feature = "const_destruct", issue = "133214")]
 pub trait Drop {
     /// Executes the destructor for this type.
     ///
diff --git a/tests/ui/consts/promoted-const-drop.rs b/tests/ui/consts/promoted-const-drop.rs
index e09c30ea785..1d1897e15d4 100644
--- a/tests/ui/consts/promoted-const-drop.rs
+++ b/tests/ui/consts/promoted-const-drop.rs
@@ -1,4 +1,4 @@
-#![feature(const_trait_impl)]
+#![feature(const_trait_impl, const_destruct)]
 
 struct A();
 
diff --git a/tests/ui/consts/promoted_const_call.rs b/tests/ui/consts/promoted_const_call.rs
index c3920ff7241..79cb2ea2a02 100644
--- a/tests/ui/consts/promoted_const_call.rs
+++ b/tests/ui/consts/promoted_const_call.rs
@@ -1,6 +1,4 @@
-//@ known-bug: #103507
-
-#![feature(const_trait_impl)]
+#![feature(const_trait_impl, const_destruct)]
 
 struct Panic;
 impl const Drop for Panic { fn drop(&mut self) { panic!(); } }
@@ -8,15 +6,15 @@ impl const Drop for Panic { fn drop(&mut self) { panic!(); } }
 pub const fn id<T>(x: T) -> T { x }
 pub const C: () = {
     let _: &'static _ = &id(&Panic);
-    //FIXME ~^ ERROR: temporary value dropped while borrowed
-    //FIXME ~| ERROR: temporary value dropped while borrowed
+    //~^ ERROR: temporary value dropped while borrowed
+    //~| ERROR: temporary value dropped while borrowed
 };
 
 fn main() {
     let _: &'static _ = &id(&Panic);
-    //FIXME ~^ ERROR: temporary value dropped while borrowed
-    //FIXME ~| ERROR: temporary value dropped while borrowed
+    //~^ ERROR: temporary value dropped while borrowed
+    //~| ERROR: temporary value dropped while borrowed
     let _: &'static _ = &&(Panic, 0).1;
-    //FIXME~^ ERROR: temporary value dropped while borrowed
-    //FIXME~| ERROR: temporary value dropped while borrowed
+    //~^ ERROR: temporary value dropped while borrowed
+    //~| ERROR: temporary value dropped while borrowed
 }
diff --git a/tests/ui/consts/promoted_const_call.stderr b/tests/ui/consts/promoted_const_call.stderr
index 40c6d083b06..7a9cdd68704 100644
--- a/tests/ui/consts/promoted_const_call.stderr
+++ b/tests/ui/consts/promoted_const_call.stderr
@@ -1,17 +1,25 @@
-error[E0493]: destructor of `Panic` cannot be evaluated at compile-time
-  --> $DIR/promoted_const_call.rs:10:30
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/promoted_const_call.rs:8:26
    |
 LL |     let _: &'static _ = &id(&Panic);
-   |                              ^^^^^ - value is dropped here
-   |                              |
-   |                              the destructor for this type cannot be evaluated in constants
-   |
-   = note: see issue #133214 <https://github.com/rust-lang/rust/issues/133214> for more information
-   = help: add `#![feature(const_destruct)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   |            ----------    ^^^^^^^^^^ creates a temporary value which is freed while still in use
+   |            |
+   |            type annotation requires that borrow lasts for `'static`
+...
+LL | };
+   | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promoted_const_call.rs:16:26
+  --> $DIR/promoted_const_call.rs:8:30
+   |
+LL |     let _: &'static _ = &id(&Panic);
+   |            ----------        ^^^^^ - temporary value is freed at the end of this statement
+   |            |                 |
+   |            |                 creates a temporary value which is freed while still in use
+   |            type annotation requires that borrow lasts for `'static`
+
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/promoted_const_call.rs:14:26
    |
 LL |     let _: &'static _ = &id(&Panic);
    |            ----------    ^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -22,7 +30,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promoted_const_call.rs:16:30
+  --> $DIR/promoted_const_call.rs:14:30
    |
 LL |     let _: &'static _ = &id(&Panic);
    |            ----------        ^^^^^ - temporary value is freed at the end of this statement
@@ -31,7 +39,7 @@ LL |     let _: &'static _ = &id(&Panic);
    |            type annotation requires that borrow lasts for `'static`
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promoted_const_call.rs:19:26
+  --> $DIR/promoted_const_call.rs:17:26
    |
 LL |     let _: &'static _ = &&(Panic, 0).1;
    |            ----------    ^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -42,7 +50,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/promoted_const_call.rs:19:27
+  --> $DIR/promoted_const_call.rs:17:27
    |
 LL |     let _: &'static _ = &&(Panic, 0).1;
    |            ----------     ^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -52,7 +60,6 @@ LL |     let _: &'static _ = &&(Panic, 0).1;
 LL | }
    | - temporary value is freed at the end of this statement
 
-error: aborting due to 5 previous errors
+error: aborting due to 6 previous errors
 
-Some errors have detailed explanations: E0493, E0716.
-For more information about an error, try `rustc --explain E0493`.
+For more information about this error, try `rustc --explain E0716`.
diff --git a/tests/ui/stability-attribute/missing-const-stability.rs b/tests/ui/stability-attribute/missing-const-stability.rs
index 19820730736..c3e72e83948 100644
--- a/tests/ui/stability-attribute/missing-const-stability.rs
+++ b/tests/ui/stability-attribute/missing-const-stability.rs
@@ -22,6 +22,7 @@ impl Foo {
 #[stable(feature = "stable", since = "1.0.0")]
 #[const_trait]
 pub trait Bar {
+//~^ ERROR trait has missing const stability attribute
     #[stable(feature = "stable", since = "1.0.0")]
     fn fun();
 }
diff --git a/tests/ui/stability-attribute/missing-const-stability.stderr b/tests/ui/stability-attribute/missing-const-stability.stderr
index baa4c34af06..09461e6fb54 100644
--- a/tests/ui/stability-attribute/missing-const-stability.stderr
+++ b/tests/ui/stability-attribute/missing-const-stability.stderr
@@ -4,8 +4,18 @@ error: function has missing const stability attribute
 LL | pub const fn foo() {}
    | ^^^^^^^^^^^^^^^^^^^^^
 
+error: trait has missing const stability attribute
+  --> $DIR/missing-const-stability.rs:24:1
+   |
+LL | / pub trait Bar {
+LL | |
+LL | |     #[stable(feature = "stable", since = "1.0.0")]
+LL | |     fn fun();
+LL | | }
+   | |_^
+
 error: function has missing const stability attribute
-  --> $DIR/missing-const-stability.rs:36:1
+  --> $DIR/missing-const-stability.rs:37:1
    |
 LL | pub const unsafe fn size_of_val<T>(x: *const T) -> usize { 42 }
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -16,5 +26,5 @@ error: associated function has missing const stability attribute
 LL |     pub const fn foo() {}
    |     ^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
 
diff --git a/tests/ui/traits/const-traits/auxiliary/staged-api.rs b/tests/ui/traits/const-traits/auxiliary/staged-api.rs
index abe22db702c..933a25769dc 100644
--- a/tests/ui/traits/const-traits/auxiliary/staged-api.rs
+++ b/tests/ui/traits/const-traits/auxiliary/staged-api.rs
@@ -4,6 +4,7 @@
 #![stable(feature = "rust1", since = "1.0.0")]
 
 #[stable(feature = "rust1", since = "1.0.0")]
+#[rustc_const_unstable(feature = "unstable", issue = "none")]
 #[const_trait]
 pub trait MyTrait {
     #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/tests/ui/traits/const-traits/call-const-trait-method-pass.rs b/tests/ui/traits/const-traits/call-const-trait-method-pass.rs
index b854b422b3a..3004647ede0 100644
--- a/tests/ui/traits/const-traits/call-const-trait-method-pass.rs
+++ b/tests/ui/traits/const-traits/call-const-trait-method-pass.rs
@@ -1,6 +1,6 @@
 //@ known-bug: #110395
 
-#![feature(const_trait_impl)]
+#![feature(const_trait_impl, const_ops)]
 
 struct Int(i32);
 
diff --git a/tests/ui/traits/const-traits/const-and-non-const-impl.rs b/tests/ui/traits/const-traits/const-and-non-const-impl.rs
index 6b96fcf0ae3..85e2c5d3df6 100644
--- a/tests/ui/traits/const-traits/const-and-non-const-impl.rs
+++ b/tests/ui/traits/const-traits/const-and-non-const-impl.rs
@@ -1,6 +1,6 @@
 //@ known-bug: #110395
 
-#![feature(const_trait_impl)]
+#![feature(const_trait_impl, const_ops)]
 
 pub struct Int(i32);
 
diff --git a/tests/ui/traits/const-traits/generic-bound.rs b/tests/ui/traits/const-traits/generic-bound.rs
index 5eb236acde2..99de21471b2 100644
--- a/tests/ui/traits/const-traits/generic-bound.rs
+++ b/tests/ui/traits/const-traits/generic-bound.rs
@@ -1,6 +1,6 @@
 //@ check-pass
 
-#![feature(const_trait_impl)]
+#![feature(const_trait_impl, const_ops)]
 
 use std::marker::PhantomData;
 
diff --git a/tests/ui/traits/const-traits/syntactical-unstable.rs b/tests/ui/traits/const-traits/syntactical-unstable.rs
new file mode 100644
index 00000000000..e192e80fabd
--- /dev/null
+++ b/tests/ui/traits/const-traits/syntactical-unstable.rs
@@ -0,0 +1,34 @@
+//@ aux-build:staged-api.rs
+
+// Ensure that we enforce const stability of traits in `~const`/`const` bounds.
+
+#![feature(const_trait_impl)]
+
+use std::ops::Deref;
+
+extern crate staged_api;
+use staged_api::MyTrait;
+
+#[const_trait]
+trait Foo: ~const MyTrait {
+    //~^ ERROR use of unstable const library feature `unstable`
+    type Item: ~const MyTrait;
+    //~^ ERROR use of unstable const library feature `unstable`
+}
+
+const fn where_clause<T>() where T: ~const MyTrait {}
+//~^ ERROR use of unstable const library feature `unstable`
+
+const fn nested<T>() where T: Deref<Target: ~const MyTrait> {}
+//~^ ERROR use of unstable const library feature `unstable`
+
+const fn rpit() -> impl ~const MyTrait { Local }
+//~^ ERROR use of unstable const library feature `unstable`
+
+struct Local;
+impl const MyTrait for Local {
+//~^ ERROR use of unstable const library feature `unstable`
+    fn func() {}
+}
+
+fn main() {}
diff --git a/tests/ui/traits/const-traits/syntactical-unstable.stderr b/tests/ui/traits/const-traits/syntactical-unstable.stderr
new file mode 100644
index 00000000000..a2ce2f2b6e9
--- /dev/null
+++ b/tests/ui/traits/const-traits/syntactical-unstable.stderr
@@ -0,0 +1,67 @@
+error[E0658]: use of unstable const library feature `unstable`
+  --> $DIR/syntactical-unstable.rs:13:19
+   |
+LL | trait Foo: ~const MyTrait {
+   |            ------ ^^^^^^^
+   |            |
+   |            trait is not stable as const yet
+   |
+   = help: add `#![feature(unstable)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: use of unstable const library feature `unstable`
+  --> $DIR/syntactical-unstable.rs:19:44
+   |
+LL | const fn where_clause<T>() where T: ~const MyTrait {}
+   |                                     ------ ^^^^^^^
+   |                                     |
+   |                                     trait is not stable as const yet
+   |
+   = help: add `#![feature(unstable)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: use of unstable const library feature `unstable`
+  --> $DIR/syntactical-unstable.rs:22:52
+   |
+LL | const fn nested<T>() where T: Deref<Target: ~const MyTrait> {}
+   |                                             ------ ^^^^^^^
+   |                                             |
+   |                                             trait is not stable as const yet
+   |
+   = help: add `#![feature(unstable)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: use of unstable const library feature `unstable`
+  --> $DIR/syntactical-unstable.rs:25:32
+   |
+LL | const fn rpit() -> impl ~const MyTrait { Local }
+   |                         ------ ^^^^^^^
+   |                         |
+   |                         trait is not stable as const yet
+   |
+   = help: add `#![feature(unstable)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: use of unstable const library feature `unstable`
+  --> $DIR/syntactical-unstable.rs:29:12
+   |
+LL | impl const MyTrait for Local {
+   |            ^^^^^^^ trait is not stable as const yet
+   |
+   = help: add `#![feature(unstable)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: use of unstable const library feature `unstable`
+  --> $DIR/syntactical-unstable.rs:15:23
+   |
+LL |     type Item: ~const MyTrait;
+   |                ------ ^^^^^^^
+   |                |
+   |                trait is not stable as const yet
+   |
+   = help: add `#![feature(unstable)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/traits/const-traits/trait-default-body-stability.rs b/tests/ui/traits/const-traits/trait-default-body-stability.rs
index 5f7486eb176..567f1b3c284 100644
--- a/tests/ui/traits/const-traits/trait-default-body-stability.rs
+++ b/tests/ui/traits/const-traits/trait-default-body-stability.rs
@@ -38,6 +38,7 @@ impl const FromResidual for T {
 }
 
 #[stable(feature = "foo", since = "1.0")]
+#[rustc_const_unstable(feature = "const_tr", issue = "none")]
 #[const_trait]
 pub trait Tr {
     #[stable(feature = "foo", since = "1.0")]
diff --git a/tests/ui/traits/const-traits/trait-default-body-stability.stderr b/tests/ui/traits/const-traits/trait-default-body-stability.stderr
index 77b81211e81..a13d9a1e075 100644
--- a/tests/ui/traits/const-traits/trait-default-body-stability.stderr
+++ b/tests/ui/traits/const-traits/trait-default-body-stability.stderr
@@ -17,7 +17,7 @@ LL | impl const FromResidual for T {
    = note: adding a non-const method body in the future would be a breaking change
 
 error[E0015]: `?` is not allowed on `T` in constant functions
-  --> $DIR/trait-default-body-stability.rs:45:9
+  --> $DIR/trait-default-body-stability.rs:46:9
    |
 LL |         T?
    |         ^^
@@ -25,7 +25,7 @@ LL |         T?
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
 
 error[E0015]: `?` is not allowed on `T` in constant functions
-  --> $DIR/trait-default-body-stability.rs:45:9
+  --> $DIR/trait-default-body-stability.rs:46:9
    |
 LL |         T?
    |         ^^