about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-10-04 06:48:13 +0000
committerbors <bors@rust-lang.org>2018-10-04 06:48:13 +0000
commit088fc7384c1ac3f7670d66880c7dcc0ec9160c79 (patch)
treea904b7f1880e2ec018f5dff0ddbde98f9e7a1de4 /src
parentc67ea54d4466f1c082f72b194c100bb4954449e6 (diff)
parentc793391e6d04106df57a4f431c7deeb4258a0592 (diff)
downloadrust-088fc7384c1ac3f7670d66880c7dcc0ec9160c79.tar.gz
rust-088fc7384c1ac3f7670d66880c7dcc0ec9160c79.zip
Auto merge of #53851 - oli-obk:local_promotion, r=eddyb
Limit the promotion of const fns to the libstd and the `rustc_promotable` attribute

There are so many questions around promoting const fn calls... it seems saner to try to limit automatic promotion to const fns which were explicitly opted in for promotion.

I added the attribute to all public stable const fns that were already promotable (e.g. not Cell::new) in order to not cause any breakage

r? @eddyb

cc @nikomatsakis
Diffstat (limited to 'src')
-rw-r--r--src/libcore/mem.rs2
-rw-r--r--src/libcore/num/mod.rs2
-rw-r--r--src/libcore/ops/range.rs1
-rw-r--r--src/libcore/ptr.rs2
-rw-r--r--src/libcore/time.rs4
-rw-r--r--src/librustc/dep_graph/dep_node.rs1
-rw-r--r--src/librustc/ich/impls_syntax.rs1
-rw-r--r--src/librustc/middle/stability.rs1
-rw-r--r--src/librustc/ty/constness.rs111
-rw-r--r--src/librustc/ty/context.rs32
-rw-r--r--src/librustc/ty/instance.rs4
-rw-r--r--src/librustc/ty/mod.rs2
-rw-r--r--src/librustc/ty/query/config.rs2
-rw-r--r--src/librustc/ty/query/mod.rs19
-rw-r--r--src/librustc/ty/query/plumbing.rs3
-rw-r--r--src/librustc_metadata/cstore_impl.rs16
-rw-r--r--src/librustc_metadata/decoder.rs2
-rw-r--r--src/librustc_mir/const_eval.rs6
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs167
-rw-r--r--src/librustc_passes/rvalue_promotion.rs61
-rw-r--r--src/libsyntax/attr/builtin.rs23
-rw-r--r--src/libsyntax/diagnostic_list.rs1
-rw-r--r--src/test/compile-fail/const-fn-error.rs (renamed from src/test/ui/consts/const-fn-error.rs)0
-rw-r--r--src/test/compile-fail/issue-52443.rs (renamed from src/test/ui/consts/const-eval/issue-52443.rs)0
-rw-r--r--src/test/run-pass/invalid_const_promotion.rs5
-rw-r--r--src/test/run-pass/issues/issue-49955-2.rs4
-rw-r--r--src/test/ui/consts/const-call.rs1
-rw-r--r--src/test/ui/consts/const-call.stderr11
-rw-r--r--src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.nll.stderr2
-rw-r--r--src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.rs2
-rw-r--r--src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr2
-rw-r--r--src/test/ui/consts/const-eval/issue-52443.stderr51
-rw-r--r--src/test/ui/consts/const-eval/promoted_const_fn_fail.nll.stderr14
-rw-r--r--src/test/ui/consts/const-eval/promoted_const_fn_fail.rs7
-rw-r--r--src/test/ui/consts/const-eval/promoted_const_fn_fail.stderr14
-rw-r--r--src/test/ui/consts/const-fn-error.stderr43
-rw-r--r--src/test/ui/consts/min_const_fn/promotion.nll.stderr68
-rw-r--r--src/test/ui/consts/min_const_fn/promotion.rs19
-rw-r--r--src/test/ui/consts/min_const_fn/promotion.stderr68
-rw-r--r--src/test/ui/issues/issue-39559-2.rs2
-rw-r--r--src/test/ui/issues/issue-39559-2.stderr21
-rw-r--r--src/test/ui/issues/issue-43105.rs1
-rw-r--r--src/test/ui/issues/issue-43105.stderr14
-rw-r--r--src/test/ui/rustc-args-required-const.rs3
-rw-r--r--src/test/ui/rustc-args-required-const.stderr4
45 files changed, 484 insertions, 335 deletions
diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs
index f537add999a..27ee9556bd0 100644
--- a/src/libcore/mem.rs
+++ b/src/libcore/mem.rs
@@ -285,6 +285,7 @@ pub fn forget<T>(t: T) {
 /// [alignment]: ./fn.align_of.html
 #[inline]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(stage0), rustc_promotable)]
 pub const fn size_of<T>() -> usize {
     intrinsics::size_of::<T>()
 }
@@ -376,6 +377,7 @@ pub fn min_align_of_val<T: ?Sized>(val: &T) -> usize {
 /// ```
 #[inline]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(stage0), rustc_promotable)]
 pub const fn align_of<T>() -> usize {
     intrinsics::min_align_of::<T>()
 }
diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs
index f054580cba7..772502cc800 100644
--- a/src/libcore/num/mod.rs
+++ b/src/libcore/num/mod.rs
@@ -216,6 +216,7 @@ $EndFeature, "
 ```"),
             #[stable(feature = "rust1", since = "1.0.0")]
             #[inline]
+            #[cfg_attr(not(stage0), rustc_promotable)]
             pub const fn min_value() -> Self {
                 !0 ^ ((!0 as $UnsignedT) >> 1) as Self
             }
@@ -234,6 +235,7 @@ $EndFeature, "
 ```"),
             #[stable(feature = "rust1", since = "1.0.0")]
             #[inline]
+            #[cfg_attr(not(stage0), rustc_promotable)]
             pub const fn max_value() -> Self {
                 !Self::min_value()
             }
diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs
index f7e5a89a7aa..07ba285ea5c 100644
--- a/src/libcore/ops/range.rs
+++ b/src/libcore/ops/range.rs
@@ -391,6 +391,7 @@ impl<Idx> RangeInclusive<Idx> {
     /// ```
     #[stable(feature = "inclusive_range_methods", since = "1.27.0")]
     #[inline]
+    #[cfg_attr(not(stage0), rustc_promotable)]
     pub const fn new(start: Idx, end: Idx) -> Self {
         Self { start, end, is_empty: None }
     }
diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs
index 14f2148a64e..bd58dcf4ae0 100644
--- a/src/libcore/ptr.rs
+++ b/src/libcore/ptr.rs
@@ -209,6 +209,7 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
 /// ```
 #[inline]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(stage0), rustc_promotable)]
 pub const fn null<T>() -> *const T { 0 as *const T }
 
 /// Creates a null mutable raw pointer.
@@ -223,6 +224,7 @@ pub const fn null<T>() -> *const T { 0 as *const T }
 /// ```
 #[inline]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(stage0), rustc_promotable)]
 pub const fn null_mut<T>() -> *mut T { 0 as *mut T }
 
 /// Swaps the values at two mutable locations of the same type, without
diff --git a/src/libcore/time.rs b/src/libcore/time.rs
index 1aed5a7b426..81ae8ade12d 100644
--- a/src/libcore/time.rs
+++ b/src/libcore/time.rs
@@ -109,6 +109,7 @@ impl Duration {
     /// ```
     #[stable(feature = "duration", since = "1.3.0")]
     #[inline]
+    #[cfg_attr(not(stage0), rustc_promotable)]
     pub const fn from_secs(secs: u64) -> Duration {
         Duration { secs, nanos: 0 }
     }
@@ -127,6 +128,7 @@ impl Duration {
     /// ```
     #[stable(feature = "duration", since = "1.3.0")]
     #[inline]
+    #[cfg_attr(not(stage0), rustc_promotable)]
     pub const fn from_millis(millis: u64) -> Duration {
         Duration {
             secs: millis / MILLIS_PER_SEC,
@@ -148,6 +150,7 @@ impl Duration {
     /// ```
     #[stable(feature = "duration_from_micros", since = "1.27.0")]
     #[inline]
+    #[cfg_attr(not(stage0), rustc_promotable)]
     pub const fn from_micros(micros: u64) -> Duration {
         Duration {
             secs: micros / MICROS_PER_SEC,
@@ -169,6 +172,7 @@ impl Duration {
     /// ```
     #[stable(feature = "duration_extras", since = "1.27.0")]
     #[inline]
+    #[cfg_attr(not(stage0), rustc_promotable)]
     pub const fn from_nanos(nanos: u64) -> Duration {
         Duration {
             secs: nanos / (NANOS_PER_SEC as u64),
diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs
index 0d0dc0832a4..994bbe727fc 100644
--- a/src/librustc/dep_graph/dep_node.rs
+++ b/src/librustc/dep_graph/dep_node.rs
@@ -515,6 +515,7 @@ define_dep_nodes!( <'tcx>
     [] ItemVarianceConstraints(DefId),
     [] ItemVariances(DefId),
     [] IsConstFn(DefId),
+    [] IsPromotableConstFn(DefId),
     [] IsForeignItem(DefId),
     [] TypeParamPredicates { item_id: DefId, param_id: DefId },
     [] SizedConstraint(DefId),
diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs
index a15411c7d83..4be467c01aa 100644
--- a/src/librustc/ich/impls_syntax.rs
+++ b/src/librustc/ich/impls_syntax.rs
@@ -130,6 +130,7 @@ impl_stable_hash_for!(struct ::syntax::attr::Stability {
     level,
     feature,
     rustc_depr,
+    promotable,
     const_stability
 });
 
diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs
index 73a55265f00..b7b149ea029 100644
--- a/src/librustc/middle/stability.rs
+++ b/src/librustc/middle/stability.rs
@@ -441,6 +441,7 @@ impl<'a, 'tcx> Index<'tcx> {
                     feature: Symbol::intern("rustc_private"),
                     rustc_depr: None,
                     const_stability: None,
+                    promotable: false,
                 });
                 annotator.parent_stab = Some(stability);
             }
diff --git a/src/librustc/ty/constness.rs b/src/librustc/ty/constness.rs
new file mode 100644
index 00000000000..64e677fbe12
--- /dev/null
+++ b/src/librustc/ty/constness.rs
@@ -0,0 +1,111 @@
+use ty::query::Providers;
+use hir::def_id::DefId;
+use hir;
+use ty::TyCtxt;
+use syntax_pos::symbol::Symbol;
+use hir::map::blocks::FnLikeNode;
+use syntax::attr;
+use rustc_target::spec::abi;
+
+impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
+    /// Whether the `def_id` counts as const fn in your current crate, considering all active
+    /// feature gates
+    pub fn is_const_fn(self, def_id: DefId) -> bool {
+        self.is_const_fn_raw(def_id) && match self.lookup_stability(def_id) {
+            Some(stab) => match stab.const_stability {
+                // has a `rustc_const_unstable` attribute, check whether the user enabled the
+                // corresponding feature gate
+                Some(feature_name) => self.features()
+                    .declared_lib_features
+                    .iter()
+                    .any(|&(sym, _)| sym == feature_name),
+                // the function has no stability attribute, it is stable as const fn or the user
+                // needs to use feature gates to use the function at all
+                None => true,
+            },
+            // functions without stability are either stable user written const fn or the user is
+            // using feature gates and we thus don't care what they do
+            None => true,
+        }
+    }
+
+    /// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it
+    pub fn is_unstable_const_fn(self, def_id: DefId) -> Option<Symbol> {
+        if self.is_const_fn_raw(def_id) {
+            self.lookup_stability(def_id)?.const_stability
+        } else {
+            None
+        }
+    }
+
+    /// Returns true if this function must conform to `min_const_fn`
+    pub fn is_min_const_fn(self, def_id: DefId) -> bool {
+        if self.features().staged_api {
+            // some intrinsics are waved through if called inside the
+            // standard library. Users never need to call them directly
+            if let abi::Abi::RustIntrinsic = self.fn_sig(def_id).abi() {
+                assert!(!self.is_const_fn(def_id));
+                match &self.item_name(def_id).as_str()[..] {
+                    | "size_of"
+                    | "min_align_of"
+                    | "needs_drop"
+                    => return true,
+                    _ => {},
+                }
+            }
+            // in order for a libstd function to be considered min_const_fn
+            // it needs to be stable and have no `rustc_const_unstable` attribute
+            match self.lookup_stability(def_id) {
+                // stable functions with unstable const fn aren't `min_const_fn`
+                Some(&attr::Stability { const_stability: Some(_), .. }) => false,
+                // unstable functions don't need to conform
+                Some(&attr::Stability { ref level, .. }) if level.is_unstable() => false,
+                // everything else needs to conform, because it would be callable from
+                // other `min_const_fn` functions
+                _ => true,
+            }
+        } else {
+            // users enabling the `const_fn` can do what they want
+            !self.sess.features_untracked().const_fn
+        }
+    }
+}
+
+
+pub fn provide<'tcx>(providers: &mut Providers<'tcx>) {
+    /// only checks whether the function has a `const` modifier
+    fn is_const_fn_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
+        let node_id = tcx.hir.as_local_node_id(def_id)
+                             .expect("Non-local call to local provider is_const_fn");
+
+        if let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(node_id)) {
+            fn_like.constness() == hir::Constness::Const
+        } else {
+            false
+        }
+    }
+
+    fn is_promotable_const_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
+        tcx.is_const_fn(def_id) && match tcx.lookup_stability(def_id) {
+            Some(stab) => {
+                if cfg!(debug_assertions) && stab.promotable {
+                    let sig = tcx.fn_sig(def_id);
+                    assert_eq!(
+                        sig.unsafety(),
+                        hir::Unsafety::Normal,
+                        "don't mark const unsafe fns as promotable",
+                        // https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
+                    );
+                }
+                stab.promotable
+            },
+            None => false,
+        }
+    }
+
+    *providers = Providers {
+        is_const_fn_raw,
+        is_promotable_const_fn,
+        ..*providers
+    };
+}
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 64e9d15092e..85559c892c4 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -1134,38 +1134,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         local as usize == global as usize
     }
 
-    /// Returns true if this function must conform to `min_const_fn`
-    pub fn is_min_const_fn(self, def_id: DefId) -> bool {
-        if self.features().staged_api {
-            // some intrinsics are waved through if called inside the
-            // standard library. Users never need to call them directly
-            if let abi::Abi::RustIntrinsic = self.fn_sig(def_id).abi() {
-                assert!(!self.is_const_fn(def_id));
-                match &self.item_name(def_id).as_str()[..] {
-                    | "size_of"
-                    | "min_align_of"
-                    | "needs_drop"
-                    => return true,
-                    _ => {},
-                }
-            }
-            // in order for a libstd function to be considered min_const_fn
-            // it needs to be stable and have no `rustc_const_unstable` attribute
-            match self.lookup_stability(def_id) {
-                // stable functions with unstable const fn aren't `min_const_fn`
-                Some(&attr::Stability { const_stability: Some(_), .. }) => false,
-                // unstable functions don't need to conform
-                Some(&attr::Stability { ref level, .. }) if level.is_unstable() => false,
-                // everything else needs to conform, because it would be callable from
-                // other `min_const_fn` functions
-                _ => true,
-            }
-        } else {
-            // users enabling the `const_fn` can do what they want
-            !self.sess.features_untracked().const_fn
-        }
-    }
-
     /// Create a type context and call the closure with a `TyCtxt` reference
     /// to the context. The closure enforces that the type context and any interned
     /// value (types, substs, etc.) can only be used while `ty::tls` has a valid
diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs
index a6e181a26f4..3416d6594c7 100644
--- a/src/librustc/ty/instance.rs
+++ b/src/librustc/ty/instance.rs
@@ -109,7 +109,9 @@ impl<'tcx> InstanceDef<'tcx> {
             return true
         }
         let codegen_fn_attrs = tcx.codegen_fn_attrs(self.def_id());
-        codegen_fn_attrs.requests_inline() || tcx.is_const_fn(self.def_id())
+        // need to use `is_const_fn_raw` since we don't really care if the user can use it as a
+        // const fn, just whether the function should be inlined
+        codegen_fn_attrs.requests_inline() || tcx.is_const_fn_raw(self.def_id())
     }
 }
 
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index b2281691bd6..57afbafc2a5 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -94,6 +94,7 @@ pub mod binding;
 pub mod cast;
 #[macro_use]
 pub mod codec;
+mod constness;
 pub mod error;
 mod erase_regions;
 pub mod fast_reject;
@@ -3056,6 +3057,7 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) {
     erase_regions::provide(providers);
     layout::provide(providers);
     util::provide(providers);
+    constness::provide(providers);
     *providers = ty::query::Providers {
         associated_item,
         associated_item_def_ids,
diff --git a/src/librustc/ty/query/config.rs b/src/librustc/ty/query/config.rs
index 9ea655ba6fd..66d7541633c 100644
--- a/src/librustc/ty/query/config.rs
+++ b/src/librustc/ty/query/config.rs
@@ -437,7 +437,7 @@ impl<'tcx> QueryDescription<'tcx> for queries::is_object_safe<'tcx> {
     }
 }
 
-impl<'tcx> QueryDescription<'tcx> for queries::is_const_fn<'tcx> {
+impl<'tcx> QueryDescription<'tcx> for queries::is_const_fn_raw<'tcx> {
     fn describe(tcx: TyCtxt<'_, '_, '_>, def_id: DefId) -> String {
         format!("checking if item is const fn: `{}`", tcx.item_path_str(def_id))
     }
diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs
index e0a503a9ceb..44c9c55b8a4 100644
--- a/src/librustc/ty/query/mod.rs
+++ b/src/librustc/ty/query/mod.rs
@@ -160,8 +160,23 @@ define_queries! { <'tcx>
             DefId
         ) -> Result<DtorckConstraint<'tcx>, NoSolution>,
 
-        /// True if this is a const fn
-        [] fn is_const_fn: IsConstFn(DefId) -> bool,
+        /// True if this is a const fn, use the `is_const_fn` to know whether your crate actually
+        /// sees it as const fn (e.g. the const-fn-ness might be unstable and you might not have
+        /// the feature gate active)
+        ///
+        /// DO NOT CALL MANUALLY, it is only meant to cache the base data for the `is_const_fn`
+        /// function
+        [] fn is_const_fn_raw: IsConstFn(DefId) -> bool,
+
+
+        /// Returns true if calls to the function may be promoted
+        ///
+        /// This is either because the function is e.g. a tuple-struct or tuple-variant constructor,
+        /// or because it has the `#[rustc_promotable]` attribute. The attribute should be removed
+        /// in the future in favour of some form of check which figures out whether the function
+        /// does not inspect the bits of any of its arguments (so is essentially just a constructor
+        /// function)
+        [] fn is_promotable_const_fn: IsPromotableConstFn(DefId) -> bool,
 
         /// True if this is a foreign item (i.e., linked via `extern { ... }`).
         [] fn is_foreign_item: IsForeignItem(DefId) -> bool,
diff --git a/src/librustc/ty/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs
index 510ca08b621..fbd3a8f69bc 100644
--- a/src/librustc/ty/query/plumbing.rs
+++ b/src/librustc/ty/query/plumbing.rs
@@ -1136,7 +1136,8 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
         DepKind::FnSignature => { force!(fn_sig, def_id!()); }
         DepKind::CoerceUnsizedInfo => { force!(coerce_unsized_info, def_id!()); }
         DepKind::ItemVariances => { force!(variances_of, def_id!()); }
-        DepKind::IsConstFn => { force!(is_const_fn, def_id!()); }
+        DepKind::IsConstFn => { force!(is_const_fn_raw, def_id!()); }
+        DepKind::IsPromotableConstFn => { force!(is_promotable_const_fn, def_id!()); }
         DepKind::IsForeignItem => { force!(is_foreign_item, def_id!()); }
         DepKind::SizedConstraint => { force!(adt_sized_constraint, def_id!()); }
         DepKind::DtorckConstraint => { force!(adt_dtorck_constraint, def_id!()); }
diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs
index fb0d6837986..7988de28b5d 100644
--- a/src/librustc_metadata/cstore_impl.rs
+++ b/src/librustc_metadata/cstore_impl.rs
@@ -26,7 +26,6 @@ use rustc::ty::{self, TyCtxt};
 use rustc::ty::query::Providers;
 use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE, CRATE_DEF_INDEX};
 use rustc::hir::map::{DefKey, DefPath, DefPathHash};
-use rustc::hir::map::blocks::FnLikeNode;
 use rustc::hir::map::definitions::DefPathTable;
 use rustc::util::nodemap::DefIdMap;
 use rustc_data_structures::svh::Svh;
@@ -43,7 +42,6 @@ use syntax::parse::source_file_to_stream;
 use syntax::symbol::Symbol;
 use syntax_pos::{Span, NO_EXPANSION, FileName};
 use rustc_data_structures::bit_set::BitSet;
-use rustc::hir;
 
 macro_rules! provide {
     (<$lt:tt> $tcx:ident, $def_id:ident, $other:ident, $cdata:ident,
@@ -145,7 +143,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
     }
     fn_sig => { cdata.fn_sig(def_id.index, tcx) }
     inherent_impls => { Lrc::new(cdata.get_inherent_implementations_for_type(def_id.index)) }
-    is_const_fn => { cdata.is_const_fn(def_id.index) }
+    is_const_fn_raw => { cdata.is_const_fn_raw(def_id.index) }
     is_foreign_item => { cdata.is_foreign_item(def_id.index) }
     describe_def => { cdata.get_def(def_id.index) }
     def_span => { cdata.get_span(def_id.index, &tcx.sess) }
@@ -264,22 +262,10 @@ provide! { <'tcx> tcx, def_id, other, cdata,
 }
 
 pub fn provide<'tcx>(providers: &mut Providers<'tcx>) {
-    fn is_const_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
-        let node_id = tcx.hir.as_local_node_id(def_id)
-                             .expect("Non-local call to local provider is_const_fn");
-
-        if let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(node_id)) {
-            fn_like.constness() == hir::Constness::Const
-        } else {
-            false
-        }
-    }
-
     // FIXME(#44234) - almost all of these queries have no sub-queries and
     // therefore no actual inputs, they're just reading tables calculated in
     // resolve! Does this work? Unsure! That's what the issue is about
     *providers = Providers {
-        is_const_fn,
         is_dllimport_foreign_item: |tcx, id| {
             tcx.native_library_kind(id) == Some(NativeLibraryKind::NativeUnknown)
         },
diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs
index 33a4af053eb..9864c1f3d7c 100644
--- a/src/librustc_metadata/decoder.rs
+++ b/src/librustc_metadata/decoder.rs
@@ -1114,7 +1114,7 @@ impl<'a, 'tcx> CrateMetadata {
         }
     }
 
