about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2025-06-26 20:15:17 -0400
committerGitHub <noreply@github.com>2025-06-26 20:15:17 -0400
commit36cde678948400f2dcda71ed09e2072f093b63d5 (patch)
tree1469a5fcbd2aa647383a6ce2e1a08c9541b905ba
parent0446a0d14bb378dbe9a59f0f3e30a2f14aabd80d (diff)
parentbade3fd0580815f1a5a4abd33244982dc458d54d (diff)
downloadrust-36cde678948400f2dcda71ed09e2072f093b63d5.tar.gz
rust-36cde678948400f2dcda71ed09e2072f093b63d5.zip
Rollup merge of #140942 - RalfJung:const-ref-to-mut, r=oli-obk
const-eval: allow constants to refer to mutable/external memory, but reject such constants as patterns

This fixes https://github.com/rust-lang/rust/issues/140653 by accepting code such as this:
```rust
static FOO: AtomicU32 = AtomicU32::new(0);
const C: &'static AtomicU32 = &FOO;
```
This can be written entirely in safe code, so there can't really be anything wrong with it.

We also accept the much more questionable following code, since it looks very similar to the interpreter:
```rust
static mut FOO2: u32 = 0;
const C2: &'static u32 = unsafe { &mut FOO2 };
```
Using this without causing UB is at least very hard (the details are unclear since it is related to how the aliasing model deals with the staging of const-eval vs runtime code).

If a constant like `C2` is used as a pattern, we emit an error:
```
error: constant BAD_PATTERN cannot be used as pattern
  --> $DIR/const_refs_to_static_fail.rs:30:9
   |
LL |         BAD_PATTERN => {},
   |         ^^^^^^^^^^^
   |
   = note: constants that reference mutable or external memory cannot be used as pattern
```
(If you somehow manage to build a pattern with constant `C`, you'd get the same error, but that should be impossible: we don't have a type that can be used in patterns and that has interior mutability.)

The same treatment is afforded for shared references to `extern static`, for the same reason: the const evaluation is entirely fine with it, we just can't build a pattern for it -- and when using interior mutability, this can be totally sound.

We do still not accept anything where there is an `&mut` in the final value of the const, as that should always require unsafe code and it's hard to imagine a sound use-case that would require this.
-rw-r--r--compiler/rustc_const_eval/messages.ftl33
-rw-r--r--compiler/rustc_const_eval/src/check_consts/check.rs6
-rw-r--r--compiler/rustc_const_eval/src/check_consts/ops.rs23
-rw-r--r--compiler/rustc_const_eval/src/const_eval/mod.rs7
-rw-r--r--compiler/rustc_const_eval/src/const_eval/valtrees.rs38
-rw-r--r--compiler/rustc_const_eval/src/errors.rs31
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs21
-rw-r--r--compiler/rustc_middle/messages.ftl7
-rw-r--r--compiler/rustc_middle/src/error.rs17
-rw-r--r--compiler/rustc_middle/src/mir/interpret/error.rs52
-rw-r--r--compiler/rustc_middle/src/mir/interpret/mod.rs4
-rw-r--r--compiler/rustc_middle/src/mir/interpret/queries.rs120
-rw-r--r--compiler/rustc_middle/src/query/erase.rs7
-rw-r--r--compiler/rustc_middle/src/ty/consts/valtree.rs8
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs6
-rw-r--r--tests/ui/const-generics/generic_const_exprs/unevaluated-const-ice-119731.rs4
-rw-r--r--tests/ui/const-generics/generic_const_exprs/unevaluated-const-ice-119731.stderr40
-rw-r--r--tests/ui/consts/const-mut-refs/issue-76510.rs2
-rw-r--r--tests/ui/consts/const-mut-refs/issue-76510.stderr2
-rw-r--r--tests/ui/consts/const-mut-refs/mut_ref_in_final.rs16
-rw-r--r--tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr39
-rw-r--r--tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs2
-rw-r--r--tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr2
-rw-r--r--tests/ui/consts/const-promoted-opaque.atomic.stderr4
-rw-r--r--tests/ui/consts/const-promoted-opaque.rs2
-rw-r--r--tests/ui/consts/const-size_of-cycle.stderr12
-rw-r--r--tests/ui/consts/const_refs_to_static.rs5
-rw-r--r--tests/ui/consts/const_refs_to_static_fail.rs16
-rw-r--r--tests/ui/consts/const_refs_to_static_fail.stderr27
-rw-r--r--tests/ui/consts/const_refs_to_static_fail_invalid.rs6
-rw-r--r--tests/ui/consts/const_refs_to_static_fail_invalid.stderr26
-rw-r--r--tests/ui/consts/issue-17718-const-bad-values.rs4
-rw-r--r--tests/ui/consts/issue-17718-const-bad-values.stderr4
-rw-r--r--tests/ui/consts/issue-17718-const-borrow.rs6
-rw-r--r--tests/ui/consts/issue-17718-const-borrow.stderr12
-rw-r--r--tests/ui/consts/issue-44415.stderr12
-rw-r--r--tests/ui/consts/miri_unleashed/const_refers_to_static.rs9
-rw-r--r--tests/ui/consts/miri_unleashed/const_refers_to_static.stderr13
-rw-r--r--tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs9
-rw-r--r--tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr49
-rw-r--r--tests/ui/consts/miri_unleashed/mutable_references.rs7
-rw-r--r--tests/ui/consts/miri_unleashed/mutable_references.stderr39
-rw-r--r--tests/ui/consts/partial_qualif.rs2
-rw-r--r--tests/ui/consts/partial_qualif.stderr4
-rw-r--r--tests/ui/consts/qualif_overwrite.rs2
-rw-r--r--tests/ui/consts/qualif_overwrite.stderr4
-rw-r--r--tests/ui/consts/qualif_overwrite_2.rs2
-rw-r--r--tests/ui/consts/qualif_overwrite_2.stderr4
-rw-r--r--tests/ui/consts/refs-to-cell-in-final.rs6
-rw-r--r--tests/ui/consts/refs-to-cell-in-final.stderr12
-rw-r--r--tests/ui/consts/write_to_static_via_mut_ref.rs2
-rw-r--r--tests/ui/consts/write_to_static_via_mut_ref.stderr2
-rw-r--r--tests/ui/error-codes/E0017.rs4
-rw-r--r--tests/ui/error-codes/E0017.stderr4
-rw-r--r--tests/ui/error-codes/E0492.stderr8
-rw-r--r--tests/ui/issues/issue-46604.rs2
-rw-r--r--tests/ui/issues/issue-46604.stderr2
-rw-r--r--tests/ui/statics/check-immutable-mut-slices.rs2
-rw-r--r--tests/ui/statics/check-immutable-mut-slices.stderr2
-rw-r--r--tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr4
61 files changed, 448 insertions, 373 deletions
diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl
index 7f9abe8aa8e..97b154ad142 100644
--- a/compiler/rustc_const_eval/messages.ftl
+++ b/compiler/rustc_const_eval/messages.ftl
@@ -124,12 +124,13 @@ const_eval_incompatible_return_types =
 const_eval_incompatible_types =
     calling a function with argument of type {$callee_ty} passing data of type {$caller_ty}
 
-const_eval_interior_mutable_ref_escaping =
-    {const_eval_const_context}s cannot refer to interior mutable data
-    .label = this borrow of an interior mutable value may end up in the final value
+const_eval_interior_mutable_borrow_escaping =
+    interior mutable shared borrows of lifetime-extended temporaries in the top-level scope of a {const_eval_const_context} are not allowed
+    .label = this borrow of an interior mutable value refers to a lifetime-extended temporary
     .help = to fix this, the value can be extracted to a separate `static` item and then referenced
     .teach_note =
-        References that escape into the final value of a constant or static must be immutable.
+        This creates a raw pointer to a temporary that has its lifetime extended to last for the entire program.
+        Lifetime-extended temporaries in constants and statics must be immutable.
         This is to avoid accidentally creating shared mutable state.
 
 
@@ -207,33 +208,23 @@ const_eval_long_running =
     .label = the const evaluator is currently interpreting this expression
     .help = the constant being evaluated
 
-const_eval_max_num_nodes_in_const = maximum number of nodes exceeded in constant {$global_const_id}
-
 const_eval_memory_exhausted =
     tried to allocate more memory than available to compiler
 
 const_eval_modified_global =
     modifying a static's initial value from another static's initializer
 
-const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind}
-
-const_eval_mutable_raw_escaping =
-    raw mutable pointers are not allowed in the final value of {const_eval_const_context}s
+const_eval_mutable_borrow_escaping =
+    mutable borrows of lifetime-extended temporaries in the top-level scope of a {const_eval_const_context} are not allowed
     .teach_note =
-        Pointers that escape into the final value of a constant or static must be immutable.
+        This creates a reference to a temporary that has its lifetime extended to last for the entire program.
+        Lifetime-extended temporaries in constants and statics must be immutable.
         This is to avoid accidentally creating shared mutable state.
 
 
         If you really want global mutable state, try using an interior mutable `static` or a `static mut`.
 
-const_eval_mutable_ref_escaping =
-    mutable references are not allowed in the final value of {const_eval_const_context}s
-    .teach_note =
-        References that escape into the final value of a constant or static must be immutable.
-        This is to avoid accidentally creating shared mutable state.
-
-
-        If you really want global mutable state, try using an interior mutable `static` or a `static mut`.
+const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind}
 
 const_eval_nested_static_in_thread_local = #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead
 
@@ -437,9 +428,6 @@ const_eval_unwind_past_top =
 ## (We'd love to sort this differently to make that more clear but tidy won't let us...)
 const_eval_validation_box_to_uninhabited = {$front_matter}: encountered a box pointing to uninhabited type {$ty}
 
-const_eval_validation_const_ref_to_extern = {$front_matter}: encountered reference to `extern` static in `const`
-const_eval_validation_const_ref_to_mutable = {$front_matter}: encountered reference to mutable memory in `const`
-
 const_eval_validation_dangling_box_no_provenance = {$front_matter}: encountered a dangling box ({$pointer} has no provenance)
 const_eval_validation_dangling_box_out_of_bounds = {$front_matter}: encountered a dangling box (going beyond the bounds of its allocation)
 const_eval_validation_dangling_box_use_after_free = {$front_matter}: encountered a dangling box (use-after-free)
