Skip to content

Commit 160cee0

Browse files
committed
const-eval: allow constants to refer to mutable/external memory, but reject such constants as patterns
1 parent e964cca commit 160cee0

35 files changed

+338
-301
lines changed

compiler/rustc_const_eval/messages.ftl

+1-5
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,6 @@ const_eval_long_running =
209209
.label = the const evaluator is currently interpreting this expression
210210
.help = the constant being evaluated
211211
212-
const_eval_max_num_nodes_in_const = maximum number of nodes exceeded in constant {$global_const_id}
213-
214212
const_eval_memory_exhausted =
215213
tried to allocate more memory than available to compiler
216214
@@ -440,9 +438,6 @@ const_eval_unwind_past_top =
440438
## (We'd love to sort this differently to make that more clear but tidy won't let us...)
441439
const_eval_validation_box_to_uninhabited = {$front_matter}: encountered a box pointing to uninhabited type {$ty}
442440
443-
const_eval_validation_const_ref_to_extern = {$front_matter}: encountered reference to `extern` static in `const`
444-
const_eval_validation_const_ref_to_mutable = {$front_matter}: encountered reference to mutable memory in `const`
445-
446441
const_eval_validation_dangling_box_no_provenance = {$front_matter}: encountered a dangling box ({$pointer} has no provenance)
447442
const_eval_validation_dangling_box_out_of_bounds = {$front_matter}: encountered a dangling box (going beyond the bounds of its allocation)
448443
const_eval_validation_dangling_box_use_after_free = {$front_matter}: encountered a dangling box (use-after-free)
@@ -482,6 +477,7 @@ const_eval_validation_invalid_ref_meta = {$front_matter}: encountered invalid re
482477
const_eval_validation_invalid_ref_slice_meta = {$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object
483478
const_eval_validation_invalid_vtable_ptr = {$front_matter}: encountered {$value}, but expected a vtable pointer
484479
const_eval_validation_invalid_vtable_trait = {$front_matter}: wrong trait in wide pointer vtable: expected `{$expected_dyn_type}`, but encountered `{$vtable_dyn_type}`
480+
const_eval_validation_mutable_ref_in_const = {$front_matter}: encountered mutable reference in `const` value
485481
const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory
486482
const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!`
487483
const_eval_validation_null_box = {$front_matter}: encountered a null box

compiler/rustc_const_eval/src/const_eval/mod.rs

-7
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

+10-28
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
use rustc_abi::{BackendRepr, 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

+2-12
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,6 @@ pub(crate) struct PanicNonStrErr {
9797
pub span: Span,
9898
}
9999

100-
#[derive(Diagnostic)]
101-
#[diag(const_eval_max_num_nodes_in_const)]
102-
pub(crate) struct MaxNumNodesInConstErr {
103-
#[primary_span]
104-
pub span: Option<Span>,
105-
pub global_const_id: String,
106-
}
107-
108100
#[derive(Diagnostic)]
109101
#[diag(const_eval_unallowed_fn_pointer_call)]
110102
pub(crate) struct UnallowedFnPointerCall {
@@ -691,9 +683,8 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
691683

692684
PointerAsInt { .. } => const_eval_validation_pointer_as_int,
693685
PartialPointer => const_eval_validation_partial_pointer,
694-
ConstRefToMutable => const_eval_validation_const_ref_to_mutable,
695-
ConstRefToExtern => const_eval_validation_const_ref_to_extern,
696686
MutableRefToImmutable => const_eval_validation_mutable_ref_to_immutable,
687+
MutableRefInConst => const_eval_validation_mutable_ref_in_const,
697688
NullFnPtr => const_eval_validation_null_fn_ptr,
698689
NeverVal => const_eval_validation_never_val,
699690
NullablePtrOutOfRange { .. } => const_eval_validation_nullable_ptr_out_of_range,
@@ -851,9 +842,8 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
851842
err.arg("expected_dyn_type", expected_dyn_type.to_string());
852843
}
853844
NullPtr { .. }
854-
| ConstRefToMutable
855-
| ConstRefToExtern
856845
| MutableRefToImmutable
846+
| MutableRefInConst
857847
| NullFnPtr
858848
| NeverVal
859849
| UnsafeCellInImmutable

compiler/rustc_const_eval/src/interpret/validity.rs

+10-11
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 on 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
}

compiler/rustc_middle/messages.ftl

+7
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ middle_erroneous_constant = erroneous constant encountered
7878
middle_failed_writing_file =
7979
failed to write file {$path}: {$error}"
8080
81+
# Note: We only mention patterns here since the error can only occur with references, and those
82+
# are forbidden in const generics.
83+
middle_invalid_const_in_valtree = constant {$global_const_id} cannot be used as pattern
84+
.note = constants that reference mutable or external memory cannot be used as pattern
85+
8186
middle_layout_cycle =
8287
a cycle occurred during layout computation
8388
@@ -95,6 +100,8 @@ middle_layout_too_generic = the type `{$ty}` does not have a fixed layout
95100
middle_layout_unknown =
96101
the type `{$ty}` has an unknown layout
97102
103+
middle_max_num_nodes_in_valtree = maximum number of nodes exceeded in constant {$global_const_id}
104+
98105
middle_opaque_hidden_type_mismatch =
99106
concrete type differs from previous defining opaque type use
100107
.label = expected `{$self_ty}`, got `{$other_ty}`

compiler/rustc_middle/src/error.rs

+17
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,20 @@ pub(crate) struct TypeLengthLimit {
170170
pub path: PathBuf,
171171
pub type_length: usize,
172172
}
173+
174+
#[derive(Diagnostic)]
175+
#[diag(middle_max_num_nodes_in_valtree)]
176+
pub(crate) struct MaxNumNodesInValtree {
177+
#[primary_span]
178+
pub span: Span,
179+
pub global_const_id: String,
180+
}
181+
182+
#[derive(Diagnostic)]
183+
#[diag(middle_invalid_const_in_valtree)]
184+
#[note]
185+
pub(crate) struct InvalidConstInValtree {
186+
#[primary_span]
187+
pub span: Span,
188+
pub global_const_id: String,
189+
}

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

+44-8
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ impl From<ReportedErrorInfo> for ErrorHandled {
3535
}
3636

3737
impl ErrorHandled {
38-
pub fn with_span(self, span: Span) -> Self {
38+
pub(crate) fn with_span(self, span: Span) -> Self {
3939
match self {
4040
ErrorHandled::Reported(err, _span) => ErrorHandled::Reported(err, span),
4141
ErrorHandled::TooGeneric(_span) => ErrorHandled::TooGeneric(span),
@@ -94,14 +94,51 @@ impl From<ReportedErrorInfo> for ErrorGuaranteed {
9494
}
9595
}
9696

97+
/// An error type for the `const_to_valtree` query. Some error should be reported with a "use-site span",
98+
/// which means the query cannot emit the error, so those errors are represented as dedicated variants here.
99+
#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
100+
pub enum ValTreeCreationError<'tcx> {
101+
/// The constant is too big to be valtree'd.
102+
NodesOverflow,
103+
/// The constant references mutable or external memory, so it cannot be valtree'd.
104+
InvalidConst,
105+
/// Values of this type, or this particular value, are not supported as valtrees.
106+
NonSupportedType(Ty<'tcx>),
107+
/// The error has already been handled by const evaluation.
108+
ErrorHandled(ErrorHandled),
109+
}
110+
111+
impl<'tcx> From<ErrorHandled> for ValTreeCreationError<'tcx> {
112+
fn from(err: ErrorHandled) -> Self {
113+
ValTreeCreationError::ErrorHandled(err)
114+
}
115+
}
116+
117+
impl<'tcx> From<InterpErrorInfo<'tcx>> for ValTreeCreationError<'tcx> {
118+
fn from(err: InterpErrorInfo<'tcx>) -> Self {
119+
// An error ocurred outside the const-eval query, as part of constructing the valtree. We
120+
// don't currently preserve the details of this error, since `InterpErrorInfo` cannot be put
121+
// into a query result and it can only be access of some mutable or external memory.
122+
let (_kind, backtrace) = err.into_parts();
123+
backtrace.print_backtrace();
124+
ValTreeCreationError::InvalidConst
125+
}
126+
}
127+
128+
impl<'tcx> ValTreeCreationError<'tcx> {
129+
pub(crate) fn with_span(self, span: Span) -> Self {
130+
use ValTreeCreationError::*;
131+
match self {
132+
ErrorHandled(handled) => ErrorHandled(handled.with_span(span)),
133+
other => other,
134+
}
135+
}
136+
}
137+
97138
pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
98139
pub type EvalStaticInitializerRawResult<'tcx> = Result<ConstAllocation<'tcx>, ErrorHandled>;
99140
pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
100-
/// `Ok(Err(ty))` indicates the constant was fine, but the valtree couldn't be constructed
101-
/// because the value contains something of type `ty` that is not valtree-compatible.
102-
/// The caller can then show an appropriate error; the query does not have the
103-
/// necessary context to give good user-facing errors for this case.
104-
pub type EvalToValTreeResult<'tcx> = Result<Result<ValTree<'tcx>, Ty<'tcx>>, ErrorHandled>;
141+
pub type EvalToValTreeResult<'tcx> = Result<ValTree<'tcx>, ValTreeCreationError<'tcx>>;
105142

106143
#[cfg(target_pointer_width = "64")]
107144
rustc_data_structures::static_assert_size!(InterpErrorInfo<'_>, 8);
@@ -450,10 +487,9 @@ pub enum ValidationErrorKind<'tcx> {
450487
ptr_kind: PointerKind,
451488
ty: Ty<'tcx>,
452489
},
453-
ConstRefToMutable,
454-
ConstRefToExtern,
455490
MutableRefToImmutable,
456491
UnsafeCellInImmutable,
492+
MutableRefInConst,
457493
NullFnPtr,
458494
NeverVal,
459495
NullablePtrOutOfRange {

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ pub use self::error::{
3838
EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, ExpectedKind,
3939
InterpErrorInfo, InterpErrorKind, InterpResult, InvalidMetaKind, InvalidProgramInfo,
4040
MachineStopType, Misalignment, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo,
41-
ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo,
42-
ValidationErrorKind, interp_ok,
41+
ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, ValTreeCreationError,
42+
ValidationErrorInfo, ValidationErrorKind, interp_ok,
4343
};
4444
pub use self::pointer::{CtfeProvenance, Pointer, PointerArithmetic, Provenance};
4545
pub use self::value::Scalar;

0 commit comments

Comments
 (0)