-    pub fn is_const_fn(&self, id: DefIndex) -> bool {
+    crate fn is_const_fn_raw(&self, id: DefIndex) -> bool {
         let constness = match self.entry(id).kind {
             EntryKind::Method(data) => data.decode(self).fn_data.constness,
             EntryKind::Fn(data) => data.decode(self).constness,
diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs
index 07e2cfe1e80..3b1eba51aaf 100644
--- a/src/librustc_mir/const_eval.rs
+++ b/src/librustc_mir/const_eval.rs
@@ -213,7 +213,6 @@ impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
 #[derive(Clone, Debug)]
 enum ConstEvalError {
     NeedsRfc(String),
-    NotConst(String),
 }
 
 impl fmt::Display for ConstEvalError {
@@ -227,7 +226,6 @@ impl fmt::Display for ConstEvalError {
                     msg
                 )
             }
-            NotConst(ref msg) => write!(f, "{}", msg),
         }
     }
 }
@@ -237,7 +235,6 @@ impl Error for ConstEvalError {
         use self::ConstEvalError::*;
         match *self {
             NeedsRfc(_) => "this feature needs an rfc before being allowed inside constants",
-            NotConst(_) => "this feature is not compatible with constant evaluation",
         }
     }
 
@@ -293,9 +290,6 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx>
                 ecx.goto_block(ret)?; // fully evaluated and done
                 return Ok(None);
             }
