Skip to content

Commit 9c0623f

Browse files
committed
validation: descend from consts into statics
1 parent 4e77e36 commit 9c0623f

25 files changed

+334
-212
lines changed

compiler/rustc_const_eval/messages.ftl

+3-3
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,6 @@ const_eval_modified_global =
215215
const_eval_mut_deref =
216216
mutation through a reference is not allowed in {const_eval_const_context}s
217217
218-
const_eval_mutable_data_in_const =
219-
constant refers to mutable data
220-
221218
const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind}
222219
223220
const_eval_non_const_fmt_macro_call =
@@ -414,6 +411,9 @@ const_eval_upcast_mismatch =
414411
## (We'd love to sort this differently to make that more clear but tidy won't let us...)
415412
const_eval_validation_box_to_static = {$front_matter}: encountered a box pointing to a static variable in a constant
416413
const_eval_validation_box_to_uninhabited = {$front_matter}: encountered a box pointing to uninhabited type {$ty}
414+
415+
const_eval_validation_const_ref_to_mutable = {$front_matter}: encountered reference to mutable memory in `const`
416+
417417
const_eval_validation_dangling_box_no_provenance = {$front_matter}: encountered a dangling box ({$pointer} has no provenance)
418418
const_eval_validation_dangling_box_out_of_bounds = {$front_matter}: encountered a dangling box (going beyond the bounds of its allocation)
419419
const_eval_validation_dangling_box_use_after_free = {$front_matter}: encountered a dangling box (use-after-free)

compiler/rustc_const_eval/src/const_eval/mod.rs

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

3-
use crate::interpret::InterpCx;
43
use rustc_middle::mir;
5-
use rustc_middle::mir::interpret::{InterpError, InterpErrorInfo};
4+
use rustc_middle::mir::interpret::InterpErrorInfo;
65
use rustc_middle::query::TyCtxtAt;
76
use rustc_middle::ty::{self, Ty};
87

8+
use crate::interpret::{format_interp_error, InterpCx};
9+
910
mod error;
1011
mod eval_queries;
1112
mod fn_queries;
@@ -25,24 +26,17 @@ pub(crate) enum ValTreeCreationError {
2526
NodesOverflow,
2627
/// Values of this type, or this particular value, are not supported as valtrees.
2728
NonSupportedType,
28-
/// The value pointed to non-read-only memory, so we cannot make it a valtree.
29-
NotReadOnly,
30-
Other,
3129
}
3230
pub(crate) type ValTreeCreationResult<'tcx> = Result<ty::ValTree<'tcx>, ValTreeCreationError>;
3331

3432
impl From<InterpErrorInfo<'_>> for ValTreeCreationError {
3533
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,
42-
}
43-
}
44-
_ => ValTreeCreationError::Other,
45-
}
34+
ty::tls::with(|tcx| {
35+
bug!(
36+
"Unexpected Undefined Behavior error during valtree construction: {}",
37+
format_interp_error(tcx.dcx(), err),
38+
)
39+
})
4640
}
4741
}
4842

compiler/rustc_const_eval/src/const_eval/valtrees.rs

+1-13
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use super::eval_queries::{mk_eval_cx, op_to_const};
99
use super::machine::CompileTimeEvalContext;
1010
use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES};
1111
use crate::const_eval::CanAccessMutGlobal;
12-
use crate::errors::{MaxNumNodesInConstErr, MutableDataInConstErr};
12+
use crate::errors::MaxNumNodesInConstErr;
1313
use crate::interpret::MPlaceTy;
1414
use crate::interpret::{
1515
intern_const_alloc_recursive, ImmTy, Immediate, InternKind, MemPlaceMeta, MemoryKind, PlaceTy,
@@ -249,18 +249,6 @@ pub(crate) fn eval_to_valtree<'tcx>(
249249
tcx.dcx().emit_err(MaxNumNodesInConstErr { span, global_const_id });
250250
Err(handled.into())
251251
}
252-
ValTreeCreationError::NotReadOnly => {
253-
let handled =
254-
tcx.dcx().emit_err(MutableDataInConstErr { span, global_const_id });
255-
Err(handled.into())
256-
}
257-
ValTreeCreationError::Other => {
258-
let handled = tcx.dcx().span_delayed_bug(
259-
span.unwrap_or(DUMMY_SP),
260-
"unexpected error during valtree construction",
261-
);
262-
Err(handled.into())
263-
}
264252
ValTreeCreationError::NonSupportedType => Ok(None),
265253
}
266254
}

