Skip to content

Commit 6b8c6b4

Browse files
authored
Unrolled build for #140942
Rollup merge of #140942 - RalfJung:const-ref-to-mut, r=oli-obk const-eval: allow constants to refer to mutable/external memory, but reject such constants as patterns This fixes #140653 by accepting code such as this: ```rust static FOO: AtomicU32 = AtomicU32::new(0); const C: &'static AtomicU32 = &FOO; ``` This can be written entirely in safe code, so there can't really be anything wrong with it. We also accept the much more questionable following code, since it looks very similar to the interpreter: ```rust static mut FOO2: u32 = 0; const C2: &'static u32 = unsafe { &mut FOO2 }; ``` Using this without causing UB is at least very hard (the details are unclear since it is related to how the aliasing model deals with the staging of const-eval vs runtime code). If a constant like `C2` is used as a pattern, we emit an error: ``` error: constant BAD_PATTERN cannot be used as pattern --> $DIR/const_refs_to_static_fail.rs:30:9 | LL | BAD_PATTERN => {}, | ^^^^^^^^^^^ | = note: constants that reference mutable or external memory cannot be used as pattern ``` (If you somehow manage to build a pattern with constant `C`, you'd get the same error, but that should be impossible: we don't have a type that can be used in patterns and that has interior mutability.) The same treatment is afforded for shared references to `extern static`, for the same reason: the const evaluation is entirely fine with it, we just can't build a pattern for it -- and when using interior mutability, this can be totally sound. We do still not accept anything where there is an `&mut` in the final value of the const, as that should always require unsafe code and it's hard to imagine a sound use-case that would require this.
2 parents 513999b + bade3fd commit 6b8c6b4

File tree

61 files changed

+448
-373
lines changed

Some content is hidden

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

61 files changed

+448
-373
lines changed