@@ -479,6 +467,7 @@ const_eval_validation_invalid_ref_meta = {$front_matter}: encountered invalid re
 const_eval_validation_invalid_ref_slice_meta = {$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object
 const_eval_validation_invalid_vtable_ptr = {$front_matter}: encountered {$value}, but expected a vtable pointer
 const_eval_validation_invalid_vtable_trait = {$front_matter}: wrong trait in wide pointer vtable: expected `{$expected_dyn_type}`, but encountered `{$vtable_dyn_type}`
+const_eval_validation_mutable_ref_in_const = {$front_matter}: encountered mutable reference in `const` value
 const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory
 const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!`
 const_eval_validation_null_box = {$front_matter}: encountered a null box
diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index 576b174369d..c151e8acf92 100644
--- a/compiler/rustc_const_eval/src/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/check_consts/check.rs
@@ -595,11 +595,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                     self.const_kind() == hir::ConstContext::Static(hir::Mutability::Mut);
 
                 if !is_allowed && self.place_may_escape(place) {
-                    self.check_op(ops::EscapingMutBorrow(if matches!(rvalue, Rvalue::Ref(..)) {
-                        hir::BorrowKind::Ref
-                    } else {
-                        hir::BorrowKind::Raw
-                    }));
+                    self.check_op(ops::EscapingMutBorrow);
                 }
             }
 
diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs
index f5b7a6066c8..02edff8f632 100644
--- a/compiler/rustc_const_eval/src/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/check_consts/ops.rs
@@ -567,7 +567,7 @@ impl<'tcx> NonConstOp<'tcx> for EscapingCellBorrow {
         DiagImportance::Secondary
     }
     fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
-        ccx.dcx().create_err(errors::InteriorMutableRefEscaping {
+        ccx.dcx().create_err(errors::InteriorMutableBorrowEscaping {
             span,
             opt_help: matches!(ccx.const_kind(), hir::ConstContext::Static(_)),
             kind: ccx.const_kind(),
@@ -580,7 +580,7 @@ impl<'tcx> NonConstOp<'tcx> for EscapingCellBorrow {
 /// This op is for `&mut` borrows in the trailing expression of a constant
 /// which uses the "enclosing scopes rule" to leak its locals into anonymous
 /// static or const items.
-pub(crate) struct EscapingMutBorrow(pub hir::BorrowKind);
+pub(crate) struct EscapingMutBorrow;
 
 impl<'tcx> NonConstOp<'tcx> for EscapingMutBorrow {
     fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
@@ -594,20 +594,11 @@ impl<'tcx> NonConstOp<'tcx> for EscapingMutBorrow {
     }
 
     fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
-        match self.0 {
-            hir::BorrowKind::Raw => ccx.tcx.dcx().create_err(errors::MutableRawEscaping {
-                span,
-                kind: ccx.const_kind(),
-                teach: ccx.tcx.sess.teach(E0764),
-            }),
-            hir::BorrowKind::Ref | hir::BorrowKind::Pin => {
-                ccx.dcx().create_err(errors::MutableRefEscaping {
-                    span,
-                    kind: ccx.const_kind(),
-                    teach: ccx.tcx.sess.teach(E0764),
-                })
-            }
-        }
+        ccx.dcx().create_err(errors::MutableBorrowEscaping {
+            span,
+            kind: ccx.const_kind(),
+            teach: ccx.tcx.sess.teach(E0764),
+        })
     }
 }
 
diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs
index 6fd0b9d26e3..73cb1e77436 100644
--- a/compiler/rustc_const_eval/src/const_eval/mod.rs
+++ b/compiler/rustc_const_eval/src/const_eval/mod.rs
@@ -26,13 +26,6 @@ pub(crate) use self::valtrees::{eval_to_valtree, valtree_to_const_value};
 // We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes.
 const VALTREE_MAX_NODES: usize = 100000;
 
-pub(crate) enum ValTreeCreationError<'tcx> {
-    NodesOverflow,
-    /// Values of this type, or this particular value, are not supported as valtrees.
-    NonSupportedType(Ty<'tcx>),
-}
-pub(crate) type ValTreeCreationResult<'tcx> = Result<ty::ValTree<'tcx>, ValTreeCreationError<'tcx>>;
-
 #[instrument(skip(tcx), level = "debug")]
 pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>(
     tcx: TyCtxt<'tcx>,
diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
index 58d230af683..b3d2e1a0598 100644
--- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs
+++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
@@ -1,17 +1,16 @@
 use rustc_abi::{BackendRepr, FieldIdx, VariantIdx};
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId, ReportedErrorInfo};
+use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId, ValTreeCreationError};
 use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::{bug, mir};
 use rustc_span::DUMMY_SP;
 use tracing::{debug, instrument, trace};
 
+use super::VALTREE_MAX_NODES;
 use super::eval_queries::{mk_eval_cx_to_read_const_val, op_to_const};
 use super::machine::CompileTimeInterpCx;
-use super::{VALTREE_MAX_NODES, ValTreeCreationError, ValTreeCreationResult};
 use crate::const_eval::CanAccessMutGlobal;
-use crate::errors::MaxNumNodesInConstErr;
 use crate::interpret::{
     ImmTy, Immediate, InternKind, MPlaceTy, MemPlaceMeta, MemoryKind, PlaceTy, Projectable, Scalar,
     intern_const_alloc_recursive,
@@ -24,7 +23,7 @@ fn branches<'tcx>(
     field_count: usize,
     variant: Option<VariantIdx>,
     num_nodes: &mut usize,
-) -> ValTreeCreationResult<'tcx> {
+) -> EvalToValTreeResult<'tcx> {
     let place = match variant {
         Some(variant) => ecx.project_downcast(place, variant).unwrap(),
         None => place.clone(),
@@ -58,7 +57,7 @@ fn slice_branches<'tcx>(
     ecx: &CompileTimeInterpCx<'tcx>,
     place: &MPlaceTy<'tcx>,
     num_nodes: &mut usize,
-) -> ValTreeCreationResult<'tcx> {
+) -> EvalToValTreeResult<'tcx> {
     let n = place.len(ecx).unwrap_or_else(|_| panic!("expected to use len of place {place:?}"));
 
     let mut elems = Vec::with_capacity(n as usize);
@@ -76,7 +75,7 @@ fn const_to_valtree_inner<'tcx>(
     ecx: &CompileTimeInterpCx<'tcx>,
     place: &MPlaceTy<'tcx>,
     num_nodes: &mut usize,
-) -> ValTreeCreationResult<'tcx> {
+) -> EvalToValTreeResult<'tcx> {
     let tcx = *ecx.tcx;
     let ty = place.layout.ty;
     debug!("ty kind: {:?}", ty.kind());
@@ -91,7 +90,7 @@ fn const_to_valtree_inner<'tcx>(
             Ok(ty::ValTree::zst(tcx))
         }
         ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
-            let val = ecx.read_immediate(place).unwrap();
+            let val = ecx.read_immediate(place).report_err()?;
             let val = val.to_scalar_int().unwrap();
             *num_nodes += 1;
 
@@ -113,7 +112,7 @@ fn const_to_valtree_inner<'tcx>(
             // equality at compile-time (see `ptr_guaranteed_cmp`).
             // However we allow those that are just integers in disguise.
             // First, get the pointer. Remember it might be wide!
-            let val = ecx.read_immediate(place).unwrap();
+            let val = ecx.read_immediate(place).report_err()?;
             // We could allow wide raw pointers where both sides are integers in the future,
             // but for now we reject them.
             if matches!(val.layout.backend_repr, BackendRepr::ScalarPair(..)) {
@@ -134,7 +133,7 @@ fn const_to_valtree_inner<'tcx>(
         ty::FnPtr(..) => Err(ValTreeCreationError::NonSupportedType(ty)),
 
         ty::Ref(_, _, _)  => {
-            let derefd_place = ecx.deref_pointer(place).unwrap();
+            let derefd_place = ecx.deref_pointer(place).report_err()?;
             const_to_valtree_inner(ecx, &derefd_place, num_nodes)
         }
 
@@ -158,7 +157,7 @@ fn const_to_valtree_inner<'tcx>(
                 bug!("uninhabited types should have errored and never gotten converted to valtree")
             }
 
-            let variant = ecx.read_discriminant(place).unwrap();
+            let variant = ecx.read_discriminant(place).report_err()?;
             branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant), num_nodes)
         }
 
@@ -249,24 +248,7 @@ pub(crate) fn eval_to_valtree<'tcx>(
     debug!(?place);
 
     let mut num_nodes = 0;
-    let valtree_result = const_to_valtree_inner(&ecx, &place, &mut num_nodes);
-
-    match valtree_result {
-        Ok(valtree) => Ok(Ok(valtree)),
-        Err(err) => {
-            let did = cid.instance.def_id();
-            let global_const_id = cid.display(tcx);
-            let span = tcx.hir_span_if_local(did);
-            match err {
-                ValTreeCreationError::NodesOverflow => {
-                    let handled =
-                        tcx.dcx().emit_err(MaxNumNodesInConstErr { span, global_const_id });
-                    Err(ReportedErrorInfo::allowed_in_infallible(handled).into())
-                }
-                ValTreeCreationError::NonSupportedType(ty) => Ok(Err(ty)),
-            }
-        }
-    }
+    const_to_valtree_inner(&ecx, &place, &mut num_nodes)
 }
 
 /// Converts a `ValTree` to a `ConstValue`, which is needed after mir
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index 609749554ce..b2c3103c34a 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -93,14 +93,6 @@ pub(crate) struct PanicNonStrErr {
 }
 
 #[derive(Diagnostic)]
-#[diag(const_eval_max_num_nodes_in_const)]
-pub(crate) struct MaxNumNodesInConstErr {
-    #[primary_span]
-    pub span: Option<Span>,
-    pub global_const_id: String,
-}
-
-#[derive(Diagnostic)]
 #[diag(const_eval_unallowed_fn_pointer_call)]
 pub(crate) struct UnallowedFnPointerCall {
     #[primary_span]
@@ -158,8 +150,8 @@ pub(crate) struct UnmarkedIntrinsicExposed {
 }
 
 #[derive(Diagnostic)]
-#[diag(const_eval_mutable_ref_escaping, code = E0764)]
-pub(crate) struct MutableRefEscaping {
+#[diag(const_eval_mutable_borrow_escaping, code = E0764)]
+pub(crate) struct MutableBorrowEscaping {
     #[primary_span]
     pub span: Span,
     pub kind: ConstContext,
@@ -168,15 +160,6 @@ pub(crate) struct MutableRefEscaping {
 }
 
 #[derive(Diagnostic)]
-#[diag(const_eval_mutable_raw_escaping, code = E0764)]
-pub(crate) struct MutableRawEscaping {
-    #[primary_span]
-    pub span: Span,
-    pub kind: ConstContext,
-    #[note(const_eval_teach_note)]
-    pub teach: bool,
-}
-#[derive(Diagnostic)]
 #[diag(const_eval_non_const_fmt_macro_call, code = E0015)]
 pub(crate) struct NonConstFmtMacroCall {
     #[primary_span]
@@ -233,8 +216,8 @@ pub(crate) struct UnallowedInlineAsm {
 }
 
 #[derive(Diagnostic)]
-#[diag(const_eval_interior_mutable_ref_escaping, code = E0492)]
-pub(crate) struct InteriorMutableRefEscaping {
+#[diag(const_eval_interior_mutable_borrow_escaping, code = E0492)]
+pub(crate) struct InteriorMutableBorrowEscaping {
     #[primary_span]
     #[label]
     pub span: Span,
@@ -655,9 +638,8 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
 
             PointerAsInt { .. } => const_eval_validation_pointer_as_int,
             PartialPointer => const_eval_validation_partial_pointer,
-            ConstRefToMutable => const_eval_validation_const_ref_to_mutable,
-            ConstRefToExtern => const_eval_validation_const_ref_to_extern,
             MutableRefToImmutable => const_eval_validation_mutable_ref_to_immutable,
+            MutableRefInConst => const_eval_validation_mutable_ref_in_const,
             NullFnPtr => const_eval_validation_null_fn_ptr,
             NeverVal => const_eval_validation_never_val,
             NullablePtrOutOfRange { .. } => const_eval_validation_nullable_ptr_out_of_range,
@@ -815,9 +797,8 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
                 err.arg("expected_dyn_type", expected_dyn_type.to_string());
             }
             NullPtr { .. }
-            | ConstRefToMutable
-            | ConstRefToExtern
             | MutableRefToImmutable
+            | MutableRefInConst
             | NullFnPtr
             | NeverVal
             | UnsafeCellInImmutable
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index 7d76d925ef2..7e26b7f3d96 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -570,6 +570,8 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
                     };
                     let (size, _align) =
                         global_alloc.size_and_align(*self.ecx.tcx, self.ecx.typing_env);
+                    let alloc_actual_mutbl =
+                        global_alloc.mutability(*self.ecx.tcx, self.ecx.typing_env);
 
                     if let GlobalAlloc::Static(did) = global_alloc {
                         let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else {
@@ -597,9 +599,11 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
                                 skip_recursive_check = !nested;
                             }
                             CtfeValidationMode::Const { .. } => {
-                                // We can't recursively validate `extern static`, so we better reject them.
-                                if self.ecx.tcx.is_foreign_item(did) {
-                                    throw_validation_failure!(self.path, ConstRefToExtern);
+                                // If this is mutable memory or an `extern static`, there's no point in checking it -- we'd
+                                // just get errors trying to read the value.
+                                if alloc_actual_mutbl.is_mut() || self.ecx.tcx.is_foreign_item(did)
+                                {
+                                    skip_recursive_check = true;
                                 }
                             }
                         }
@@ -618,9 +622,6 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
                                 mutbl
                             }
                         };
-                        // Determine what it actually points to.
-                        let alloc_actual_mutbl =
-                            global_alloc.mutability(*self.ecx.tcx, self.ecx.typing_env);
                         // Mutable pointer to immutable memory is no good.
                         if ptr_expected_mutbl == Mutability::Mut
                             && alloc_actual_mutbl == Mutability::Not
@@ -628,12 +629,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
                             // This can actually occur with transmutes.
                             throw_validation_failure!(self.path, MutableRefToImmutable);
                         }
-                        // In a const, everything must be completely immutable.
+                        // In a const, any kind of mutable reference is not good.
                         if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. })) {
-                            if ptr_expected_mutbl == Mutability::Mut
-                                || alloc_actual_mutbl == Mutability::Mut
-                            {
-                                throw_validation_failure!(self.path, ConstRefToMutable);
+                            if ptr_expected_mutbl == Mutability::Mut {
+                                throw_validation_failure!(self.path, MutableRefInConst);
                             }
                         }
                     }
diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl
index 3d27e587b6c..0073deb18da 100644
--- a/compiler/rustc_middle/messages.ftl
+++ b/compiler/rustc_middle/messages.ftl
@@ -78,6 +78,11 @@ middle_erroneous_constant = erroneous constant encountered
 middle_failed_writing_file =
     failed to write file {$path}: {$error}"
 
+# Note: We only mention patterns here since the error can only occur with references, and those
+# are forbidden in const generics.
+middle_invalid_const_in_valtree = constant {$global_const_id} cannot be used as pattern
+    .note = constants that reference mutable or external memory cannot be used as pattern
+
 middle_layout_cycle =
     a cycle occurred during layout computation
 
@@ -95,6 +100,8 @@ middle_layout_too_generic = the type `{$ty}` does not have a fixed layout
 middle_layout_unknown =
     the type `{$ty}` has an unknown layout
 
+middle_max_num_nodes_in_valtree = maximum number of nodes exceeded in constant {$global_const_id}
+
 middle_opaque_hidden_type_mismatch =
     concrete type differs from previous defining opaque type use
     .label = expected `{$self_ty}`, got `{$other_ty}`
diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs
index 6c6b12fed67..f36ae831653 100644
--- a/compiler/rustc_middle/src/error.rs
+++ b/compiler/rustc_middle/src/error.rs
@@ -170,3 +170,20 @@ pub(crate) struct TypeLengthLimit {
     pub path: PathBuf,
     pub type_length: usize,
 }
+
+#[derive(Diagnostic)]
+#[diag(middle_max_num_nodes_in_valtree)]
+pub(crate) struct MaxNumNodesInValtree {
+    #[primary_span]
+    pub span: Span,
+    pub global_const_id: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(middle_invalid_const_in_valtree)]
+#[note]
+pub(crate) struct InvalidConstInValtree {
+    #[primary_span]
+    pub span: Span,
+    pub global_const_id: String,
+}
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index 6ff3cac049b..41a166083d0 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -35,7 +35,7 @@ impl From<ReportedErrorInfo> for ErrorHandled {
 }
 
 impl ErrorHandled {
-    pub fn with_span(self, span: Span) -> Self {
+    pub(crate) fn with_span(self, span: Span) -> Self {
         match self {
             ErrorHandled::Reported(err, _span) => ErrorHandled::Reported(err, span),
             ErrorHandled::TooGeneric(_span) => ErrorHandled::TooGeneric(span),
@@ -94,14 +94,51 @@ impl From<ReportedErrorInfo> for ErrorGuaranteed {
     }
 }
 
+/// An error type for the `const_to_valtree` query. Some error should be reported with a "use-site span",
+/// which means the query cannot emit the error, so those errors are represented as dedicated variants here.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
+pub enum ValTreeCreationError<'tcx> {
+    /// The constant is too big to be valtree'd.
+    NodesOverflow,
+    /// The constant references mutable or external memory, so it cannot be valtree'd.
+    InvalidConst,
+    /// Values of this type, or this particular value, are not supported as valtrees.
+    NonSupportedType(Ty<'tcx>),
+    /// The error has already been handled by const evaluation.
+    ErrorHandled(ErrorHandled),
+}
+
+impl<'tcx> From<ErrorHandled> for ValTreeCreationError<'tcx> {
+    fn from(err: ErrorHandled) -> Self {
+        ValTreeCreationError::ErrorHandled(err)
+    }
+}
+
+impl<'tcx> From<InterpErrorInfo<'tcx>> for ValTreeCreationError<'tcx> {
+    fn from(err: InterpErrorInfo<'tcx>) -> Self {
+        // An error ocurred outside the const-eval query, as part of constructing the valtree. We
+        // don't currently preserve the details of this error, since `InterpErrorInfo` cannot be put
+        // into a query result and it can only be access of some mutable or external memory.
+        let (_kind, backtrace) = err.into_parts();
+        backtrace.print_backtrace();
+        ValTreeCreationError::InvalidConst
+    }
+}
+
+impl<'tcx> ValTreeCreationError<'tcx> {
+    pub(crate) fn with_span(self, span: Span) -> Self {
+        use ValTreeCreationError::*;
+        match self {
+            ErrorHandled(handled) => ErrorHandled(handled.with_span(span)),
+            other => other,
+        }
+    }
+}
+
 pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
 pub type EvalStaticInitializerRawResult<'tcx> = Result<ConstAllocation<'tcx>, ErrorHandled>;
 pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
-/// `Ok(Err(ty))` indicates the constant was fine, but the valtree couldn't be constructed
-/// because the value contains something of type `ty` that is not valtree-compatible.
-/// The caller can then show an appropriate error; the query does not have the
-/// necessary context to give good user-facing errors for this case.
-pub type EvalToValTreeResult<'tcx> = Result<Result<ValTree<'tcx>, Ty<'tcx>>, ErrorHandled>;
+pub type EvalToValTreeResult<'tcx> = Result<ValTree<'tcx>, ValTreeCreationError<'tcx>>;
 
 #[cfg(target_pointer_width = "64")]
 rustc_data_structures::static_assert_size!(InterpErrorInfo<'_>, 8);
@@ -450,10 +487,9 @@ pub enum ValidationErrorKind<'tcx> {
         ptr_kind: PointerKind,
         ty: Ty<'tcx>,
     },
-    ConstRefToMutable,
-    ConstRefToExtern,
     MutableRefToImmutable,
     UnsafeCellInImmutable,
+    MutableRefInConst,
     NullFnPtr,
     NeverVal,
     NullablePtrOutOfRange {
diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs
index c2438af6a1e..ea2f84d46d7 100644
--- a/compiler/rustc_middle/src/mir/interpret/mod.rs
+++ b/compiler/rustc_middle/src/mir/interpret/mod.rs
@@ -38,8 +38,8 @@ pub use self::error::{
     EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, ExpectedKind,
     InterpErrorInfo, InterpErrorKind, InterpResult, InvalidMetaKind, InvalidProgramInfo,
     MachineStopType, Misalignment, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo,
-    ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo,
-    ValidationErrorKind, interp_ok,
+    ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, ValTreeCreationError,
+    ValidationErrorInfo, ValidationErrorKind, interp_ok,
 };
 pub use self::pointer::{CtfeProvenance, Pointer, PointerArithmetic, Provenance};
 pub use self::value::Scalar;
diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs
index 97db45a70d7..e25f35c59c2 100644
--- a/compiler/rustc_middle/src/mir/interpret/queries.rs
+++ b/compiler/rustc_middle/src/mir/interpret/queries.rs
@@ -5,11 +5,11 @@ use rustc_span::{DUMMY_SP, Span};
 use tracing::{debug, instrument};
 
 use super::{
-    ErrorHandled, EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, GlobalId,
-    ReportedErrorInfo,
+    ErrorHandled, EvalToAllocationRawResult, EvalToConstValueResult, GlobalId, ReportedErrorInfo,
 };
-use crate::mir;
-use crate::ty::{self, GenericArgs, TyCtxt, TypeVisitableExt};
+use crate::mir::interpret::ValTreeCreationError;
+use crate::ty::{self, ConstToValTreeResult, GenericArgs, TyCtxt, TypeVisitableExt};
+use crate::{error, mir};
 
 impl<'tcx> TyCtxt<'tcx> {
     /// Evaluates a constant without providing any generic parameters. This is useful to evaluate consts
@@ -92,7 +92,7 @@ impl<'tcx> TyCtxt<'tcx> {
         typing_env: ty::TypingEnv<'tcx>,
         ct: ty::UnevaluatedConst<'tcx>,
         span: Span,
-    ) -> EvalToValTreeResult<'tcx> {
+    ) -> ConstToValTreeResult<'tcx> {
         // Cannot resolve `Unevaluated` constants that contain inference
         // variables. We reject those here since `resolve`
         // would fail otherwise.
@@ -103,47 +103,54 @@ impl<'tcx> TyCtxt<'tcx> {
             bug!("did not expect inference variables here");
         }
 
-        match ty::Instance::try_resolve(self, typing_env, ct.def, ct.args) {
-            Ok(Some(instance)) => {
-                let cid = GlobalId { instance, promoted: None };
-                self.const_eval_global_id_for_typeck(typing_env, cid, span).inspect(|_| {
-                    // We are emitting the lint here instead of in `is_const_evaluatable`
-                    // as we normalize obligations before checking them, and normalization
-                    // uses this function to evaluate this constant.
-                    //
-                    // @lcnr believes that successfully evaluating even though there are
-                    // used generic parameters is a bug of evaluation, so checking for it
-                    // here does feel somewhat sensible.
-                    if !self.features().generic_const_exprs()
-                        && ct.args.has_non_region_param()
-                        // We only FCW for anon consts as repeat expr counts with anon consts are the only place
-                        // that we have a back compat hack for. We don't need to check this is a const argument
-                        // as only anon consts as const args should get evaluated "for the type system".
-                        //
-                        // If we don't *only* FCW anon consts we can wind up incorrectly FCW'ing uses of assoc
-                        // consts in pattern positions. #140447
-                        && self.def_kind(instance.def_id()) == DefKind::AnonConst
-                    {
-                        let mir_body = self.mir_for_ctfe(instance.def_id());
-                        if mir_body.is_polymorphic {
-                            let Some(local_def_id) = ct.def.as_local() else { return };
-                            self.node_span_lint(
-                                lint::builtin::CONST_EVALUATABLE_UNCHECKED,
-                                self.local_def_id_to_hir_id(local_def_id),
-                                self.def_span(ct.def),
-                                |lint| { lint.primary_message("cannot use constants which depend on generic parameters in types"); },
-                            )
-                        }
-                    }
-                })
-            }
+        let cid = match ty::Instance::try_resolve(self, typing_env, ct.def, ct.args) {
+            Ok(Some(instance)) => GlobalId { instance, promoted: None },
             // For errors during resolution, we deliberately do not point at the usage site of the constant,
             // since for these errors the place the constant is used shouldn't matter.
-            Ok(None) => Err(ErrorHandled::TooGeneric(DUMMY_SP)),
+            Ok(None) => return Err(ErrorHandled::TooGeneric(DUMMY_SP).into()),
             Err(err) => {
-                Err(ErrorHandled::Reported(ReportedErrorInfo::non_const_eval_error(err), DUMMY_SP))
+                return Err(ErrorHandled::Reported(
+                    ReportedErrorInfo::non_const_eval_error(err),
+                    DUMMY_SP,
+                )
+                .into());
             }
-        }
+        };
+
+        self.const_eval_global_id_for_typeck(typing_env, cid, span).inspect(|_| {
+            // We are emitting the lint here instead of in `is_const_evaluatable`
+            // as we normalize obligations before checking them, and normalization
+            // uses this function to evaluate this constant.
+            //
+            // @lcnr believes that successfully evaluating even though there are
+            // used generic parameters is a bug of evaluation, so checking for it
+            // here does feel somewhat sensible.
+            if !self.features().generic_const_exprs()
+                && ct.args.has_non_region_param()
+                // We only FCW for anon consts as repeat expr counts with anon consts are the only place
+                // that we have a back compat hack for. We don't need to check this is a const argument
+                // as only anon consts as const args should get evaluated "for the type system".
+                //
+                // If we don't *only* FCW anon consts we can wind up incorrectly FCW'ing uses of assoc
+                // consts in pattern positions. #140447
+                && self.def_kind(cid.instance.def_id()) == DefKind::AnonConst
+            {
+                let mir_body = self.mir_for_ctfe(cid.instance.def_id());
+                if mir_body.is_polymorphic {
+                    let Some(local_def_id) = ct.def.as_local() else { return };
+                    self.node_span_lint(
+                        lint::builtin::CONST_EVALUATABLE_UNCHECKED,
+                        self.local_def_id_to_hir_id(local_def_id),
+                        self.def_span(ct.def),
+                        |lint| {
+                            lint.primary_message(
+                                "cannot use constants which depend on generic parameters in types",
+                            );
+                        },
+                    )
+                }
+            }
+        })
     }
 
     pub fn const_eval_instance(
@@ -182,17 +189,42 @@ impl<'tcx> TyCtxt<'tcx> {
         typing_env: ty::TypingEnv<'tcx>,
         cid: GlobalId<'tcx>,
         span: Span,
-    ) -> EvalToValTreeResult<'tcx> {
+    ) -> ConstToValTreeResult<'tcx> {
         // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should
         // improve caching of queries.
         let inputs =
             self.erase_regions(typing_env.with_post_analysis_normalized(self).as_query_input(cid));
         debug!(?inputs);
-        if !span.is_dummy() {
+        let res = if !span.is_dummy() {
             // The query doesn't know where it is being invoked, so we need to fix the span.
             self.at(span).eval_to_valtree(inputs).map_err(|e| e.with_span(span))
         } else {
             self.eval_to_valtree(inputs)
+        };
+        match res {
+            Ok(valtree) => Ok(Ok(valtree)),
+            Err(err) => {
+                match err {
+                    // Let the caller decide how to handle this.
+                    ValTreeCreationError::NonSupportedType(ty) => Ok(Err(ty)),
+                    // Report the others.
+                    ValTreeCreationError::NodesOverflow => {
+                        let handled = self.dcx().emit_err(error::MaxNumNodesInValtree {
+                            span,
+                            global_const_id: cid.display(self),
+                        });
+                        Err(ReportedErrorInfo::allowed_in_infallible(handled).into())
+                    }
+                    ValTreeCreationError::InvalidConst => {
+                        let handled = self.dcx().emit_err(error::InvalidConstInValtree {
+                            span,
+                            global_const_id: cid.display(self),
+                        });
+                        Err(ReportedErrorInfo::allowed_in_infallible(handled).into())
+                    }
+                    ValTreeCreationError::ErrorHandled(handled) => Err(handled),
+                }
+            }
         }
     }
 }
diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs
index 7c998761a9d..26a31cb055e 100644
--- a/compiler/rustc_middle/src/query/erase.rs
+++ b/compiler/rustc_middle/src/query/erase.rs
@@ -4,6 +4,7 @@ use std::mem::MaybeUninit;
 
 use rustc_span::ErrorGuaranteed;
 
+use crate::mir::interpret::EvalToValTreeResult;
 use crate::query::CyclePlaceholder;
 use crate::ty::adjustment::CoerceUnsizedInfo;
 use crate::ty::{self, Ty, TyCtxt};
@@ -156,10 +157,8 @@ impl EraseType for Result<mir::ConstValue<'_>, mir::interpret::ErrorHandled> {
     type Result = [u8; size_of::<Result<mir::ConstValue<'static>, mir::interpret::ErrorHandled>>()];
 }
 
-impl EraseType for Result<Result<ty::ValTree<'_>, Ty<'_>>, mir::interpret::ErrorHandled> {
-    type Result = [u8; size_of::<
-        Result<Result<ty::ValTree<'static>, Ty<'static>>, mir::interpret::ErrorHandled>,
-    >()];
+impl EraseType for EvalToValTreeResult<'_> {
+    type Result = [u8; size_of::<EvalToValTreeResult<'static>>()];
 }
 
 impl EraseType for Result<&'_ ty::List<Ty<'_>>, ty::util::AlwaysRequiresDrop> {
diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs
index 2f21d19e03c..d95006dcf4a 100644
--- a/compiler/rustc_middle/src/ty/consts/valtree.rs
+++ b/compiler/rustc_middle/src/ty/consts/valtree.rs
@@ -5,7 +5,7 @@ use rustc_data_structures::intern::Interned;
 use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
 
 use super::ScalarInt;
-use crate::mir::interpret::Scalar;
+use crate::mir::interpret::{ErrorHandled, Scalar};
 use crate::ty::{self, Ty, TyCtxt};
 
 /// This datastructure is used to represent the value of constants used in the type system.
@@ -124,6 +124,12 @@ impl fmt::Debug for ValTree<'_> {
     }
 }
 
+/// `Ok(Err(ty))` indicates the constant was fine, but the valtree couldn't be constructed
+/// because the value contains something of type `ty` that is not valtree-compatible.
+/// The caller can then show an appropriate error; the query does not have the
+/// necessary context to give good user-facing errors for this case.
+pub type ConstToValTreeResult<'tcx> = Result<Result<ValTree<'tcx>, Ty<'tcx>>, ErrorHandled>;
+
 /// A type-level constant value.
 ///
 /// Represents a typed, fully evaluated constant.
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 97408e31854..40ea2a23a70 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -75,8 +75,8 @@ pub use self::closure::{
     place_to_string_for_capture,
 };
 pub use self::consts::{
-    AnonConstKind, AtomicOrdering, Const, ConstInt, ConstKind, Expr, ExprKind, ScalarInt,
-    UnevaluatedConst, ValTree, ValTreeKind, Value,
+    AnonConstKind, AtomicOrdering, Const, ConstInt, ConstKind, ConstToValTreeResult, Expr,
+    ExprKind, ScalarInt, UnevaluatedConst, ValTree, ValTreeKind, Value,
 };
 pub use self::context::{
     CtxtInterners, CurrentGcx, DeducedParamAttrs, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt,
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 999ef97683c..43806d3977b 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -36,8 +36,8 @@ use rustc_middle::ty::{
     self, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
     TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypingMode, Upcast,
 };
+use rustc_span::Span;
 use rustc_span::def_id::DefId;
-use rustc_span::{DUMMY_SP, Span};
 use tracing::{debug, instrument};
 
 pub use self::coherence::{
@@ -644,7 +644,9 @@ pub fn try_evaluate_const<'tcx>(
             let erased_uv = tcx.erase_regions(uv);
 
             use rustc_middle::mir::interpret::ErrorHandled;
-            match tcx.const_eval_resolve_for_typeck(typing_env, erased_uv, DUMMY_SP) {
+            // FIXME: `def_span` will point at the definition of this const; ideally, we'd point at
+            // where it gets used as a const generic.
+            match tcx.const_eval_resolve_for_typeck(typing_env, erased_uv, tcx.def_span(uv.def)) {
                 Ok(Ok(val)) => Ok(ty::Const::new_value(
                     tcx,
                     val,
diff --git a/tests/ui/const-generics/generic_const_exprs/unevaluated-const-ice-119731.rs b/tests/ui/const-generics/generic_const_exprs/unevaluated-const-ice-119731.rs
index a55be99fc0b..02a95ed3e90 100644
--- a/tests/ui/const-generics/generic_const_exprs/unevaluated-const-ice-119731.rs
+++ b/tests/ui/const-generics/generic_const_exprs/unevaluated-const-ice-119731.rs
@@ -28,6 +28,10 @@ mod v20 {
     impl<const v10: usize> v17<v10, v2> {
         //~^ ERROR maximum number of nodes exceeded in constant v20::v17::<v10, v2>::{constant#0}
         //~| ERROR maximum number of nodes exceeded in constant v20::v17::<v10, v2>::{constant#0}
+        //~| ERROR maximum number of nodes exceeded in constant v20::v17::<v10, v2>::{constant#0}
+        //~| ERROR maximum number of nodes exceeded in constant v20::v17::<v10, v2>::{constant#0}
+        //~| ERROR maximum number of nodes exceeded in constant v20::v17::<v10, v2>::{constant#0}
+        //~| ERROR maximum number of nodes exceeded in constant v20::v17::<v10, v2>::{constant#0}
         pub const fn v21() -> v18 {
             //~^ ERROR cannot find type `v18` in this scope
             v18 { _p: () }
diff --git a/tests/ui/const-generics/generic_const_exprs/unevaluated-const-ice-119731.stderr b/tests/ui/const-generics/generic_const_exprs/unevaluated-const-ice-119731.stderr
index b73611c79b2..cf0bdd0e9a1 100644
--- a/tests/ui/const-generics/generic_const_exprs/unevaluated-const-ice-119731.stderr
+++ b/tests/ui/const-generics/generic_const_exprs/unevaluated-const-ice-119731.stderr
@@ -1,5 +1,5 @@
 error[E0432]: unresolved import `v20::v13`
-  --> $DIR/unevaluated-const-ice-119731.rs:38:15
+  --> $DIR/unevaluated-const-ice-119731.rs:42:15
    |
 LL | pub use v20::{v13, v17};
    |               ^^^
@@ -23,7 +23,7 @@ LL |         pub const fn v21() -> v18 {}
    |                               ^^^ help: a type alias with a similar name exists: `v11`
 
 error[E0412]: cannot find type `v18` in this scope
-  --> $DIR/unevaluated-const-ice-119731.rs:31:31
+  --> $DIR/unevaluated-const-ice-119731.rs:35:31
    |
 LL |     pub type v11 = [[usize; v4]; v4];
    |     --------------------------------- similarly named type alias `v11` defined here
@@ -32,7 +32,7 @@ LL |         pub const fn v21() -> v18 {
    |                               ^^^ help: a type alias with a similar name exists: `v11`
 
 error[E0422]: cannot find struct, variant or union type `v18` in this scope
-  --> $DIR/unevaluated-const-ice-119731.rs:33:13
+  --> $DIR/unevaluated-const-ice-119731.rs:37:13
    |
 LL |     pub type v11 = [[usize; v4]; v4];
    |     --------------------------------- similarly named type alias `v11` defined here
@@ -86,6 +86,38 @@ LL |     impl<const v10: usize> v17<v10, v2> {
    |
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
+error: maximum number of nodes exceeded in constant v20::v17::<v10, v2>::{constant#0}
+  --> $DIR/unevaluated-const-ice-119731.rs:28:37
+   |
+LL |     impl<const v10: usize> v17<v10, v2> {
+   |                                     ^^
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: maximum number of nodes exceeded in constant v20::v17::<v10, v2>::{constant#0}
+  --> $DIR/unevaluated-const-ice-119731.rs:28:37
+   |
+LL |     impl<const v10: usize> v17<v10, v2> {
+   |                                     ^^
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: maximum number of nodes exceeded in constant v20::v17::<v10, v2>::{constant#0}
+  --> $DIR/unevaluated-const-ice-119731.rs:28:37
+   |
+LL |     impl<const v10: usize> v17<v10, v2> {
+   |                                     ^^
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: maximum number of nodes exceeded in constant v20::v17::<v10, v2>::{constant#0}
+  --> $DIR/unevaluated-const-ice-119731.rs:28:37
+   |
+LL |     impl<const v10: usize> v17<v10, v2> {
+   |                                     ^^
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
 error[E0592]: duplicate definitions with name `v21`
   --> $DIR/unevaluated-const-ice-119731.rs:23:9
    |
@@ -95,7 +127,7 @@ LL |         pub const fn v21() -> v18 {}
 LL |         pub const fn v21() -> v18 {
    |         ------------------------- other definition for `v21`
 
-error: aborting due to 10 previous errors; 2 warnings emitted
+error: aborting due to 14 previous errors; 2 warnings emitted
 
 Some errors have detailed explanations: E0412, E0422, E0425, E0432, E0592.
 For more information about an error, try `rustc --explain E0412`.
diff --git a/tests/ui/consts/const-mut-refs/issue-76510.rs b/tests/ui/consts/const-mut-refs/issue-76510.rs
index 6ebbd4e50f6..3b248d77a6b 100644
--- a/tests/ui/consts/const-mut-refs/issue-76510.rs
+++ b/tests/ui/consts/const-mut-refs/issue-76510.rs
@@ -1,7 +1,7 @@
 use std::mem::{transmute, ManuallyDrop};
 
 const S: &'static mut str = &mut " hello ";
-//~^ ERROR: mutable references are not allowed in the final value of constants
+//~^ ERROR: mutable borrows of lifetime-extended temporaries
 
 const fn trigger() -> [(); unsafe {
         let s = transmute::<(*const u8, usize), &ManuallyDrop<str>>((S.as_ptr(), 3));
diff --git a/tests/ui/consts/const-mut-refs/issue-76510.stderr b/tests/ui/consts/const-mut-refs/issue-76510.stderr
index aff86e83578..6d5dac268e5 100644
--- a/tests/ui/consts/const-mut-refs/issue-76510.stderr
+++ b/tests/ui/consts/const-mut-refs/issue-76510.stderr
@@ -1,4 +1,4 @@
-error[E0764]: mutable references are not allowed in the final value of constants
+error[E0764]: mutable borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
   --> $DIR/issue-76510.rs:3:29
    |
 LL | const S: &'static mut str = &mut " hello ";
diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs b/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs
index 28facc18886..2af92bd1b9d 100644
--- a/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs
+++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs
@@ -12,13 +12,13 @@ const A: *const i32 = &4;
 // It could be made sound to allow it to compile,
 // but we do not want to allow this to compile,
 // as that would be an enormous footgun in oli-obk's opinion.
-const B: *mut i32 = &mut 4; //~ ERROR mutable references are not allowed
+const B: *mut i32 = &mut 4; //~ ERROR mutable borrows of lifetime-extended temporaries
 
 // Ok, no actual mutable allocation exists
 const B2: Option<&mut i32> = None;
 
 // Not ok, can't prove that no mutable allocation ends up in final value
-const B3: Option<&mut i32> = Some(&mut 42); //~ ERROR mutable references are not allowed
+const B3: Option<&mut i32> = Some(&mut 42); //~ ERROR mutable borrows of lifetime-extended temporaries
 
 const fn helper(x: &mut i32) -> Option<&mut i32> { Some(x) }
 const B4: Option<&mut i32> = helper(&mut 42); //~ ERROR temporary value dropped while borrowed
@@ -26,8 +26,10 @@ const B4: Option<&mut i32> = helper(&mut 42); //~ ERROR temporary value dropped
 // Not ok, since it points to read-only memory.
 const IMMUT_MUT_REF: &mut u16 = unsafe { mem::transmute(&13) };
 //~^ ERROR pointing to read-only memory
+static IMMUT_MUT_REF_STATIC: &mut u16 = unsafe { mem::transmute(&13) };
+//~^ ERROR pointing to read-only memory
 
-// Ok, because no references to mutable data exist here, since the `{}` moves
+// Ok, because no borrows of mutable data exist here, since the `{}` moves
 // its value and then takes a reference to that.
 const C: *const i32 = &{
     let mut x = 42;
@@ -67,13 +69,13 @@ unsafe impl<T> Sync for SyncPtr<T> {}
 // (This relies on `SyncPtr` being a curly brace struct.)
 // However, we intern the inner memory as read-only, so this must be rejected.
 static RAW_MUT_CAST_S: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
-//~^ ERROR mutable references are not allowed
+//~^ ERROR mutable borrows of lifetime-extended temporaries
 static RAW_MUT_COERCE_S: SyncPtr<i32> = SyncPtr { x: &mut 0 };
-//~^ ERROR mutable references are not allowed
+//~^ ERROR mutable borrows of lifetime-extended temporaries
 const RAW_MUT_CAST_C: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
-//~^ ERROR mutable references are not allowed
+//~^ ERROR mutable borrows of lifetime-extended temporaries
 const RAW_MUT_COERCE_C: SyncPtr<i32> = SyncPtr { x: &mut 0 };
-//~^ ERROR mutable references are not allowed
+//~^ ERROR mutable borrows of lifetime-extended temporaries
 
 fn main() {
     println!("{}", unsafe { *A });
diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr b/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr
index 122e5c1bdf0..a55ccf71bc4 100644
--- a/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr
+++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr
@@ -1,10 +1,10 @@
-error[E0764]: mutable references are not allowed in the final value of constants
+error[E0764]: mutable borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
   --> $DIR/mut_ref_in_final.rs:15:21
    |
 LL | const B: *mut i32 = &mut 4;
    |                     ^^^^^^
 
-error[E0764]: mutable references are not allowed in the final value of constants
+error[E0764]: mutable borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
   --> $DIR/mut_ref_in_final.rs:21:35
    |
 LL | const B3: Option<&mut i32> = Some(&mut 42);
@@ -31,8 +31,19 @@ LL | const IMMUT_MUT_REF: &mut u16 = unsafe { mem::transmute(&13) };
                HEX_DUMP
            }
 
+error[E0080]: constructing invalid value: encountered mutable reference or box pointing to read-only memory
+  --> $DIR/mut_ref_in_final.rs:29:1
+   |
+LL | static IMMUT_MUT_REF_STATIC: &mut u16 = unsafe { mem::transmute(&13) };
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+   = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
+               HEX_DUMP
+           }
+
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/mut_ref_in_final.rs:50:65
+  --> $DIR/mut_ref_in_final.rs:52:65
    |
 LL | const FOO: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42));
    |                                  -------------------------------^^--
@@ -42,7 +53,7 @@ LL | const FOO: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42));
    |                                  using this value as a constant requires that borrow lasts for `'static`
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/mut_ref_in_final.rs:53:67
+  --> $DIR/mut_ref_in_final.rs:55:67
    |
 LL | static FOO2: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42));
    |                                    -------------------------------^^--
