about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2024-01-05 12:18:11 +0100
committerRalf Jung <post@ralfj.de>2024-02-10 16:12:55 +0100
commit4e77e368ebc1bf21ae23137c253138c9ffbc3c7f (patch)
tree25c8fec03548477f540fc370b2aee475f9f40307 /compiler
parent5f40394baa07b6fb50bc70dedd8b780524b20934 (diff)
downloadrust-4e77e368ebc1bf21ae23137c253138c9ffbc3c7f.tar.gz
rust-4e77e368ebc1bf21ae23137c253138c9ffbc3c7f.zip
unstably allow constants to refer to statics and read from immutable statics
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_const_eval/messages.ftl13
-rw-r--r--compiler/rustc_const_eval/src/const_eval/error.rs6
-rw-r--r--compiler/rustc_const_eval/src/const_eval/eval_queries.rs14
-rw-r--r--compiler/rustc_const_eval/src/const_eval/machine.rs38
-rw-r--r--compiler/rustc_const_eval/src/const_eval/mod.rs57
-rw-r--r--compiler/rustc_const_eval/src/const_eval/valtrees.rs96
-rw-r--r--compiler/rustc_const_eval/src/errors.rs22
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs15
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/ops.rs17
-rw-r--r--compiler/rustc_const_eval/src/util/caller_location.rs4
-rw-r--r--compiler/rustc_const_eval/src/util/check_validity_requirement.rs4
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0013.md4
-rw-r--r--compiler/rustc_feature/src/unstable.rs2
-rw-r--r--compiler/rustc_span/src/symbol.rs1
14 files changed, 148 insertions, 145 deletions
diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl
index e7e8b2b3600..d932bf3c2a2 100644
--- a/compiler/rustc_const_eval/messages.ftl
+++ b/compiler/rustc_const_eval/messages.ftl
@@ -30,7 +30,9 @@ const_eval_closure_non_const =
     cannot call non-const closure in {const_eval_const_context}s
 const_eval_consider_dereferencing =
     consider dereferencing here
-const_eval_const_accesses_static = constant accesses static
+
+const_eval_const_accesses_mut_global =
+    constant accesses mutable global memory
 
 const_eval_const_context = {$kind ->
     [const] constant
@@ -213,6 +215,9 @@ const_eval_modified_global =
 const_eval_mut_deref =
     mutation through a reference is not allowed in {const_eval_const_context}s
 
+const_eval_mutable_data_in_const =
+    constant refers to mutable data
+
 const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind}
 
 const_eval_non_const_fmt_macro_call =
@@ -319,12 +324,6 @@ const_eval_size_overflow =
 const_eval_stack_frame_limit_reached =
     reached the configured maximum number of stack frames
 
