1//! This module specifies the type based interner for constants.
2//!
3//! After a const evaluation has computed a value, before we destroy the const evaluator's session
4//! memory, we need to extract all memory allocations to the global memory pool so they stay around.
5//!
6//! In principle, this is not very complicated: we recursively walk the final value, follow all the
7//! pointers, and move all reachable allocations to the global `tcx` memory. The only complication
8//! is picking the right mutability: the outermost allocation generally has a clear mutability, but
9//! what about the other allocations it points to that have also been created with this value? We
10//! don't want to do guesswork here. The rules are: `static`, `const`, and promoted can only create
11//! immutable allocations that way. `static mut` can be initialized with expressions like `&mut 42`,
12//! so all inner allocations are marked mutable. Some of them could potentially be made immutable,
13//! but that would require relying on type information, and given how many ways Rust has to lie
14//! about type information, we want to avoid doing that.
1516use hir::def::DefKind;
17use rustc_ast::Mutability;
18use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
19use rustc_hir::definitions::{DefPathData, PerParentDisambiguatorState};
20use rustc_hir::{selfas hir};
21use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
22use rustc_middle::mir::interpret::{
23AllocBytes, ConstAllocation, CtfeProvenance, InterpResult, Provenance,
24};
25use rustc_middle::query::TyCtxtAt;
26use rustc_middle::span_bug;
27use rustc_middle::ty::TyCtxt;
28use rustc_middle::ty::layout::TyAndLayout;
29use rustc_span::def_id::LocalDefId;
30use tracing::{instrument, trace};
3132use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, interp_ok};
33use crate::const_eval::DummyMachine;
34use crate::{const_eval, errors};
3536pub trait CompileTimeMachine<'tcx> = Machine<
37'tcx,
38 MemoryKind = const_eval::MemoryKind,
39 Provenance = CtfeProvenance,
40 ExtraFnVal = !,
41 FrameExtra = (),
42 AllocExtra = (),
43 MemoryMap = FxIndexMap<AllocId, (MemoryKind<const_eval::MemoryKind>, Allocation)>,
44 > + HasStaticRootDefId;
4546pub trait HasStaticRootDefId {
47/// Returns the `DefId` of the static item that is currently being evaluated.
48 /// Used for interning to be able to handle nested allocations.
49fn static_def_id(&self) -> Option<LocalDefId>;
50}
5152impl HasStaticRootDefIdfor const_eval::CompileTimeMachine<'_> {
53fn static_def_id(&self) -> Option<LocalDefId> {
54Some(self.static_root_ids?.1)
55 }
56}
5758fn prepare_alloc<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(
59 tcx: TyCtxt<'tcx>,
60 kind: MemoryKind<const_eval::MemoryKind>,
61 alloc: &mut Allocation<Prov, Extra, Bytes>,
62 mutability: Mutability,
63) -> Result<(), InternError> {
64match kind {
65 MemoryKind::Machine(const_eval::MemoryKind::Heap { was_made_global }) => {
66if !was_made_global {
67// Attempting to intern a `const_allocate`d pointer that was not made global via
68 // `const_make_global`.
69tcx.dcx().delayed_bug("non-global heap allocation in const value");
70return Err(InternError::ConstAllocNotGlobal);
71 }
72 }
73 MemoryKind::Stack | MemoryKind::CallerLocation => {}
74 }
7576if !alloc.provenance_merge_bytes(&tcx) {
77// Per-byte provenance is not supported by backends, so we cannot accept it here.
78tcx.dcx().delayed_bug("partial pointer in const value");
79return Err(InternError::PartialPointer);
80 }
8182// Set allocation mutability as appropriate. This is used by LLVM to put things into
83 // read-only memory, and also by Miri when evaluating other globals that
84 // access this one.
85match mutability {
86 Mutability::Not => {
87alloc.mutability = Mutability::Not;
88 }
89 Mutability::Mut => {
90// This must be already mutable, we won't "un-freeze" allocations ever.
91match (&alloc.mutability, &Mutability::Mut) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val, &*right_val,
::core::option::Option::None);
}
}
};assert_eq!(alloc.mutability, Mutability::Mut);
92 }
93 }
94Ok(())
95}
9697/// Intern an allocation. Returns `Err` if the allocation does not exist in the local memory.
98///
99/// `mutability` can be used to force immutable interning: if it is `Mutability::Not`, the
100/// allocation is interned immutably; if it is `Mutability::Mut`, then the allocation *must be*
101/// already mutable (as a sanity check).
102///
103/// Returns an iterator over all relocations referred to by this allocation.
104fn intern_shallow<'tcx, M: CompileTimeMachine<'tcx>>(
105 ecx: &mut InterpCx<'tcx, M>,
106 alloc_id: AllocId,
107 mutability: Mutability,
108 disambiguator: Option<&mut PerParentDisambiguatorState>,
109) -> Result<impl Iterator<Item = CtfeProvenance> + 'tcx, InternError> {
110{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_const_eval/src/interpret/intern.rs:110",
"rustc_const_eval::interpret::intern",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_const_eval/src/interpret/intern.rs"),
::tracing_core::__macro_support::Option::Some(110u32),
::tracing_core::__macro_support::Option::Some("rustc_const_eval::interpret::intern"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("intern_shallow {0:?}",
alloc_id) as &dyn Value))])
});
} else { ; }
};trace!("intern_shallow {:?}", alloc_id);
111// remove allocation
112 // FIXME(#120456) - is `swap_remove` correct?
113let Some((kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else {
114return Err(InternError::DanglingPointer);
115 };
116117if let Err(err) = prepare_alloc(*ecx.tcx, kind, &mut alloc, mutability) {
118// We want to error here, but we have to first put the
119 // allocation back into the `alloc_map` to keep things in a consistent state.
120ecx.memory.alloc_map.insert(alloc_id, (kind, alloc));
121return Err(err);
122 }
123124// link the alloc id to the actual allocation
125let alloc = ecx.tcx.mk_const_alloc(alloc);
126if let Some(static_id) = ecx.machine.static_def_id() {
127intern_as_new_static(
128ecx.tcx,
129static_id,
130alloc_id,
131alloc,
132disambiguator.expect("disambiguator needed"),
133 );
134 } else {
135ecx.tcx.set_alloc_id_memory(alloc_id, alloc);
136 }
137Ok(alloc.inner().provenance().ptrs().iter().map(|&(_, prov)| prov))
138}
139140/// Creates a new `DefId` and feeds all the right queries to make this `DefId`
141/// appear as if it were a user-written `static` (though it has no HIR).
142fn intern_as_new_static<'tcx>(
143 tcx: TyCtxtAt<'tcx>,
144 static_id: LocalDefId,
145 alloc_id: AllocId,
146 alloc: ConstAllocation<'tcx>,
147 disambiguator: &mut PerParentDisambiguatorState,
148) {
149// `intern_const_alloc_recursive` is called once per static and it contains the `PerParentDisambiguatorState`.
150 // The `<static_id>::{{nested}}` path is thus unique to `intern_const_alloc_recursive` and the
151 // `PerParentDisambiguatorState` ensures the generated path is unique for this call as we generate
152 // `<static_id>::{{nested#n}}` where `n` is the `n`th `intern_as_new_static` call.
153let feed = tcx.create_def(
154static_id,
155None,
156 DefKind::Static { safety: hir::Safety::Safe, mutability: alloc.0.mutability, nested: true },
157Some(DefPathData::NestedStatic),
158disambiguator,
159 );
160tcx.set_nested_alloc_id_static(alloc_id, feed.def_id());
161162if tcx.is_thread_local_static(static_id.into()) {
163tcx.dcx().emit_err(errors::NestedStaticInThreadLocal { span: tcx.def_span(static_id) });
164 }
165166// These do not inherit the codegen attrs of the parent static allocation, since
167 // it doesn't make sense for them to inherit their `#[no_mangle]` and `#[link_name = ..]`
168 // and the like.
169feed.codegen_fn_attrs(CodegenFnAttrs::new());
170171feed.eval_static_initializer(Ok(alloc));
172feed.generics_of(tcx.generics_of(static_id).clone());
173feed.def_ident_span(tcx.def_ident_span(static_id));
174feed.explicit_predicates_of(tcx.explicit_predicates_of(static_id));
175feed.feed_hir();
176}
177178/// How a constant value should be interned.
179#[derive(#[automatically_derived]
impl ::core::marker::Copy for InternKind { }Copy, #[automatically_derived]
impl ::core::clone::Clone for InternKind {
#[inline]
fn clone(&self) -> InternKind {
let _: ::core::clone::AssertParamIsClone<hir::Mutability>;
*self
}
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for InternKind {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
InternKind::Static(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Static",
&__self_0),
InternKind::Constant =>
::core::fmt::Formatter::write_str(f, "Constant"),
InternKind::Promoted =>
::core::fmt::Formatter::write_str(f, "Promoted"),
}
}
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for InternKind {
#[inline]
fn eq(&self, other: &InternKind) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr &&
match (self, other) {
(InternKind::Static(__self_0), InternKind::Static(__arg1_0))
=> __self_0 == __arg1_0,
_ => true,
}
}
}PartialEq, #[automatically_derived]
impl ::core::hash::Hash for InternKind {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
let __self_discr = ::core::intrinsics::discriminant_value(self);
::core::hash::Hash::hash(&__self_discr, state);
match self {
InternKind::Static(__self_0) =>
::core::hash::Hash::hash(__self_0, state),
_ => {}
}
}
}Hash, #[automatically_derived]
impl ::core::cmp::Eq for InternKind {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<hir::Mutability>;
}
}Eq)]
180pub enum InternKind {
181/// The `mutability` of the static, ignoring the type which may have interior mutability.
182Static(hir::Mutability),
183/// A `const` item
184Constant,
185 Promoted,
186}
187188#[derive(#[automatically_derived]
impl ::core::fmt::Debug for InternError {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
InternError::BadMutablePointer => "BadMutablePointer",
InternError::DanglingPointer => "DanglingPointer",
InternError::ConstAllocNotGlobal => "ConstAllocNotGlobal",
InternError::PartialPointer => "PartialPointer",
})
}
}Debug)]
189pub enum InternError {
190 BadMutablePointer,
191 DanglingPointer,
192 ConstAllocNotGlobal,
193 PartialPointer,
194}
195196/// Intern `ret` and everything it references.
197///
198/// This *cannot raise an interpreter error*. Doing so is left to validation, which
199/// tracks where in the value we are and thus can show much better error messages.
200///
201/// For `InternKind::Static` the root allocation will not be interned, but must be handled by the caller.
202#[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("intern_const_alloc_recursive",
"rustc_const_eval::interpret::intern",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_const_eval/src/interpret/intern.rs"),
::tracing_core::__macro_support::Option::Some(202u32),
::tracing_core::__macro_support::Option::Some("rustc_const_eval::interpret::intern"),
::tracing_core::field::FieldSet::new(&["intern_kind",
"ret"], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::DEBUG <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = meta.fields().iter();
meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&intern_kind)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&ret)
as &dyn Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: Result<(), InternError> = loop {};
return __tracing_attr_fake_return;
}
{
let mut disambiguator =
ecx.machine.static_def_id().map(|id|
PerParentDisambiguatorState::new(id));
let mut disambiguator = disambiguator.as_mut();
let (base_mutability, inner_mutability, is_static) =
match intern_kind {
InternKind::Constant | InternKind::Promoted => {
(Mutability::Not, Mutability::Not, false)
}
InternKind::Static(Mutability::Not) => {
(if ret.layout.ty.is_freeze(*ecx.tcx, ecx.typing_env) {
Mutability::Not
} else { Mutability::Mut }, Mutability::Not, true)
}
InternKind::Static(Mutability::Mut) => {
(Mutability::Mut, Mutability::Mut, true)
}
};
let base_alloc_id = ret.ptr().provenance.unwrap().alloc_id();
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_const_eval/src/interpret/intern.rs:244",
"rustc_const_eval::interpret::intern",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_const_eval/src/interpret/intern.rs"),
::tracing_core::__macro_support::Option::Some(244u32),
::tracing_core::__macro_support::Option::Some("rustc_const_eval::interpret::intern"),
::tracing_core::field::FieldSet::new(&["base_alloc_id",
"base_mutability"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&base_alloc_id)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&base_mutability)
as &dyn Value))])
});
} else { ; }
};
let mut todo: Vec<_> =
if is_static {
if !disambiguator.is_some() {
::core::panicking::panic("assertion failed: disambiguator.is_some()")
};
let (kind, alloc) =
ecx.memory.alloc_map.get_mut(&base_alloc_id).unwrap();
prepare_alloc(*ecx.tcx, *kind, alloc, base_mutability)?;
alloc.provenance().ptrs().iter().map(|&(_, prov)|
prov).collect()
} else {
if !disambiguator.is_none() {
::core::panicking::panic("assertion failed: disambiguator.is_none()")
};
intern_shallow(ecx, base_alloc_id, base_mutability,
None)?.collect()
};
let mut just_interned: FxHashSet<_> =
std::iter::once(base_alloc_id).collect();
let mut found_bad_mutable_ptr = false;
while let Some(prov) = todo.pop() {
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_const_eval/src/interpret/intern.rs:275",
"rustc_const_eval::interpret::intern",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_const_eval/src/interpret/intern.rs"),
::tracing_core::__macro_support::Option::Some(275u32),
::tracing_core::__macro_support::Option::Some("rustc_const_eval::interpret::intern"),
::tracing_core::field::FieldSet::new(&["prov"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&prov) as
&dyn Value))])
});
} else { ; }
};
let alloc_id = prov.alloc_id();
if base_alloc_id == alloc_id && is_static { continue; }
if intern_kind != InternKind::Promoted &&
inner_mutability == Mutability::Not && !prov.shared_ref() {
let is_already_global =
ecx.tcx.try_get_global_alloc(alloc_id).is_some();
if is_already_global && !just_interned.contains(&alloc_id) {
continue;
}
let dangling =
!is_already_global &&
!ecx.memory.alloc_map.contains_key(&alloc_id);
if !dangling { found_bad_mutable_ptr = true; }
}
if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
if true {
if !!ecx.memory.alloc_map.contains_key(&alloc_id) {
::core::panicking::panic("assertion failed: !ecx.memory.alloc_map.contains_key(&alloc_id)")
};
};
continue;
}
just_interned.insert(alloc_id);
let next =
intern_shallow(ecx, alloc_id, inner_mutability,
disambiguator.as_deref_mut())?;
todo.extend(next);
}
if found_bad_mutable_ptr {
if ecx.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you
{
return Err(InternError::BadMutablePointer);
} else {
::rustc_middle::util::bug::span_bug_fmt(ecx.tcx.span,
format_args!("the static const safety checks accepted a mutable pointer they should not have accepted"));
}
}
Ok(())
}
}
}#[instrument(level = "debug", skip(ecx))]203pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx>>(
204 ecx: &mut InterpCx<'tcx, M>,
205 intern_kind: InternKind,
206 ret: &MPlaceTy<'tcx>,
207) -> Result<(), InternError> {
208let mut disambiguator =
209 ecx.machine.static_def_id().map(|id| PerParentDisambiguatorState::new(id));
210let mut disambiguator = disambiguator.as_mut();
211212// We are interning recursively, and for mutability we are distinguishing the "root" allocation
213 // that we are starting in, and all other allocations that we are encountering recursively.
214let (base_mutability, inner_mutability, is_static) = match intern_kind {
215 InternKind::Constant | InternKind::Promoted => {
216// Completely immutable. Interning anything mutably here can only lead to unsoundness,
217 // since all consts are conceptually independent values but share the same underlying
218 // memory.
219(Mutability::Not, Mutability::Not, false)
220 }
221 InternKind::Static(Mutability::Not) => {
222 (
223// Outermost allocation is mutable if `!Freeze` i.e. contains interior mutable types.
224if ret.layout.ty.is_freeze(*ecx.tcx, ecx.typing_env) {
225 Mutability::Not
226 } else {
227 Mutability::Mut
228 },
229// Inner allocations are never mutable. They can only arise via the "tail
230 // expression" / "outer scope" rule, and we treat them consistently with `const`.
231Mutability::Not,
232true,
233 )
234 }
235 InternKind::Static(Mutability::Mut) => {
236// Just make everything mutable. We accept code like
237 // `static mut X = &mut [42]`, so even inner allocations need to be mutable.
238(Mutability::Mut, Mutability::Mut, true)
239 }
240 };
241242// Intern the base allocation, and initialize todo list for recursive interning.
243let base_alloc_id = ret.ptr().provenance.unwrap().alloc_id();
244trace!(?base_alloc_id, ?base_mutability);
245// First we intern the base allocation, as it requires a different mutability.
246 // This gives us the initial set of nested allocations, which will then all be processed
247 // recursively in the loop below.
248let mut todo: Vec<_> = if is_static {
249assert!(disambiguator.is_some());
250// Do not steal the root allocation, we need it later to create the return value of `eval_static_initializer`.
251 // But still change its mutability to match the requested one.
252let (kind, alloc) = ecx.memory.alloc_map.get_mut(&base_alloc_id).unwrap();
253 prepare_alloc(*ecx.tcx, *kind, alloc, base_mutability)?;
254 alloc.provenance().ptrs().iter().map(|&(_, prov)| prov).collect()
255 } else {
256assert!(disambiguator.is_none());
257 intern_shallow(ecx, base_alloc_id, base_mutability, None)?.collect()
258 };
259// We need to distinguish "has just been interned" from "was already in `tcx`",
260 // so we track this in a separate set.
261let mut just_interned: FxHashSet<_> = std::iter::once(base_alloc_id).collect();
262// Whether we encountered a bad mutable pointer.
263 // We want to first report "dangling" and then "mutable", so we need to delay reporting these
264 // errors.
265let mut found_bad_mutable_ptr = false;
266267// Keep interning as long as there are things to intern.
268 // We show errors if there are dangling pointers, or mutable pointers in immutable contexts
269 // (i.e., everything except for `static mut`). We only return these errors as a `Result`
270 // so that the caller can run validation, and subsequently only report interning errors
271 // if validation fails. Validation has the better error messages so we prefer those, but
272 // interning has better coverage since it "sees" *all* pointers, including raw pointers and
273 // references stored in unions.
274while let Some(prov) = todo.pop() {
275trace!(?prov);
276let alloc_id = prov.alloc_id();
277278if base_alloc_id == alloc_id && is_static {
279// This is a pointer to the static itself. It's ok for a static to refer to itself,
280 // even mutably. Whether that mutable pointer is legal at all is checked in validation.
281 // See tests/ui/statics/recursive_interior_mut.rs for how such a situation can occur.
282 // We also already collected all the nested allocations, so there's no need to do that again.
283continue;
284 }
285286// Ensure that this is derived from a shared reference. Crucially, we check this *before*
287 // checking whether the `alloc_id` has already been interned. The point of this check is to
288 // ensure that when there are multiple pointers to the same allocation, they are *all*
289 // derived from a shared reference. Therefore it would be bad if we only checked the first
290 // pointer to any given allocation.
291 // (It is likely not possible to actually have multiple pointers to the same allocation,
292 // so alternatively we could also check that and ICE if there are multiple such pointers.)
293 // See <https://github.com/rust-lang/rust/pull/128543> for why we are checking for "shared
294 // reference" and not "immutable", i.e., for why we are allowing interior-mutable shared
295 // references: they can actually be created in safe code while pointing to apparently
296 // "immutable" values, via promotion or tail expression lifetime extension of
297 // `&None::<Cell<T>>`.
298 // We also exclude promoteds from this as `&mut []` can be promoted, which is a mutable
299 // reference pointing to an immutable (zero-sized) allocation. We rely on the promotion
300 // analysis not screwing up to ensure that it is sound to intern promoteds as immutable.
301if intern_kind != InternKind::Promoted
302 && inner_mutability == Mutability::Not
303 && !prov.shared_ref()
304 {
305let is_already_global = ecx.tcx.try_get_global_alloc(alloc_id).is_some();
306if is_already_global && !just_interned.contains(&alloc_id) {
307// This is a pointer to some memory from another constant. We encounter mutable
308 // pointers to such memory since we do not always track immutability through
309 // these "global" pointers. Allowing them is harmless; the point of these checks
310 // during interning is to justify why we intern the *new* allocations immutably,
311 // so we can completely ignore existing allocations.
312 // We can also skip the rest of this loop iteration, since after all it is already
313 // interned.
314continue;
315 }
316// If this is a dangling pointer, that's actually fine -- the problematic case is
317 // when there is memory there that someone might expect to be mutable, but we make it immutable.
318let dangling = !is_already_global && !ecx.memory.alloc_map.contains_key(&alloc_id);
319if !dangling {
320 found_bad_mutable_ptr = true;
321 }
322 }
323if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
324// Already interned.
325debug_assert!(!ecx.memory.alloc_map.contains_key(&alloc_id));
326continue;
327 }
328// We always intern with `inner_mutability`, and furthermore we ensured above that if
329 // that is "immutable", then there are *no* mutable pointers anywhere in the newly
330 // interned memory -- justifying that we can indeed intern immutably. However this also
331 // means we can *not* easily intern immutably here if `prov.immutable()` is true and
332 // `inner_mutability` is `Mut`: there might be other pointers to that allocation, and
333 // we'd have to somehow check that they are *all* immutable before deciding that this
334 // allocation can be made immutable. In the future we could consider analyzing all
335 // pointers before deciding which allocations can be made immutable; but for now we are
336 // okay with losing some potential for immutability here. This can anyway only affect
337 // `static mut`.
338just_interned.insert(alloc_id);
339let next = intern_shallow(ecx, alloc_id, inner_mutability, disambiguator.as_deref_mut())?;
340 todo.extend(next);
341 }
342if found_bad_mutable_ptr {
343// We found a mutable pointer inside a const where inner allocations should be immutable,
344 // and there was no other error. This should usually never happen! However, this can happen
345 // in unleash-miri mode, so report it as a normal error then.
346if ecx.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you {
347return Err(InternError::BadMutablePointer);
348 } else {
349span_bug!(
350 ecx.tcx.span,
351"the static const safety checks accepted a mutable pointer they should not have accepted"
352);
353 }
354 }
355Ok(())
356}
357358/// Intern `ret`. This function assumes that `ret` references no other allocation.
359#[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("intern_const_alloc_for_constprop",
"rustc_const_eval::interpret::intern",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_const_eval/src/interpret/intern.rs"),
::tracing_core::__macro_support::Option::Some(359u32),
::tracing_core::__macro_support::Option::Some("rustc_const_eval::interpret::intern"),
::tracing_core::field::FieldSet::new(&["alloc_id"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::DEBUG <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = meta.fields().iter();
meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&alloc_id)
as &dyn Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: InterpResult<'tcx, ()> = loop {};
return __tracing_attr_fake_return;
}
{
if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
return interp_ok(());
}
if let Some(_) =
intern_shallow(ecx, alloc_id, Mutability::Not,
None).unwrap().next() {
{
::core::panicking::panic_fmt(format_args!("`intern_const_alloc_for_constprop` called on allocation with nested provenance"));
}
}
interp_ok(())
}
}
}#[instrument(level = "debug", skip(ecx))]360pub fn intern_const_alloc_for_constprop<'tcx, M: CompileTimeMachine<'tcx>>(
361 ecx: &mut InterpCx<'tcx, M>,
362 alloc_id: AllocId,
363) -> InterpResult<'tcx, ()> {
364if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
365// The constant is already in global memory. Do nothing.
366return interp_ok(());
367 }
368// Move allocation to `tcx`.
369if let Some(_) = intern_shallow(ecx, alloc_id, Mutability::Not, None).unwrap().next() {
370// We are not doing recursive interning, so we don't currently support provenance.
371 // (If this assertion ever triggers, we should just implement a
372 // proper recursive interning loop -- or just call `intern_const_alloc_recursive`.
373panic!("`intern_const_alloc_for_constprop` called on allocation with nested provenance")
374 }
375 interp_ok(())
376}
377378impl<'tcx> InterpCx<'tcx, DummyMachine> {
379/// A helper function that allocates memory for the layout given and gives you access to mutate
380 /// it. Once your own mutation code is done, the backing `Allocation` is removed from the
381 /// current `Memory` and interned as read-only into the global memory.
382pub fn intern_with_temp_alloc(
383&mut self,
384 layout: TyAndLayout<'tcx>,
385 f: impl FnOnce(
386&mut InterpCx<'tcx, DummyMachine>,
387&PlaceTy<'tcx, CtfeProvenance>,
388 ) -> InterpResult<'tcx, ()>,
389 ) -> InterpResult<'tcx, AllocId> {
390// `allocate` picks a fresh AllocId that we will associate with its data below.
391let dest = self.allocate(layout, MemoryKind::Stack)?;
392f(self, &dest.clone().into())?;
393let alloc_id = dest.ptr().provenance.unwrap().alloc_id(); // this was just allocated, it must have provenance
394for prov in intern_shallow(self, alloc_id, Mutability::Not, None).unwrap() {
395// We are not doing recursive interning, so we don't currently support provenance.
396 // (If this assertion ever triggers, we should just implement a
397 // proper recursive interning loop -- or just call `intern_const_alloc_recursive`.
398if self.tcx.try_get_global_alloc(prov.alloc_id()).is_none() {
399{
::core::panicking::panic_fmt(format_args!("`intern_with_temp_alloc` with nested allocations"));
};panic!("`intern_with_temp_alloc` with nested allocations");
400 }
401 }
402interp_ok(alloc_id)
403 }
404}