-            return Err(
-                ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(),
-            );
         }
         // This is a const fn. Call it.
         Ok(Some(match ecx.load_mir(instance.def) {
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index 2811848424b..17fe78d325c 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -29,7 +29,6 @@ use rustc::mir::traversal::ReversePostorder;
 use rustc::mir::visit::{PlaceContext, Visitor};
 use rustc::middle::lang_items;
 use rustc_target::spec::abi::Abi;
-use syntax::attr;
 use syntax::ast::LitKind;
 use syntax::feature_gate::{UnstableFeatures, feature_err, emit_feature_err, GateIssue};
 use syntax_pos::{Span, DUMMY_SP};
@@ -813,7 +812,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
 
             let fn_ty = func.ty(self.mir, self.tcx);
             let mut callee_def_id = None;
-            let (mut is_shuffle, mut is_const_fn) = (false, None);
+            let (mut is_shuffle, mut is_const_fn) = (false, false);
             if let ty::FnDef(def_id, _) = fn_ty.sty {
                 callee_def_id = Some(def_id);
                 match self.tcx.fn_sig(def_id).abi() {
@@ -839,10 +838,17 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                             | "unchecked_shr"
                             | "add_with_overflow"
                             | "sub_with_overflow"
-                            | "mul_with_overflow" => is_const_fn = Some(def_id),
+                            | "mul_with_overflow"
+                            // no need to check feature gates, intrinsics are only callable from the
+                            // libstd or with forever unstable feature gates
+                            => is_const_fn = true,
+                            // special intrinsic that can be called diretly without an intrinsic
+                            // feature gate needs a language feature gate
                             "transmute" => {
+                                // never promote transmute calls
                                 if self.mode != Mode::Fn {
-                                    is_const_fn = Some(def_id);
+                                    is_const_fn = true;
+                                    // const eval transmute calls only with the feature gate
                                     if !self.tcx.sess.features_untracked().const_transmute {
                                         emit_feature_err(
                                             &self.tcx.sess.parse_sess, "const_transmute",
@@ -861,8 +867,81 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                         }
                     }
                     _ => {
-                        if self.tcx.is_const_fn(def_id) || self.is_const_panic_fn(def_id) {
-                            is_const_fn = Some(def_id);
+                        // in normal functions we only care about promotion
+                        if self.mode == Mode::Fn {
+                            // never promote const fn calls of
+                            // functions without #[rustc_promotable]
+                            if self.tcx.is_promotable_const_fn(def_id) {
+                                is_const_fn = true;
+                            }
+                        } else {
+                            // stable const fn or unstable const fns with their feature gate
+                            // active
+                            if self.tcx.is_const_fn(def_id) {
+                                is_const_fn = true;
+                            } else if self.is_const_panic_fn(def_id) {
+                                // check the const_panic feature gate
+                                // FIXME: cannot allow this inside `allow_internal_unstable` because
+                                // that would make `panic!` insta stable in constants, since the
+                                // macro is marked with the attr
+                                if self.tcx.sess.features_untracked().const_panic {
+                                    is_const_fn = true;
+                                } else {
+                                    // don't allow panics in constants without the feature gate
+                                    emit_feature_err(
+                                        &self.tcx.sess.parse_sess,
+                                        "const_panic",
+                                        self.span,
+                                        GateIssue::Language,
+                                        &format!("panicking in {}s is unstable", self.mode),
+                                    );
+                                }
+                            } else if let Some(feature) = self.tcx.is_unstable_const_fn(def_id) {
+                                // check `#[unstable]` const fns or `#[rustc_const_unstable]`
+                                // functions without the feature gate active in this crate to report
+                                // a better error message than the one below
+                                if self.span.allows_unstable() {
+                                    // `allow_internal_unstable` can make such calls stable
+                                    is_const_fn = true;
+                                } else {
+                                    let mut err = self.tcx.sess.struct_span_err(self.span,
+                                        &format!("`{}` is not yet stable as a const fn",
+                                                self.tcx.item_path_str(def_id)));
+                                    help!(&mut err,
+                                        "in Nightly builds, add `#![feature({})]` \
+                                        to the crate attributes to enable",
+                                        feature);
+                                    err.emit();
+                                }
+                            } else {
+                                // FIXME(#24111) Remove this check when const fn stabilizes
+                                let (msg, note) = if let UnstableFeatures::Disallow =
+                                        self.tcx.sess.opts.unstable_features {
+                                    (format!("calls in {}s are limited to \
+                                            tuple structs and tuple variants",
+                                            self.mode),
+                                    Some("a limited form of compile-time function \
+                                        evaluation is available on a nightly \
+                                        compiler via `const fn`"))
+                                } else {
+                                    (format!("calls in {}s are limited \
+                                            to constant functions, \
+                                            tuple structs and tuple variants",
+                                            self.mode),
+                                    None)
+                                };
+                                let mut err = struct_span_err!(
+                                    self.tcx.sess,
+                                    self.span,
+                                    E0015,
+                                    "{}",
+                                    msg,
+                                );
+                                if let Some(note) = note {
+                                    err.span_note(self.span, note);
+                                }
+                                err.emit();
+                            }
                         }
                     }
                 }
@@ -905,78 +984,14 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                 });
             }
 
-            // Const fn calls.
-            if let Some(def_id) = is_const_fn {
-                // check the const_panic feature gate or
-                // find corresponding rustc_const_unstable feature
-                // FIXME: cannot allow this inside `allow_internal_unstable` because that would make
-                // `panic!` insta stable in constants, since the macro is marked with the attr
-                if self.is_const_panic_fn(def_id) {
-                    if self.mode == Mode::Fn {
-                        // never promote panics
-                        self.qualif = Qualif::NOT_CONST;
-                    } else if !self.tcx.sess.features_untracked().const_panic {
-                        // don't allow panics in constants without the feature gate
-                        emit_feature_err(
-                            &self.tcx.sess.parse_sess,
-                            "const_panic",
-                            self.span,
-                            GateIssue::Language,
-                            &format!("panicking in {}s is unstable", self.mode),
-                        );
-                    }
-                } else if let Some(&attr::Stability {
-                    const_stability: Some(ref feature_name),
-                .. }) = self.tcx.lookup_stability(def_id) {
-                    if
-                        // feature-gate is not enabled,
-                        !self.tcx.features()
-                            .declared_lib_features
-                            .iter()
-                            .any(|&(ref sym, _)| sym == feature_name) &&
-
-                        // this doesn't come from a macro that has #[allow_internal_unstable]
-                        !self.span.allows_unstable()
-                    {
-                        self.qualif = Qualif::NOT_CONST;
-                        if self.mode != Mode::Fn {
-                            // inside a constant environment, not having the feature gate is
-                            // an error
-                            let mut err = self.tcx.sess.struct_span_err(self.span,
-                                &format!("`{}` is not yet stable as a const fn",
-                                        self.tcx.item_path_str(def_id)));
-                            help!(&mut err,
-                                "in Nightly builds, add `#![feature({})]` \
-                                to the crate attributes to enable",
-                                feature_name);
-                            err.emit();
-                        }
-                    }
-                }
-            } else {
+            // non-const fn calls.
+            if !is_const_fn {
                 self.qualif = Qualif::NOT_CONST;
                 if self.mode != Mode::Fn {
-                    // FIXME(#24111) Remove this check when const fn stabilizes
-                    let (msg, note) = if let UnstableFeatures::Disallow =
-                            self.tcx.sess.opts.unstable_features {
-                        (format!("calls in {}s are limited to \
-                                  tuple structs and tuple variants",
-                                 self.mode),
-                         Some("a limited form of compile-time function \
-                               evaluation is available on a nightly \
-                               compiler via `const fn`"))
-                    } else {
-                        (format!("calls in {}s are limited \
-                                  to constant functions, \
-                                  tuple structs and tuple variants",
-                                 self.mode),
-                         None)
-                    };
-                    let mut err = struct_span_err!(self.tcx.sess, self.span, E0015, "{}", msg);
-                    if let Some(note) = note {
-                        err.span_note(self.span, note);
-                    }
-                    err.emit();
+                    self.tcx.sess.delay_span_bug(
+                        self.span,
+                        "should have reported an error about non-const fn calls in constants",
+                    )
                 }
             }
 
diff --git a/src/librustc_passes/rvalue_promotion.rs b/src/librustc_passes/rvalue_promotion.rs
index efe59e4face..5e9169e86a9 100644
--- a/src/librustc_passes/rvalue_promotion.rs
+++ b/src/librustc_passes/rvalue_promotion.rs
@@ -27,7 +27,6 @@
 use rustc::ty::cast::CastKind;
 use rustc::hir::def::{Def, CtorKind};
 use rustc::hir::def_id::DefId;
-use rustc::hir::map::blocks::FnLikeNode;
 use rustc::middle::expr_use_visitor as euv;
 use rustc::middle::mem_categorization as mc;
 use rustc::middle::mem_categorization::Categorization;
@@ -38,7 +37,6 @@ use rustc::util::nodemap::{ItemLocalSet, NodeSet};
 use rustc::hir;
 use rustc_data_structures::sync::Lrc;
 use syntax::ast;
-use syntax::attr;
 use syntax_pos::{Span, DUMMY_SP};
 use self::Promotability::*;
 use std::ops::{BitAnd, BitAndAssign, BitOr};
@@ -160,41 +158,15 @@ impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> {
         }
     }
 
-    fn handle_const_fn_call(&mut self, def_id: DefId,
-                            ret_ty: Ty<'gcx>, span: Span) -> Promotability {
-        if self.type_promotability(ret_ty) == NotPromotable {
-            return NotPromotable;
-        }
-
-        let node_check = if let Some(fn_id) = self.tcx.hir.as_local_node_id(def_id) {
-            FnLikeNode::from_node(self.tcx.hir.get(fn_id)).map_or(false, |fn_like| {
-                fn_like.constness() == hir::Constness::Const
-            })
+    fn handle_const_fn_call(
+        &mut self,
+        def_id: DefId,
+    ) -> Promotability {
+        if self.tcx.is_promotable_const_fn(def_id) {
+            Promotable
         } else {
-            self.tcx.is_const_fn(def_id)
-        };
-
-        if !node_check {
-            return NotPromotable
+            NotPromotable
         }
-
-        if let Some(&attr::Stability {
-            const_stability: Some(ref feature_name),
-            .. }) = self.tcx.lookup_stability(def_id) {
-            let stable_check =
-                // feature-gate is enabled,
-                self.tcx.features()
-                    .declared_lib_features
-                    .iter()
-                    .any(|&(ref sym, _)| sym == feature_name) ||
-
-                    // this comes from a macro that has #[allow_internal_unstable]
-                    span.allows_unstable();
-            if !stable_check {
-                return NotPromotable
-            }
-        };
-        Promotable
     }
 
     /// While the `ExprUseVisitor` walks, we will identify which
@@ -443,14 +415,10 @@ fn check_expr_kind<'a, 'tcx>(
                 Def::StructCtor(_, CtorKind::Fn) |
                 Def::VariantCtor(_, CtorKind::Fn) |
                 Def::SelfCtor(..) => Promotable,
-                Def::Fn(did) => {
-                    v.handle_const_fn_call(did, node_ty, e.span)
-                }
+                Def::Fn(did) => v.handle_const_fn_call(did),
                 Def::Method(did) => {
                     match v.tcx.associated_item(did).container {
-                        ty::ImplContainer(_) => {
-                            v.handle_const_fn_call(did, node_ty, e.span)
-                        }
+                        ty::ImplContainer(_) => v.handle_const_fn_call(did),
                         ty::TraitContainer(_) => NotPromotable,
                     }
                 }
@@ -466,16 +434,13 @@ fn check_expr_kind<'a, 'tcx>(
             if let Some(def) = v.tables.type_dependent_defs().get(e.hir_id) {
                 let def_id = def.def_id();
                 match v.tcx.associated_item(def_id).container {
-                    ty::ImplContainer(_) => {
-                        method_call_result = method_call_result
-                            & v.handle_const_fn_call(def_id, node_ty, e.span);
-                    }
-                    ty::TraitContainer(_) => return NotPromotable,
-                };
+                    ty::ImplContainer(_) => method_call_result & v.handle_const_fn_call(def_id),
+                    ty::TraitContainer(_) => NotPromotable,
+                }
             } else {
                 v.tcx.sess.delay_span_bug(e.span, "no type-dependent def for method call");
+                NotPromotable
             }
-            method_call_result
         }
         hir::ExprKind::Struct(ref _qpath, ref hirvec, ref option_expr) => {
             let mut struct_result = Promotable;
diff --git a/src/libsyntax/attr/builtin.rs b/src/libsyntax/attr/builtin.rs
index 5fc9c5578e1..f1cec422420 100644
--- a/src/libsyntax/attr/builtin.rs
+++ b/src/libsyntax/attr/builtin.rs
@@ -112,6 +112,8 @@ pub struct Stability {
     /// `Some` contains the feature gate required to be able to use the function
     /// as const fn
     pub const_stability: Option<Symbol>,
+    /// whether the function has a `#[rustc_promotable]` attribute
+    pub promotable: bool,
 }
 
 /// The available stability levels.
@@ -176,6 +178,7 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
     let mut stab: Option<Stability> = None;
     let mut rustc_depr: Option<RustcDeprecation> = None;
     let mut rustc_const_unstable: Option<Symbol> = None;
+    let mut promotable = false;
 
     'outer: for attr in attrs_iter {
         if ![
@@ -183,6 +186,7 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
             "rustc_const_unstable",
             "unstable",
             "stable",
+            "rustc_promotable",
         ].iter().any(|&s| attr.path == s) {
             continue // not a stability level
         }
@@ -190,8 +194,12 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
         mark_used(attr);
 
         let meta = attr.meta();
+
+        if attr.path == "rustc_promotable" {
+            promotable = true;
+        }
         // attributes with data
-        if let Some(MetaItem { node: MetaItemKind::List(ref metas), .. }) = meta {
+        else if let Some(MetaItem { node: MetaItemKind::List(ref metas), .. }) = meta {
             let meta = meta.as_ref().unwrap();
             let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
                 if item.is_some() {
@@ -329,6 +337,7 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
                                 feature,
                                 rustc_depr: None,
                                 const_stability: None,
+                                promotable: false,
                             })
                         }
                         (None, _, _) => {
@@ -378,6 +387,7 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
                                 feature,
                                 rustc_depr: None,
                                 const_stability: None,
+                                promotable: false,
                             })
                         }
                         (None, _) => {
@@ -420,6 +430,17 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
         }
     }
 
+    // Merge the const-unstable info into the stability info
+    if promotable {
+        if let Some(ref mut stab) = stab {
+            stab.promotable = true;
+        } else {
+            span_err!(diagnostic, item_sp, E0717,
+                      "rustc_promotable attribute must be paired with \
+                       either stable or unstable attribute");
+        }
+    }
+
     stab
 }
 
diff --git a/src/libsyntax/diagnostic_list.rs b/src/libsyntax/diagnostic_list.rs
index 23ce7fc6a65..5155ebbe19d 100644
--- a/src/libsyntax/diagnostic_list.rs
+++ b/src/libsyntax/diagnostic_list.rs
@@ -413,4 +413,5 @@ register_diagnostics! {
     E0694, // an unknown tool name found in scoped attributes
     E0703, // invalid ABI
     E0704, // incorrect visibility restriction
+    E0717, // rustc_promotable without stability attribute
 }
diff --git a/src/test/ui/consts/const-fn-error.rs b/src/test/compile-fail/const-fn-error.rs
index 6eda41730b3..6eda41730b3 100644
--- a/src/test/ui/consts/const-fn-error.rs
+++ b/src/test/compile-fail/const-fn-error.rs
diff --git a/src/test/ui/consts/const-eval/issue-52443.rs b/src/test/compile-fail/issue-52443.rs
index 2ae94f6d642..2ae94f6d642 100644
--- a/src/test/ui/consts/const-eval/issue-52443.rs
+++ b/src/test/compile-fail/issue-52443.rs
diff --git a/src/test/run-pass/invalid_const_promotion.rs b/src/test/run-pass/invalid_const_promotion.rs
index e861f217629..ed8c4992417 100644
--- a/src/test/run-pass/invalid_const_promotion.rs
+++ b/src/test/run-pass/invalid_const_promotion.rs
@@ -14,7 +14,8 @@
 
 // compile-flags: -C debug_assertions=yes
 
-#![feature(const_fn, libc)]
+#![stable(feature = "rustc", since = "1.0.0")]
+#![feature(const_fn, libc, staged_api, rustc_attrs)]
 #![allow(const_err)]
 
 extern crate libc;
@@ -23,6 +24,8 @@ use std::env;
 use std::process::{Command, Stdio};
 
 // this will panic in debug mode and overflow in release mode
+#[stable(feature = "rustc", since = "1.0.0")]
+#[rustc_promotable]
 const fn bar() -> usize { 0 - 1 }
 
 fn foo() {
diff --git a/src/test/run-pass/issues/issue-49955-2.rs b/src/test/run-pass/issues/issue-49955-2.rs
index 40827b01622..88883821073 100644
--- a/src/test/run-pass/issues/issue-49955-2.rs
+++ b/src/test/run-pass/issues/issue-49955-2.rs
@@ -13,13 +13,15 @@
 
 use std::cell::Cell;
 
+const FIVE: Cell<i32> = Cell::new(5);
+
 #[inline(never)]
 fn tuple_field() -> &'static u32 {
     // This test is MIR-borrowck-only because the old borrowck
     // doesn't agree that borrows of "frozen" (i.e. without any
     // interior mutability) fields of non-frozen temporaries,
     // should be promoted, while MIR promotion does promote them.
-    &(Cell::new(5), 42).1
+    &(FIVE, 42).1
 }
 
 fn main() {
diff --git a/src/test/ui/consts/const-call.rs b/src/test/ui/consts/const-call.rs
index 02264228a6b..18476494300 100644
--- a/src/test/ui/consts/const-call.rs
+++ b/src/test/ui/consts/const-call.rs
@@ -15,5 +15,4 @@ fn f(x: usize) -> usize {
 fn main() {
     let _ = [0; f(2)];
     //~^ ERROR calls in constants are limited to constant functions
-    //~| E0080
 }
diff --git a/src/test/ui/consts/const-call.stderr b/src/test/ui/consts/const-call.stderr
index efc3c58c681..81be93e916e 100644
--- a/src/test/ui/consts/const-call.stderr
+++ b/src/test/ui/consts/const-call.stderr
@@ -4,13 +4,6 @@ error[E0015]: calls in constants are limited to constant functions, tuple struct
 LL |     let _ = [0; f(2)];
    |                 ^^^^
 
-error[E0080]: could not evaluate repeat length
-  --> $DIR/const-call.rs:16:17
-   |
-LL |     let _ = [0; f(2)];
-   |                 ^^^^ calling non-const fn `f`
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
-Some errors occurred: E0015, E0080.
-For more information about an error, try `rustc --explain E0015`.
+For more information about this error, try `rustc --explain E0015`.
diff --git a/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.nll.stderr b/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.nll.stderr
index fe7187af5f5..bae8b89f3d0 100644
--- a/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.nll.stderr
+++ b/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.nll.stderr
@@ -32,7 +32,7 @@ error[E0716]: temporary value dropped while borrowed
    |
 LL |     let x: &'static _ = &std::time::Duration::from_millis(42).subsec_millis();
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
-LL |     //~^ does not live long enough
+LL |     //~^ ERROR does not live long enough
 LL | }
    | - temporary value is freed at the end of this statement
    |
diff --git a/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.rs b/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.rs
index 623e99480a7..a371d5485e1 100644
--- a/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.rs
+++ b/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.rs
@@ -31,5 +31,5 @@ fn a() {
 fn main() {
     let _: &'static u32 = &meh(); //~ ERROR does not live long enough
     let x: &'static _ = &std::time::Duration::from_millis(42).subsec_millis();
-    //~^ does not live long enough
+    //~^ ERROR does not live long enough
 }
diff --git a/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr b/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr
index b9856d37b0e..b3d7ba3e5c4 100644
--- a/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr
+++ b/src/test/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr
@@ -32,7 +32,7 @@ error[E0597]: borrowed value does not live long enough
    |
 LL |     let x: &'static _ = &std::time::Duration::from_millis(42).subsec_millis();
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough
-LL |     //~^ does not live long enough
+LL |     //~^ ERROR does not live long enough
 LL | }
    | - temporary value only lives until here
    |
diff --git a/src/test/ui/consts/const-eval/issue-52443.stderr b/src/test/ui/consts/const-eval/issue-52443.stderr
deleted file mode 100644
index 3a145ba78d9..00000000000
--- a/src/test/ui/consts/const-eval/issue-52443.stderr
+++ /dev/null
@@ -1,51 +0,0 @@
-error[E0308]: mismatched types
-  --> $DIR/issue-52443.rs:12:10
-   |
-LL |     [(); & { loop { continue } } ]; //~ ERROR mismatched types
-   |          ^^^^^^^^^^^^^^^^^^^^^^^
-   |          |
-   |          expected usize, found reference
-   |          help: consider removing the borrow: `{ loop { continue } }`
-   |
-   = note: expected type `usize`
-              found type `&_`
-
-error[E0308]: mismatched types
-  --> $DIR/issue-52443.rs:13:17
-   |
-LL |     [(); loop { break }]; //~ ERROR mismatched types
-   |                 ^^^^^ expected (), found usize
-   |
-   = note: expected type `()`
-              found type `usize`
-
-error[E0019]: constant contains unimplemented expression type
-  --> $DIR/issue-52443.rs:14:11
-   |
-LL |     [(); {while true {break}; 0}]; //~ ERROR constant contains unimplemented expression type
-   |           ^^^^^^^^^^^^^^^^^^
-
-error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
-  --> $DIR/issue-52443.rs:15:21
-   |
-LL |     [(); { for _ in 0usize.. {}; 0}]; //~ ERROR calls in constants are limited to constant functions
-   |                     ^^^^^^^^
-
-error[E0019]: constant contains unimplemented expression type
-  --> $DIR/issue-52443.rs:15:21
-   |
-LL |     [(); { for _ in 0usize.. {}; 0}]; //~ ERROR calls in constants are limited to constant functions
-   |                     ^^^^^^^^
-
-error[E0080]: could not evaluate repeat length
-  --> $DIR/issue-52443.rs:15:10
-   |
-LL |     [(); { for _ in 0usize.. {}; 0}]; //~ ERROR calls in constants are limited to constant functions
-   |          ^^^^^^^^^^^--------^^^^^^^
-   |                     |
-   |                     calling non-const fn `<I as std::iter::IntoIterator><std::ops::RangeFrom<usize>>::into_iter`
-
-error: aborting due to 6 previous errors
-
-Some errors occurred: E0015, E0019, E0080, E0308.
-For more information about an error, try `rustc --explain E0015`.
diff --git a/src/test/ui/consts/const-eval/promoted_const_fn_fail.nll.stderr b/src/test/ui/consts/const-eval/promoted_const_fn_fail.nll.stderr
new file mode 100644
index 00000000000..b8e2d6a96c5
--- /dev/null
+++ b/src/test/ui/consts/const-eval/promoted_const_fn_fail.nll.stderr
@@ -0,0 +1,14 @@
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/promoted_const_fn_fail.rs:30:27
+   |
+LL |     let x: &'static u8 = &(bar() + 1); //~ ERROR does not live long enough
+   |                           ^^^^^^^^^^^ creates a temporary which is freed while still in use
+...
+LL | }
+   | - temporary value is freed at the end of this statement
+   |
+   = note: borrowed value must be valid for the static lifetime...
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0716`.
diff --git a/src/test/ui/consts/const-eval/promoted_const_fn_fail.rs b/src/test/ui/consts/const-eval/promoted_const_fn_fail.rs
index 19db07dd330..9b03ddd1891 100644
--- a/src/test/ui/consts/const-eval/promoted_const_fn_fail.rs
+++ b/src/test/ui/consts/const-eval/promoted_const_fn_fail.rs
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// compile-pass
-
 #![feature(const_fn, const_fn_union)]
 
 #![deny(const_err)]
@@ -29,10 +27,7 @@ const fn bar() -> u8 {
 }
 
 fn main() {
-    // FIXME(oli-obk): this should panic at runtime
-    // this will actually compile, but then
-    // abort at runtime (not panic, hard abort).
-    let x: &'static u8 = &(bar() + 1);
+    let x: &'static u8 = &(bar() + 1); //~ ERROR does not live long enough
     let y = *x;
     unreachable!();
 }
diff --git a/src/test/ui/consts/const-eval/promoted_const_fn_fail.stderr b/src/test/ui/consts/const-eval/promoted_const_fn_fail.stderr
new file mode 100644
index 00000000000..4d80f701bcf
--- /dev/null
+++ b/src/test/ui/consts/const-eval/promoted_const_fn_fail.stderr
@@ -0,0 +1,14 @@
+error[E0597]: borrowed value does not live long enough
+  --> $DIR/promoted_const_fn_fail.rs:30:27
+   |
+LL |     let x: &'static u8 = &(bar() + 1); //~ ERROR does not live long enough
+   |                           ^^^^^^^^^^^ temporary value does not live long enough
+...
+LL | }
+   | - temporary value only lives until here
+   |
+   = note: borrowed value must be valid for the static lifetime...
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/consts/const-fn-error.stderr b/src/test/ui/consts/const-fn-error.stderr
deleted file mode 100644
index cdbf86f42ec..00000000000
--- a/src/test/ui/consts/const-fn-error.stderr
+++ /dev/null
@@ -1,43 +0,0 @@
-error[E0658]: let bindings in constant functions are unstable (see issue #48821)
-  --> $DIR/const-fn-error.rs:16:19
-   |
-LL |     let mut sum = 0;
-   |                   ^
-   |
-   = help: add #![feature(const_let)] to the crate attributes to enable
-
-error[E0658]: statements in constant functions are unstable (see issue #48821)
-  --> $DIR/const-fn-error.rs:16:19
-   |
-LL |     let mut sum = 0;
-   |                   ^
-   |
-   = help: add #![feature(const_let)] to the crate attributes to enable
-
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-  --> $DIR/const-fn-error.rs:19:14
-   |
-LL |     for i in 0..x {
-   |              ^^^^
-
-error[E0019]: constant function contains unimplemented expression type
-  --> $DIR/const-fn-error.rs:19:14
-   |
-LL |     for i in 0..x {
-   |              ^^^^
-
-error[E0080]: could not evaluate constant expression
-  --> $DIR/const-fn-error.rs:29:13
-   |
-LL |     for i in 0..x {
-   |              ---- calling non-const fn `<I as std::iter::IntoIterator><std::ops::Range<usize>>::into_iter`
-...
-LL |     let a : [i32; f(X)]; //~ ERROR E0080
-   |             ^^^^^^----^
-   |                   |
-   |                   inside call to `f`
-
-error: aborting due to 5 previous errors
-
-Some errors occurred: E0015, E0019, E0080, E0658.
-For more information about an error, try `rustc --explain E0015`.
diff --git a/src/test/ui/consts/min_const_fn/promotion.nll.stderr b/src/test/ui/consts/min_const_fn/promotion.nll.stderr
new file mode 100644
index 00000000000..999c26b535e
--- /dev/null
+++ b/src/test/ui/consts/min_const_fn/promotion.nll.stderr
@@ -0,0 +1,68 @@
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/promotion.rs:13:27
+   |
+LL |     let x: &'static () = &foo1(); //~ ERROR does not live long enough
+   |                           ^^^^^^ creates a temporary which is freed while still in use
+...
+LL | }
+   | - temporary value is freed at the end of this statement
+   |
+   = note: borrowed value must be valid for the static lifetime...
+
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/promotion.rs:14:28
+   |
+LL |     let y: &'static i32 = &foo2(42); //~ ERROR does not live long enough
+   |                            ^^^^^^^^ creates a temporary which is freed while still in use
+...
+LL | }
+   | - temporary value is freed at the end of this statement
+   |
+   = note: borrowed value must be valid for the static lifetime...
+
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/promotion.rs:15:28
+   |
+LL |     let z: &'static i32 = &foo3(); //~ ERROR does not live long enough
+   |                            ^^^^^^ creates a temporary which is freed while still in use
+...
+LL | }
+   | - temporary value is freed at the end of this statement
+   |
+   = note: borrowed value must be valid for the static lifetime...
+
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/promotion.rs:16:34
+   |
+LL |     let a: &'static Cell<i32> = &foo4();  //~ ERROR does not live long enough
+   |                                  ^^^^^^ creates a temporary which is freed while still in use
+...
+LL | }
+   | - temporary value is freed at the end of this statement
+   |
+   = note: borrowed value must be valid for the static lifetime...
+
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/promotion.rs:17:42
+   |
+LL |     let a: &'static Option<Cell<i32>> = &foo5(); //~ ERROR does not live long enough
+   |                                          ^^^^^^ creates a temporary which is freed while still in use
+LL |     let a: &'static Option<Cell<i32>> = &foo6(); //~ ERROR does not live long enough
+LL | }
+   | - temporary value is freed at the end of this statement
+   |
+   = note: borrowed value must be valid for the static lifetime...
+
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/promotion.rs:18:42
+   |
+LL |     let a: &'static Option<Cell<i32>> = &foo6(); //~ ERROR does not live long enough
+   |                                          ^^^^^^ creates a temporary which is freed while still in use
+LL | }
+   | - temporary value is freed at the end of this statement
+   |
+   = note: borrowed value must be valid for the static lifetime...
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0716`.
diff --git a/src/test/ui/consts/min_const_fn/promotion.rs b/src/test/ui/consts/min_const_fn/promotion.rs
new file mode 100644
index 00000000000..cb3bfd74711
--- /dev/null
+++ b/src/test/ui/consts/min_const_fn/promotion.rs
@@ -0,0 +1,19 @@
+#![feature(min_const_fn)]
+
+use std::cell::Cell;
+
+const fn foo1() {}
+const fn foo2(x: i32) -> i32 { x }
+const fn foo3() -> i32 { 42 }
+const fn foo4() -> Cell<i32> { Cell::new(42) }
+const fn foo5() -> Option<Cell<i32>> { Some(Cell::new(42)) }
+const fn foo6() -> Option<Cell<i32>> { None }
+
+fn main() {
+    let x: &'static () = &foo1(); //~ ERROR does not live long enough
+    let y: &'static i32 = &foo2(42); //~ ERROR does not live long enough
+    let z: &'static i32 = &foo3(); //~ ERROR does not live long enough
+    let a: &'static Cell<i32> = &foo4();  //~ ERROR does not live long enough
+    let a: &'static Option<Cell<i32>> = &foo5(); //~ ERROR does not live long enough
+    let a: &'static Option<Cell<i32>> = &foo6(); //~ ERROR does not live long enough
+}
diff --git a/src/test/ui/consts/min_const_fn/promotion.stderr b/src/test/ui/consts/min_const_fn/promotion.stderr
new file mode 100644
index 00000000000..68d58465822
--- /dev/null
+++ b/src/test/ui/consts/min_const_fn/promotion.stderr
@@ -0,0 +1,68 @@
+error[E0597]: borrowed value does not live long enough
+  --> $DIR/promotion.rs:13:27
+   |
+LL |     let x: &'static () = &foo1(); //~ ERROR does not live long enough
+   |                           ^^^^^^ temporary value does not live long enough
+...
+LL | }
+   | - temporary value only lives until here
+   |
+   = note: borrowed value must be valid for the static lifetime...
+
+error[E0597]: borrowed value does not live long enough
+  --> $DIR/promotion.rs:14:28
+   |
+LL |     let y: &'static i32 = &foo2(42); //~ ERROR does not live long enough
+   |                            ^^^^^^^^ temporary value does not live long enough
+...
+LL | }
+   | - temporary value only lives until here
+   |
+   = note: borrowed value must be valid for the static lifetime...
+
+error[E0597]: borrowed value does not live long enough
+  --> $DIR/promotion.rs:15:28
+   |
+LL |     let z: &'static i32 = &foo3(); //~ ERROR does not live long enough
+   |                            ^^^^^^ temporary value does not live long enough
+...
+LL | }
+   | - temporary value only lives until here
+   |
+   = note: borrowed value must be valid for the static lifetime...
+
+error[E0597]: borrowed value does not live long enough
+  --> $DIR/promotion.rs:16:34
+   |
+LL |     let a: &'static Cell<i32> = &foo4();  //~ ERROR does not live long enough
+   |                                  ^^^^^^ temporary value does not live long enough
+...
+LL | }
+   | - temporary value only lives until here
+   |
+   = note: borrowed value must be valid for the static lifetime...
+
+error[E0597]: borrowed value does not live long enough
+  --> $DIR/promotion.rs:17:42
+   |
+LL |     let a: &'static Option<Cell<i32>> = &foo5(); //~ ERROR does not live long enough
+   |                                          ^^^^^^ temporary value does not live long enough
+LL |     let a: &'static Option<Cell<i32>> = &foo6(); //~ ERROR does not live long enough
+LL | }
+   | - temporary value only lives until here
+   |
+   = note: borrowed value must be valid for the static lifetime...
+
+error[E0597]: borrowed value does not live long enough
+  --> $DIR/promotion.rs:18:42
+   |
+LL |     let a: &'static Option<Cell<i32>> = &foo6(); //~ ERROR does not live long enough
+   |                                          ^^^^^^ temporary value does not live long enough
+LL | }
+   | - temporary value only lives until here
+   |
+   = note: borrowed value must be valid for the static lifetime...
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/issues/issue-39559-2.rs b/src/test/ui/issues/issue-39559-2.rs
index f01fd1fd8f1..2e480a29774 100644
--- a/src/test/ui/issues/issue-39559-2.rs
+++ b/src/test/ui/issues/issue-39559-2.rs
@@ -23,8 +23,6 @@ impl Dim for Dim3 {
 fn main() {
     let array: [usize; Dim3::dim()]
     //~^ ERROR E0015
-    //~| ERROR E0080
         = [0; Dim3::dim()];
         //~^ ERROR E0015
-        //~| ERROR E0080
 }
diff --git a/src/test/ui/issues/issue-39559-2.stderr b/src/test/ui/issues/issue-39559-2.stderr
index 91520a7ec08..ca9da096b6c 100644
--- a/src/test/ui/issues/issue-39559-2.stderr
+++ b/src/test/ui/issues/issue-39559-2.stderr
@@ -5,26 +5,11 @@ LL |     let array: [usize; Dim3::dim()]
    |                        ^^^^^^^^^^^
 
 error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
-  --> $DIR/issue-39559-2.rs:27:15
+  --> $DIR/issue-39559-2.rs:26:15
    |
 LL |         = [0; Dim3::dim()];
    |               ^^^^^^^^^^^
 
-error[E0080]: could not evaluate repeat length
-  --> $DIR/issue-39559-2.rs:27:15
-   |
-LL |         = [0; Dim3::dim()];
-   |               ^^^^^^^^^^^ calling non-const fn `<Dim3 as Dim>::dim`
-
-error[E0080]: could not evaluate constant expression
-  --> $DIR/issue-39559-2.rs:24:16
-   |
-LL |     let array: [usize; Dim3::dim()]
-   |                ^^^^^^^^-----------^
-   |                        |
-   |                        calling non-const fn `<Dim3 as Dim>::dim`
-
-error: aborting due to 4 previous errors
+error: aborting due to 2 previous errors
 
-Some errors occurred: E0015, E0080.
-For more information about an error, try `rustc --explain E0015`.
+For more information about this error, try `rustc --explain E0015`.
diff --git a/src/test/ui/issues/issue-43105.rs b/src/test/ui/issues/issue-43105.rs
index 8a0471135af..2bddc443d5b 100644
--- a/src/test/ui/issues/issue-43105.rs
+++ b/src/test/ui/issues/issue-43105.rs
@@ -16,7 +16,6 @@ const NUM: u8 = xyz();
 fn main() {
     match 1 {
         NUM => unimplemented!(),
-        //~^ ERROR could not evaluate constant pattern
         _ => unimplemented!(),
     }
 }
diff --git a/src/test/ui/issues/issue-43105.stderr b/src/test/ui/issues/issue-43105.stderr
index 18da9310503..67a6008cd8e 100644
--- a/src/test/ui/issues/issue-43105.stderr
+++ b/src/test/ui/issues/issue-43105.stderr
@@ -4,16 +4,6 @@ error[E0015]: calls in constants are limited to constant functions, tuple struct
 LL | const NUM: u8 = xyz();
    |                 ^^^^^
 
-error[E0080]: could not evaluate constant pattern
-  --> $DIR/issue-43105.rs:18:9
-   |
-LL | const NUM: u8 = xyz();
-   |                 ----- calling non-const fn `xyz`
-...
-LL |         NUM => unimplemented!(),
-   |         ^^^
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
-Some errors occurred: E0015, E0080.
-For more information about an error, try `rustc --explain E0015`.
+For more information about this error, try `rustc --explain E0015`.
diff --git a/src/test/ui/rustc-args-required-const.rs b/src/test/ui/rustc-args-required-const.rs
index 35b43b4c460..15465ac52aa 100644
--- a/src/test/ui/rustc-args-required-const.rs
+++ b/src/test/ui/rustc-args-required-const.rs
@@ -27,7 +27,8 @@ const fn baz() -> i32 {
 fn main() {
     foo(2);
     foo(2 + 3);
-    foo(baz());
+    const BAZ: i32 = baz();
+    foo(BAZ);
     let a = 4;
     foo(A);
     foo(a); //~ ERROR: argument 1 is required to be a constant
diff --git a/src/test/ui/rustc-args-required-const.stderr b/src/test/ui/rustc-args-required-const.stderr
index 07f2d880c82..efd4c58f152 100644
--- a/src/test/ui/rustc-args-required-const.stderr
+++ b/src/test/ui/rustc-args-required-const.stderr
@@ -1,11 +1,11 @@
 error: argument 1 is required to be a constant
-  --> $DIR/rustc-args-required-const.rs:33:5
+  --> $DIR/rustc-args-required-const.rs:34:5
    |
 LL |     foo(a); //~ ERROR: argument 1 is required to be a constant
    |     ^^^^^^
 
 error: argument 2 is required to be a constant
-  --> $DIR/rustc-args-required-const.rs:35:5
+  --> $DIR/rustc-args-required-const.rs:36:5
    |
 LL |     bar(a, a); //~ ERROR: argument 2 is required to be a constant
    |     ^^^^^^^^^