-const_eval_static_access =
-    {const_eval_const_context}s cannot refer to statics
-    .help = consider extracting the value of the `static` to a `const`, and referring to that
-    .teach_note = `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable.
-    .teach_help = To fix this, the value can be extracted to a `const` and then used.
-
 const_eval_thread_local_access =
     thread-local statics cannot be accessed at compile-time
 
diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs
index 62af21396ab..71085c2b2a5 100644
--- a/compiler/rustc_const_eval/src/const_eval/error.rs
+++ b/compiler/rustc_const_eval/src/const_eval/error.rs
@@ -17,7 +17,7 @@ use crate::interpret::{ErrorHandled, InterpError, InterpErrorInfo, MachineStopTy
 /// The CTFE machine has some custom error kinds.
 #[derive(Clone, Debug)]
 pub enum ConstEvalErrKind {
-    ConstAccessesStatic,
+    ConstAccessesMutGlobal,
     ModifiedGlobal,
     AssertFailure(AssertKind<ConstInt>),
     Panic { msg: Symbol, line: u32, col: u32, file: Symbol },
@@ -28,7 +28,7 @@ impl MachineStopType for ConstEvalErrKind {
         use crate::fluent_generated::*;
         use ConstEvalErrKind::*;
         match self {
-            ConstAccessesStatic => const_eval_const_accesses_static,
+            ConstAccessesMutGlobal => const_eval_const_accesses_mut_global,
             ModifiedGlobal => const_eval_modified_global,
             Panic { .. } => const_eval_panic,
             AssertFailure(x) => x.diagnostic_message(),
@@ -37,7 +37,7 @@ impl MachineStopType for ConstEvalErrKind {
     fn add_args(self: Box<Self>, adder: &mut dyn FnMut(DiagnosticArgName, DiagnosticArgValue)) {
         use ConstEvalErrKind::*;
         match *self {
-            ConstAccessesStatic | ModifiedGlobal => {}
+            ConstAccessesMutGlobal | ModifiedGlobal => {}
             AssertFailure(kind) => kind.add_args(adder),
             Panic { msg, line, col, file } => {
                 adder("msg".into(), msg.into_diagnostic_arg());
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index a2d0f1c5583..52060a8693f 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -11,7 +11,7 @@ use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::Span;
 use rustc_target::abi::{self, Abi};
 
-use super::{CanAccessStatics, CompileTimeEvalContext, CompileTimeInterpreter};
+use super::{CanAccessMutGlobal, CompileTimeEvalContext, CompileTimeInterpreter};
 use crate::const_eval::CheckAlignment;
 use crate::errors;
 use crate::errors::ConstEvalError;
@@ -90,14 +90,14 @@ pub(crate) fn mk_eval_cx<'mir, 'tcx>(
     tcx: TyCtxt<'tcx>,
     root_span: Span,
     param_env: ty::ParamEnv<'tcx>,
-    can_access_statics: CanAccessStatics,
+    can_access_mut_global: CanAccessMutGlobal,
 ) -> CompileTimeEvalContext<'mir, 'tcx> {
     debug!("mk_eval_cx: {:?}", param_env);
     InterpCx::new(
         tcx,
         root_span,
         param_env,
-        CompileTimeInterpreter::new(can_access_statics, CheckAlignment::No),
+        CompileTimeInterpreter::new(can_access_mut_global, CheckAlignment::No),
     )
 }
 
@@ -200,7 +200,7 @@ pub(crate) fn turn_into_const_value<'tcx>(
         tcx,
         tcx.def_span(key.value.instance.def_id()),
         key.param_env,
-        CanAccessStatics::from(is_static),
+        CanAccessMutGlobal::from(is_static),
     );
 
     let mplace = ecx.raw_const_to_mplace(constant).expect(
@@ -277,9 +277,11 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
         tcx,
         tcx.def_span(def),
         key.param_env,
-        // Statics (and promoteds inside statics) may access other statics, because unlike consts
+        // Statics (and promoteds inside statics) may access mutable global memory, because unlike consts
         // they do not have to behave "as if" they were evaluated at runtime.
-        CompileTimeInterpreter::new(CanAccessStatics::from(is_static), CheckAlignment::Error),
+        // For consts however we want to ensure they behave "as if" they were evaluated at runtime,
+        // so we have to reject reading mutable global memory.
+        CompileTimeInterpreter::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error),
     );
     eval_in_interpreter(ecx, cid, is_static)
 }
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 274ff25fb9b..d08985edb76 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -51,13 +51,10 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
     /// The virtual call stack.
     pub(super) stack: Vec<Frame<'mir, 'tcx>>,
 
-    /// We need to make sure consts never point to anything mutable, even recursively. That is
-    /// relied on for pattern matching on consts with references.
-    /// To achieve this, two pieces have to work together:
-    /// * Interning makes everything outside of statics immutable.
-    /// * Pointers to allocations inside of statics can never leak outside, to a non-static global.
-    /// This boolean here controls the second part.
-    pub(super) can_access_statics: CanAccessStatics,
+    /// Pattern matching on consts with references would be unsound if those references
+    /// could point to anything mutable. Therefore, when evaluating consts and when constructing valtrees,
+    /// we ensure that only immutable global memory can be accessed.
+    pub(super) can_access_mut_global: CanAccessMutGlobal,
 
     /// Whether to check alignment during evaluation.
     pub(super) check_alignment: CheckAlignment,
@@ -73,12 +70,12 @@ pub enum CheckAlignment {
 }
 
 #[derive(Copy, Clone, PartialEq)]
-pub(crate) enum CanAccessStatics {
+pub(crate) enum CanAccessMutGlobal {
     No,
     Yes,
 }
 
-impl From<bool> for CanAccessStatics {
+impl From<bool> for CanAccessMutGlobal {
     fn from(value: bool) -> Self {
         if value { Self::Yes } else { Self::No }
     }
@@ -86,13 +83,13 @@ impl From<bool> for CanAccessStatics {
 
 impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
     pub(crate) fn new(
-        can_access_statics: CanAccessStatics,
+        can_access_mut_global: CanAccessMutGlobal,
         check_alignment: CheckAlignment,
     ) -> Self {
         CompileTimeInterpreter {
             num_evaluated_steps: 0,
             stack: Vec::new(),
-            can_access_statics,
+            can_access_mut_global,
             check_alignment,
         }
     }
@@ -680,7 +677,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
         machine: &Self,
         alloc_id: AllocId,
         alloc: ConstAllocation<'tcx>,
-        static_def_id: Option<DefId>,
+        _static_def_id: Option<DefId>,
         is_write: bool,
     ) -> InterpResult<'tcx> {
         let alloc = alloc.inner();
@@ -692,22 +689,15 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
             }
         } else {
             // Read access. These are usually allowed, with some exceptions.
-            if machine.can_access_statics == CanAccessStatics::Yes {
+            if machine.can_access_mut_global == CanAccessMutGlobal::Yes {
                 // Machine configuration allows us read from anything (e.g., `static` initializer).
                 Ok(())
-            } else if static_def_id.is_some() {
-                // Machine configuration does not allow us to read statics
-                // (e.g., `const` initializer).
-                // See const_eval::machine::MemoryExtra::can_access_statics for why
-                // this check is so important: if we could read statics, we could read pointers
-                // to mutable allocations *inside* statics. These allocations are not themselves
-                // statics, so pointers to them can get around the check in `validity.rs`.
-                Err(ConstEvalErrKind::ConstAccessesStatic.into())
+            } else if alloc.mutability == Mutability::Mut {
+                // Machine configuration does not allow us to read statics (e.g., `const`
+                // initializer).
+                Err(ConstEvalErrKind::ConstAccessesMutGlobal.into())
             } else {
                 // Immutable global, this read is fine.
-                // But make sure we never accept a read from something mutable, that would be
-                // unsound. The reason is that as the content of this allocation may be different
-                // now and at run-time, so if we permit reading now we might return the wrong value.
                 assert_eq!(alloc.mutability, Mutability::Not);
                 Ok(())
             }
diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs
index 29cbb7f07e8..826b4b278ed 100644
--- a/compiler/rustc_const_eval/src/const_eval/mod.rs
+++ b/compiler/rustc_const_eval/src/const_eval/mod.rs
@@ -1,12 +1,10 @@
 // Not in interpret to make sure we do not use private implementation details
 
-use crate::errors::MaxNumNodesInConstErr;
 use crate::interpret::InterpCx;
 use rustc_middle::mir;
-use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId};
+use rustc_middle::mir::interpret::{InterpError, InterpErrorInfo};
 use rustc_middle::query::TyCtxtAt;
-use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_span::DUMMY_SP;
+use rustc_middle::ty::{self, Ty};
 
 mod error;
 mod eval_queries;
@@ -18,55 +16,32 @@ pub use error::*;
 pub use eval_queries::*;
 pub use fn_queries::*;
 pub use machine::*;
-pub(crate) use valtrees::{const_to_valtree_inner, valtree_to_const_value};
+pub(crate) use 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 {
     NodesOverflow,
+    /// Values of this type, or this particular value, are not supported as valtrees.
     NonSupportedType,
+    /// The value pointed to non-read-only memory, so we cannot make it a valtree.
+    NotReadOnly,
     Other,
 }
 pub(crate) type ValTreeCreationResult<'tcx> = Result<ty::ValTree<'tcx>, ValTreeCreationError>;
 
-/// Evaluates a constant and turns it into a type-level constant value.
-pub(crate) fn eval_to_valtree<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    cid: GlobalId<'tcx>,
-) -> EvalToValTreeResult<'tcx> {
-    let const_alloc = tcx.eval_to_allocation_raw(param_env.and(cid))?;
-
-    // FIXME Need to provide a span to `eval_to_valtree`
-    let ecx = mk_eval_cx(
-        tcx,
-        DUMMY_SP,
-        param_env,
-        // It is absolutely crucial for soundness that
-        // we do not read from static items or other mutable memory.
-        CanAccessStatics::No,
-    );
-    let place = ecx.raw_const_to_mplace(const_alloc).unwrap();
-    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(Some(valtree)),
-        Err(err) => {
-            let did = cid.instance.def_id();
-            let global_const_id = cid.display(tcx);
-            match err {
-                ValTreeCreationError::NodesOverflow => {
-                    let span = tcx.hir().span_if_local(did);
-                    tcx.dcx().emit_err(MaxNumNodesInConstErr { span, global_const_id });
-
-                    Ok(None)
+impl From<InterpErrorInfo<'_>> for ValTreeCreationError {
+    fn from(err: InterpErrorInfo<'_>) -> Self {
+        match err.kind() {
+            InterpError::MachineStop(err) => {
+                let err = err.downcast_ref::<ConstEvalErrKind>().unwrap();
+                match err {
+                    ConstEvalErrKind::ConstAccessesMutGlobal => ValTreeCreationError::NotReadOnly,
+                    _ => ValTreeCreationError::Other,
                 }
-                ValTreeCreationError::NonSupportedType | ValTreeCreationError::Other => Ok(None),
             }
+            _ => ValTreeCreationError::Other,
         }
     }
 }
@@ -78,7 +53,7 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>(
     ty: Ty<'tcx>,
 ) -> Option<mir::DestructuredConstant<'tcx>> {
     let param_env = ty::ParamEnv::reveal_all();
-    let ecx = mk_eval_cx(tcx.tcx, tcx.span, param_env, CanAccessStatics::No);
+    let ecx = mk_eval_cx(tcx.tcx, tcx.span, param_env, CanAccessMutGlobal::No);
     let op = ecx.const_val_to_op(val, ty, None).ok()?;
 
     // We go to `usize` as we cannot allocate anything bigger anyway.
diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
index 5c2bf4626c4..31fcdc9a3bd 100644
--- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs
+++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
@@ -1,17 +1,20 @@
+use rustc_middle::mir;
+use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId};
+use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
+use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
+use rustc_span::DUMMY_SP;
+use rustc_target::abi::{Abi, VariantIdx};
+
 use super::eval_queries::{mk_eval_cx, op_to_const};
 use super::machine::CompileTimeEvalContext;
 use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES};
-use crate::const_eval::CanAccessStatics;
+use crate::const_eval::CanAccessMutGlobal;
+use crate::errors::{MaxNumNodesInConstErr, MutableDataInConstErr};
 use crate::interpret::MPlaceTy;
 use crate::interpret::{
     intern_const_alloc_recursive, ImmTy, Immediate, InternKind, MemPlaceMeta, MemoryKind, PlaceTy,
     Projectable, Scalar,
 };
-use rustc_middle::mir;
-use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
-use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
-use rustc_span::DUMMY_SP;
-use rustc_target::abi::{Abi, VariantIdx};
 
 #[instrument(skip(ecx), level = "debug")]
 fn branches<'tcx>(
@@ -70,7 +73,7 @@ fn slice_branches<'tcx>(
 }
 
 #[instrument(skip(ecx), level = "debug")]
-pub(crate) fn const_to_valtree_inner<'tcx>(
+fn const_to_valtree_inner<'tcx>(
     ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
     place: &MPlaceTy<'tcx>,
     num_nodes: &mut usize,
@@ -88,9 +91,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
             Ok(ty::ValTree::zst())
         }
         ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
-            let Ok(val) = ecx.read_immediate(place) else {
-                return Err(ValTreeCreationError::Other);
-            };
+            let val = ecx.read_immediate(place)?;
             let val = val.to_scalar();
             *num_nodes += 1;
 
@@ -102,19 +103,17 @@ pub(crate) 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 Ok(val) = ecx.read_immediate(place) else {
-                return Err(ValTreeCreationError::Other);
-            };
+            let val = ecx.read_immediate(place)?;
             // We could allow wide raw pointers where both sides are integers in the future,
             // but for now we reject them.
             if matches!(val.layout.abi, Abi::ScalarPair(..)) {
-                return Err(ValTreeCreationError::Other);
+                return Err(ValTreeCreationError::NonSupportedType);
             }
             let val = val.to_scalar();
             // We are in the CTFE machine, so ptr-to-int casts will fail.
             // This can only be `Ok` if `val` already is an integer.
             let Ok(val) = val.try_to_int() else {
-                return Err(ValTreeCreationError::Other);
+                return Err(ValTreeCreationError::NonSupportedType);
             };
             // It's just a ScalarInt!
             Ok(ty::ValTree::Leaf(val))
@@ -125,11 +124,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
         ty::FnPtr(_) => Err(ValTreeCreationError::NonSupportedType),
 
         ty::Ref(_, _, _)  => {
-            let Ok(derefd_place)= ecx.deref_pointer(place) else {
-                return Err(ValTreeCreationError::Other);
-            };
-            debug!(?derefd_place);
-
+            let derefd_place = ecx.deref_pointer(place)?;
             const_to_valtree_inner(ecx, &derefd_place, num_nodes)
         }
 
@@ -153,9 +148,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
                 bug!("uninhabited types should have errored and never gotten converted to valtree")
             }
 
-            let Ok(variant) = ecx.read_discriminant(place) else {
-                return Err(ValTreeCreationError::Other);
-            };
+            let variant = ecx.read_discriminant(place)?;
             branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant), num_nodes)
         }
 
@@ -221,6 +214,59 @@ fn create_valtree_place<'tcx>(
     ecx.allocate_dyn(layout, MemoryKind::Stack, meta).unwrap()
 }
 
+/// Evaluates a constant and turns it into a type-level constant value.
+pub(crate) fn eval_to_valtree<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    cid: GlobalId<'tcx>,
+) -> EvalToValTreeResult<'tcx> {
+    let const_alloc = tcx.eval_to_allocation_raw(param_env.and(cid))?;
+
+    // FIXME Need to provide a span to `eval_to_valtree`
+    let ecx = mk_eval_cx(
+        tcx,
+        DUMMY_SP,
+        param_env,
+        // It is absolutely crucial for soundness that
+        // we do not read from mutable memory.
+        CanAccessMutGlobal::No,
+    );
+    let place = ecx.raw_const_to_mplace(const_alloc).unwrap();
+    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(Some(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(handled.into())
+                }
+                ValTreeCreationError::NotReadOnly => {
+                    let handled =
+                        tcx.dcx().emit_err(MutableDataInConstErr { span, global_const_id });
+                    Err(handled.into())
+                }
+                ValTreeCreationError::Other => {
+                    let handled = tcx.dcx().span_delayed_bug(
+                        span.unwrap_or(DUMMY_SP),
+                        "unexpected error during valtree construction",
+                    );
+                    Err(handled.into())
+                }
+                ValTreeCreationError::NonSupportedType => Ok(None),
+            }
+        }
+    }
+}
+
 /// Converts a `ValTree` to a `ConstValue`, which is needed after mir
 /// construction has finished.
 // FIXME Merge `valtree_to_const_value` and `valtree_into_mplace` into one function
@@ -253,7 +299,7 @@ pub fn valtree_to_const_value<'tcx>(
             }
         }
         ty::Ref(_, inner_ty, _) => {
-            let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessStatics::No);
+            let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessMutGlobal::No);
             let imm = valtree_to_ref(&mut ecx, valtree, *inner_ty);
             let imm = ImmTy::from_immediate(imm, tcx.layout_of(param_env_ty).unwrap());
             op_to_const(&ecx, &imm.into(), /* for diagnostics */ false)
@@ -280,7 +326,7 @@ pub fn valtree_to_const_value<'tcx>(
                 bug!("could not find non-ZST field during in {layout:#?}");
             }
 
-            let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessStatics::No);
+            let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessMutGlobal::No);
 
             // Need to create a place for this valtree.
             let place = create_valtree_place(&mut ecx, layout, valtree);
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index 4d2b1ba3eec..296329f5d1e 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -56,24 +56,12 @@ pub(crate) struct UnstableInStable {
 
 #[derive(Diagnostic)]
 #[diag(const_eval_thread_local_access, code = E0625)]
-pub(crate) struct NonConstOpErr {
+pub(crate) struct ThreadLocalAccessErr {
     #[primary_span]
     pub span: Span,
 }
 
 #[derive(Diagnostic)]
-#[diag(const_eval_static_access, code = E0013)]
-#[help]
-pub(crate) struct StaticAccessErr {
-    #[primary_span]
-    pub span: Span,
-    pub kind: ConstContext,
-    #[note(const_eval_teach_note)]
-    #[help(const_eval_teach_help)]
-    pub teach: Option<()>,
-}
-
-#[derive(Diagnostic)]
 #[diag(const_eval_raw_ptr_to_int)]
 #[note]
 #[note(const_eval_note2)]
@@ -130,6 +118,14 @@ pub(crate) struct MaxNumNodesInConstErr {
 }
 
 #[derive(Diagnostic)]
+#[diag(const_eval_mutable_data_in_const)]
+pub(crate) struct MutableDataInConstErr {
+    #[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]
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index 90cde81c018..67665c4aed2 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -132,7 +132,6 @@ pub enum CtfeValidationMode {
     /// `allow_immutable_unsafe_cell` says whether we allow `UnsafeCell` in immutable memory (which is the
     /// case for the top-level allocation of a `const`, where this is fine because the allocation will be
     /// copied at each use site).
-    /// `allow_static_ptrs` says if pointers to statics are permitted (which is the case for promoteds in statics).
     Const { allow_immutable_unsafe_cell: bool, allow_static_ptrs: bool },
 }
 
@@ -146,13 +145,6 @@ impl CtfeValidationMode {
         }
     }
 
-    fn allow_static_ptrs(self) -> bool {
-        match self {
-            CtfeValidationMode::Static { .. } => true, // statics can point to statics
-            CtfeValidationMode::Const { allow_static_ptrs, .. } => allow_static_ptrs,
-        }
-    }
-
     fn may_contain_mutable_ref(self) -> bool {
         match self {
             CtfeValidationMode::Static { mutbl } => mutbl == Mutability::Mut,
@@ -468,13 +460,6 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
                         // Special handling for pointers to statics (irrespective of their type).
                         assert!(!self.ecx.tcx.is_thread_local_static(did));
                         assert!(self.ecx.tcx.is_static(did));
-                        if self.ctfe_mode.is_some_and(|c| !c.allow_static_ptrs()) {
-                            // See const_eval::machine::MemoryExtra::can_access_statics for why
-                            // this check is so important.
-                            // This check is reachable when the const just referenced the static,
-                            // but never read it (so we never entered `before_access_global`).
-                            throw_validation_failure!(self.path, PtrToStatic { ptr_kind });
-                        }
                         // Mutability check.
                         if ptr_expected_mutbl == Mutability::Mut {
                             if matches!(
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
index fb705d91977..a9d472d377c 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
@@ -580,16 +580,21 @@ impl<'tcx> NonConstOp<'tcx> for StaticAccess {
         if let hir::ConstContext::Static(_) = ccx.const_kind() {
             Status::Allowed
         } else {
-            Status::Forbidden
+            Status::Unstable(sym::const_refs_to_static)
         }
     }
 
     fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
-        ccx.dcx().create_err(errors::StaticAccessErr {
+        let mut err = feature_err(
+            &ccx.tcx.sess,
+            sym::const_refs_to_static,
             span,
-            kind: ccx.const_kind(),
-            teach: ccx.tcx.sess.teach(E0013).then_some(()),
-        })
+            format!("referencing statics in {}s is unstable", ccx.const_kind(),),
+        );
+        err
+            .note("`static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable.")
+            .help("to fix this, the value can be extracted to a `const` and then used.");
+        err
     }
 }
 
