Skip to main content

rustc_const_eval/interpret/
intern.rs

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.
15
16use hir::def::DefKind;
17use rustc_ast::Mutability;
18use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
19use rustc_hir::definitions::{DefPathData, PerParentDisambiguatorState};
20use rustc_hir::{self as hir};
21use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
22use rustc_middle::mir::interpret::{
23    AllocBytes, 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};
31
32use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, interp_ok};
33use crate::const_eval::DummyMachine;
34use crate::{const_eval, diagnostics};
35
36pub 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;
45
46pub 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.
49    fn static_def_id(&self) -> Option<LocalDefId>;
50}
51
52impl HasStaticRootDefId for const_eval::CompileTimeMachine<'_> {
53    fn static_def_id(&self) -> Option<LocalDefId> {
54        Some(self.static_root_ids?.1)
55    }
56}
57
58fn 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> {
64    match kind {
65        MemoryKind::Machine(const_eval::MemoryKind::Heap { was_made_global }) => {
66            if !was_made_global {
67                // Attempting to intern a `const_allocate`d pointer that was not made global via
68                // `const_make_global`.
69                tcx.dcx().delayed_bug("non-global heap allocation in const value");
70                return Err(InternError::ConstAllocNotGlobal);
71            }
72        }
73        MemoryKind::Stack | MemoryKind::CallerLocation => {}
74    }
75
76    if !alloc.provenance_merge_bytes(&tcx) {
77        // Per-byte provenance is not supported by backends, so we cannot accept it here.
78        tcx.dcx().delayed_bug("partial pointer in const value");
79        return Err(InternError::PartialPointer);
80    }
81
82    // 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.
85    match mutability {
86        Mutability::Not => {
87            alloc.mutability = Mutability::Not;
88        }
89        Mutability::Mut => {
90            // This must be already mutable, we won't "un-freeze" allocations ever.
91            {
    match (&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    }
94    Ok(())
95}
96
97/// 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};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("intern_shallow {0:?}",
                                                    alloc_id) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};trace!("intern_shallow {:?}", alloc_id);
111    // remove allocation
112    // FIXME(#120456) - is `swap_remove` correct?
113    let Some((kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else {
114        return Err(InternError::DanglingPointer);
115    };
116
117    if 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.
120        ecx.memory.alloc_map.insert(alloc_id, (kind, alloc));
121        return Err(err);
122    }
123
124    // link the alloc id to the actual allocation
125    let alloc = ecx.tcx.mk_const_alloc(alloc);
126    if let Some(static_id) = ecx.machine.static_def_id() {
127        intern_as_new_static(
128            ecx.tcx,
129            static_id,
130            alloc_id,
131            alloc,
132            disambiguator.expect("disambiguator needed"),
133        );
134    } else {
135        ecx.tcx.set_alloc_id_memory(alloc_id, alloc);
136    }
137    Ok(alloc.inner().provenance().ptrs().iter().map(|&(_, prov)| prov))
138}
139
140/// 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.
153    let feed = tcx.create_def(
154        static_id,
155        None,
156        DefKind::Static { safety: hir::Safety::Safe, mutability: alloc.0.mutability, nested: true },
157        Some(DefPathData::NestedStatic),
158        disambiguator,
159    );
160    tcx.set_nested_alloc_id_static(alloc_id, feed.def_id());
161
162    if tcx.is_thread_local_static(static_id.into()) {
163        tcx.dcx()
164            .emit_err(diagnostics::NestedStaticInThreadLocal { span: tcx.def_span(static_id) });
165    }
166
167    // These do not inherit the codegen attrs of the parent static allocation, since
168    // it doesn't make sense for them to inherit their `#[no_mangle]` and `#[link_name = ..]`
169    // and the like.
170    feed.codegen_fn_attrs(CodegenFnAttrs::new());
171
172    feed.eval_static_initializer(Ok(alloc));
173    feed.generics_of(tcx.generics_of(static_id).clone());
174    feed.def_ident_span(tcx.def_ident_span(static_id));
175    feed.explicit_predicates_of(tcx.explicit_predicates_of(static_id));
176    feed.feed_hir();
177}
178
179/// How a constant value should be interned.
180#[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)]
181pub enum InternKind {
182    /// The `mutability` of the static, ignoring the type which may have interior mutability.
183    Static(hir::Mutability),
184    /// A `const` item
185    Constant,
186    Promoted,
187}
188
189#[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)]
190pub enum InternError {
191    BadMutablePointer,
192    DanglingPointer,
193    ConstAllocNotGlobal,
194    PartialPointer,
195}
196
197/// Intern `ret` and everything it references.
198///
199/// This *cannot raise an interpreter error*. Doing so is left to validation, which
200/// tracks where in the value we are and thus can show much better error messages.
201///
202/// For `InternKind::Static` the root allocation will not be interned, but must be handled by the caller.
203#[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(203u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_const_eval::interpret::intern"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("intern_kind")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("intern_kind");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("ret")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("ret");
                                                        NAME.as_str()
                                                    }], ::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};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&intern_kind)
                                                            as &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&ret)
                                                            as &dyn ::tracing::field::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:245",
                                    "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(245u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_const_eval::interpret::intern"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("base_alloc_id")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("base_alloc_id");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("base_mutability")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("base_mutability");
                                                        NAME.as_str()
                                                    }], ::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};
                            __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&base_alloc_id)
                                                        as &dyn ::tracing::field::Value)),
                                            (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&base_mutability)
                                                        as &dyn ::tracing::field::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:276",
                                        "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(276u32),
                                        ::tracing_core::__macro_support::Option::Some("rustc_const_eval::interpret::intern"),
                                        ::tracing_core::field::FieldSet::new(&[{
                                                            const NAME:
                                                                ::tracing::__macro_support::FieldName<{
                                                                    ::tracing::__macro_support::FieldName::len("prov")
                                                                }> =
                                                                ::tracing::__macro_support::FieldName::new("prov");
                                                            NAME.as_str()
                                                        }], ::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};
                                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&prov)
                                                            as &dyn ::tracing::field::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))]
204pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx>>(
205    ecx: &mut InterpCx<'tcx, M>,
206    intern_kind: InternKind,
207    ret: &MPlaceTy<'tcx>,
208) -> Result<(), InternError> {
209    let mut disambiguator =
210        ecx.machine.static_def_id().map(|id| PerParentDisambiguatorState::new(id));
211    let mut disambiguator = disambiguator.as_mut();
212
213    // We are interning recursively, and for mutability we are distinguishing the "root" allocation
214    // that we are starting in, and all other allocations that we are encountering recursively.
215    let (base_mutability, inner_mutability, is_static) = match intern_kind {
216        InternKind::Constant | InternKind::Promoted => {
217            // Completely immutable. Interning anything mutably here can only lead to unsoundness,
218            // since all consts are conceptually independent values but share the same underlying
219            // memory.
220            (Mutability::Not, Mutability::Not, false)
221        }
222        InternKind::Static(Mutability::Not) => {
223            (
224                // Outermost allocation is mutable if `!Freeze` i.e. contains interior mutable types.
225                if ret.layout.ty.is_freeze(*ecx.tcx, ecx.typing_env) {
226                    Mutability::Not
227                } else {
228                    Mutability::Mut
229                },
230                // Inner allocations are never mutable. They can only arise via the "tail
231                // expression" / "outer scope" rule, and we treat them consistently with `const`.
232                Mutability::Not,
233                true,
234            )
235        }
236        InternKind::Static(Mutability::Mut) => {
237            // Just make everything mutable. We accept code like
238            // `static mut X = &mut [42]`, so even inner allocations need to be mutable.
239            (Mutability::Mut, Mutability::Mut, true)
240        }
241    };
242
243    // Intern the base allocation, and initialize todo list for recursive interning.
244    let base_alloc_id = ret.ptr().provenance.unwrap().alloc_id();
245    trace!(?base_alloc_id, ?base_mutability);
246    // First we intern the base allocation, as it requires a different mutability.
247    // This gives us the initial set of nested allocations, which will then all be processed
248    // recursively in the loop below.
249    let mut todo: Vec<_> = if is_static {
250        assert!(disambiguator.is_some());
251        // Do not steal the root allocation, we need it later to create the return value of `eval_static_initializer`.
252        // But still change its mutability to match the requested one.
253        let (kind, alloc) = ecx.memory.alloc_map.get_mut(&base_alloc_id).unwrap();
254        prepare_alloc(*ecx.tcx, *kind, alloc, base_mutability)?;
255        alloc.provenance().ptrs().iter().map(|&(_, prov)| prov).collect()
256    } else {
257        assert!(disambiguator.is_none());
258        intern_shallow(ecx, base_alloc_id, base_mutability, None)?.collect()
259    };
260    // We need to distinguish "has just been interned" from "was already in `tcx`",
261    // so we track this in a separate set.
262    let mut just_interned: FxHashSet<_> = std::iter::once(base_alloc_id).collect();
263    // Whether we encountered a bad mutable pointer.
264    // We want to first report "dangling" and then "mutable", so we need to delay reporting these
265    // errors.
266    let mut found_bad_mutable_ptr = false;
267
268    // Keep interning as long as there are things to intern.
269    // We show errors if there are dangling pointers, or mutable pointers in immutable contexts
270    // (i.e., everything except for `static mut`). We only return these errors as a `Result`
271    // so that the caller can run validation, and subsequently only report interning errors
272    // if validation fails. Validation has the better error messages so we prefer those, but
273    // interning has better coverage since it "sees" *all* pointers, including raw pointers and
274    // references stored in unions.
275    while let Some(prov) = todo.pop() {
276        trace!(?prov);
277        let alloc_id = prov.alloc_id();
278
279        if base_alloc_id == alloc_id && is_static {
280            // This is a pointer to the static itself. It's ok for a static to refer to itself,
281            // even mutably. Whether that mutable pointer is legal at all is checked in validation.
282            // See tests/ui/statics/recursive_interior_mut.rs for how such a situation can occur.
283            // We also already collected all the nested allocations, so there's no need to do that again.
284            continue;
285        }
286
287        // Ensure that this is derived from a shared reference. Crucially, we check this *before*
288        // checking whether the `alloc_id` has already been interned. The point of this check is to
289        // ensure that when there are multiple pointers to the same allocation, they are *all*
290        // derived from a shared reference. Therefore it would be bad if we only checked the first
291        // pointer to any given allocation.
292        // (It is likely not possible to actually have multiple pointers to the same allocation,
293        // so alternatively we could also check that and ICE if there are multiple such pointers.)
294        // See <https://github.com/rust-lang/rust/pull/128543> for why we are checking for "shared
295        // reference" and not "immutable", i.e., for why we are allowing interior-mutable shared
296        // references: they can actually be created in safe code while pointing to apparently
297        // "immutable" values, via promotion or tail expression lifetime extension of
298        // `&None::<Cell<T>>`.
299        // We also exclude promoteds from this as `&mut []` can be promoted, which is a mutable
300        // reference pointing to an immutable (zero-sized) allocation. We rely on the promotion
301        // analysis not screwing up to ensure that it is sound to intern promoteds as immutable.
302        if intern_kind != InternKind::Promoted
303            && inner_mutability == Mutability::Not
304            && !prov.shared_ref()
305        {
306            let is_already_global = ecx.tcx.try_get_global_alloc(alloc_id).is_some();
307            if is_already_global && !just_interned.contains(&alloc_id) {
308                // This is a pointer to some memory from another constant. We encounter mutable
309                // pointers to such memory since we do not always track immutability through
310                // these "global" pointers. Allowing them is harmless; the point of these checks
311                // during interning is to justify why we intern the *new* allocations immutably,
312                // so we can completely ignore existing allocations.
313                // We can also skip the rest of this loop iteration, since after all it is already
314                // interned.
315                continue;
316            }
317            // If this is a dangling pointer, that's actually fine -- the problematic case is
318            // when there is memory there that someone might expect to be mutable, but we make it immutable.
319            let dangling = !is_already_global && !ecx.memory.alloc_map.contains_key(&alloc_id);
320            if !dangling {
321                found_bad_mutable_ptr = true;
322            }
323        }
324        if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
325            // Already interned.
326            debug_assert!(!ecx.memory.alloc_map.contains_key(&alloc_id));
327            continue;
328        }
329        // We always intern with `inner_mutability`, and furthermore we ensured above that if
330        // that is "immutable", then there are *no* mutable pointers anywhere in the newly
331        // interned memory -- justifying that we can indeed intern immutably. However this also
332        // means we can *not* easily intern immutably here if `prov.immutable()` is true and
333        // `inner_mutability` is `Mut`: there might be other pointers to that allocation, and
334        // we'd have to somehow check that they are *all* immutable before deciding that this
335        // allocation can be made immutable. In the future we could consider analyzing all
336        // pointers before deciding which allocations can be made immutable; but for now we are
337        // okay with losing some potential for immutability here. This can anyway only affect
338        // `static mut`.
339        just_interned.insert(alloc_id);
340        let next = intern_shallow(ecx, alloc_id, inner_mutability, disambiguator.as_deref_mut())?;
341        todo.extend(next);
342    }
343    if found_bad_mutable_ptr {
344        // We found a mutable pointer inside a const where inner allocations should be immutable,
345        // and there was no other error. This should usually never happen! However, this can happen
346        // in unleash-miri mode, so report it as a normal error then.
347        if ecx.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you {
348            return Err(InternError::BadMutablePointer);
349        } else {
350            span_bug!(
351                ecx.tcx.span,
352                "the static const safety checks accepted a mutable pointer they should not have accepted"
353            );
354        }
355    }
356    Ok(())
357}
358
359/// Intern `ret`. This function assumes that `ret` references no other allocation.
360#[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(360u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_const_eval::interpret::intern"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("alloc_id")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("alloc_id");
                                                        NAME.as_str()
                                                    }], ::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};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&alloc_id)
                                                            as &dyn ::tracing::field::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))]
