Skip to content

Commit 768c020

Browse files
committed
unstably allow constants to refer to statics and read from immutable statics
1 parent 6424e5f commit 768c020

31 files changed

+418
-426
lines changed

Diff for: compiler/rustc_const_eval/messages.ftl

+6-1
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
@@ -213,6 +215,9 @@ const_eval_modified_global =
213215
const_eval_mut_deref =
214216
mutation through a reference is not allowed in {const_eval_const_context}s
215217
218+
const_eval_mutable_data_in_const =
219+
constant refers to mutable data
220+
216221
const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind}
217222
218223
const_eval_non_const_fmt_macro_call =

Diff for: compiler/rustc_const_eval/src/const_eval/error.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::interpret::{ErrorHandled, InterpError, InterpErrorInfo, MachineStopTy
1515
/// The CTFE machine has some custom error kinds.
1616
#[derive(Clone, Debug)]
1717
pub enum ConstEvalErrKind {
18-
ConstAccessesStatic,
18+
ConstAccessesMutGlobal,
1919
ModifiedGlobal,
2020
AssertFailure(AssertKind<ConstInt>),
2121
Panic { msg: Symbol, line: u32, col: u32, file: Symbol },
@@ -26,7 +26,7 @@ impl MachineStopType for ConstEvalErrKind {
2626
use crate::fluent_generated::*;
2727
use ConstEvalErrKind::*;
2828
match self {
29-
ConstAccessesStatic => const_eval_const_accesses_static,
29+
ConstAccessesMutGlobal => const_eval_const_accesses_mut_global,
3030
ModifiedGlobal => const_eval_modified_global,
3131
Panic { .. } => const_eval_panic,
3232
AssertFailure(x) => x.diagnostic_message(),
@@ -38,7 +38,7 @@ impl MachineStopType for ConstEvalErrKind {
3838
) {
3939
use ConstEvalErrKind::*;
4040
match *self {
41-
ConstAccessesStatic | ModifiedGlobal => {}
41+
ConstAccessesMutGlobal | ModifiedGlobal => {}
4242
AssertFailure(kind) => kind.add_args(adder),
4343
Panic { msg, line, col, file } => {
4444
adder("msg".into(), msg.into_diagnostic_arg());

Diff for: compiler/rustc_const_eval/src/const_eval/eval_queries.rs

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

16-
use super::{CanAccessStatics, CompileTimeEvalContext, CompileTimeInterpreter};
16+
use super::{CanAccessMutGlobal, CompileTimeEvalContext, CompileTimeInterpreter};
1717
use crate::const_eval::CheckAlignment;
1818
use crate::errors;
1919
use crate::errors::ConstEvalError;
@@ -94,14 +94,14 @@ pub(crate) fn mk_eval_cx<'mir, 'tcx>(
9494
tcx: TyCtxt<'tcx>,
9595
root_span: Span,
9696
param_env: ty::ParamEnv<'tcx>,
97-
can_access_statics: CanAccessStatics,
97+
can_access_mut_global: CanAccessMutGlobal,
9898
) -> CompileTimeEvalContext<'mir, 'tcx> {
9999
debug!("mk_eval_cx: {:?}", param_env);
100100
InterpCx::new(
101101
tcx,
102102
root_span,
103103
param_env,
104-
CompileTimeInterpreter::new(can_access_statics, CheckAlignment::No),
104+
CompileTimeInterpreter::new(can_access_mut_global, CheckAlignment::No),
105105
)
106106
}
107107

@@ -204,7 +204,7 @@ pub(crate) fn turn_into_const_value<'tcx>(
204204
tcx,
205205
tcx.def_span(key.value.instance.def_id()),
206206
key.param_env,
207-
CanAccessStatics::from(is_static),
207+
CanAccessMutGlobal::from(is_static),
208208
);
209209

210210
let mplace = ecx.raw_const_to_mplace(constant).expect(
@@ -301,9 +301,11 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
301301
tcx,
302302
tcx.def_span(def),
303303
key.param_env,
304-
// Statics (and promoteds inside statics) may access other statics, because unlike consts
304+
// Statics (and promoteds inside statics) may access mutable global memory, because unlike consts
305305
// they do not have to behave "as if" they were evaluated at runtime.
306-
CompileTimeInterpreter::new(CanAccessStatics::from(is_static), CheckAlignment::Error),
306+
// For consts however we want to ensure they behave "as if" they were evaluated at runtime,
307+
// so we have to reject reading mutable global memory.
308+
CompileTimeInterpreter::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error),
307309
);
308310
eval_in_interpreter(ecx, cid, is_static)
309311
}

Diff for: 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
}
@@ -675,7 +672,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
675672
machine: &Self,
676673
alloc_id: AllocId,
677674
alloc: ConstAllocation<'tcx>,
678-
static_def_id: Option<DefId>,
675+
_static_def_id: Option<DefId>,
679676
is_write: bool,
680677
) -> InterpResult<'tcx> {
681678
let alloc = alloc.inner();
@@ -687,22 +684,15 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
687684
}
688685
} else {
689686
// Read access. These are usually allowed, with some exceptions.
690-
if machine.can_access_statics == CanAccessStatics::Yes {
687+
if machine.can_access_mut_global == CanAccessMutGlobal::Yes {
691688
// Machine configuration allows us read from anything (e.g., `static` initializer).
692689
Ok(())
693-
} else if static_def_id.is_some() {
694-
// Machine configuration does not allow us to read statics
695-
// (e.g., `const` initializer).
696-
// See const_eval::machine::MemoryExtra::can_access_statics for why
697-
// this check is so important: if we could read statics, we could read pointers
698-
// to mutable allocations *inside* statics. These allocations are not themselves
699-
// statics, so pointers to them can get around the check in `validity.rs`.
700-
Err(ConstEvalErrKind::ConstAccessesStatic.into())
690+
} else if alloc.mutability == Mutability::Mut {
691+
// Machine configuration does not allow us to read statics (e.g., `const`
692+
// initializer).
693+
Err(ConstEvalErrKind::ConstAccessesMutGlobal.into())
701694
} else {
702695
// Immutable global, this read is fine.
703-
// But make sure we never accept a read from something mutable, that would be
704-
// unsound. The reason is that as the content of this allocation may be different
705-
// now and at run-time, so if we permit reading now we might return the wrong value.
706696
assert_eq!(alloc.mutability, Mutability::Not);
707697
Ok(())
708698
}

Diff for: compiler/rustc_const_eval/src/const_eval/mod.rs

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

3-
use crate::errors::MaxNumNodesInConstErr;
43
use crate::interpret::InterpCx;
54
use rustc_middle::mir;
6-
use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId};
5+
use rustc_middle::mir::interpret::{InterpError, InterpErrorInfo};
76
use rustc_middle::query::TyCtxtAt;
8-
use rustc_middle::ty::{self, Ty, TyCtxt};
9-
use rustc_span::DUMMY_SP;
7+
use rustc_middle::ty::{self, Ty};
108

119
mod error;
1210
mod eval_queries;
@@ -18,55 +16,32 @@ pub use error::*;
1816
pub use eval_queries::*;
1917
pub use fn_queries::*;
2018
pub use machine::*;
21-
pub(crate) use valtrees::{const_to_valtree_inner, valtree_to_const_value};
19+
pub(crate) use valtrees::{eval_to_valtree, valtree_to_const_value};
2220

2321
// We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes.
2422
const VALTREE_MAX_NODES: usize = 100000;
2523

2624
pub(crate) enum ValTreeCreationError {
2725
NodesOverflow,
26+
/// Values of this type, or this particular value, are not supported as valtrees.
2827
NonSupportedType,
28+
/// The value pointed to non-read-only memory, so we cannot make it a valtree.
29+
NotReadOnly,
2930
Other,
3031
}
3132
pub(crate) type ValTreeCreationResult<'tcx> = Result<ty::ValTree<'tcx>, ValTreeCreationError>;
3233

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)
34+
impl From<InterpErrorInfo<'_>> for ValTreeCreationError {
35+
fn from(err: InterpErrorInfo<'_>) -> Self {
36+
match err.kind() {
37+
InterpError::MachineStop(err) => {
38+
let err = err.downcast_ref::<ConstEvalErrKind>().unwrap();
39+
match err {
40+
ConstEvalErrKind::ConstAccessesMutGlobal => ValTreeCreationError::NotReadOnly,
41+
_ => ValTreeCreationError::Other,
6742
}
68-
ValTreeCreationError::NonSupportedType | ValTreeCreationError::Other => Ok(None),
6943
}
44+
_ => ValTreeCreationError::Other,
7045
}
7146
}
7247
}
@@ -78,7 +53,7 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>(
7853
ty: Ty<'tcx>,
7954
) -> Option<mir::DestructuredConstant<'tcx>> {
8055
let param_env = ty::ParamEnv::reveal_all();
81-
let ecx = mk_eval_cx(tcx.tcx, tcx.span, param_env, CanAccessStatics::No);
56+
let ecx = mk_eval_cx(tcx.tcx, tcx.span, param_env, CanAccessMutGlobal::No);
8257
let op = ecx.const_val_to_op(val, ty, None).ok()?;
8358

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

0 commit comments

Comments
 (0)