compiler/rustc_const_eval/src/errors.rs

+2-8
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,6 @@ pub(crate) struct MaxNumNodesInConstErr {
117117
pub global_const_id: String,
118118
}
119119

120-
#[derive(Diagnostic)]
121-
#[diag(const_eval_mutable_data_in_const)]
122-
pub(crate) struct MutableDataInConstErr {
123-
#[primary_span]
124-
pub span: Option<Span>,
125-
pub global_const_id: String,
126-
}
127-
128120
#[derive(Diagnostic)]
129121
#[diag(const_eval_unallowed_fn_pointer_call)]
130122
pub(crate) struct UnallowedFnPointerCall {
@@ -619,6 +611,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
619611

620612
PointerAsInt { .. } => const_eval_validation_pointer_as_int,
621613
PartialPointer => const_eval_validation_partial_pointer,
614+
ConstRefToMutable => const_eval_validation_const_ref_to_mutable,
622615
MutableRefInConst => const_eval_validation_mutable_ref_in_const,
623616
MutableRefToImmutable => const_eval_validation_mutable_ref_to_immutable,
624617
NullFnPtr => const_eval_validation_null_fn_ptr,
@@ -773,6 +766,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
773766
NullPtr { .. }
774767
| PtrToStatic { .. }
775768
| MutableRefInConst
769+
| ConstRefToMutable
776770
| MutableRefToImmutable
777771
| NullFnPtr
778772
| NeverVal

compiler/rustc_const_eval/src/interpret/eval_context.rs

+21-21
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::{fmt, mem};
44
use either::{Either, Left, Right};
55

66
use hir::CRATE_HIR_ID;
7+
use rustc_errors::DiagCtxt;
78
use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData};
89
use rustc_index::IndexVec;
910
use rustc_middle::mir;
@@ -430,6 +431,26 @@ pub(super) fn from_known_layout<'tcx>(
430431
}
431432
}
432433

434+
/// Turn the given error into a human-readable string. Expects the string to be printed, so if
435+
/// `RUSTC_CTFE_BACKTRACE` is set this will show a backtrace of the rustc internals that
436+
/// triggered the error.
437+
///
438+
/// This is NOT the preferred way to render an error; use `report` from `const_eval` instead.
439+
/// However, this is useful when error messages appear in ICEs.
440+
pub fn format_interp_error<'tcx>(dcx: &DiagCtxt, e: InterpErrorInfo<'tcx>) -> String {
441+
let (e, backtrace) = e.into_parts();
442+
backtrace.print_backtrace();
443+
// FIXME(fee1-dead), HACK: we want to use the error as title therefore we can just extract the
444+
// label and arguments from the InterpError.
445+
#[allow(rustc::untranslatable_diagnostic)]
446+
let mut diag = dcx.struct_allow("");
447+
let msg = e.diagnostic_message();
448+
e.add_args(dcx, &mut diag);
449+
let s = dcx.eagerly_translate_to_string(msg, diag.args());
450+
diag.cancel();
451+
s
452+
}
453+
433454
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
434455
pub fn new(
435456
tcx: TyCtxt<'tcx>,
@@ -462,27 +483,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
462483
.map_or(CRATE_HIR_ID, |def_id| self.tcx.local_def_id_to_hir_id(def_id))
463484
}
464485