@@ -52,7 +63,7 @@ LL | static FOO2: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42));
    |                                    using this value as a static requires that borrow lasts for `'static`
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/mut_ref_in_final.rs:56:71
+  --> $DIR/mut_ref_in_final.rs:58:71
    |
 LL | static mut FOO3: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42));
    |                                        -------------------------------^^--
@@ -61,31 +72,31 @@ LL | static mut FOO3: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42));
    |                                        |                              creates a temporary value which is freed while still in use
    |                                        using this value as a static requires that borrow lasts for `'static`
 
-error[E0764]: mutable references are not allowed in the final value of statics
-  --> $DIR/mut_ref_in_final.rs:69:53
+error[E0764]: mutable borrows of lifetime-extended temporaries in the top-level scope of a static are not allowed
+  --> $DIR/mut_ref_in_final.rs:71:53
    |
 LL | static RAW_MUT_CAST_S: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
    |                                                     ^^^^^^^
 
-error[E0764]: mutable references are not allowed in the final value of statics
-  --> $DIR/mut_ref_in_final.rs:71:54
+error[E0764]: mutable borrows of lifetime-extended temporaries in the top-level scope of a static are not allowed
+  --> $DIR/mut_ref_in_final.rs:73:54
    |
 LL | static RAW_MUT_COERCE_S: SyncPtr<i32> = SyncPtr { x: &mut 0 };
    |                                                      ^^^^^^
 
