Skip to content

Commit 6cc4843

Browse files
committed
Auto merge of #119614 - RalfJung:const-refs-to-static, r=oli-obk
unstably allow constants to refer to statics and read from immutable statics I am not aware of any fundamental reason why we cannot allow constants to mention statics. What we really need is that constants do not *read from* statics that can change their value: - This would break the principle that "constants behave as-if their expression was inlined everywhere and executed at runtime". This is enforced by halting const-eval interpretation when a read from a mutable global occurs. - When building a valtree we want to be sure that the constant and everything it refers to is truly immutable. This is enforced by aborting valtree construction when a read from a mutable global occurs. r? `@oli-obk` -- if you are okay with experimenting with this feature, I will create a tracking issue. Based on and blocked on #119044; only the last commit is new.
2 parents 5f40394 + 4def373 commit 6cc4843

File tree

57 files changed

+750
-518
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+750
-518
lines changed

compiler/rustc_const_eval/messages.ftl

+7-7
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ const_eval_closure_non_const =
3030
cannot call non-const closure in {const_eval_const_context}s
3131
const_eval_consider_dereferencing =
3232
consider dereferencing here
33-
const_eval_const_accesses_static = constant accesses static
33+
34+
const_eval_const_accesses_mut_global =
35+
constant accesses mutable global memory
3436
3537
const_eval_const_context = {$kind ->
3638
[const] constant
@@ -319,12 +321,6 @@ const_eval_size_overflow =
319321
const_eval_stack_frame_limit_reached =
320322
reached the configured maximum number of stack frames
321323
322-
const_eval_static_access =
323-
{const_eval_const_context}s cannot refer to statics
324-
.help = consider extracting the value of the `static` to a `const`, and referring to that
325-
.teach_note = `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable.
326-
.teach_help = To fix this, the value can be extracted to a `const` and then used.
327-
328324
const_eval_thread_local_access =
329325
thread-local statics cannot be accessed at compile-time
330326
@@ -415,6 +411,10 @@ const_eval_upcast_mismatch =
415411
## (We'd love to sort this differently to make that more clear but tidy won't let us...)
416412
const_eval_validation_box_to_static = {$front_matter}: encountered a box pointing to a static variable in a constant
417413
const_eval_validation_box_to_uninhabited = {$front_matter}: encountered a box pointing to uninhabited type {$ty}
414+
415+
const_eval_validation_const_ref_to_extern = {$front_matter}: encountered reference to `extern` static in `const`
416+
const_eval_validation_const_ref_to_mutable = {$front_matter}: encountered reference to mutable memory in `const`
417+
418418
const_eval_validation_dangling_box_no_provenance = {$front_matter}: encountered a dangling box ({$pointer} has no provenance)
419419
const_eval_validation_dangling_box_out_of_bounds = {$front_matter}: encountered a dangling box (going beyond the bounds of its allocation)
420420
const_eval_validation_dangling_box_use_after_free = {$front_matter}: encountered a dangling box (use-after-free)

compiler/rustc_const_eval/src/const_eval/error.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::interpret::{ErrorHandled, InterpError, InterpErrorInfo, MachineStopTy
1717
/// The CTFE machine has some custom error kinds.
1818
#[derive(Clone, Debug)]
1919
pub enum ConstEvalErrKind {
20-
ConstAccessesStatic,
20+
ConstAccessesMutGlobal,
2121
ModifiedGlobal,
2222
AssertFailure(AssertKind<ConstInt>),
2323
Panic { msg: Symbol, line: u32, col: u32, file: Symbol },
@@ -28,7 +28,7 @@ impl MachineStopType for ConstEvalErrKind {
2828
use crate::fluent_generated::*;
2929
use ConstEvalErrKind::*;
3030
match self {
31-
ConstAccessesStatic => const_eval_const_accesses_static,
31+
ConstAccessesMutGlobal => const_eval_const_accesses_mut_global,
3232
ModifiedGlobal => const_eval_modified_global,
3333
Panic { .. } => const_eval_panic,
3434
AssertFailure(x) => x.diagnostic_message(),
@@ -37,7 +37,7 @@ impl MachineStopType for ConstEvalErrKind {
3737
fn add_args(self: Box<Self>, adder: &mut dyn FnMut(DiagnosticArgName, DiagnosticArgValue)) {
3838
use ConstEvalErrKind::*;
3939
match *self {
40-
ConstAccessesStatic | ModifiedGlobal => {}
40+
ConstAccessesMutGlobal | ModifiedGlobal => {}
4141
AssertFailure(kind) => kind.add_args(adder),
4242
Panic { msg, line, col, file } => {
4343
adder("msg".into(), msg.into_diagnostic_arg());

compiler/rustc_const_eval/src/const_eval/eval_queries.rs

+13-8
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_middle::ty::{self, TyCtxt};
1111
use rustc_span::Span;
1212
use rustc_target::abi::{self, Abi};
1313

14-
use super::{CanAccessStatics, CompileTimeEvalContext, CompileTimeInterpreter};
14+
use super::{CanAccessMutGlobal, CompileTimeEvalContext, CompileTimeInterpreter};
1515
use crate::const_eval::CheckAlignment;
1616
use crate::errors;
1717
use crate::errors::ConstEvalError;
@@ -90,14 +90,14 @@ pub(crate) fn mk_eval_cx<'mir, 'tcx>(
9090
tcx: TyCtxt<'tcx>,
9191
root_span: Span,
9292
param_env: ty::ParamEnv<'tcx>,
93-
can_access_statics: CanAccessStatics,
93+
can_access_mut_global: CanAccessMutGlobal,
9494
) -> CompileTimeEvalContext<'mir, 'tcx> {
9595
debug!("mk_eval_cx: {:?}", param_env);
9696
InterpCx::new(
9797
tcx,
9898
root_span,
9999
param_env,
100-
CompileTimeInterpreter::new(can_access_statics, CheckAlignment::No),
100+
CompileTimeInterpreter::new(can_access_mut_global, CheckAlignment::No),
101101
)
102102
}
103103

@@ -200,7 +200,7 @@ pub(crate) fn turn_into_const_value<'tcx>(
200200
tcx,
201201
tcx.def_span(key.value.instance.def_id()),
202202
key.param_env,
203-
CanAccessStatics::from(is_static),
203+
CanAccessMutGlobal::from(is_static),
204204
);
205205

206206
let mplace = ecx.raw_const_to_mplace(constant).expect(
@@ -277,9 +277,11 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
277277
tcx,
278278
tcx.def_span(def),
279279
key.param_env,
280-
// Statics (and promoteds inside statics) may access other statics, because unlike consts
280+
// Statics (and promoteds inside statics) may access mutable global memory, because unlike consts
281281
// they do not have to behave "as if" they were evaluated at runtime.
282-
CompileTimeInterpreter::new(CanAccessStatics::from(is_static), CheckAlignment::Error),
282+
// For consts however we want to ensure they behave "as if" they were evaluated at runtime,
283+
// so we have to reject reading mutable global memory.
284+
CompileTimeInterpreter::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error),
283285
);
284286
eval_in_interpreter(ecx, cid, is_static)
285287
}
@@ -358,15 +360,18 @@ pub fn const_validate_mplace<'mir, 'tcx>(
358360
// Promoteds in statics are consts that re allowed to point to statics.
359361
CtfeValidationMode::Const {
360362
allow_immutable_unsafe_cell: false,
361-
allow_static_ptrs: true,
363+
allow_extern_static_ptrs: true,
362364
}
363365
}
364366
Some(mutbl) => CtfeValidationMode::Static { mutbl }, // a `static`
365367
None => {
366368
// In normal `const` (not promoted), the outermost allocation is always only copied,
367369
// so having `UnsafeCell` in there is okay despite them being in immutable memory.
368370
let allow_immutable_unsafe_cell = cid.promoted.is_none() && !inner;
369-
CtfeValidationMode::Const { allow_immutable_unsafe_cell, allow_static_ptrs: false }
371+
CtfeValidationMode::Const {
372+
allow_immutable_unsafe_cell,
373+
allow_extern_static_ptrs: false,
374+
}
370375
}
371376
};
372377
ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?;

compiler/rustc_const_eval/src/const_eval/machine.rs

+14-24
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,10 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
5151
/// The virtual call stack.
5252
pub(super) stack: Vec<Frame<'mir, 'tcx>>,
5353

54-
/// We need to make sure consts never point to anything mutable, even recursively. That is
55-
/// relied on for pattern matching on consts with references.
56-
/// To achieve this, two pieces have to work together:
57-
/// * Interning makes everything outside of statics immutable.
58-
/// * Pointers to allocations inside of statics can never leak outside, to a non-static global.
59-
/// This boolean here controls the second part.
60-
pub(super) can_access_statics: CanAccessStatics,
54+
/// Pattern matching on consts with references would be unsound if those references
55+
/// could point to anything mutable. Therefore, when evaluating consts and when constructing valtrees,
56+
/// we ensure that only immutable global memory can be accessed.
57+
pub(super) can_access_mut_global: CanAccessMutGlobal,
6158

6259
/// Whether to check alignment during evaluation.
6360
pub(super) check_alignment: CheckAlignment,
@@ -73,26 +70,26 @@ pub enum CheckAlignment {
7370
}
7471

7572
#[derive(Copy, Clone, PartialEq)]
76-
pub(crate) enum CanAccessStatics {
73+
pub(crate) enum CanAccessMutGlobal {
7774
No,
7875
Yes,
7976
}
8077

81-
impl From<bool> for CanAccessStatics {
78+
impl From<bool> for CanAccessMutGlobal {
8279
fn from(value: bool) -> Self {
8380
if value { Self::Yes } else { Self::No }
8481
}
8582
}
8683

8784
impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
8885
pub(crate) fn new(
89-
can_access_statics: CanAccessStatics,
86+
can_access_mut_global: CanAccessMutGlobal,
9087
check_alignment: CheckAlignment,
9188
) -> Self {
9289
CompileTimeInterpreter {
9390
num_evaluated_steps: 0,
9491
stack: Vec::new(),
95-
can_access_statics,
92+
can_access_mut_global,
9693
check_alignment,
9794
}
9895
}
@@ -680,7 +677,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
680677
machine: &Self,
681678
alloc_id: AllocId,
682679
alloc: ConstAllocation<'tcx>,
683-
static_def_id: Option<DefId>,
680+
_static_def_id: Option<DefId>,
684681
is_write: bool,
685682
) -> InterpResult<'tcx> {
686683
let alloc = alloc.inner();
@@ -692,22 +689,15 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
692689
}
693690
} else {
694691
// Read access. These are usually allowed, with some exceptions.
695-
if machine.can_access_statics == CanAccessStatics::Yes {
692+
if machine.can_access_mut_global == CanAccessMutGlobal::Yes {
696693
// Machine configuration allows us read from anything (e.g., `static` initializer).
697694
Ok(())
698-
} else if static_def_id.is_some() {
699-
// Machine configuration does not allow us to read statics
700-
// (e.g., `const` initializer).
701-
// See const_eval::machine::MemoryExtra::can_access_statics for why
702-
// this check is so important: if we could read statics, we could read pointers
703-
// to mutable allocations *inside* statics. These allocations are not themselves
704-
// statics, so pointers to them can get around the check in `validity.rs`.
705-
Err(ConstEvalErrKind::ConstAccessesStatic.into())
695+
} else if alloc.mutability == Mutability::Mut {
696+
// Machine configuration does not allow us to read statics (e.g., `const`
697+
// initializer).
698+
Err(ConstEvalErrKind::ConstAccessesMutGlobal.into())
706699
} else {
707700
// Immutable global, this read is fine.
708-
// But make sure we never accept a read from something mutable, that would be
709-
// unsound. The reason is that as the content of this allocation may be different
710-
// now and at run-time, so if we permit reading now we might return the wrong value.
711701
assert_eq!(alloc.mutability, Mutability::Not);
712702
Ok(())
713703
}

compiler/rustc_const_eval/src/const_eval/mod.rs

+15-46
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
// Not in interpret to make sure we do not use private implementation details
22

3-
use crate::errors::MaxNumNodesInConstErr;
4-
use crate::interpret::InterpCx;
53
use rustc_middle::mir;
6-
use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId};
4+
use rustc_middle::mir::interpret::InterpErrorInfo;
75
use rustc_middle::query::TyCtxtAt;
8-
use rustc_middle::ty::{self, Ty, TyCtxt};
9-
use rustc_span::DUMMY_SP;
6+
use rustc_middle::ty::{self, Ty};
7+
8+
use crate::interpret::{format_interp_error, InterpCx};
109

1110
mod error;
1211
mod eval_queries;
@@ -18,56 +17,26 @@ pub use error::*;
1817
pub use eval_queries::*;
1918
pub use fn_queries::*;
2019
pub use machine::*;
21-
pub(crate) use valtrees::{const_to_valtree_inner, valtree_to_const_value};
20+
pub(crate) use valtrees::{eval_to_valtree, valtree_to_const_value};
2221

2322
// We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes.
2423
const VALTREE_MAX_NODES: usize = 100000;
2524

2625
pub(crate) enum ValTreeCreationError {
2726
NodesOverflow,
27+
/// Values of this type, or this particular value, are not supported as valtrees.
2828
NonSupportedType,
29-
Other,
3029
}
3130
pub(crate) type ValTreeCreationResult<'tcx> = Result<ty::ValTree<'tcx>, ValTreeCreationError>;
3231

33-
/// Evaluates a constant and turns it into a type-level constant value.
34-
pub(crate) fn eval_to_valtree<'tcx>(
35-
tcx: TyCtxt<'tcx>,
36-
param_env: ty::ParamEnv<'tcx>,
37-
cid: GlobalId<'tcx>,
38-
) -> EvalToValTreeResult<'tcx> {
39-
let const_alloc = tcx.eval_to_allocation_raw(param_env.and(cid))?;
40-
41-
// FIXME Need to provide a span to `eval_to_valtree`
42-
let ecx = mk_eval_cx(
43-
tcx,
44-
DUMMY_SP,
45-
param_env,
46-
// It is absolutely crucial for soundness that
47-
// we do not read from static items or other mutable memory.
48-
CanAccessStatics::No,
49-
);
50-
let place = ecx.raw_const_to_mplace(const_alloc).unwrap();
51-
debug!(?place);
52-
53-
let mut num_nodes = 0;
54-
let valtree_result = const_to_valtree_inner(&ecx, &place, &mut num_nodes);
55-
56-
match valtree_result {
57-
Ok(valtree) => Ok(Some(valtree)),
58-
Err(err) => {
59-
let did = cid.instance.def_id();
60-
let global_const_id = cid.display(tcx);
61-
match err {
62-
ValTreeCreationError::NodesOverflow => {
63-
let span = tcx.hir().span_if_local(did);
64-
tcx.dcx().emit_err(MaxNumNodesInConstErr { span, global_const_id });
65-
66-
Ok(None)
67-
}
68-
ValTreeCreationError::NonSupportedType | ValTreeCreationError::Other => Ok(None),
69-
}
70-
}
32+
impl From<InterpErrorInfo<'_>> for ValTreeCreationError {
33+
fn from(err: InterpErrorInfo<'_>) -> Self {
34+
ty::tls::with(|tcx| {
35+
bug!(
36+
"Unexpected Undefined Behavior error during valtree construction: {}",
37+
format_interp_error(tcx.dcx(), err),
38+
)
39+
})
7140
}
7241
}
7342

@@ -78,7 +47,7 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>(
7847
ty: Ty<'tcx>,
7948
) -> Option<mir::DestructuredConstant<'tcx>> {
8049
let param_env = ty::ParamEnv::reveal_all();
81-
let ecx = mk_eval_cx(tcx.tcx, tcx.span, param_env, CanAccessStatics::No);
50+
let ecx = mk_eval_cx(tcx.tcx, tcx.span, param_env, CanAccessMutGlobal::No);
8251
let op = ecx.const_val_to_op(val, ty, None).ok()?;
8352

8453
// We go to `usize` as we cannot allocate anything bigger anyway.

0 commit comments

Comments
 (0)