Skip to main content

rustc_const_eval/interpret/
util.rs

1use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
2use rustc_infer::infer::TyCtxtInferExt;
3use rustc_infer::traits::{Obligation, ObligationCause};
4use rustc_middle::mir::interpret::{AllocInit, Allocation, GlobalAlloc, InterpResult, Pointer};
5use rustc_middle::ty::layout::TyAndLayout;
6use rustc_middle::ty::{PolyExistentialPredicate, Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
7use rustc_middle::{mir, ty};
8use rustc_span::span_bug;
9use rustc_trait_selection::traits::ObligationCtxt;
10use tracing::debug;
11
12use super::{InterpCx, MPlaceTy, MemoryKind, interp_ok, throw_inval};
13use crate::const_eval::{CompileTimeInterpCx, CompileTimeMachine, InterpretationResult};
14use crate::interpret::Machine;
15
16/// Checks if a type implements predicates.
17/// Calls `ensure_monomorphic_enough` on `ty` and `trait_ty` for you.
18pub(crate) fn type_implements_dyn_trait<'tcx, M: Machine<'tcx>>(
19    ecx: &mut InterpCx<'tcx, M>,
20    ty: Ty<'tcx>,
21    trait_ty: Ty<'tcx>,
22) -> InterpResult<'tcx, (bool, &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>)> {
23    ensure_monomorphic_enough(ty)?;
24    ensure_monomorphic_enough(trait_ty)?;
25
26    let ty::Dynamic(preds, _) = trait_ty.kind() else {
27        bug_impl(Some(ecx.find_closest_untracked_caller_location()),
    format_args!("Invalid type provided to type_implements_predicates. U must be dyn Trait, got {0}.",
        trait_ty), Location::caller());span_bug!(
28            ecx.find_closest_untracked_caller_location(),
29            "Invalid type provided to type_implements_predicates. U must be dyn Trait, got {trait_ty}."
30        );
31    };
32
33    let (infcx, param_env) = ecx.tcx.infer_ctxt().build_with_typing_env(ty::TypingEnv::new(
34        ecx.typing_env.param_env,
35        ty::TypingMode::Reflection,
36    ));
37
38    let ocx = ObligationCtxt::new(&infcx);
39    ocx.register_obligations(preds.iter().map(|pred: PolyExistentialPredicate<'_>| {
40        let pred = pred.with_self_ty(ecx.tcx.tcx, ty);
41        // Lifetimes can only be 'static because of the bound on T
42        let pred = rustc_middle::ty::fold_regions(ecx.tcx.tcx, pred, |r, _| {
43            if r == ecx.tcx.tcx.lifetimes.re_erased { ecx.tcx.tcx.lifetimes.re_static } else { r }
44        });
45        Obligation::new(ecx.tcx.tcx, ObligationCause::dummy(), param_env, pred)
46    }));
47    let type_impls_trait = ocx.evaluate_obligations_error_on_ambiguity().no_errors();
48    // Since `assumed_wf_tys=[]` the choice of LocalDefId is irrelevant, so using the "default"
49    let regions_are_valid = ocx.resolve_regions(CRATE_DEF_ID, param_env, []).is_empty();
50
51    interp_ok((regions_are_valid && type_impls_trait, preds))
52}
53
54/// Checks whether a type contains generic parameters which must be instantiated.
55///
56/// In case it does, returns a `TooGeneric` const eval error.
57pub(crate) fn ensure_monomorphic_enough<'tcx, T>(ty: T) -> InterpResult<'tcx>
58where
59    T: TypeVisitable<TyCtxt<'tcx>>,
60{
61    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/923c95cdf5ba65cea505aa2ea829f578e1506ed8/compiler/rustc_const_eval/src/interpret/util.rs:61",
                        "rustc_const_eval::interpret::util",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/923c95cdf5ba65cea505aa2ea829f578e1506ed8/compiler/rustc_const_eval/src/interpret/util.rs"),
                        ::tracing_core::__macro_support::Option::Some(61u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_const_eval::interpret::util"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::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!("ensure_monomorphic_enough: ty={0:?}",
                                                    ty) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("ensure_monomorphic_enough: ty={:?}", ty);
62    if ty.has_param() {
63        do yeet ::rustc_middle::mir::interpret::InterpErrorKind::InvalidProgram(::rustc_middle::mir::interpret::InvalidProgramInfo::TooGeneric);throw_inval!(TooGeneric);
64    }
65    interp_ok(())
66}
67
68impl<'tcx> InterpretationResult<'tcx> for mir::interpret::ConstAllocation<'tcx> {
69    fn make_result(
70        mplace: MPlaceTy<'tcx>,
71        ecx: &mut InterpCx<'tcx, CompileTimeMachine<'tcx>>,
72    ) -> Self {
73        let alloc_id = mplace.ptr().provenance.unwrap().alloc_id();
74        let alloc = ecx.memory.alloc_map.swap_remove(&alloc_id).unwrap().1;
75        ecx.tcx.mk_const_alloc(alloc)
76    }
77}
78
79pub(crate) fn create_static_alloc<'tcx>(
80    ecx: &mut CompileTimeInterpCx<'tcx>,
81    static_def_id: LocalDefId,
82    layout: TyAndLayout<'tcx>,
83) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
84    // Inherit size and align from the `GlobalAlloc::Static` so we can avoid duplicating
85    // the alignment attribute logic.
86    let (size, align) =
87        GlobalAlloc::Static(static_def_id.into()).size_and_align(*ecx.tcx, ecx.typing_env);
88    {
    match (&size, &layout.size) {
        (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!(size, layout.size);
89    if !(align >= layout.align.abi) {
    ::core::panicking::panic("assertion failed: align >= layout.align.abi")
};assert!(align >= layout.align.abi);
90
91    let alloc = Allocation::try_new(size, align, AllocInit::Uninit, ())?;
92    let alloc_id = ecx.tcx.reserve_and_set_static_alloc(static_def_id.into());
93    {
    match (&ecx.machine.static_root_ids, &None) {
        (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!(ecx.machine.static_root_ids, None);
94    ecx.machine.static_root_ids = Some((alloc_id, static_def_id));
95    if !ecx.memory.alloc_map.insert(alloc_id,
                (MemoryKind::Stack, alloc)).is_none() {
    ::core::panicking::panic("assertion failed: ecx.memory.alloc_map.insert(alloc_id, (MemoryKind::Stack, alloc)).is_none()")
};assert!(ecx.memory.alloc_map.insert(alloc_id, (MemoryKind::Stack, alloc)).is_none());
96    interp_ok(ecx.ptr_to_mplace(Pointer::from(alloc_id).into(), layout))
97}
98
99/// A marker trait returned by [crate::interpret::Machine::enter_trace_span], identifying either a
100/// real [tracing::span::EnteredSpan] in case tracing is enabled, or the dummy type `()` when
101/// tracing is disabled. Also see [crate::enter_trace_span!] below.
102pub trait EnteredTraceSpan {
103    /// Allows executing an alternative function when tracing is disabled. Useful for example if you
104    /// want to open a trace span when tracing is enabled, and alternatively just log a line when
105    /// tracing is disabled.
106    fn or_if_tracing_disabled(self, f: impl FnOnce()) -> Self;
107}
108impl EnteredTraceSpan for () {
109    fn or_if_tracing_disabled(self, f: impl FnOnce()) -> Self {
110        f(); // tracing is disabled, execute the function
111        self
112    }
113}
114impl EnteredTraceSpan for tracing::span::EnteredSpan {
115    fn or_if_tracing_disabled(self, _f: impl FnOnce()) -> Self {
116        self // tracing is enabled, don't execute anything
117    }
118}
119
120/// Shorthand for calling [crate::interpret::Machine::enter_trace_span] on a [tracing::info_span!].
121/// This is supposed to be compiled out when [crate::interpret::Machine::enter_trace_span] has the
122/// default implementation (i.e. when it does not actually enter the span but instead returns `()`).
123/// This macro takes a type implementing the [crate::interpret::Machine] trait as its first argument
124/// and otherwise accepts the same syntax as [tracing::span!] (see some tips below).
125/// Note: the result of this macro **must be used** because the span is exited when it's dropped.
126///
127/// ### Syntax accepted by this macro
128///
129/// The full documentation for the [tracing::span!] syntax can be found at [tracing] under "Using the
130/// Macros". A few possibly confusing syntaxes are listed here:
131/// ```rust
132/// # use rustc_const_eval::enter_trace_span;
133/// # type M = rustc_const_eval::const_eval::CompileTimeMachine<'static>;
134/// # let my_display_var = String::new();
135/// # let my_debug_var = String::new();
136/// // logs a span named "hello" with a field named "arg" of value 42 (works only because
137/// // 42 implements the tracing::Value trait, otherwise use one of the options below)
138/// let _trace = enter_trace_span!(M, "hello", arg = 42);
139/// // logs a field called "my_display_var" using the Display implementation
140/// let _trace = enter_trace_span!(M, "hello", %my_display_var);
141/// // logs a field called "my_debug_var" using the Debug implementation
142/// let _trace = enter_trace_span!(M, "hello", ?my_debug_var);
143///  ```
144///
145/// ### `NAME::SUBNAME` syntax
146///
147/// In addition to the syntax accepted by [tracing::span!], this macro optionally allows passing
148/// the span name (i.e. the first macro argument) in the form `NAME::SUBNAME` (without quotes) to
149/// indicate that the span has name "NAME" (usually the name of the component) and has an additional
150/// more specific name "SUBNAME" (usually the function name). The latter is passed to the [tracing]
151/// infrastructure as a span field with the name "NAME". This allows not being distracted by
152/// subnames when looking at the trace in <https://ui.perfetto.dev>, but when deeper introspection
153/// is needed within a component, it's still possible to view the subnames directly in the UI by
154/// selecting a span, clicking on the "NAME" argument on the right, and clicking on "Visualize
155/// argument values".
156/// ```rust
157/// # use rustc_const_eval::enter_trace_span;
158/// # type M = rustc_const_eval::const_eval::CompileTimeMachine<'static>;
159/// // for example, the first will expand to the second
160/// let _trace = enter_trace_span!(M, borrow_tracker::on_stack_pop, /* ... */);
161/// let _trace = enter_trace_span!(M, "borrow_tracker", borrow_tracker = "on_stack_pop", /* ... */);
162/// ```
163///
164/// ### `tracing_separate_thread` parameter
165///
166/// This macro was introduced to obtain better traces of Miri without impacting release performance.
167/// Miri saves traces using the `tracing_chrome` `tracing::Layer` so that they can be visualized
168/// in <https://ui.perfetto.dev>. To instruct `tracing_chrome` to put some spans on a separate trace
169/// thread/line than other spans when viewed in <https://ui.perfetto.dev>, you can pass
170/// `tracing_separate_thread = tracing::field::Empty` to the tracing macros. This is useful to
171/// separate out spans which just indicate the current step or program frame being processed by the
172/// interpreter. You should use a value of [tracing::field::Empty] so that other tracing layers
173/// (e.g. the logger) will ignore the `tracing_separate_thread` field. For example:
174/// ```rust
175/// # use rustc_const_eval::enter_trace_span;
176/// # type M = rustc_const_eval::const_eval::CompileTimeMachine<'static>;
177/// let _trace = enter_trace_span!(M, step::eval_statement, tracing_separate_thread = tracing::field::Empty);
178/// ```
179///
180/// ### Executing something else when tracing is disabled
181///
182/// [crate::interpret::Machine::enter_trace_span] returns [EnteredTraceSpan], on which you can call
183/// [EnteredTraceSpan::or_if_tracing_disabled], to e.g. log a line as an alternative to the tracing
184/// span for when tracing is disabled. For example:
185/// ```rust
186/// # use rustc_const_eval::enter_trace_span;
187/// # use rustc_const_eval::interpret::EnteredTraceSpan;
188/// # type M = rustc_const_eval::const_eval::CompileTimeMachine<'static>;
189/// let _trace = enter_trace_span!(M, step::eval_statement)
190///     .or_if_tracing_disabled(|| tracing::info!("eval_statement"));
191/// ```
192#[macro_export]
193macro_rules! enter_trace_span {
194    ($machine:ty, $name:ident :: $subname:ident $($tt:tt)*) => {
195        $crate::enter_trace_span!($machine, stringify!($name), $name = %stringify!($subname) $($tt)*)
196    };
197
198    ($machine:ty, $($tt:tt)*) => {
199        <$machine as $crate::interpret::Machine>::enter_trace_span(|| tracing::info_span!($($tt)*))
200    };
201}