-error[E0764]: mutable references are not allowed in the final value of constants
-  --> $DIR/mut_ref_in_final.rs:73:52
+error[E0764]: mutable borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
+  --> $DIR/mut_ref_in_final.rs:75:52
    |
 LL | const RAW_MUT_CAST_C: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
    |                                                    ^^^^^^^
 
-error[E0764]: mutable references are not allowed in the final value of constants
-  --> $DIR/mut_ref_in_final.rs:75:53
+error[E0764]: mutable borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
+  --> $DIR/mut_ref_in_final.rs:77:53
    |
 LL | const RAW_MUT_COERCE_C: SyncPtr<i32> = SyncPtr { x: &mut 0 };
    |                                                     ^^^^^^
 
-error: aborting due to 11 previous errors
+error: aborting due to 12 previous errors
 
 Some errors have detailed explanations: E0080, E0716, E0764.
 For more information about an error, try `rustc --explain E0080`.
diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs
index 2707e8a14ec..1ae901f1653 100644
--- a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs
+++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs
@@ -16,7 +16,7 @@ static mut BUFFER: i32 = 42;
 const fn helper() -> Option<&'static mut i32> { unsafe {
     Some(&mut *std::ptr::addr_of_mut!(BUFFER))
 } }
-const MUT: Option<&mut i32> = helper(); //~ ERROR encountered reference to mutable
+const MUT: Option<&mut i32> = helper(); //~ ERROR encountered mutable reference
 
 const fn helper_int2ptr() -> Option<&'static mut i32> { unsafe {
     // Undefined behaviour (integer as pointer), who doesn't love tests like this.
diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr
index 6456587b77a..302e342bce6 100644
--- a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr
+++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr
@@ -1,4 +1,4 @@
-error[E0080]: constructing invalid value at .<enum-variant(Some)>.0: encountered reference to mutable memory in `const`
+error[E0080]: constructing invalid value at .<enum-variant(Some)>.0: encountered mutable reference in `const` value
   --> $DIR/mut_ref_in_final_dynamic_check.rs:19:1
    |
 LL | const MUT: Option<&mut i32> = helper();
diff --git a/tests/ui/consts/const-promoted-opaque.atomic.stderr b/tests/ui/consts/const-promoted-opaque.atomic.stderr
index 9c0c969d586..337bfa4137a 100644
--- a/tests/ui/consts/const-promoted-opaque.atomic.stderr
+++ b/tests/ui/consts/const-promoted-opaque.atomic.stderr
@@ -7,11 +7,11 @@ LL |
 LL | };
    | - value is dropped here
 
-error[E0492]: constants cannot refer to interior mutable data
+error[E0492]: interior mutable shared borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
   --> $DIR/const-promoted-opaque.rs:36:19
    |
 LL | const BAZ: &Foo = &FOO;
-   |                   ^^^^ this borrow of an interior mutable value may end up in the final value
+   |                   ^^^^ this borrow of an interior mutable value refers to a lifetime-extended temporary
 
 error[E0716]: temporary value dropped while borrowed
   --> $DIR/const-promoted-opaque.rs:40:26
diff --git a/tests/ui/consts/const-promoted-opaque.rs b/tests/ui/consts/const-promoted-opaque.rs
index 188dacd1003..9ad2b108ca7 100644
--- a/tests/ui/consts/const-promoted-opaque.rs
+++ b/tests/ui/consts/const-promoted-opaque.rs
@@ -34,7 +34,7 @@ const BAR: () = {
 };
 
 const BAZ: &Foo = &FOO;
-//[atomic]~^ ERROR: constants cannot refer to interior mutable data
+//[atomic]~^ ERROR: interior mutable shared borrows of lifetime-extended temporaries
 
 fn main() {
     let _: &'static _ = &FOO;
diff --git a/tests/ui/consts/const-size_of-cycle.stderr b/tests/ui/consts/const-size_of-cycle.stderr
index bf17d76a092..b127f83d885 100644
--- a/tests/ui/consts/const-size_of-cycle.stderr
+++ b/tests/ui/consts/const-size_of-cycle.stderr
@@ -11,13 +11,17 @@ LL |     bytes: [u8; std::mem::size_of::<Foo>()]
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: ...which requires computing layout of `Foo`...
    = note: ...which requires computing layout of `[u8; std::mem::size_of::<Foo>()]`...
-   = note: ...which requires normalizing `[u8; std::mem::size_of::<Foo>()]`...
+note: ...which requires normalizing `[u8; std::mem::size_of::<Foo>()]`...
+  --> $DIR/const-size_of-cycle.rs:2:17
+   |
+LL |     bytes: [u8; std::mem::size_of::<Foo>()]
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: ...which again requires evaluating type-level constant, completing the cycle
 note: cycle used when checking that `Foo` is well-formed
-  --> $DIR/const-size_of-cycle.rs:1:1
+  --> $DIR/const-size_of-cycle.rs:2:17
    |
-LL | struct Foo {
-   | ^^^^^^^^^^
+LL |     bytes: [u8; std::mem::size_of::<Foo>()]
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
 
 error: aborting due to 1 previous error
diff --git a/tests/ui/consts/const_refs_to_static.rs b/tests/ui/consts/const_refs_to_static.rs
index 3c59697e8ed..187fab86a89 100644
--- a/tests/ui/consts/const_refs_to_static.rs
+++ b/tests/ui/consts/const_refs_to_static.rs
@@ -1,4 +1,5 @@
 //@ run-pass
+use std::sync::atomic::AtomicU32;
 
 static S: i32 = 0;
 static mut S_MUT: i32 = 0;
@@ -10,9 +11,13 @@ const C1_READ: () = {
 };
 const C2: *const i32 = std::ptr::addr_of!(S_MUT);
 
+static FOO: AtomicU32 = AtomicU32::new(0);
+const NOT_VALID_AS_PATTERN: &'static AtomicU32 = &FOO;
+
 fn main() {
     assert_eq!(*C1, 0);
     assert_eq!(unsafe { *C2 }, 0);
     // Computing this pattern will read from an immutable static. That's fine.
     assert!(matches!(&0, C1));
+    let _val = NOT_VALID_AS_PATTERN;
 }
diff --git a/tests/ui/consts/const_refs_to_static_fail.rs b/tests/ui/consts/const_refs_to_static_fail.rs
index b8bab91e005..5bb9ca0a65e 100644
--- a/tests/ui/consts/const_refs_to_static_fail.rs
+++ b/tests/ui/consts/const_refs_to_static_fail.rs
@@ -9,13 +9,23 @@ use std::cell::SyncUnsafeCell;
 static S: SyncUnsafeCell<i32> = SyncUnsafeCell::new(0);
 static mut S_MUT: i32 = 0;
 
-const C1: &SyncUnsafeCell<i32> = &S; //~ERROR encountered reference to mutable memory
+const C1: &SyncUnsafeCell<i32> = &S;
 const C1_READ: () = unsafe {
-    assert!(*C1.get() == 0);
+    assert!(*C1.get() == 0); //~ERROR constant accesses mutable global memory
 };
 const C2: *const i32 = unsafe { std::ptr::addr_of!(S_MUT) };
 const C2_READ: () = unsafe {
     assert!(*C2 == 0); //~ERROR constant accesses mutable global memory
 };
 
-fn main() {}
+const BAD_PATTERN: &i32 = {
+    static mut S: i32 = 0;
+    unsafe { &mut S }
+};
+
+fn main() {
+    match &0 {
+        BAD_PATTERN => {}, //~ ERROR cannot be used as pattern
+        _ => {},
+    }
+}
diff --git a/tests/ui/consts/const_refs_to_static_fail.stderr b/tests/ui/consts/const_refs_to_static_fail.stderr
index 86d6c11dc0c..c567b3e0ce1 100644
--- a/tests/ui/consts/const_refs_to_static_fail.stderr
+++ b/tests/ui/consts/const_refs_to_static_fail.stderr
@@ -1,19 +1,8 @@
-error[E0080]: constructing invalid value: encountered reference to mutable memory in `const`
-  --> $DIR/const_refs_to_static_fail.rs:12:1
-   |
-LL | const C1: &SyncUnsafeCell<i32> = &S;
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
-   |
-   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
-   = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
-               HEX_DUMP
-           }
-
-note: erroneous constant encountered
-  --> $DIR/const_refs_to_static_fail.rs:14:14
+error[E0080]: constant accesses mutable global memory
+  --> $DIR/const_refs_to_static_fail.rs:14:13
    |
 LL |     assert!(*C1.get() == 0);
-   |              ^^
+   |             ^^^^^^^^^ evaluation of `C1_READ` failed here
 
 error[E0080]: constant accesses mutable global memory
   --> $DIR/const_refs_to_static_fail.rs:18:13
@@ -21,6 +10,14 @@ error[E0080]: constant accesses mutable global memory
 LL |     assert!(*C2 == 0);
    |             ^^^ evaluation of `C2_READ` failed here
 
-error: aborting due to 2 previous errors
+error: constant BAD_PATTERN cannot be used as pattern
+  --> $DIR/const_refs_to_static_fail.rs:28:9
+   |
+LL |         BAD_PATTERN => {},
+   |         ^^^^^^^^^^^
+   |
+   = note: constants that reference mutable or external memory cannot be used as pattern
+
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/tests/ui/consts/const_refs_to_static_fail_invalid.rs b/tests/ui/consts/const_refs_to_static_fail_invalid.rs
index 34ed8540f3f..229b9fdcc60 100644
--- a/tests/ui/consts/const_refs_to_static_fail_invalid.rs
+++ b/tests/ui/consts/const_refs_to_static_fail_invalid.rs
@@ -23,11 +23,10 @@ fn extern_() {
     }
 
     const C: &i8 = unsafe { &S };
-    //~^ERROR: `extern` static
 
     // This must be rejected here (or earlier), since the pattern cannot be read.
     match &0 {
-        C => {} // ok, `const` already emitted an error
+        C => {} //~ ERROR cannot be used as pattern
         _ => {}
     }
 }
