diff --git a/compiler/rustc_mir/src/const_eval/fn_queries.rs b/compiler/rustc_mir/src/const_eval/fn_queries.rs
index 9ef63b3322dd5..1db1f6ceedac5 100644
--- a/compiler/rustc_mir/src/const_eval/fn_queries.rs
+++ b/compiler/rustc_mir/src/const_eval/fn_queries.rs
@@ -50,7 +50,7 @@ pub fn is_min_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
             None => {
                 if let Some(stab) = tcx.lookup_stability(def_id) {
                     if stab.level.is_stable() {
-                        tcx.sess.span_err(
+                        tcx.sess.delay_span_bug(
                             tcx.def_span(def_id),
                             "stable const functions must have either `rustc_const_stable` or \
                              `rustc_const_unstable` attribute",
diff --git a/compiler/rustc_mir/src/transform/check_consts/validation.rs b/compiler/rustc_mir/src/transform/check_consts/validation.rs
index dc28ba46d7cbb..3a5ae4c8348f5 100644
--- a/compiler/rustc_mir/src/transform/check_consts/validation.rs
+++ b/compiler/rustc_mir/src/transform/check_consts/validation.rs
@@ -204,9 +204,6 @@ impl Validator<'mir, 'tcx> {
     pub fn check_body(&mut self) {
         let ConstCx { tcx, body, def_id, .. } = *self.ccx;
 
-        // HACK: This function has side-effects???? Make sure we call it.
-        let _ = crate::const_eval::is_min_const_fn(tcx, def_id.to_def_id());
-
         // The local type and predicate checks are not free and only relevant for `const fn`s.
         if self.const_kind() == hir::ConstContext::ConstFn {
             // Prevent const trait methods from being annotated as `stable`.
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 4ca52f405fb94..24972b5cc6a6f 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -368,6 +368,21 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> {
             self.tcx.sess.span_err(span, &format!("{} has missing stability attribute", descr));
         }
     }
+
+    fn check_missing_const_stability(&self, hir_id: HirId, span: Span) {
+        let stab_map = self.tcx.stability();
+        let stab = stab_map.local_stability(hir_id);
+        if stab.map_or(false, |stab| stab.level.is_stable()) {
+            let const_stab = stab_map.local_const_stability(hir_id);
+            if const_stab.is_none() {
+                self.tcx.sess.span_err(
+                    span,
+                    "`#[stable]` const functions must also be either \
+                    `#[rustc_const_stable]` or `#[rustc_const_unstable]`",
+                );
+            }
+        }
+    }
 }
 
 impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> {
@@ -378,14 +393,23 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> {
     }
 
     fn visit_item(&mut self, i: &'tcx Item<'tcx>) {
-        match i.kind {
-            // Inherent impls and foreign modules serve only as containers for other items,
-            // they don't have their own stability. They still can be annotated as unstable
-            // and propagate this unstability to children, but this annotation is completely
-            // optional. They inherit stability from their parents when unannotated.
-            hir::ItemKind::Impl { of_trait: None, .. } | hir::ItemKind::ForeignMod(..) => {}
+        // Inherent impls and foreign modules serve only as containers for other items,
+        // they don't have their own stability. They still can be annotated as unstable
+        // and propagate this unstability to children, but this annotation is completely
+        // optional. They inherit stability from their parents when unannotated.
+        if !matches!(
+            i.kind,
+            hir::ItemKind::Impl { of_trait: None, .. } | hir::ItemKind::ForeignMod(..)
+        ) {
+            self.check_missing_stability(i.hir_id, i.span);
+        }
 
-            _ => self.check_missing_stability(i.hir_id, i.span),
+        // Ensure `const fn` that are `stable` have one of `rustc_const_unstable` or
+        // `rustc_const_stable`.
+        if self.tcx.features().staged_api
+            && matches!(&i.kind, hir::ItemKind::Fn(sig, ..) if sig.header.is_const())
+        {
+            self.check_missing_const_stability(i.hir_id, i.span);
         }
 
         intravisit::walk_item(self, i)
diff --git a/src/test/ui/stability-attribute/missing-const-stability.rs b/src/test/ui/stability-attribute/missing-const-stability.rs
new file mode 100644
index 0000000000000..7d499c611a43c
--- /dev/null
+++ b/src/test/ui/stability-attribute/missing-const-stability.rs
@@ -0,0 +1,12 @@
+#![feature(staged_api)]
+
+#![stable(feature = "rust1", since = "1.0.0")]
+
+#[stable(feature = "foo", since = "1.0.0")]
+pub const fn foo() {}
+//~^ ERROR rustc_const_stable
+
+#[unstable(feature = "bar", issue = "none")]
+pub const fn bar() {} // ok
+
+fn main() {}
diff --git a/src/test/ui/stability-attribute/missing-const-stability.stderr b/src/test/ui/stability-attribute/missing-const-stability.stderr
new file mode 100644
index 0000000000000..450a5303fd86f
--- /dev/null
+++ b/src/test/ui/stability-attribute/missing-const-stability.stderr
@@ -0,0 +1,8 @@
+error: `#[stable]` const functions must also be either `#[rustc_const_stable]` or `#[rustc_const_unstable]`
+  --> $DIR/missing-const-stability.rs:6:1
+   |
+LL | pub const fn foo() {}
+   | ^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+