@@ -598,7 +603,7 @@ impl<'tcx> NonConstOp<'tcx> for StaticAccess {
 pub struct ThreadLocalAccess;
 impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess {
     fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
-        ccx.dcx().create_err(errors::NonConstOpErr { span })
+        ccx.dcx().create_err(errors::ThreadLocalAccessErr { span })
     }
 }
 
diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs
index 36a315b8f30..d1c2d22b5a9 100644
--- a/compiler/rustc_const_eval/src/util/caller_location.rs
+++ b/compiler/rustc_const_eval/src/util/caller_location.rs
@@ -6,7 +6,7 @@ use rustc_middle::ty::layout::LayoutOf;
 use rustc_span::symbol::Symbol;
 use rustc_type_ir::Mutability;
 
-use crate::const_eval::{mk_eval_cx, CanAccessStatics, CompileTimeEvalContext};
+use crate::const_eval::{mk_eval_cx, CanAccessMutGlobal, CompileTimeEvalContext};
 use crate::interpret::*;
 
 /// Allocate a `const core::panic::Location` with the provided filename and line/column numbers.
@@ -57,7 +57,7 @@ pub(crate) fn const_caller_location_provider(
     col: u32,
 ) -> mir::ConstValue<'_> {
     trace!("const_caller_location: {}:{}:{}", file, line, col);