@@ -36,12 +35,11 @@ fn mutable() {
     static mut S_MUT: i32 = 0;
 
     const C: &i32 = unsafe { &S_MUT };
-    //~^ERROR: encountered reference to mutable memory
 
     // This *must not build*, the constant we are matching against
     // could change its value!
     match &42 {
-        C => {} // ok, `const` already emitted an error
+        C => {} //~ ERROR cannot be used as pattern
         _ => {}
     }
 }
diff --git a/tests/ui/consts/const_refs_to_static_fail_invalid.stderr b/tests/ui/consts/const_refs_to_static_fail_invalid.stderr
index 8a034aa00bc..8be8b4bc50f 100644
--- a/tests/ui/consts/const_refs_to_static_fail_invalid.stderr
+++ b/tests/ui/consts/const_refs_to_static_fail_invalid.stderr
@@ -9,27 +9,21 @@ LL |     const C: &bool = unsafe { std::mem::transmute(&S) };
                HEX_DUMP
            }
 
-error[E0080]: constructing invalid value: encountered reference to `extern` static in `const`
-  --> $DIR/const_refs_to_static_fail_invalid.rs:25:5
+error: constant extern_::C cannot be used as pattern
+  --> $DIR/const_refs_to_static_fail_invalid.rs:29:9
    |
-LL |     const C: &i8 = unsafe { &S };
-   |     ^^^^^^^^^^^^ it is undefined behavior to use this value
+LL |         C => {}
+   |         ^
    |
-   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
-   = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
-               HEX_DUMP
-           }
+   = note: constants that reference mutable or external memory cannot be used as pattern
 
-error[E0080]: constructing invalid value: encountered reference to mutable memory in `const`
-  --> $DIR/const_refs_to_static_fail_invalid.rs:38:5
+error: constant mutable::C cannot be used as pattern
+  --> $DIR/const_refs_to_static_fail_invalid.rs:42:9
    |
-LL |     const C: &i32 = unsafe { &S_MUT };
-   |     ^^^^^^^^^^^^^ it is undefined behavior to use this value
+LL |         C => {}
+   |         ^
    |
-   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
-   = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
-               HEX_DUMP
-           }
+   = note: constants that reference mutable or external memory cannot be used as pattern
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/consts/issue-17718-const-bad-values.rs b/tests/ui/consts/issue-17718-const-bad-values.rs
index 40fc02cf644..ae101927fd4 100644
--- a/tests/ui/consts/issue-17718-const-bad-values.rs
+++ b/tests/ui/consts/issue-17718-const-bad-values.rs
@@ -5,10 +5,10 @@
 #![allow(static_mut_refs)]
 
 const C1: &'static mut [usize] = &mut [];
-//~^ ERROR: mutable references are not allowed
+//~^ ERROR: mutable borrows of lifetime-extended temporaries
 
 static mut S: i32 = 3;
 const C2: &'static mut i32 = unsafe { &mut S };
-//~^ ERROR: reference to mutable memory
+//~^ ERROR: encountered mutable reference
 
 fn main() {}
diff --git a/tests/ui/consts/issue-17718-const-bad-values.stderr b/tests/ui/consts/issue-17718-const-bad-values.stderr
index effb614b15b..f0b02bdbc45 100644
--- a/tests/ui/consts/issue-17718-const-bad-values.stderr
+++ b/tests/ui/consts/issue-17718-const-bad-values.stderr
@@ -1,10 +1,10 @@
-error[E0764]: mutable references are not allowed in the final value of constants
+error[E0764]: mutable borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
   --> $DIR/issue-17718-const-bad-values.rs:7:34
    |
 LL | const C1: &'static mut [usize] = &mut [];
    |                                  ^^^^^^^
 
-error[E0080]: constructing invalid value: encountered reference to mutable memory in `const`
+error[E0080]: constructing invalid value: encountered mutable reference in `const` value
   --> $DIR/issue-17718-const-bad-values.rs:11:1
    |
 LL | const C2: &'static mut i32 = unsafe { &mut S };
diff --git a/tests/ui/consts/issue-17718-const-borrow.rs b/tests/ui/consts/issue-17718-const-borrow.rs
index 89316dbd5c4..541d27965f2 100644
--- a/tests/ui/consts/issue-17718-const-borrow.rs
+++ b/tests/ui/consts/issue-17718-const-borrow.rs
@@ -2,13 +2,13 @@ use std::cell::UnsafeCell;
 
 const A: UnsafeCell<usize> = UnsafeCell::new(1);
 const B: &'static UnsafeCell<usize> = &A;
-//~^ ERROR: cannot refer to interior mutable
+//~^ ERROR: interior mutable shared borrows of lifetime-extended temporaries
 
 struct C { a: UnsafeCell<usize> }
 const D: C = C { a: UnsafeCell::new(1) };
 const E: &'static UnsafeCell<usize> = &D.a;
-//~^ ERROR: cannot refer to interior mutable
+//~^ ERROR: interior mutable shared borrows of lifetime-extended temporaries
 const F: &'static C = &D;
-//~^ ERROR: cannot refer to interior mutable
+//~^ ERROR: interior mutable shared borrows of lifetime-extended temporaries
 
 fn main() {}
diff --git a/tests/ui/consts/issue-17718-const-borrow.stderr b/tests/ui/consts/issue-17718-const-borrow.stderr
index e3ff6c923ad..962a81e70b3 100644
--- a/tests/ui/consts/issue-17718-const-borrow.stderr
+++ b/tests/ui/consts/issue-17718-const-borrow.stderr
@@ -1,20 +1,20 @@
-error[E0492]: constants cannot refer to interior mutable data
+error[E0492]: interior mutable shared borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
   --> $DIR/issue-17718-const-borrow.rs:4:39
    |
 LL | const B: &'static UnsafeCell<usize> = &A;
-   |                                       ^^ this borrow of an interior mutable value may end up in the final value
+   |                                       ^^ this borrow of an interior mutable value refers to a lifetime-extended temporary
 
-error[E0492]: constants cannot refer to interior mutable data
+error[E0492]: interior mutable shared borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
   --> $DIR/issue-17718-const-borrow.rs:9:39
    |
 LL | const E: &'static UnsafeCell<usize> = &D.a;
-   |                                       ^^^^ this borrow of an interior mutable value may end up in the final value
+   |                                       ^^^^ this borrow of an interior mutable value refers to a lifetime-extended temporary
 
-error[E0492]: constants cannot refer to interior mutable data
+error[E0492]: interior mutable shared borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
   --> $DIR/issue-17718-const-borrow.rs:11:23
    |
 LL | const F: &'static C = &D;
-   |                       ^^ this borrow of an interior mutable value may end up in the final value
+   |                       ^^ this borrow of an interior mutable value refers to a lifetime-extended temporary
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/consts/issue-44415.stderr b/tests/ui/consts/issue-44415.stderr
index 641945fce9f..0e3f2e6199f 100644
--- a/tests/ui/consts/issue-44415.stderr
+++ b/tests/ui/consts/issue-44415.stderr
@@ -11,13 +11,17 @@ LL |     bytes: [u8; unsafe { intrinsics::size_of::<Foo>() }],
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: ...which requires computing layout of `Foo`...
    = note: ...which requires computing layout of `[u8; unsafe { intrinsics::size_of::<Foo>() }]`...
-   = note: ...which requires normalizing `[u8; unsafe { intrinsics::size_of::<Foo>() }]`...
+note: ...which requires normalizing `[u8; unsafe { intrinsics::size_of::<Foo>() }]`...
+  --> $DIR/issue-44415.rs:6:17
+   |
+LL |     bytes: [u8; unsafe { intrinsics::size_of::<Foo>() }],
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: ...which again requires evaluating type-level constant, completing the cycle
 note: cycle used when checking that `Foo` is well-formed
-  --> $DIR/issue-44415.rs:5:1
+  --> $DIR/issue-44415.rs:6:17
    |