compiler/rustc_const_eval/messages.ftl

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,13 @@ const_eval_incompatible_return_types =
124124
const_eval_incompatible_types =
125125
calling a function with argument of type {$callee_ty} passing data of type {$caller_ty}
126126
127-
const_eval_interior_mutable_ref_escaping =
128-
{const_eval_const_context}s cannot refer to interior mutable data
129-
.label = this borrow of an interior mutable value may end up in the final value
127+
const_eval_interior_mutable_borrow_escaping =
128+
interior mutable shared borrows of lifetime-extended temporaries in the top-level scope of a {const_eval_const_context} are not allowed
129+
.label = this borrow of an interior mutable value refers to a lifetime-extended temporary
130130
.help = to fix this, the value can be extracted to a separate `static` item and then referenced
131131
.teach_note =
132-
References that escape into the final value of a constant or static must be immutable.
132+
This creates a raw pointer to a temporary that has its lifetime extended to last for the entire program.
133+
Lifetime-extended temporaries in constants and statics must be immutable.
133134
This is to avoid accidentally creating shared mutable state.
134135
135136
@@ -207,33 +208,23 @@ const_eval_long_running =
207208
.label = the const evaluator is currently interpreting this expression
208209
.help = the constant being evaluated
209210
210-
const_eval_max_num_nodes_in_const = maximum number of nodes exceeded in constant {$global_const_id}
211-
212211
const_eval_memory_exhausted =
213212
tried to allocate more memory than available to compiler
214213
215214
const_eval_modified_global =
216215
modifying a static's initial value from another static's initializer
217216
218-
const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind}
219-
220-
const_eval_mutable_raw_escaping =
221-
raw mutable pointers are not allowed in the final value of {const_eval_const_context}s
217+
const_eval_mutable_borrow_escaping =
218+
mutable borrows of lifetime-extended temporaries in the top-level scope of a {const_eval_const_context} are not allowed
222219
.teach_note =
223-
Pointers that escape into the final value of a constant or static must be immutable.
220+
This creates a reference to a temporary that has its lifetime extended to last for the entire program.
221+
Lifetime-extended temporaries in constants and statics must be immutable.
224222
This is to avoid accidentally creating shared mutable state.
225223
226224
227225
If you really want global mutable state, try using an interior mutable `static` or a `static mut`.
228226
229-
const_eval_mutable_ref_escaping =
230-
mutable references are not allowed in the final value of {const_eval_const_context}s
231-
.teach_note =
232-
References that escape into the final value of a constant or static must be immutable.
233-
This is to avoid accidentally creating shared mutable state.
234-
235-
236-
If you really want global mutable state, try using an interior mutable `static` or a `static mut`.
227+
const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind}
237228
238229
const_eval_nested_static_in_thread_local = #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead
239230
@@ -437,9 +428,6 @@ const_eval_unwind_past_top =
437428
## (We'd love to sort this differently to make that more clear but tidy won't let us...)
438429
const_eval_validation_box_to_uninhabited = {$front_matter}: encountered a box pointing to uninhabited type {$ty}
439430
440-
const_eval_validation_const_ref_to_extern = {$front_matter}: encountered reference to `extern` static in `const`
441-
const_eval_validation_const_ref_to_mutable = {$front_matter}: encountered reference to mutable memory in `const`
442-
443431
const_eval_validation_dangling_box_no_provenance = {$front_matter}: encountered a dangling box ({$pointer} has no provenance)
444432
const_eval_validation_dangling_box_out_of_bounds = {$front_matter}: encountered a dangling box (going beyond the bounds of its allocation)
445433
const_eval_validation_dangling_box_use_after_free = {$front_matter}: encountered a dangling box (use-after-free)
@@ -479,6 +467,7 @@ const_eval_validation_invalid_ref_meta = {$front_matter}: encountered invalid re
479467
const_eval_validation_invalid_ref_slice_meta = {$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object
480468
const_eval_validation_invalid_vtable_ptr = {$front_matter}: encountered {$value}, but expected a vtable pointer
481469
const_eval_validation_invalid_vtable_trait = {$front_matter}: wrong trait in wide pointer vtable: expected `{$expected_dyn_type}`, but encountered `{$vtable_dyn_type}`
470+
const_eval_validation_mutable_ref_in_const = {$front_matter}: encountered mutable reference in `const` value
482471
const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory
483472
const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!`
484473
const_eval_validation_null_box = {$front_matter}: encountered a null box

compiler/rustc_const_eval/src/check_consts/check.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -595,11 +595,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
595595
self.const_kind() == hir::ConstContext::Static(hir::Mutability::Mut);
596596

597597
if !is_allowed && self.place_may_escape(place) {
598-
self.check_op(ops::EscapingMutBorrow(if matches!(rvalue, Rvalue::Ref(..)) {
599-
hir::BorrowKind::Ref
600-
} else {
601-
hir::BorrowKind::Raw
602-
}));
598+
self.check_op(ops::EscapingMutBorrow);
603599
}
604600
}
605601

compiler/rustc_const_eval/src/check_consts/ops.rs

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ impl<'tcx> NonConstOp<'tcx> for EscapingCellBorrow {
567567
DiagImportance::Secondary
568568
}
569569
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
570-
ccx.dcx().create_err(errors::InteriorMutableRefEscaping {
570+
ccx.dcx().create_err(errors::InteriorMutableBorrowEscaping {
571571
span,
572572
opt_help: matches!(ccx.const_kind(), hir::ConstContext::Static(_)),
573573
kind: ccx.const_kind(),
@@ -580,7 +580,7 @@ impl<'tcx> NonConstOp<'tcx> for EscapingCellBorrow {
580580
/// This op is for `&mut` borrows in the trailing expression of a constant
581581
/// which uses the "enclosing scopes rule" to leak its locals into anonymous
582582
/// static or const items.
583-
pub(crate) struct EscapingMutBorrow(pub hir::BorrowKind);
583+
pub(crate) struct EscapingMutBorrow;
584584

585585
impl<'tcx> NonConstOp<'tcx> for EscapingMutBorrow {
586586
fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
@@ -594,20 +594,11 @@ impl<'tcx> NonConstOp<'tcx> for EscapingMutBorrow {
594594
}
595595

596596
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
597-
match self.0 {
598-
hir::BorrowKind::Raw => ccx.tcx.dcx().create_err(errors::MutableRawEscaping {
599-
span,
600-
kind: ccx.const_kind(),
601-
teach: ccx.tcx.sess.teach(E0764),
602-
}),
603-
hir::BorrowKind::Ref | hir::BorrowKind::Pin => {
604-
ccx.dcx().create_err(errors::MutableRefEscaping {
605-
span,
606-
kind: ccx.const_kind(),
607-
teach: ccx.tcx.sess.teach(E0764),
608-
})
609-
}
610-
}
597+
ccx.dcx().create_err(errors::MutableBorrowEscaping {
598+
span,
599+
kind: ccx.const_kind(),
600+
teach: ccx.tcx.sess.teach(E0764),
601+
})
611602
}
612603
}
613604

compiler/rustc_const_eval/src/const_eval/mod.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,6 @@ pub(crate) use self::valtrees::{eval_to_valtree, valtree_to_const_value};
2626
// We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes.
2727
const VALTREE_MAX_NODES: usize = 100000;
2828

29-
pub(crate) enum ValTreeCreationError<'tcx> {
30-
NodesOverflow,
31-
/// Values of this type, or this particular value, are not supported as valtrees.
32-
NonSupportedType(Ty<'tcx>),
33-
}
34-
pub(crate) type ValTreeCreationResult<'tcx> = Result<ty::ValTree<'tcx>, ValTreeCreationError<'tcx>>;
35-
3629
#[instrument(skip(tcx), level = "debug")]
3730
pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>(
3831
tcx: TyCtxt<'tcx>,

compiler/rustc_const_eval/src/const_eval/valtrees.rs

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
use rustc_abi::{BackendRepr, FieldIdx, VariantIdx};
22
use rustc_data_structures::stack::ensure_sufficient_stack;
3-
use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId, ReportedErrorInfo};
3+
use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId, ValTreeCreationError};
44
use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
55
use rustc_middle::ty::{self, Ty, TyCtxt};
66
use rustc_middle::{bug, mir};
77
use rustc_span::DUMMY_SP;
88
use tracing::{debug, instrument, trace};
99

10+
use super::VALTREE_MAX_NODES;
1011
use super::eval_queries::{mk_eval_cx_to_read_const_val, op_to_const};
1112
use super::machine::CompileTimeInterpCx;
12-
use super::{VALTREE_MAX_NODES, ValTreeCreationError, ValTreeCreationResult};
1313
use crate::const_eval::CanAccessMutGlobal;
14-
use crate::errors::MaxNumNodesInConstErr;
1514
use crate::interpret::{
1615
ImmTy, Immediate, InternKind, MPlaceTy, MemPlaceMeta, MemoryKind, PlaceTy, Projectable, Scalar,
1716
intern_const_alloc_recursive,
@@ -24,7 +23,7 @@ fn branches<'tcx>(
2423
field_count: usize,
2524
variant: Option<VariantIdx>,
2625
num_nodes: &mut usize,
27-
) -> ValTreeCreationResult<'tcx> {
26+
) -> EvalToValTreeResult<'tcx> {
2827
let place = match variant {
2928
Some(variant) => ecx.project_downcast(place, variant).unwrap(),
3029
None => place.clone(),
@@ -58,7 +57,7 @@ fn slice_branches<'tcx>(
5857
ecx: &CompileTimeInterpCx<'tcx>,
5958
place: &MPlaceTy<'tcx>,
6059
num_nodes: &mut usize,
61-
) -> ValTreeCreationResult<'tcx> {
60+
) -> EvalToValTreeResult<'tcx> {
6261
let n = place.len(ecx).unwrap_or_else(|_| panic!("expected to use len of place {place:?}"));
6362

6463
let mut elems = Vec::with_capacity(n as usize);
@@ -76,7 +75,7 @@ fn const_to_valtree_inner<'tcx>(
7675
ecx: &CompileTimeInterpCx<'tcx>,
7776
place: &MPlaceTy<'tcx>,
7877
num_nodes: &mut usize,
79-
) -> ValTreeCreationResult<'tcx> {
78+
) -> EvalToValTreeResult<'tcx> {
8079
let tcx = *ecx.tcx;
8180
let ty = place.layout.ty;
8281
debug!("ty kind: {:?}", ty.kind());
@@ -91,7 +90,7 @@ fn const_to_valtree_inner<'tcx>(
9190
Ok(ty::ValTree::zst(tcx))
9291
}
9392
ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
94-
let val = ecx.read_immediate(place).unwrap();
93+
let val = ecx.read_immediate(place).report_err()?;
9594
let val = val.to_scalar_int().unwrap();
9695
*num_nodes += 1;
9796

@@ -113,7 +112,7 @@ fn const_to_valtree_inner<'tcx>(
113112
// equality at compile-time (see `ptr_guaranteed_cmp`).
114113
// However we allow those that are just integers in disguise.
115114
// First, get the pointer. Remember it might be wide!
116-
let val = ecx.read_immediate(place).unwrap();
115+
let val = ecx.read_immediate(place).report_err()?;
117116
// We could allow wide raw pointers where both sides are integers in the future,
118117
// but for now we reject them.
119118
if matches!(val.layout.backend_repr, BackendRepr::ScalarPair(..)) {
@@ -134,7 +133,7 @@ fn const_to_valtree_inner<'tcx>(
134133
ty::FnPtr(..) => Err(ValTreeCreationError::NonSupportedType(ty)),
135134

136135
ty::Ref(_, _, _) => {
137-
let derefd_place = ecx.deref_pointer(place).unwrap();
136+
let derefd_place = ecx.deref_pointer(place).report_err()?;
138137
const_to_valtree_inner(ecx, &derefd_place, num_nodes)
139138
}
140139

@@ -158,7 +157,7 @@ fn const_to_valtree_inner<'tcx>(
158157
bug!("uninhabited types should have errored and never gotten converted to valtree")
159158
}
160159

161-
let variant = ecx.read_discriminant(place).unwrap();
160+
let variant = ecx.read_discriminant(place).report_err()?;
162161
branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant), num_nodes)
163162
}
164163

@@ -249,24 +248,7 @@ pub(crate) fn eval_to_valtree<'tcx>(
249248
debug!(?place);
250249

251250
let mut num_nodes = 0;
252-
let valtree_result = const_to_valtree_inner(&ecx, &place, &mut num_nodes);
253-
254-
match valtree_result {
255-
Ok(valtree) => Ok(Ok(valtree)),
256-
Err(err) => {
257-
let did = cid.instance.def_id();
258-
let global_const_id = cid.display(tcx);
259-
let span = tcx.hir_span_if_local(did);
260-
match err {
261-
ValTreeCreationError::NodesOverflow => {
262-
let handled =
263-
tcx.dcx().emit_err(MaxNumNodesInConstErr { span, global_const_id });
264-
Err(ReportedErrorInfo::allowed_in_infallible(handled).into())
265-
}
266-
ValTreeCreationError::NonSupportedType(ty) => Ok(Err(ty)),
267-
}
268-
}
269-
}
251+
const_to_valtree_inner(&ecx, &place, &mut num_nodes)
270252
}
271253

272254
/// Converts a `ValTree` to a `ConstValue`, which is needed after mir

compiler/rustc_const_eval/src/errors.rs

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,6 @@ pub(crate) struct PanicNonStrErr {
9292
pub span: Span,
9393
}
9494

95-
#[derive(Diagnostic)]
96-
#[diag(const_eval_max_num_nodes_in_const)]
97-
pub(crate) struct MaxNumNodesInConstErr {
98-
#[primary_span]
99-
pub span: Option<Span>,
100-
pub global_const_id: String,
101-
}
102-
10395
#[derive(Diagnostic)]
10496
#[diag(const_eval_unallowed_fn_pointer_call)]
10597
pub(crate) struct UnallowedFnPointerCall {
@@ -158,24 +150,15 @@ pub(crate) struct UnmarkedIntrinsicExposed {
158150
}
159151

160152
#[derive(Diagnostic)]
161-
#[diag(const_eval_mutable_ref_escaping, code = E0764)]
162-
pub(crate) struct MutableRefEscaping {
153+
#[diag(const_eval_mutable_borrow_escaping, code = E0764)]
154+
pub(crate) struct MutableBorrowEscaping {
163155
#[primary_span]
164156
pub span: Span,
165157
pub kind: ConstContext,
166158
#[note(const_eval_teach_note)]
167159
pub teach: bool,
168160
}
169161

170-
#[derive(Diagnostic)]
171-
#[diag(const_eval_mutable_raw_escaping, code = E0764)]
172-
pub(crate) struct MutableRawEscaping {
173-
#[primary_span]
174-
pub span: Span,
175-
pub kind: ConstContext,
176-
#[note(const_eval_teach_note)]
177-
pub teach: bool,
178-
}
179162
#[derive(Diagnostic)]
180163
#[diag(const_eval_non_const_fmt_macro_call, code = E0015)]
181164
pub(crate) struct NonConstFmtMacroCall {
@@ -233,8 +216,8 @@ pub(crate) struct UnallowedInlineAsm {
233216
}
234217

235218
#[derive(Diagnostic)]
236-
#[diag(const_eval_interior_mutable_ref_escaping, code = E0492)]
237-
pub(crate) struct InteriorMutableRefEscaping {
219+
#[diag(const_eval_interior_mutable_borrow_escaping, code = E0492)]
220+
pub(crate) struct InteriorMutableBorrowEscaping {
238221
#[primary_span]
239222
#[label]
240223
pub span: Span,
@@ -655,9 +638,8 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
655638

656639
PointerAsInt { .. } => const_eval_validation_pointer_as_int,
657640
PartialPointer => const_eval_validation_partial_pointer,
658-
ConstRefToMutable => const_eval_validation_const_ref_to_mutable,
659-
ConstRefToExtern => const_eval_validation_const_ref_to_extern,
660641
MutableRefToImmutable => const_eval_validation_mutable_ref_to_immutable,
642+
MutableRefInConst => const_eval_validation_mutable_ref_in_const,
661643
NullFnPtr => const_eval_validation_null_fn_ptr,
662644
NeverVal => const_eval_validation_never_val,
663645
NullablePtrOutOfRange { .. } => const_eval_validation_nullable_ptr_out_of_range,
@@ -815,9 +797,8 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
815797
err.arg("expected_dyn_type", expected_dyn_type.to_string());
816798
}
817799
NullPtr { .. }
818-
| ConstRefToMutable
819-
| ConstRefToExtern
820800
| MutableRefToImmutable
801+
| MutableRefInConst
821802
| NullFnPtr
822803
| NeverVal
823804
| UnsafeCellInImmutable

compiler/rustc_const_eval/src/interpret/validity.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,8 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
570570
};
571571
let (size, _align) =
572572
global_alloc.size_and_align(*self.ecx.tcx, self.ecx.typing_env);
573+
let alloc_actual_mutbl =
574+
global_alloc.mutability(*self.ecx.tcx, self.ecx.typing_env);
573575

574576
if let GlobalAlloc::Static(did) = global_alloc {
575577
let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else {
@@ -597,9 +599,11 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
597599
skip_recursive_check = !nested;
598600
}
599601
CtfeValidationMode::Const { .. } => {
600-
// We can't recursively validate `extern static`, so we better reject them.
601-
if self.ecx.tcx.is_foreign_item(did) {
602-
throw_validation_failure!(self.path, ConstRefToExtern);
602+
// If this is mutable memory or an `extern static`, there's no point in checking it -- we'd
603+
// just get errors trying to read the value.
604+
if alloc_actual_mutbl.is_mut() || self.ecx.tcx.is_foreign_item(did)
605+
{
606+
skip_recursive_check = true;
603607
}
604608
}
605609
}
@@ -618,22 +622,17 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
618622
mutbl
619623
}
620624
};
621-
// Determine what it actually points to.
622-
let alloc_actual_mutbl =
623-
global_alloc.mutability(*self.ecx.tcx, self.ecx.typing_env);
624625
// Mutable pointer to immutable memory is no good.
625626
if ptr_expected_mutbl == Mutability::Mut
626627
&& alloc_actual_mutbl == Mutability::Not
627628
{
628629
// This can actually occur with transmutes.
629630
throw_validation_failure!(self.path, MutableRefToImmutable);
630631
}
631-
// In a const, everything must be completely immutable.
632+
// In a const, any kind of mutable reference is not good.
632633
if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. })) {
633-
if ptr_expected_mutbl == Mutability::Mut
634-
|| alloc_actual_mutbl == Mutability::Mut
635-
{
636-
throw_validation_failure!(self.path, ConstRefToMutable);
634+
if ptr_expected_mutbl == Mutability::Mut {
635+
throw_validation_failure!(self.path, MutableRefInConst);
637636
}
638637
}
639638
}

0 commit comments

Comments
 (0)