-    let mut ecx = mk_eval_cx(tcx.tcx, tcx.span, ty::ParamEnv::reveal_all(), CanAccessStatics::No);
+    let mut ecx = mk_eval_cx(tcx.tcx, tcx.span, ty::ParamEnv::reveal_all(), CanAccessMutGlobal::No);
 
     let loc_place = alloc_caller_location(&mut ecx, file, line, col);
     if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() {
diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs
index e9e0690f07d..8c4af5e5132 100644
--- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs
+++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs
@@ -2,7 +2,7 @@ use rustc_middle::ty::layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout, Val
 use rustc_middle::ty::{ParamEnv, ParamEnvAnd, Ty, TyCtxt};
 use rustc_target::abi::{Abi, FieldsShape, Scalar, Variants};
 
-use crate::const_eval::{CanAccessStatics, CheckAlignment, CompileTimeInterpreter};
+use crate::const_eval::{CanAccessMutGlobal, CheckAlignment, CompileTimeInterpreter};
 use crate::interpret::{InterpCx, MemoryKind, OpTy};
 
 /// Determines if this type permits "raw" initialization by just transmuting some memory into an
@@ -44,7 +44,7 @@ fn might_permit_raw_init_strict<'tcx>(
     tcx: TyCtxt<'tcx>,
     kind: ValidityRequirement,
 ) -> Result<bool, &'tcx LayoutError<'tcx>> {
-    let machine = CompileTimeInterpreter::new(CanAccessStatics::No, CheckAlignment::Error);
+    let machine = CompileTimeInterpreter::new(CanAccessMutGlobal::No, CheckAlignment::Error);
 
     let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine);
 