-LL | struct Foo {
-   | ^^^^^^^^^^
+LL |     bytes: [u8; unsafe { intrinsics::size_of::<Foo>() }],
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
 
 error: aborting due to 1 previous error
diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static.rs b/tests/ui/consts/miri_unleashed/const_refers_to_static.rs
index 6cc67094346..eb78b5335cb 100644
--- a/tests/ui/consts/miri_unleashed/const_refers_to_static.rs
+++ b/tests/ui/consts/miri_unleashed/const_refers_to_static.rs
@@ -20,7 +20,7 @@ static mut MUTABLE: u32 = 0;
 const READ_MUT: u32 = unsafe { MUTABLE }; //~ERROR constant accesses mutable global memory
 
 // Evaluating this does not read anything mutable, but validation does, so this should error.
-const REF_INTERIOR_MUT: &usize = { //~ ERROR encountered reference to mutable memory
+const REF_INTERIOR_MUT: &usize = {
     static FOO: AtomicUsize = AtomicUsize::new(0);
     unsafe { &*(&FOO as *const _ as *const usize) }
 };
@@ -30,6 +30,13 @@ static MY_STATIC: u8 = 4;
 const REF_IMMUT: &u8 = &MY_STATIC;
 const READ_IMMUT: u8 = *REF_IMMUT;
 
+fn foo() {
+    match &0 {
+        REF_INTERIOR_MUT => {}, //~ ERROR cannot be used as pattern
+        _ => {},
+    }
+}
+
 fn main() {}
 
 //~? WARN skipping const checks
diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static.stderr b/tests/ui/consts/miri_unleashed/const_refers_to_static.stderr
index eed3b4d9065..6b70a211a72 100644
--- a/tests/ui/consts/miri_unleashed/const_refers_to_static.stderr
+++ b/tests/ui/consts/miri_unleashed/const_refers_to_static.stderr
@@ -16,16 +16,13 @@ error[E0080]: constant accesses mutable global memory
 LL | const READ_MUT: u32 = unsafe { MUTABLE };
    |                                ^^^^^^^ evaluation of `READ_MUT` failed here
 
-error[E0080]: constructing invalid value: encountered reference to mutable memory in `const`
-  --> $DIR/const_refers_to_static.rs:23:1
+error: constant REF_INTERIOR_MUT cannot be used as pattern
+  --> $DIR/const_refers_to_static.rs:35:9
    |
-LL | const REF_INTERIOR_MUT: &usize = {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
+LL |         REF_INTERIOR_MUT => {},
+   |         ^^^^^^^^^^^^^^^^
    |
-   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
-   = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
-               HEX_DUMP
-           }
+   = note: constants that reference mutable or external memory cannot be used as pattern
 
 warning: skipping const checks
    |
diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs
index 6c7e7835661..cb093305429 100644
--- a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs
+++ b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs
@@ -11,18 +11,15 @@ extern crate static_cross_crate;
 // Sneaky: reference to a mutable static.
 // Allowing this would be a disaster for pattern matching, we could violate exhaustiveness checking!
 const SLICE_MUT: &[u8; 1] = {
-    //~^ ERROR encountered reference to mutable memory
     unsafe { &static_cross_crate::ZERO }
 };
 
 const U8_MUT: &u8 = {
-    //~^ ERROR encountered reference to mutable memory
     unsafe { &static_cross_crate::ZERO[0] }
 };
 
 // Also test indirection that reads from other static.
 const U8_MUT2: &u8 = {
-    //~^ ERROR encountered reference to mutable memory
     unsafe { &(*static_cross_crate::ZERO_REF)[0] }
 };
 const U8_MUT3: &u8 = {
@@ -37,14 +34,14 @@ const U8_MUT3: &u8 = {
 
 pub fn test(x: &[u8; 1]) -> bool {
     match x {
-        SLICE_MUT => true, // ok, `const` error already emitted
+        SLICE_MUT => true, //~ ERROR cannot be used as pattern
         &[1..] => false,
     }
 }
 
 pub fn test2(x: &u8) -> bool {
     match x {
-        U8_MUT => true, // ok, `const` error already emitted
+        U8_MUT => true, //~ ERROR cannot be used as pattern
         &(1..) => false,
     }
 }
@@ -53,7 +50,7 @@ pub fn test2(x: &u8) -> bool {
 // the errors above otherwise stop compilation too early?
 pub fn test3(x: &u8) -> bool {
     match x {
-        U8_MUT2 => true, // ok, `const` error already emitted
+        U8_MUT2 => true, //~ ERROR cannot be used as pattern
         &(1..) => false,
     }
 }
diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr
index 8af3a1948f0..d753506cc94 100644
--- a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr
+++ b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr
@@ -1,41 +1,32 @@
-error[E0080]: constructing invalid value: encountered reference to mutable memory in `const`
-  --> $DIR/const_refers_to_static_cross_crate.rs:13:1
-   |
-LL | const SLICE_MUT: &[u8; 1] = {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
+error[E0080]: constant accesses mutable global memory
+  --> $DIR/const_refers_to_static_cross_crate.rs:27:15
    |
-   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
-   = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
-               HEX_DUMP
-           }
+LL |         match static_cross_crate::OPT_ZERO {
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `U8_MUT3` failed here
 
-error[E0080]: constructing invalid value: encountered reference to mutable memory in `const`
-  --> $DIR/const_refers_to_static_cross_crate.rs:18:1
+error: constant SLICE_MUT cannot be used as pattern
+  --> $DIR/const_refers_to_static_cross_crate.rs:37:9
    |
-LL | const U8_MUT: &u8 = {
-   | ^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
+LL |         SLICE_MUT => true,
+   |         ^^^^^^^^^
    |
-   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
-   = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
-               HEX_DUMP
-           }
+   = note: constants that reference mutable or external memory cannot be used as pattern
 
-error[E0080]: constructing invalid value: encountered reference to mutable memory in `const`
-  --> $DIR/const_refers_to_static_cross_crate.rs:24:1
+error: constant U8_MUT cannot be used as pattern
+  --> $DIR/const_refers_to_static_cross_crate.rs:44:9
    |
-LL | const U8_MUT2: &u8 = {
-   | ^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
+LL |         U8_MUT => true,
+   |         ^^^^^^
    |
-   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
-   = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
-               HEX_DUMP
-           }
+   = note: constants that reference mutable or external memory cannot be used as pattern
 
-error[E0080]: constant accesses mutable global memory
-  --> $DIR/const_refers_to_static_cross_crate.rs:30:15
+error: constant U8_MUT2 cannot be used as pattern
+  --> $DIR/const_refers_to_static_cross_crate.rs:53:9
    |
-LL |         match static_cross_crate::OPT_ZERO {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `U8_MUT3` failed here
+LL |         U8_MUT2 => true,
+   |         ^^^^^^^
+   |
+   = note: constants that reference mutable or external memory cannot be used as pattern
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/consts/miri_unleashed/mutable_references.rs b/tests/ui/consts/miri_unleashed/mutable_references.rs
index 63d243f892c..2e95393ccbf 100644
--- a/tests/ui/consts/miri_unleashed/mutable_references.rs
+++ b/tests/ui/consts/miri_unleashed/mutable_references.rs
@@ -26,7 +26,7 @@ const BLUNT: &mut i32 = &mut 42;
 //~^ ERROR: pointing to read-only memory
 
 const SUBTLE: &mut i32 = unsafe {
-    //~^ ERROR: constructing invalid value: encountered reference to mutable memory in `const`
+    //~^ ERROR: encountered mutable reference
     static mut STATIC: i32 = 0;
     &mut STATIC
 };
@@ -65,7 +65,10 @@ static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as
 // # Check for consts pointing to mutable memory
 
 static mut MUTABLE: i32 = 42;
-const POINTS_TO_MUTABLE: &i32 = unsafe { &MUTABLE }; //~ ERROR encountered reference to mutable memory
+const POINTS_TO_MUTABLE: &i32 = unsafe { &MUTABLE }; // OK, as long as it is not used as a pattern.
+
+// This fails since `&*MUTABLE_REF` is basically a copy of `MUTABLE_REF`, but we
+// can't read from that static as it is mutable.
 static mut MUTABLE_REF: &mut i32 = &mut 42;
 const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF };
 //~^ ERROR accesses mutable global memory
diff --git a/tests/ui/consts/miri_unleashed/mutable_references.stderr b/tests/ui/consts/miri_unleashed/mutable_references.stderr
index 22860e4f6d9..137efde44b3 100644
--- a/tests/ui/consts/miri_unleashed/mutable_references.stderr
+++ b/tests/ui/consts/miri_unleashed/mutable_references.stderr
@@ -43,7 +43,7 @@ LL | const BLUNT: &mut i32 = &mut 42;
                HEX_DUMP
            }
 
-error[E0080]: constructing invalid value: encountered reference to mutable memory in `const`
+error[E0080]: constructing invalid value: encountered mutable reference in `const` value
   --> $DIR/mutable_references.rs:28:1
    |
 LL | const SUBTLE: &mut i32 = unsafe {
@@ -98,49 +98,38 @@ LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const
                HEX_DUMP
            }
 
-error[E0080]: constructing invalid value: encountered reference to mutable memory in `const`
-  --> $DIR/mutable_references.rs:68:1
-   |
-LL | const POINTS_TO_MUTABLE: &i32 = unsafe { &MUTABLE };
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
-   |
-   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
-   = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
-               HEX_DUMP
-           }
-
 error[E0080]: constant accesses mutable global memory
-  --> $DIR/mutable_references.rs:70:43
+  --> $DIR/mutable_references.rs:73:43
    |
 LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF };
    |                                           ^^^^^^^^^^^^^ evaluation of `POINTS_TO_MUTABLE2` failed here
 
 error: encountered mutable pointer in final value of constant
-  --> $DIR/mutable_references.rs:73:1
+  --> $DIR/mutable_references.rs:76:1
    |
 LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: encountered mutable pointer in final value of constant
-  --> $DIR/mutable_references.rs:76:1
+  --> $DIR/mutable_references.rs:79:1
    |
 LL | const POINTS_TO_MUTABLE_INNER2: *const i32 = &mut 42 as *const _;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: encountered mutable pointer in final value of constant
-  --> $DIR/mutable_references.rs:96:1
+  --> $DIR/mutable_references.rs:99:1
    |
 LL | const RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x: &mut 42 as *mut _ as *const _ };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: encountered mutable pointer in final value of constant
-  --> $DIR/mutable_references.rs:99:1
+  --> $DIR/mutable_references.rs:102:1
    |
 LL | const RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0594]: cannot assign to `*OH_YES`, as `OH_YES` is an immutable static item
-  --> $DIR/mutable_references.rs:106:5
+  --> $DIR/mutable_references.rs:109:5
    |
 LL |     *OH_YES = 99;
    |     ^^^^^^^^^^^^ cannot assign
@@ -188,37 +177,37 @@ help: skipping check that does not even have a feature gate
 LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) };
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/mutable_references.rs:73:45
+  --> $DIR/mutable_references.rs:76:45
    |
 LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _;
    |                                             ^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/mutable_references.rs:76:46
+  --> $DIR/mutable_references.rs:79:46
    |
 LL | const POINTS_TO_MUTABLE_INNER2: *const i32 = &mut 42 as *const _;
    |                                              ^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/mutable_references.rs:81:47
+  --> $DIR/mutable_references.rs:84:47
    |
 LL | const INTERIOR_MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _;
    |                                               ^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/mutable_references.rs:93:51
+  --> $DIR/mutable_references.rs:96:51
    |
 LL | const RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
    |                                                   ^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/mutable_references.rs:96:49
+  --> $DIR/mutable_references.rs:99:49
    |
 LL | const RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x: &mut 42 as *mut _ as *const _ };
    |                                                 ^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/mutable_references.rs:99:51
+  --> $DIR/mutable_references.rs:102:51
    |
 LL | const RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
    |                                                   ^^^^^^
 
-error: aborting due to 17 previous errors; 1 warning emitted
+error: aborting due to 16 previous errors; 1 warning emitted
 
 Some errors have detailed explanations: E0080, E0594.
 For more information about an error, try `rustc --explain E0080`.
diff --git a/tests/ui/consts/partial_qualif.rs b/tests/ui/consts/partial_qualif.rs
index 7c28b8b8a62..6770e138eca 100644
--- a/tests/ui/consts/partial_qualif.rs
+++ b/tests/ui/consts/partial_qualif.rs
@@ -3,7 +3,7 @@ use std::cell::Cell;
 const FOO: &(Cell<usize>, bool) = {
     let mut a = (Cell::new(0), false);
     a.1 = true; // sets `qualif(a)` to `qualif(a) | qualif(true)`
-    &{a} //~ ERROR cannot refer to interior mutable
+    &{a} //~ ERROR interior mutable shared borrows of lifetime-extended temporaries
 };
 
 fn main() {}
diff --git a/tests/ui/consts/partial_qualif.stderr b/tests/ui/consts/partial_qualif.stderr
index 05e0eeee133..eb1a388ddee 100644
--- a/tests/ui/consts/partial_qualif.stderr
+++ b/tests/ui/consts/partial_qualif.stderr
@@ -1,8 +1,8 @@
-error[E0492]: constants cannot refer to interior mutable data
+error[E0492]: interior mutable shared borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
   --> $DIR/partial_qualif.rs:6:5
    |
 LL |     &{a}
-   |     ^^^^ this borrow of an interior mutable value may end up in the final value
+   |     ^^^^ this borrow of an interior mutable value refers to a lifetime-extended temporary
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/consts/qualif_overwrite.rs b/tests/ui/consts/qualif_overwrite.rs
index aae4e41ffd7..3b494caa2c1 100644
--- a/tests/ui/consts/qualif_overwrite.rs
+++ b/tests/ui/consts/qualif_overwrite.rs
@@ -7,7 +7,7 @@ use std::cell::Cell;
 const FOO: &Option<Cell<usize>> = {
     let mut a = Some(Cell::new(0));
     a = None; // sets `qualif(a)` to `qualif(a) | qualif(None)`
-    &{a} //~ ERROR cannot refer to interior mutable
+    &{a} //~ ERROR interior mutable shared borrows of lifetime-extended temporaries
 };
 
 fn main() {}
diff --git a/tests/ui/consts/qualif_overwrite.stderr b/tests/ui/consts/qualif_overwrite.stderr
index 976cf7bd79e..92430a89b9e 100644
--- a/tests/ui/consts/qualif_overwrite.stderr
+++ b/tests/ui/consts/qualif_overwrite.stderr
@@ -1,8 +1,8 @@
-error[E0492]: constants cannot refer to interior mutable data
+error[E0492]: interior mutable shared borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
   --> $DIR/qualif_overwrite.rs:10:5
    |
 LL |     &{a}
-   |     ^^^^ this borrow of an interior mutable value may end up in the final value
+   |     ^^^^ this borrow of an interior mutable value refers to a lifetime-extended temporary
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/consts/qualif_overwrite_2.rs b/tests/ui/consts/qualif_overwrite_2.rs
index 1819d9a6d20..e06fd322553 100644
--- a/tests/ui/consts/qualif_overwrite_2.rs
+++ b/tests/ui/consts/qualif_overwrite_2.rs
@@ -5,7 +5,7 @@ use std::cell::Cell;
 const FOO: &Option<Cell<usize>> = {
     let mut a = (Some(Cell::new(0)),);
     a.0 = None; // sets `qualif(a)` to `qualif(a) | qualif(None)`
-    &{a.0} //~ ERROR cannot refer to interior mutable
+    &{a.0} //~ ERROR interior mutable shared borrows of lifetime-extended temporaries
 };
 
 fn main() {}
