rustc_const_eval/interpret/
util.rs

1use rustc_hir::def_id::LocalDefId;
2use rustc_middle::mir;
3use rustc_middle::mir::interpret::{AllocInit, Allocation, InterpResult, Pointer};
4use rustc_middle::ty::layout::TyAndLayout;
5use rustc_middle::ty::{TyCtxt, TypeVisitable, TypeVisitableExt};
6use tracing::debug;
7
8use super::{InterpCx, MPlaceTy, MemoryKind, interp_ok, throw_inval};
9use crate::const_eval::{CompileTimeInterpCx, CompileTimeMachine, InterpretationResult};
10
11/// Checks whether a type contains generic parameters which must be instantiated.
12///
13/// In case it does, returns a `TooGeneric` const eval error.
14pub(crate) fn ensure_monomorphic_enough<'tcx, T>(_tcx: TyCtxt<'tcx>, ty: T) -> InterpResult<'tcx>
15where
16    T: TypeVisitable<TyCtxt<'tcx>>,
17{
18    debug!("ensure_monomorphic_enough: ty={:?}", ty);
19    if ty.has_param() {
20        throw_inval!(TooGeneric);
21    }
22    interp_ok(())
23}
24
25impl<'tcx> InterpretationResult<'tcx> for mir::interpret::ConstAllocation<'tcx> {
26    fn make_result(
27        mplace: MPlaceTy<'tcx>,
28        ecx: &mut InterpCx<'tcx, CompileTimeMachine<'tcx>>,
29    ) -> Self {
30        let alloc_id = mplace.ptr().provenance.unwrap().alloc_id();
31        let alloc = ecx.memory.alloc_map.swap_remove(&alloc_id).unwrap().1;
32        ecx.tcx.mk_const_alloc(alloc)
33    }
34}
35
36pub(crate) fn create_static_alloc<'tcx>(
37    ecx: &mut CompileTimeInterpCx<'tcx>,
38    static_def_id: LocalDefId,
39    layout: TyAndLayout<'tcx>,
40) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
41    let alloc = Allocation::try_new(layout.size, layout.align.abi, AllocInit::Uninit, ())?;
42    let alloc_id = ecx.tcx.reserve_and_set_static_alloc(static_def_id.into());
43    assert_eq!(ecx.machine.static_root_ids, None);
44    ecx.machine.static_root_ids = Some((alloc_id, static_def_id));
45    assert!(ecx.memory.alloc_map.insert(alloc_id, (MemoryKind::Stack, alloc)).is_none());
46    interp_ok(ecx.ptr_to_mplace(Pointer::from(alloc_id).into(), layout))
47}
48
49/// A marker trait returned by [crate::interpret::Machine::enter_trace_span], identifying either a
50/// real [tracing::span::EnteredSpan] in case tracing is enabled, or the dummy type `()` when
51/// tracing is disabled. Also see [crate::enter_trace_span!] below.
52pub trait EnteredTraceSpan {
53    /// Allows executing an alternative function when tracing is disabled. Useful for example if you
54    /// want to open a trace span when tracing is enabled, and alternatively just log a line when
55    /// tracing is disabled.
56    fn or_if_tracing_disabled(self, f: impl FnOnce()) -> Self;
57}
58impl EnteredTraceSpan for () {
59    fn or_if_tracing_disabled(self, f: impl FnOnce()) -> Self {
60        f(); // tracing is disabled, execute the function
61        self
62    }
63}
64impl EnteredTraceSpan for tracing::span::EnteredSpan {
65    fn or_if_tracing_disabled(self, _f: impl FnOnce()) -> Self {
66        self // tracing is enabled, don't execute anything
67    }
68}
69
70/// Shortand for calling [crate::interpret::Machine::enter_trace_span] on a [tracing::info_span!].
71/// This is supposed to be compiled out when [crate::interpret::Machine::enter_trace_span] has the
72/// default implementation (i.e. when it does not actually enter the span but instead returns `()`).
73/// This macro takes a type implementing the [crate::interpret::Machine] trait as its first argument
74/// and otherwise accepts the same syntax as [tracing::span!] (see some tips below).
75/// Note: the result of this macro **must be used** because the span is exited when it's dropped.
76///
77/// ### Syntax accepted by this macro
78///
79/// The full documentation for the [tracing::span!] syntax can be found at [tracing] under "Using the
80/// Macros". A few possibly confusing syntaxes are listed here:
81/// ```rust
82/// # use rustc_const_eval::enter_trace_span;
83/// # type M = rustc_const_eval::const_eval::CompileTimeMachine<'static>;
84/// # let my_display_var = String::new();
85/// # let my_debug_var = String::new();
86/// // logs a span named "hello" with a field named "arg" of value 42 (works only because
87/// // 42 implements the tracing::Value trait, otherwise use one of the options below)
88/// let _trace = enter_trace_span!(M, "hello", arg = 42);
89/// // logs a field called "my_display_var" using the Display implementation
90/// let _trace = enter_trace_span!(M, "hello", %my_display_var);
91/// // logs a field called "my_debug_var" using the Debug implementation
92/// let _trace = enter_trace_span!(M, "hello", ?my_debug_var);
93///  ```
94///
95/// ### `NAME::SUBNAME` syntax
96///
97/// In addition to the syntax accepted by [tracing::span!], this macro optionally allows passing
98/// the span name (i.e. the first macro argument) in the form `NAME::SUBNAME` (without quotes) to
99/// indicate that the span has name "NAME" (usually the name of the component) and has an additional
100/// more specific name "SUBNAME" (usually the function name). The latter is passed to the [tracing]
101/// infrastructure as a span field with the name "NAME". This allows not being distracted by
102/// subnames when looking at the trace in <https://ui.perfetto.dev>, but when deeper introspection
103/// is needed within a component, it's still possible to view the subnames directly in the UI by
104/// selecting a span, clicking on the "NAME" argument on the right, and clicking on "Visualize
105/// argument values".
106/// ```rust
107/// # use rustc_const_eval::enter_trace_span;
108/// # type M = rustc_const_eval::const_eval::CompileTimeMachine<'static>;
109/// // for example, the first will expand to the second
110/// let _trace = enter_trace_span!(M, borrow_tracker::on_stack_pop, /* ... */);
111/// let _trace = enter_trace_span!(M, "borrow_tracker", borrow_tracker = "on_stack_pop", /* ... */);
112/// ```
113///
114/// ### `tracing_separate_thread` parameter
115///
116/// This macro was introduced to obtain better traces of Miri without impacting release performance.
117/// Miri saves traces using the the `tracing_chrome` `tracing::Layer` so that they can be visualized
118/// in <https://ui.perfetto.dev>. To instruct `tracing_chrome` to put some spans on a separate trace
119/// thread/line than other spans when viewed in <https://ui.perfetto.dev>, you can pass
120/// `tracing_separate_thread = tracing::field::Empty` to the tracing macros. This is useful to
121/// separate out spans which just indicate the current step or program frame being processed by the
122/// interpreter. You should use a value of [tracing::field::Empty] so that other tracing layers
123/// (e.g. the logger) will ignore the `tracing_separate_thread` field. For example:
124/// ```rust
125/// # use rustc_const_eval::enter_trace_span;
126/// # type M = rustc_const_eval::const_eval::CompileTimeMachine<'static>;
127/// let _trace = enter_trace_span!(M, step::eval_statement, tracing_separate_thread = tracing::field::Empty);
128/// ```
129///
130/// ### Executing something else when tracing is disabled
131///
132/// [crate::interpret::Machine::enter_trace_span] returns [EnteredTraceSpan], on which you can call
133/// [EnteredTraceSpan::or_if_tracing_disabled], to e.g. log a line as an alternative to the tracing
134/// span for when tracing is disabled. For example:
135/// ```rust
136/// # use rustc_const_eval::enter_trace_span;
137/// # use rustc_const_eval::interpret::EnteredTraceSpan;
138/// # type M = rustc_const_eval::const_eval::CompileTimeMachine<'static>;
139/// let _trace = enter_trace_span!(M, step::eval_statement)
140///     .or_if_tracing_disabled(|| tracing::info!("eval_statement"));
141/// ```
142#[macro_export]
143macro_rules! enter_trace_span {
144    ($machine:ty, $name:ident :: $subname:ident $($tt:tt)*) => {
145        $crate::enter_trace_span!($machine, stringify!($name), $name = %stringify!($subname) $($tt)*)
146    };
147
148    ($machine:ty, $($tt:tt)*) => {
149        <$machine as $crate::interpret::Machine>::enter_trace_span(|| tracing::info_span!($($tt)*))
150    };
151}