diff --git a/compiler/rustc_error_codes/src/error_codes/E0013.md b/compiler/rustc_error_codes/src/error_codes/E0013.md
index 5605302772f..9f4848343ff 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0013.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0013.md
@@ -1,9 +1,11 @@
+#### Note: this error code is no longer emitted by the compiler
+
 Static and const variables can refer to other const variables. But a const
 variable cannot refer to a static variable.
 
 Erroneous code example:
 
-```compile_fail,E0013
+```compile_fail,E0658
 static X: i32 = 42;
 const Y: i32 = X;
 ```
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 42fc24c937b..efb0b1fbabb 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -409,6 +409,8 @@ declare_features! (
     (unstable, const_precise_live_drops, "1.46.0", Some(73255)),
     /// Allows references to types with interior mutability within constants
     (unstable, const_refs_to_cell, "1.51.0", Some(80384)),
+    /// Allows creating pointers and references to `static` items in constants.
+    (unstable, const_refs_to_static, "CURRENT_RUSTC_VERSION", Some(119618)),
     /// Allows `impl const Trait for T` syntax.
     (unstable, const_trait_impl, "1.42.0", Some(67792)),
     /// Allows the `?` operator in const contexts.
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index ed6e69d9199..aa912c93c08 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -582,6 +582,7 @@ symbols! {
         const_raw_ptr_deref,
         const_raw_ptr_to_usize_cast,
         const_refs_to_cell,
+        const_refs_to_static,
         const_trait,
         const_trait_bound_opt_out,
         const_trait_impl,