diff --git a/tests/ui/consts/qualif_overwrite_2.stderr b/tests/ui/consts/qualif_overwrite_2.stderr
index a107c4a5c6d..a994ab61ea7 100644
--- a/tests/ui/consts/qualif_overwrite_2.stderr
+++ b/tests/ui/consts/qualif_overwrite_2.stderr
@@ -1,8 +1,8 @@
-error[E0492]: constants cannot refer to interior mutable data
+error[E0492]: interior mutable shared borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
   --> $DIR/qualif_overwrite_2.rs:8:5
    |
 LL |     &{a.0}
-   |     ^^^^^^ this borrow of an interior mutable value may end up in the final value
+   |     ^^^^^^ this borrow of an interior mutable value refers to a lifetime-extended temporary
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/consts/refs-to-cell-in-final.rs b/tests/ui/consts/refs-to-cell-in-final.rs
index 844b140cff2..28a26f86909 100644
--- a/tests/ui/consts/refs-to-cell-in-final.rs
+++ b/tests/ui/consts/refs-to-cell-in-final.rs
@@ -11,9 +11,9 @@ unsafe impl<T> Sync for SyncPtr<T> {}
 // The resulting constant would pass all validation checks, so it is crucial that this gets rejected
 // by static const checks!
 static RAW_SYNC_S: SyncPtr<Cell<i32>> = SyncPtr { x: &Cell::new(42) };
-//~^ ERROR: cannot refer to interior mutable data
+//~^ ERROR: interior mutable shared borrows of lifetime-extended temporaries
 const RAW_SYNC_C: SyncPtr<Cell<i32>> = SyncPtr { x: &Cell::new(42) };
-//~^ ERROR: cannot refer to interior mutable data
+//~^ ERROR: interior mutable shared borrows of lifetime-extended temporaries
 
 // This one does not get promoted because of `Drop`, and then enters interesting codepaths because
 // as a value it has no interior mutability, but as a type it does. See
@@ -39,7 +39,7 @@ const NONE_EXPLICIT_PROMOTED: &'static Option<Cell<i32>> = {
 
 // Not okay, since we are borrowing something with interior mutability.
 const INTERIOR_MUT_VARIANT: &Option<UnsafeCell<bool>> = &{
-    //~^ERROR: cannot refer to interior mutable data
+    //~^ERROR: interior mutable shared borrows of lifetime-extended temporaries
     let mut x = None;
     assert!(x.is_none());
     x = Some(UnsafeCell::new(false));
diff --git a/tests/ui/consts/refs-to-cell-in-final.stderr b/tests/ui/consts/refs-to-cell-in-final.stderr
index 8d82d94f412..41f7a23852c 100644
--- a/tests/ui/consts/refs-to-cell-in-final.stderr
+++ b/tests/ui/consts/refs-to-cell-in-final.stderr
@@ -1,18 +1,18 @@
-error[E0492]: statics cannot refer to interior mutable data
+error[E0492]: interior mutable shared borrows of lifetime-extended temporaries in the top-level scope of a static are not allowed
   --> $DIR/refs-to-cell-in-final.rs:13:54
    |
 LL | static RAW_SYNC_S: SyncPtr<Cell<i32>> = SyncPtr { x: &Cell::new(42) };
-   |                                                      ^^^^^^^^^^^^^^ this borrow of an interior mutable value may end up in the final value
+   |                                                      ^^^^^^^^^^^^^^ this borrow of an interior mutable value refers to a lifetime-extended temporary
    |
    = help: to fix this, the value can be extracted to a separate `static` item and then referenced
 
-error[E0492]: constants cannot refer to interior mutable data
+error[E0492]: interior mutable shared borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
   --> $DIR/refs-to-cell-in-final.rs:15:53
    |
 LL | const RAW_SYNC_C: SyncPtr<Cell<i32>> = SyncPtr { x: &Cell::new(42) };
-   |                                                     ^^^^^^^^^^^^^^ this borrow of an interior mutable value may end up in the final value
+   |                                                     ^^^^^^^^^^^^^^ this borrow of an interior mutable value refers to a lifetime-extended temporary
 
-error[E0492]: constants cannot refer to interior mutable data
+error[E0492]: interior mutable shared borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
   --> $DIR/refs-to-cell-in-final.rs:41:57
    |
 LL |   const INTERIOR_MUT_VARIANT: &Option<UnsafeCell<bool>> = &{
@@ -23,7 +23,7 @@ LL | |     assert!(x.is_none());
 LL | |     x = Some(UnsafeCell::new(false));
 LL | |     x
 LL | | };
-   | |_^ this borrow of an interior mutable value may end up in the final value
+   | |_^ this borrow of an interior mutable value refers to a lifetime-extended temporary
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/consts/write_to_static_via_mut_ref.rs b/tests/ui/consts/write_to_static_via_mut_ref.rs
index 82ac85bd250..6d3ddf01a2d 100644
--- a/tests/ui/consts/write_to_static_via_mut_ref.rs
+++ b/tests/ui/consts/write_to_static_via_mut_ref.rs
@@ -1,4 +1,4 @@
-static OH_NO: &mut i32 = &mut 42; //~ ERROR mutable references are not allowed
+static OH_NO: &mut i32 = &mut 42; //~ ERROR mutable borrows of lifetime-extended temporaries
 fn main() {
     assert_eq!(*OH_NO, 42);
     *OH_NO = 43; //~ ERROR cannot assign to `*OH_NO`, as `OH_NO` is an immutable static
diff --git a/tests/ui/consts/write_to_static_via_mut_ref.stderr b/tests/ui/consts/write_to_static_via_mut_ref.stderr
index 63ef788032f..76926e69491 100644
--- a/tests/ui/consts/write_to_static_via_mut_ref.stderr
+++ b/tests/ui/consts/write_to_static_via_mut_ref.stderr
@@ -1,4 +1,4 @@
-error[E0764]: mutable references are not allowed in the final value of statics
+error[E0764]: mutable borrows of lifetime-extended temporaries in the top-level scope of a static are not allowed
   --> $DIR/write_to_static_via_mut_ref.rs:1:26
    |
 LL | static OH_NO: &mut i32 = &mut 42;
diff --git a/tests/ui/error-codes/E0017.rs b/tests/ui/error-codes/E0017.rs
index 8c685aad030..52b81c8a09d 100644
--- a/tests/ui/error-codes/E0017.rs
+++ b/tests/ui/error-codes/E0017.rs
@@ -5,12 +5,12 @@ static X: i32 = 1;
 const C: i32 = 2;
 static mut M: i32 = 3;
 
-const CR: &'static mut i32 = &mut C; //~ ERROR mutable references are not allowed
+const CR: &'static mut i32 = &mut C; //~ ERROR mutable borrows of lifetime-extended temporaries
 //~| WARN taking a mutable
 
 static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR cannot borrow immutable static item `X` as mutable
 
-static CONST_REF: &'static mut i32 = &mut C; //~ ERROR mutable references are not allowed
+static CONST_REF: &'static mut i32 = &mut C; //~ ERROR mutable borrows of lifetime-extended temporaries
 //~| WARN taking a mutable
 
 fn main() {}
diff --git a/tests/ui/error-codes/E0017.stderr b/tests/ui/error-codes/E0017.stderr
index 285d363592f..b1a94ca3e9d 100644
--- a/tests/ui/error-codes/E0017.stderr
+++ b/tests/ui/error-codes/E0017.stderr
@@ -13,7 +13,7 @@ LL | const C: i32 = 2;
    | ^^^^^^^^^^^^
    = note: `#[warn(const_item_mutation)]` on by default
 
-error[E0764]: mutable references are not allowed in the final value of constants
+error[E0764]: mutable borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
   --> $DIR/E0017.rs:8:30
    |
 LL | const CR: &'static mut i32 = &mut C;
@@ -39,7 +39,7 @@ note: `const` item defined here
 LL | const C: i32 = 2;
    | ^^^^^^^^^^^^
 
-error[E0764]: mutable references are not allowed in the final value of statics
+error[E0764]: mutable borrows of lifetime-extended temporaries in the top-level scope of a static are not allowed
   --> $DIR/E0017.rs:13:38
    |
 LL | static CONST_REF: &'static mut i32 = &mut C;
diff --git a/tests/ui/error-codes/E0492.stderr b/tests/ui/error-codes/E0492.stderr
index 557c977e87d..fbbcf8f7d92 100644
--- a/tests/ui/error-codes/E0492.stderr
+++ b/tests/ui/error-codes/E0492.stderr
@@ -1,14 +1,14 @@
-error[E0492]: constants cannot refer to interior mutable data
+error[E0492]: interior mutable shared borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
   --> $DIR/E0492.rs:4:33
    |
 LL | const B: &'static AtomicUsize = &A;
-   |                                 ^^ this borrow of an interior mutable value may end up in the final value
+   |                                 ^^ this borrow of an interior mutable value refers to a lifetime-extended temporary
 
-error[E0492]: statics cannot refer to interior mutable data
+error[E0492]: interior mutable shared borrows of lifetime-extended temporaries in the top-level scope of a static are not allowed
   --> $DIR/E0492.rs:5:34
    |
 LL | static C: &'static AtomicUsize = &A;
-   |                                  ^^ this borrow of an interior mutable value may end up in the final value
+   |                                  ^^ this borrow of an interior mutable value refers to a lifetime-extended temporary
    |
    = help: to fix this, the value can be extracted to a separate `static` item and then referenced
 
diff --git a/tests/ui/issues/issue-46604.rs b/tests/ui/issues/issue-46604.rs
index 6ec6e7bdcb8..03960941b2e 100644
--- a/tests/ui/issues/issue-46604.rs
+++ b/tests/ui/issues/issue-46604.rs
@@ -1,4 +1,4 @@
-static buf: &mut [u8] = &mut [1u8,2,3,4,5,7];   //~ ERROR mutable references are not allowed
+static buf: &mut [u8] = &mut [1u8,2,3,4,5,7];   //~ ERROR mutable borrows of lifetime-extended temporaries
 fn write<T: AsRef<[u8]>>(buffer: T) { }
 
 fn main() {
diff --git a/tests/ui/issues/issue-46604.stderr b/tests/ui/issues/issue-46604.stderr
index 7faa2d79ba4..f00f3f0d23f 100644
--- a/tests/ui/issues/issue-46604.stderr
+++ b/tests/ui/issues/issue-46604.stderr
@@ -1,4 +1,4 @@
-error[E0764]: mutable references are not allowed in the final value of statics
+error[E0764]: mutable borrows of lifetime-extended temporaries in the top-level scope of a static are not allowed
   --> $DIR/issue-46604.rs:1:25
    |
 LL | static buf: &mut [u8] = &mut [1u8,2,3,4,5,7];
diff --git a/tests/ui/statics/check-immutable-mut-slices.rs b/tests/ui/statics/check-immutable-mut-slices.rs
index 8f9680778aa..9e5ca5059d6 100644
--- a/tests/ui/statics/check-immutable-mut-slices.rs
+++ b/tests/ui/statics/check-immutable-mut-slices.rs
@@ -1,6 +1,6 @@
 // Checks that immutable static items can't have mutable slices
 
 static TEST: &'static mut [isize] = &mut [];
-//~^ ERROR mutable references are not allowed
+//~^ ERROR mutable borrows of lifetime-extended temporaries
 
 pub fn main() { }
diff --git a/tests/ui/statics/check-immutable-mut-slices.stderr b/tests/ui/statics/check-immutable-mut-slices.stderr
index 5cb35a7c21e..35521755650 100644
--- a/tests/ui/statics/check-immutable-mut-slices.stderr
+++ b/tests/ui/statics/check-immutable-mut-slices.stderr
@@ -1,4 +1,4 @@
-error[E0764]: mutable references are not allowed in the final value of statics
+error[E0764]: mutable borrows of lifetime-extended temporaries in the top-level scope of a static are not allowed
   --> $DIR/check-immutable-mut-slices.rs:3:37
    |
 LL | static TEST: &'static mut [isize] = &mut [];
diff --git a/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr b/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr
index 03e26615d7e..f5cadc9f183 100644
--- a/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr
+++ b/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr
@@ -64,10 +64,10 @@ LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
    |                                   ^^^^^^^^^^^^^
    = note: ...which again requires evaluating type-level constant, completing the cycle
 note: cycle used when checking that `accept0` is well-formed
-  --> $DIR/unsatisfied-const-trait-bound.rs:29:1
+  --> $DIR/unsatisfied-const-trait-bound.rs:29:35
    |
 LL | fn accept0<T: Trait>(_: Container<{ T::make() }>) {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                                   ^^^^^^^^^^^^^
    = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
 
 error[E0391]: cycle detected when caching mir of `accept1::{constant#0}` for CTFE