465-
/// Turn the given error into a human-readable string. Expects the string to be printed, so if
466-
/// `RUSTC_CTFE_BACKTRACE` is set this will show a backtrace of the rustc internals that
467-
/// triggered the error.
468-
///
469-
/// This is NOT the preferred way to render an error; use `report` from `const_eval` instead.
470-
/// However, this is useful when error messages appear in ICEs.
471-
pub fn format_error(&self, e: InterpErrorInfo<'tcx>) -> String {
472-
let (e, backtrace) = e.into_parts();
473-
backtrace.print_backtrace();
474-
// FIXME(fee1-dead), HACK: we want to use the error as title therefore we can just extract the
475-
// label and arguments from the InterpError.
476-
let dcx = self.tcx.dcx();
477-
#[allow(rustc::untranslatable_diagnostic)]
478-
let mut diag = dcx.struct_allow("");
479-
let msg = e.diagnostic_message();
480-
e.add_args(dcx, &mut diag);
481-
let s = dcx.eagerly_translate_to_string(msg, diag.args());
482-
diag.cancel();
483-
s
484-
}
485-
486486
#[inline(always)]
487487
pub(crate) fn stack(&self) -> &[Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>] {
488488
M::stack(self)

compiler/rustc_const_eval/src/interpret/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ mod visitor;
2020

2121
pub use rustc_middle::mir::interpret::*; // have all the `interpret` symbols in one place: here
2222

23-
pub use self::eval_context::{Frame, FrameInfo, InterpCx, StackPopCleanup};
23+
pub use self::eval_context::{format_interp_error, Frame, FrameInfo, InterpCx, StackPopCleanup};
2424
pub use self::intern::{
2525
intern_const_alloc_for_constprop, intern_const_alloc_recursive, InternKind,
2626
};

compiler/rustc_const_eval/src/interpret/validity.rs

+40-33
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ use rustc_target::abi::{
2727
use std::hash::Hash;
2828

2929
use super::{
30-
AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy,
31-
Machine, MemPlaceMeta, OpTy, Pointer, Projectable, Scalar, ValueVisitor,
30+
format_interp_error, AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx,
31+
InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Pointer, Projectable, Scalar,
32+
ValueVisitor,
3233
};
3334

3435
// for the validation errors
@@ -460,46 +461,49 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
460461
// Special handling for pointers to statics (irrespective of their type).
461462
assert!(!self.ecx.tcx.is_thread_local_static(did));
462463
assert!(self.ecx.tcx.is_static(did));
464+
let is_mut =
465+
matches!(self.ecx.tcx.def_kind(did), DefKind::Static(Mutability::Mut))
466+
|| !self
467+
.ecx
468+
.tcx
469+
.type_of(did)
470+
.no_bound_vars()
471+
.expect("statics should not have generic parameters")
472+
.is_freeze(*self.ecx.tcx, ty::ParamEnv::reveal_all());
463473
// Mutability check.
464474
if ptr_expected_mutbl == Mutability::Mut {
465-
if matches!(
466-
self.ecx.tcx.def_kind(did),
467-
DefKind::Static(Mutability::Not)
468-
) && self
469-
.ecx
470-
.tcx
471-
.type_of(did)
472-
.no_bound_vars()
473-
.expect("statics should not have generic parameters")
474-
.is_freeze(*self.ecx.tcx, ty::ParamEnv::reveal_all())
475-
{
475+
if !is_mut {
476476
throw_validation_failure!(self.path, MutableRefToImmutable);
477477
}
478478
}
479-
// We skip recursively checking other statics. These statics must be sound by
480-
// themselves, and the only way to get broken statics here is by using
481-
// unsafe code.
482-
// The reasons we don't check other statics is twofold. For one, in all
483-
// sound cases, the static was already validated on its own, and second, we
484-
// trigger cycle errors if we try to compute the value of the other static
485-
// and that static refers back to us.
486-
// We might miss const-invalid data,
487-
// but things are still sound otherwise (in particular re: consts
488-
// referring to statics).
489-
return Ok(());
479+
match self.ctfe_mode {
480+
Some(CtfeValidationMode::Static { .. }) => {
481+
// We skip recursively checking other statics. These statics must be sound by
482+
// themselves, and the only way to get broken statics here is by using
483+
// unsafe code.
484+
// The reasons we don't check other statics is twofold. For one, in all
485+
// sound cases, the static was already validated on its own, and second, we
486+
// trigger cycle errors if we try to compute the value of the other static
487+
// and that static refers back to us.
488+
// This could miss some UB, but that's fine.
489+
return Ok(());
490+
}
491+
Some(CtfeValidationMode::Const { .. }) => {
492+
// For consts on the other hand we have to recursively check;
493+
// pattern matching assumes a valid value. However we better make
494+
// sure this is not mutable.
495+
if is_mut {
496+
throw_validation_failure!(self.path, ConstRefToMutable);
497+
}
498+
}
499+
None => {}
500+
}
490501
}
491502
GlobalAlloc::Memory(alloc) => {
492503
if alloc.inner().mutability == Mutability::Mut
493504
&& matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. }))
494505
{
495-
// This is impossible: this can only be some inner allocation of a
496-
// `static mut` (everything else either hits the `GlobalAlloc::Static`
497-
// case or is interned immutably). To get such a pointer we'd have to
498-
// load it from a static, but such loads lead to a CTFE error.
499-
span_bug!(
500-
self.ecx.tcx.span,
501-
"encountered reference to mutable memory inside a `const`"
502-
);
506+
throw_validation_failure!(self.path, ConstRefToMutable);
503507
}
504508
if ptr_expected_mutbl == Mutability::Mut
505509
&& alloc.inner().mutability == Mutability::Not
@@ -978,7 +982,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
978982
// Complain about any other kind of error -- those are bad because we'd like to
979983
// report them in a way that shows *where* in the value the issue lies.
980984
Err(err) => {
981-
bug!("Unexpected error during validation: {}", self.format_error(err));
985+
bug!(
986+
"Unexpected error during validation: {}",
987+
format_interp_error(self.tcx.dcx(), err)
988+
);
982989
}
983990
}
984991
}

