diff options
| author | Ralf Jung <post@ralfj.de> | 2024-01-05 12:18:11 +0100 |
|---|---|---|
| committer | Ralf Jung <post@ralfj.de> | 2024-02-10 16:12:55 +0100 |
| commit | 4e77e368ebc1bf21ae23137c253138c9ffbc3c7f (patch) | |
| tree | 25c8fec03548477f540fc370b2aee475f9f40307 /compiler | |
| parent | 5f40394baa07b6fb50bc70dedd8b780524b20934 (diff) | |
| download | rust-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.ftl | 13 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/const_eval/error.rs | 6 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/const_eval/eval_queries.rs | 14 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/const_eval/machine.rs | 38 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/const_eval/mod.rs | 57 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/const_eval/valtrees.rs | 96 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/errors.rs | 22 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/validity.rs | 15 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/transform/check_consts/ops.rs | 17 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/util/caller_location.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/util/check_validity_requirement.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_error_codes/src/error_codes/E0013.md | 4 | ||||
| -rw-r--r-- | compiler/rustc_feature/src/unstable.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_span/src/symbol.rs | 1 |
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, |
