miri/
diagnostics.rs

1use std::fmt::{self, Write};
2use std::num::NonZero;
3
4use rustc_abi::{Align, Size};
5use rustc_errors::{Diag, DiagMessage, Level};
6use rustc_span::{DUMMY_SP, SpanData, Symbol};
7
8use crate::borrow_tracker::stacked_borrows::diagnostics::TagHistory;
9use crate::borrow_tracker::tree_borrows::diagnostics as tree_diagnostics;
10use crate::*;
11
12/// Details of premature program termination.
13pub enum TerminationInfo {
14    Exit {
15        code: i32,
16        leak_check: bool,
17    },
18    Abort(String),
19    UnsupportedInIsolation(String),
20    StackedBorrowsUb {
21        msg: String,
22        help: Vec<String>,
23        history: Option<TagHistory>,
24    },
25    TreeBorrowsUb {
26        title: String,
27        details: Vec<String>,
28        history: tree_diagnostics::HistoryData,
29    },
30    Int2PtrWithStrictProvenance,
31    Deadlock,
32    MultipleSymbolDefinitions {
33        link_name: Symbol,
34        first: SpanData,
35        first_crate: Symbol,
36        second: SpanData,
37        second_crate: Symbol,
38    },
39    SymbolShimClashing {
40        link_name: Symbol,
41        span: SpanData,
42    },
43    DataRace {
44        involves_non_atomic: bool,
45        ptr: interpret::Pointer<AllocId>,
46        op1: RacingOp,
47        op2: RacingOp,
48        extra: Option<&'static str>,
49        retag_explain: bool,
50    },
51    UnsupportedForeignItem(String),
52}
53
54pub struct RacingOp {
55    pub action: String,
56    pub thread_info: String,
57    pub span: SpanData,
58}
59
60impl fmt::Display for TerminationInfo {
61    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62        use TerminationInfo::*;
63        match self {
64            Exit { code, .. } => write!(f, "the evaluated program completed with exit code {code}"),
65            Abort(msg) => write!(f, "{msg}"),
66            UnsupportedInIsolation(msg) => write!(f, "{msg}"),
67            Int2PtrWithStrictProvenance =>
68                write!(
69                    f,
70                    "integer-to-pointer casts and `ptr::with_exposed_provenance` are not supported with `-Zmiri-strict-provenance`"
71                ),
72            StackedBorrowsUb { msg, .. } => write!(f, "{msg}"),
73            TreeBorrowsUb { title, .. } => write!(f, "{title}"),
74            Deadlock => write!(f, "the evaluated program deadlocked"),
75            MultipleSymbolDefinitions { link_name, .. } =>
76                write!(f, "multiple definitions of symbol `{link_name}`"),
77            SymbolShimClashing { link_name, .. } =>
78                write!(f, "found `{link_name}` symbol definition that clashes with a built-in shim",),
79            DataRace { involves_non_atomic, ptr, op1, op2, .. } =>
80                write!(
81                    f,
82                    "{} detected between (1) {} on {} and (2) {} on {} at {ptr:?}. (2) just happened here",
83                    if *involves_non_atomic { "Data race" } else { "Race condition" },
84                    op1.action,
85                    op1.thread_info,
86                    op2.action,
87                    op2.thread_info
88                ),
89            UnsupportedForeignItem(msg) => write!(f, "{msg}"),
90        }
91    }
92}
93
94impl fmt::Debug for TerminationInfo {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        write!(f, "{self}")
97    }
98}
99
100impl MachineStopType for TerminationInfo {
101    fn diagnostic_message(&self) -> DiagMessage {
102        self.to_string().into()
103    }
104    fn add_args(
105        self: Box<Self>,
106        _: &mut dyn FnMut(std::borrow::Cow<'static, str>, rustc_errors::DiagArgValue),
107    ) {
108    }
109}
110
111/// Miri specific diagnostics
112pub enum NonHaltingDiagnostic {
113    /// (new_tag, new_perm, (alloc_id, base_offset, orig_tag))
114    ///
115    /// new_perm is `None` for base tags.
116    CreatedPointerTag(NonZero<u64>, Option<String>, Option<(AllocId, AllocRange, ProvenanceExtra)>),
117    /// This `Item` was popped from the borrow stack. The string explains the reason.
118    PoppedPointerTag(Item, String),
119    CreatedAlloc(AllocId, Size, Align, MemoryKind),
120    FreedAlloc(AllocId),
121    AccessedAlloc(AllocId, AccessKind),
122    RejectedIsolatedOp(String),
123    ProgressReport {
124        block_count: u64, // how many basic blocks have been run so far
125    },
126    Int2Ptr {
127        details: bool,
128    },
129    NativeCallSharedMem,
130    WeakMemoryOutdatedLoad {
131        ptr: Pointer,
132    },
133    ExternTypeReborrow,
134}
135
136/// Level of Miri specific diagnostics
137pub enum DiagLevel {
138    Error,
139    Warning,
140    Note,
141}
142
143/// Generate a note/help text without a span.
144macro_rules! note {
145    ($($tt:tt)*) => { (None, format!($($tt)*)) };
146}
147/// Generate a note/help text with a span.
148macro_rules! note_span {
149    ($span:expr, $($tt:tt)*) => { (Some($span), format!($($tt)*)) };
150}
151
152/// Attempts to prune a stacktrace to omit the Rust runtime, and returns a bool indicating if any
153/// frames were pruned. If the stacktrace does not have any local frames, we conclude that it must
154/// be pointing to a problem in the Rust runtime itself, and do not prune it at all.
155pub fn prune_stacktrace<'tcx>(
156    mut stacktrace: Vec<FrameInfo<'tcx>>,
157    machine: &MiriMachine<'tcx>,
158) -> (Vec<FrameInfo<'tcx>>, bool) {
159    match machine.backtrace_style {
160        BacktraceStyle::Off => {
161            // Remove all frames marked with `caller_location` -- that attribute indicates we
162            // usually want to point at the caller, not them.
163            stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(machine.tcx));
164            // Retain one frame so that we can print a span for the error itself
165            stacktrace.truncate(1);
166            (stacktrace, false)
167        }
168        BacktraceStyle::Short => {
169            let original_len = stacktrace.len();
170            // Only prune frames if there is at least one local frame. This check ensures that if
171            // we get a backtrace that never makes it to the user code because it has detected a
172            // bug in the Rust runtime, we don't prune away every frame.
173            let has_local_frame = stacktrace.iter().any(|frame| machine.is_local(frame));
174            if has_local_frame {
175                // Remove all frames marked with `caller_location` -- that attribute indicates we
176                // usually want to point at the caller, not them.
177                stacktrace
178                    .retain(|frame| !frame.instance.def.requires_caller_location(machine.tcx));
179
180                // This is part of the logic that `std` uses to select the relevant part of a
181                // backtrace. But here, we only look for __rust_begin_short_backtrace, not
182                // __rust_end_short_backtrace because the end symbol comes from a call to the default
183                // panic handler.
184                stacktrace = stacktrace
185                    .into_iter()
186                    .take_while(|frame| {
187                        let def_id = frame.instance.def_id();
188                        let path = machine.tcx.def_path_str(def_id);
189                        !path.contains("__rust_begin_short_backtrace")
190                    })
191                    .collect::<Vec<_>>();
192
193                // After we prune frames from the bottom, there are a few left that are part of the
194                // Rust runtime. So we remove frames until we get to a local symbol, which should be
195                // main or a test.
196                // This len check ensures that we don't somehow remove every frame, as doing so breaks
197                // the primary error message.
198                while stacktrace.len() > 1
199                    && stacktrace.last().is_some_and(|frame| !machine.is_local(frame))
200                {
201                    stacktrace.pop();
202                }
203            }
204            let was_pruned = stacktrace.len() != original_len;
205            (stacktrace, was_pruned)
206        }
207        BacktraceStyle::Full => (stacktrace, false),
208    }
209}
210
211/// Emit a custom diagnostic without going through the miri-engine machinery.
212///
213/// Returns `Some` if this was regular program termination with a given exit code and a `bool` indicating whether a leak check should happen; `None` otherwise.
214pub fn report_error<'tcx>(
215    ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
216    e: InterpErrorInfo<'tcx>,
217) -> Option<(i32, bool)> {
218    use InterpErrorKind::*;
219    use UndefinedBehaviorInfo::*;
220
221    let mut msg = vec![];
222
223    let (title, helps) = if let MachineStop(info) = e.kind() {
224        let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
225        use TerminationInfo::*;
226        let title = match info {
227            &Exit { code, leak_check } => return Some((code, leak_check)),
228            Abort(_) => Some("abnormal termination"),
229            UnsupportedInIsolation(_) | Int2PtrWithStrictProvenance | UnsupportedForeignItem(_) =>
230                Some("unsupported operation"),
231            StackedBorrowsUb { .. } | TreeBorrowsUb { .. } | DataRace { .. } =>
232                Some("Undefined Behavior"),
233            Deadlock => Some("deadlock"),
234            MultipleSymbolDefinitions { .. } | SymbolShimClashing { .. } => None,
235        };
236        #[rustfmt::skip]
237        let helps = match info {
238            UnsupportedInIsolation(_) =>
239                vec![
240                    note!("set `MIRIFLAGS=-Zmiri-disable-isolation` to disable isolation;"),
241                    note!("or set `MIRIFLAGS=-Zmiri-isolation-error=warn` to make Miri return an error code from isolated operations (if supported for that operation) and continue with a warning"),
242                ],
243            UnsupportedForeignItem(_) => {
244                vec![
245                    note!("if this is a basic API commonly used on this target, please report an issue with Miri"),
246                    note!("however, note that Miri does not aim to support every FFI function out there; for instance, we will not support APIs for things such as GUIs, scripting languages, or databases"),
247                ]
248            }
249            StackedBorrowsUb { help, history, .. } => {
250                msg.extend(help.clone());
251                let mut helps = vec![
252                    note!("this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental"),
253                    note!("see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information"),
254                ];
255                if let Some(TagHistory {created, invalidated, protected}) = history.clone() {
256                    helps.push((Some(created.1), created.0));
257                    if let Some((msg, span)) = invalidated {
258                        helps.push(note_span!(span, "{msg}"));
259                    }
260                    if let Some((protector_msg, protector_span)) = protected {
261                        helps.push(note_span!(protector_span, "{protector_msg}"));
262                    }
263                }
264                helps
265            },
266            TreeBorrowsUb { title: _, details, history } => {
267                let mut helps = vec![
268                    note!("this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental")
269                ];
270                for m in details {
271                    helps.push(note!("{m}"));
272                }
273                for event in history.events.clone() {
274                    helps.push(event);
275                }
276                helps
277            }
278            MultipleSymbolDefinitions { first, first_crate, second, second_crate, .. } =>
279                vec![
280                    note_span!(*first, "it's first defined here, in crate `{first_crate}`"),
281                    note_span!(*second, "then it's defined here again, in crate `{second_crate}`"),
282                ],
283            SymbolShimClashing { link_name, span } =>
284                vec![note_span!(*span, "the `{link_name}` symbol is defined here")],
285            Int2PtrWithStrictProvenance =>
286                vec![note!("use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead")],
287            DataRace { op1, extra, retag_explain, .. } => {
288                let mut helps = vec![note_span!(op1.span, "and (1) occurred earlier here")];
289                if let Some(extra) = extra {
290                    helps.push(note!("{extra}"));
291                    helps.push(note!("see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model"));
292                }
293                if *retag_explain {
294                    helps.push(note!("retags occur on all (re)borrows and as well as when references are copied or moved"));
295                    helps.push(note!("retags permit optimizations that insert speculative reads or writes"));
296                    helps.push(note!("therefore from the perspective of data races, a retag has the same implications as a read or write"));
297                }
298                helps.push(note!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"));
299                helps.push(note!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information"));
300                helps
301            }
302                ,
303            _ => vec![],
304        };
305        (title, helps)
306    } else {
307        let title = match e.kind() {
308            UndefinedBehavior(ValidationError(validation_err))
309                if matches!(
310                    validation_err.kind,
311                    ValidationErrorKind::PointerAsInt { .. } | ValidationErrorKind::PartialPointer
312                ) =>
313            {
314                ecx.handle_ice(); // print interpreter backtrace (this is outside the eval `catch_unwind`)
315                bug!(
316                    "This validation error should be impossible in Miri: {}",
317                    format_interp_error(ecx.tcx.dcx(), e)
318                );
319            }
320            UndefinedBehavior(_) => "Undefined Behavior",
321            ResourceExhaustion(_) => "resource exhaustion",
322            Unsupported(
323                // We list only the ones that can actually happen.
324                UnsupportedOpInfo::Unsupported(_)
325                | UnsupportedOpInfo::UnsizedLocal
326                | UnsupportedOpInfo::ExternTypeField,
327            ) => "unsupported operation",
328            InvalidProgram(
329                // We list only the ones that can actually happen.
330                InvalidProgramInfo::AlreadyReported(_) | InvalidProgramInfo::Layout(..),
331            ) => "post-monomorphization error",
332            _ => {
333                ecx.handle_ice(); // print interpreter backtrace (this is outside the eval `catch_unwind`)
334                bug!(
335                    "This error should be impossible in Miri: {}",
336                    format_interp_error(ecx.tcx.dcx(), e)
337                );
338            }
339        };
340        #[rustfmt::skip]
341        let helps = match e.kind() {
342            Unsupported(_) =>
343                vec![
344                    note!("this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support"),
345                ],
346            UndefinedBehavior(AlignmentCheckFailed { .. })
347                if ecx.machine.check_alignment == AlignmentCheck::Symbolic
348            =>
349                vec![
350                    note!("this usually indicates that your program performed an invalid operation and caused Undefined Behavior"),
351                    note!("but due to `-Zmiri-symbolic-alignment-check`, alignment errors can also be false positives"),
352                ],
353            UndefinedBehavior(info) => {
354                let mut helps = vec![
355                    note!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"),
356                    note!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information"),
357                ];
358                match info {
359                    PointerUseAfterFree(alloc_id, _) | PointerOutOfBounds { alloc_id, .. } => {
360                        if let Some(span) = ecx.machine.allocated_span(*alloc_id) {
361                            helps.push(note_span!(span, "{:?} was allocated here:", alloc_id));
362                        }
363                        if let Some(span) = ecx.machine.deallocated_span(*alloc_id) {
364                            helps.push(note_span!(span, "{:?} was deallocated here:", alloc_id));
365                        }
366                    }
367                    AbiMismatchArgument { .. } | AbiMismatchReturn { .. } => {
368                        helps.push(note!("this means these two types are not *guaranteed* to be ABI-compatible across all targets"));
369                        helps.push(note!("if you think this code should be accepted anyway, please report an issue with Miri"));
370                    }
371                    _ => {},
372                }
373                helps
374            }
375            InvalidProgram(
376                InvalidProgramInfo::AlreadyReported(_)
377            ) => {
378                // This got already reported. No point in reporting it again.
379                return None;
380            }
381            _ =>
382                vec![],
383        };
384        (Some(title), helps)
385    };
386
387    let stacktrace = ecx.generate_stacktrace();
388    let (stacktrace, mut any_pruned) = prune_stacktrace(stacktrace, &ecx.machine);
389
390    let mut show_all_threads = false;
391
392    // We want to dump the allocation if this is `InvalidUninitBytes`.
393    // Since `format_interp_error` consumes `e`, we compute the outut early.
394    let mut extra = String::new();
395    match e.kind() {
396        UndefinedBehavior(InvalidUninitBytes(Some((alloc_id, access)))) => {
397            writeln!(
398                extra,
399                "Uninitialized memory occurred at {alloc_id:?}{range:?}, in this allocation:",
400                range = access.bad,
401            )
402            .unwrap();
403            writeln!(extra, "{:?}", ecx.dump_alloc(*alloc_id)).unwrap();
404        }
405        MachineStop(info) => {
406            let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
407            match info {
408                TerminationInfo::Deadlock => {
409                    show_all_threads = true;
410                }
411                _ => {}
412            }
413        }
414        _ => {}
415    }
416
417    msg.insert(0, format_interp_error(ecx.tcx.dcx(), e));
418
419    report_msg(
420        DiagLevel::Error,
421        if let Some(title) = title { format!("{title}: {}", msg[0]) } else { msg[0].clone() },
422        msg,
423        vec![],
424        helps,
425        &stacktrace,
426        Some(ecx.active_thread()),
427        &ecx.machine,
428    );
429
430    eprint!("{extra}"); // newlines are already in the string
431
432    if show_all_threads {
433        for (thread, stack) in ecx.machine.threads.all_stacks() {
434            if thread != ecx.active_thread() {
435                let stacktrace = Frame::generate_stacktrace_from_stack(stack);
436                let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine);
437                any_pruned |= was_pruned;
438                report_msg(
439                    DiagLevel::Error,
440                    format!("deadlock: the evaluated program deadlocked"),
441                    vec![format!("the evaluated program deadlocked")],
442                    vec![],
443                    vec![],
444                    &stacktrace,
445                    Some(thread),
446                    &ecx.machine,
447                )
448            }
449        }
450    }
451
452    // Include a note like `std` does when we omit frames from a backtrace
453    if any_pruned {
454        ecx.tcx.dcx().note(
455            "some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace",
456        );
457    }
458
459    // Debug-dump all locals.
460    for (i, frame) in ecx.active_thread_stack().iter().enumerate() {
461        trace!("-------------------");
462        trace!("Frame {}", i);
463        trace!("    return: {:?}", frame.return_place);
464        for (i, local) in frame.locals.iter().enumerate() {
465            trace!("    local {}: {:?}", i, local);
466        }
467    }
468
469    None
470}
471
472pub fn report_leaks<'tcx>(
473    ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
474    leaks: Vec<(AllocId, MemoryKind, Allocation<Provenance, AllocExtra<'tcx>, MiriAllocBytes>)>,
475) {
476    let mut any_pruned = false;
477    for (id, kind, alloc) in leaks {
478        let mut title = format!(
479            "memory leaked: {id:?} ({}, size: {:?}, align: {:?})",
480            kind,
481            alloc.size().bytes(),
482            alloc.align.bytes()
483        );
484        let Some(backtrace) = alloc.extra.backtrace else {
485            ecx.tcx.dcx().err(title);
486            continue;
487        };
488        title.push_str(", allocated here:");
489        let (backtrace, pruned) = prune_stacktrace(backtrace, &ecx.machine);
490        any_pruned |= pruned;
491        report_msg(
492            DiagLevel::Error,
493            title,
494            vec![],
495            vec![],
496            vec![],
497            &backtrace,
498            None, // we don't know the thread this is from
499            &ecx.machine,
500        );
501    }
502    if any_pruned {
503        ecx.tcx.dcx().note(
504            "some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace",
505        );
506    }
507}
508
509/// Report an error or note (depending on the `error` argument) with the given stacktrace.
510/// Also emits a full stacktrace of the interpreter stack.
511/// We want to present a multi-line span message for some errors. Diagnostics do not support this
512/// directly, so we pass the lines as a `Vec<String>` and display each line after the first with an
513/// additional `span_label` or `note` call.
514pub fn report_msg<'tcx>(
515    diag_level: DiagLevel,
516    title: String,
517    span_msg: Vec<String>,
518    notes: Vec<(Option<SpanData>, String)>,
519    helps: Vec<(Option<SpanData>, String)>,
520    stacktrace: &[FrameInfo<'tcx>],
521    thread: Option<ThreadId>,
522    machine: &MiriMachine<'tcx>,
523) {
524    let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span);
525    let sess = machine.tcx.sess;
526    let level = match diag_level {
527        DiagLevel::Error => Level::Error,
528        DiagLevel::Warning => Level::Warning,
529        DiagLevel::Note => Level::Note,
530    };
531    let mut err = Diag::<()>::new(sess.dcx(), level, title);
532    err.span(span);
533
534    // Show main message.
535    if span != DUMMY_SP {
536        for line in span_msg {
537            err.span_label(span, line);
538        }
539    } else {
540        // Make sure we show the message even when it is a dummy span.
541        for line in span_msg {
542            err.note(line);
543        }
544        err.note("(no span available)");
545    }
546
547    // Show note and help messages.
548    let mut extra_span = false;
549    for (span_data, note) in notes {
550        if let Some(span_data) = span_data {
551            err.span_note(span_data.span(), note);
552            extra_span = true;
553        } else {
554            err.note(note);
555        }
556    }
557    for (span_data, help) in helps {
558        if let Some(span_data) = span_data {
559            err.span_help(span_data.span(), help);
560            extra_span = true;
561        } else {
562            err.help(help);
563        }
564    }
565
566    // Add backtrace
567    let mut backtrace_title = String::from("BACKTRACE");
568    if extra_span {
569        write!(backtrace_title, " (of the first span)").unwrap();
570    }
571    if let Some(thread) = thread {
572        let thread_name = machine.threads.get_thread_display_name(thread);
573        if thread_name != "main" {
574            // Only print thread name if it is not `main`.
575            write!(backtrace_title, " on thread `{thread_name}`").unwrap();
576        };
577    }
578    write!(backtrace_title, ":").unwrap();
579    err.note(backtrace_title);
580    for (idx, frame_info) in stacktrace.iter().enumerate() {
581        let is_local = machine.is_local(frame_info);
582        // No span for non-local frames and the first frame (which is the error site).
583        if is_local && idx > 0 {
584            err.subdiagnostic(frame_info.as_note(machine.tcx));
585        } else {
586            let sm = sess.source_map();
587            let span = sm.span_to_embeddable_string(frame_info.span);
588            err.note(format!("{frame_info} at {span}"));
589        }
590    }
591
592    err.emit();
593}
594
595impl<'tcx> MiriMachine<'tcx> {
596    pub fn emit_diagnostic(&self, e: NonHaltingDiagnostic) {
597        use NonHaltingDiagnostic::*;
598
599        let stacktrace = Frame::generate_stacktrace_from_stack(self.threads.active_thread_stack());
600        let (stacktrace, _was_pruned) = prune_stacktrace(stacktrace, self);
601
602        let (title, diag_level) = match &e {
603            RejectedIsolatedOp(_) =>
604                ("operation rejected by isolation".to_string(), DiagLevel::Warning),
605            Int2Ptr { .. } => ("integer-to-pointer cast".to_string(), DiagLevel::Warning),
606            NativeCallSharedMem =>
607                ("sharing memory with a native function".to_string(), DiagLevel::Warning),
608            ExternTypeReborrow =>
609                ("reborrow of reference to `extern type`".to_string(), DiagLevel::Warning),
610            CreatedPointerTag(..)
611            | PoppedPointerTag(..)
612            | CreatedAlloc(..)
613            | AccessedAlloc(..)
614            | FreedAlloc(..)
615            | ProgressReport { .. }
616            | WeakMemoryOutdatedLoad { .. } =>
617                ("tracking was triggered".to_string(), DiagLevel::Note),
618        };
619
620        let msg = match &e {
621            CreatedPointerTag(tag, None, _) => format!("created base tag {tag:?}"),
622            CreatedPointerTag(tag, Some(perm), None) =>
623                format!("created {tag:?} with {perm} derived from unknown tag"),
624            CreatedPointerTag(tag, Some(perm), Some((alloc_id, range, orig_tag))) =>
625                format!(
626                    "created tag {tag:?} with {perm} at {alloc_id:?}{range:?} derived from {orig_tag:?}"
627                ),
628            PoppedPointerTag(item, cause) => format!("popped tracked tag for item {item:?}{cause}"),
629            CreatedAlloc(AllocId(id), size, align, kind) =>
630                format!(
631                    "created {kind} allocation of {size} bytes (alignment {align} bytes) with id {id}",
632                    size = size.bytes(),
633                    align = align.bytes(),
634                ),
635            AccessedAlloc(AllocId(id), access_kind) =>
636                format!("{access_kind} to allocation with id {id}"),
637            FreedAlloc(AllocId(id)) => format!("freed allocation with id {id}"),
638            RejectedIsolatedOp(ref op) =>
639                format!("{op} was made to return an error due to isolation"),
640            ProgressReport { .. } =>
641                format!("progress report: current operation being executed is here"),
642            Int2Ptr { .. } => format!("integer-to-pointer cast"),
643            NativeCallSharedMem => format!("sharing memory with a native function called via FFI"),
644            WeakMemoryOutdatedLoad { ptr } =>
645                format!("weak memory emulation: outdated value returned from load at {ptr}"),
646            ExternTypeReborrow =>
647                format!("reborrow of a reference to `extern type` is not properly supported"),
648        };
649
650        let notes = match &e {
651            ProgressReport { block_count } => {
652                vec![note!("so far, {block_count} basic blocks have been executed")]
653            }
654            _ => vec![],
655        };
656
657        let helps = match &e {
658            Int2Ptr { details: true } => {
659                let mut v = vec![
660                    note!(
661                        "this program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`, which means that Miri might miss pointer bugs in this program"
662                    ),
663                    note!(
664                        "see https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation"
665                    ),
666                    note!(
667                        "to ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead"
668                    ),
669                    note!(
670                        "you can then set `MIRIFLAGS=-Zmiri-strict-provenance` to ensure you are not relying on `with_exposed_provenance` semantics"
671                    ),
672                ];
673                if self.borrow_tracker.as_ref().is_some_and(|b| {
674                    matches!(b.borrow().borrow_tracker_method(), BorrowTrackerMethod::TreeBorrows)
675                }) {
676                    v.push(
677                        note!("Tree Borrows does not support integer-to-pointer casts, so the program is likely to go wrong when this pointer gets used")
678                    );
679                } else {
680                    v.push(
681                        note!("alternatively, `MIRIFLAGS=-Zmiri-permissive-provenance` disables this warning")
682                    );
683                }
684                v
685            }
686            NativeCallSharedMem => {
687                vec![
688                    note!(
689                        "when memory is shared with a native function call, Miri stops tracking initialization and provenance for that memory"
690                    ),
691                    note!(
692                        "in particular, Miri assumes that the native call initializes all memory it has access to"
693                    ),
694                    note!(
695                        "Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory"
696                    ),
697                    note!(
698                        "what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free"
699                    ),
700                ]
701            }
702            ExternTypeReborrow => {
703                assert!(self.borrow_tracker.as_ref().is_some_and(|b| {
704                    matches!(
705                        b.borrow().borrow_tracker_method(),
706                        BorrowTrackerMethod::StackedBorrows
707                    )
708                }));
709                vec![
710                    note!(
711                        "`extern type` are not compatible with the Stacked Borrows aliasing model implemented by Miri; Miri may miss bugs in this code"
712                    ),
713                    note!(
714                        "try running with `MIRIFLAGS=-Zmiri-tree-borrows` to use the more permissive but also even more experimental Tree Borrows aliasing checks instead"
715                    ),
716                ]
717            }
718            _ => vec![],
719        };
720
721        report_msg(
722            diag_level,
723            title,
724            vec![msg],
725            notes,
726            helps,
727            &stacktrace,
728            Some(self.threads.active_thread()),
729            self,
730        );
731    }
732}
733
734impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
735pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
736    fn emit_diagnostic(&self, e: NonHaltingDiagnostic) {
737        let this = self.eval_context_ref();
738        this.machine.emit_diagnostic(e);
739    }
740
741    /// We had a panic in Miri itself, try to print something useful.
742    fn handle_ice(&self) {
743        eprintln!();
744        eprintln!(
745            "Miri caused an ICE during evaluation. Here's the interpreter backtrace at the time of the panic:"
746        );
747        let this = self.eval_context_ref();
748        let stacktrace = this.generate_stacktrace();
749        report_msg(
750            DiagLevel::Note,
751            "the place in the program where the ICE was triggered".to_string(),
752            vec![],
753            vec![],
754            vec![],
755            &stacktrace,
756            Some(this.active_thread()),
757            &this.machine,
758        );
759    }
760}