361pub fn intern_const_alloc_for_constprop<'tcx, M: CompileTimeMachine<'tcx>>(
362    ecx: &mut InterpCx<'tcx, M>,
363    alloc_id: AllocId,
364) -> InterpResult<'tcx, ()> {
365    if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
366        // The constant is already in global memory. Do nothing.
367        return interp_ok(());
368    }
369    // Move allocation to `tcx`.
370    if let Some(_) = intern_shallow(ecx, alloc_id, Mutability::Not, None).unwrap().next() {
371        // We are not doing recursive interning, so we don't currently support provenance.
372        // (If this assertion ever triggers, we should just implement a
373        // proper recursive interning loop -- or just call `intern_const_alloc_recursive`.
374        panic!("`intern_const_alloc_for_constprop` called on allocation with nested provenance")
375    }
376    interp_ok(())
377}
378
379impl<'tcx> InterpCx<'tcx, DummyMachine> {
380    /// A helper function that allocates memory for the layout given and gives you access to mutate
381    /// it. Once your own mutation code is done, the backing `Allocation` is removed from the
382    /// current `Memory` and interned as read-only into the global memory.
383    pub fn intern_with_temp_alloc(
384        &mut self,
385        layout: TyAndLayout<'tcx>,
386        f: impl FnOnce(
387            &mut InterpCx<'tcx, DummyMachine>,
388            &PlaceTy<'tcx, CtfeProvenance>,
389        ) -> InterpResult<'tcx, ()>,
390    ) -> InterpResult<'tcx, AllocId> {
391        // `allocate` picks a fresh AllocId that we will associate with its data below.
392        let dest = self.allocate(layout, MemoryKind::Stack)?;
393        f(self, &dest.clone().into())?;
394        let alloc_id = dest.ptr().provenance.unwrap().alloc_id(); // this was just allocated, it must have provenance
395        for prov in intern_shallow(self, alloc_id, Mutability::Not, None).unwrap() {
396            // We are not doing recursive interning, so we don't currently support provenance.
397            // (If this assertion ever triggers, we should just implement a
398            // proper recursive interning loop -- or just call `intern_const_alloc_recursive`.
399            if self.tcx.try_get_global_alloc(prov.alloc_id()).is_none() {
400                {
    ::core::panicking::panic_fmt(format_args!("`intern_with_temp_alloc` with nested allocations"));
};panic!("`intern_with_temp_alloc` with nested allocations");
401            }
402        }
403        interp_ok(alloc_id)
404    }
405}