compiler/rustc_middle/src/mir/interpret/error.rs

+1
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@ pub enum ValidationErrorKind<'tcx> {
417417
PtrToUninhabited { ptr_kind: PointerKind, ty: Ty<'tcx> },
418418
PtrToStatic { ptr_kind: PointerKind },
419419
MutableRefInConst,
420+
ConstRefToMutable,
420421
MutableRefToImmutable,
421422
UnsafeCellInImmutable,
422423
NullFnPtr,

compiler/rustc_mir_transform/src/const_prop_lint.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
44
use std::fmt::Debug;
55

6-
use rustc_const_eval::interpret::{ImmTy, Projectable};
7-
use rustc_const_eval::interpret::{InterpCx, InterpResult, Scalar};
6+
use rustc_const_eval::interpret::{
7+
format_interp_error, ImmTy, InterpCx, InterpResult, Projectable, Scalar,
8+
};
89
use rustc_data_structures::fx::FxHashSet;
910
use rustc_hir::def::DefKind;
1011
use rustc_hir::HirId;
@@ -246,7 +247,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
246247
assert!(
247248
!error.kind().formatted_string(),
248249
"const-prop encountered formatting error: {}",
249-
self.ecx.format_error(error),
250+
format_interp_error(self.ecx.tcx.dcx(), error),
250251
);
251252
None
252253
}

src/tools/miri/src/diagnostics.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ pub fn report_error<'tcx, 'mir>(
290290
) =>
291291
{
292292
ecx.handle_ice(); // print interpreter backtrace
293-
bug!("This validation error should be impossible in Miri: {}", ecx.format_error(e));
293+
bug!("This validation error should be impossible in Miri: {}", format_interp_error(ecx.tcx.dcx(), e));
294294
}
295295
UndefinedBehavior(_) => "Undefined Behavior",
296296
ResourceExhaustion(_) => "resource exhaustion",
@@ -304,7 +304,7 @@ pub fn report_error<'tcx, 'mir>(
304304
) => "post-monomorphization error",
305305
_ => {
306306
ecx.handle_ice(); // print interpreter backtrace
307-
bug!("This error should be impossible in Miri: {}", ecx.format_error(e));
307+
bug!("This error should be impossible in Miri: {}", format_interp_error(ecx.tcx.dcx(), e));
308308
}
309309
};
310310
#[rustfmt::skip]
@@ -370,7 +370,7 @@ pub fn report_error<'tcx, 'mir>(
370370
_ => {}
371371
}
372372

373-
msg.insert(0, ecx.format_error(e));
373+
msg.insert(0, format_interp_error(ecx.tcx.dcx(), e));
374374

375375
report_msg(
376376
DiagLevel::Error,

tests/ui/consts/const_refs_to_static_fail.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1+
// normalize-stderr-test "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)"
2+
// normalize-stderr-test "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?(<imm>)?─*╼ )+ *│.*" -> "HEX_DUMP"
13
#![feature(const_refs_to_static, const_mut_refs, sync_unsafe_cell)]
24
use std::cell::SyncUnsafeCell;
35

46
static S: SyncUnsafeCell<i32> = SyncUnsafeCell::new(0);
57
static mut S_MUT: i32 = 0;
68

7-
const C1: &SyncUnsafeCell<i32> = &S;
9+
const C1: &SyncUnsafeCell<i32> = &S; //~ERROR undefined behavior
10+
//~| encountered reference to mutable memory
811
const C1_READ: () = unsafe {
9-
assert!(*C1.get() == 0); //~ERROR evaluation of constant value failed
10-
//~^ constant accesses mutable global memory
12+
assert!(*C1.get() == 0);
1113
};
1214
const C2: *const i32 = unsafe { std::ptr::addr_of!(S_MUT) };
1315
const C2_READ: () = unsafe {

0 commit comments

Comments
 (0)