1 use std::cell::RefCell;
3 use rustc_span::DUMMY_SP;
7 /// Miri specific diagnostics
8 pub enum NonHaltingDiagnostic {
9 PoppedTrackedPointerTag(Item),
10 CreatedAlloc(AllocId),
13 /// Emit a custom diagnostic without going through the miri-engine machinery
14 pub fn report_diagnostic<'tcx, 'mir>(
15 ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
16 mut e: InterpErrorInfo<'tcx>,
19 let title = match e.kind {
20 Unsupported(_) => "unsupported operation",
21 UndefinedBehavior(_) => "Undefined Behavior",
22 InvalidProgram(_) => bug!("This error should be impossible in Miri: {}", e),
23 ResourceExhaustion(_) => "resource exhaustion",
24 MachineStop(_) => "program stopped",
26 let msg = match e.kind {
27 MachineStop(ref info) => {
28 let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
30 TerminationInfo::Exit(code) => return Some(*code),
31 TerminationInfo::Abort(None) => format!("the evaluated program aborted execution"),
32 TerminationInfo::Abort(Some(msg)) => format!("the evaluated program aborted execution: {}", msg),
37 let help = match e.kind {
38 Unsupported(UnsupportedOpInfo::NoMirFor(..)) =>
39 Some("set `MIRI_SYSROOT` to a Miri sysroot, which you can prepare with `cargo miri setup`"),
41 Some("this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support"),
42 UndefinedBehavior(UndefinedBehaviorInfo::UbExperimental(_)) =>
43 Some("this indicates a potential bug in the program: it violated *experimental* rules, and caused Undefined Behavior"),
44 UndefinedBehavior(_) =>
45 Some("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"),
49 report_msg(ecx, &format!("{}: {}", title, msg), msg, help, true)
52 /// Report an error or note (depending on the `error` argument) at the current frame's current statement.
53 /// Also emits a full stacktrace of the interpreter stack.
54 pub fn report_msg<'tcx, 'mir>(
55 ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
61 let span = if let Some(frame) = ecx.stack().last() {
62 frame.current_source_info().unwrap().span
66 let mut err = if error {
67 ecx.tcx.sess.struct_span_err(span, title)
69 ecx.tcx.sess.diagnostic().span_note_diag(span, title)
71 err.span_label(span, span_msg);
72 if let Some(help) = help {
76 let frames = ecx.generate_stacktrace(None);
77 // We iterate with indices because we need to look at the next frame (the caller).
78 for idx in 0..frames.len() {
79 let frame_info = &frames[idx];
80 let call_site_is_local = frames
82 .map_or(false, |caller_info| caller_info.instance.def_id().is_local());
83 if call_site_is_local {
84 err.span_note(frame_info.call_site, &frame_info.to_string());
86 err.note(&frame_info.to_string());
92 for (i, frame) in ecx.stack().iter().enumerate() {
93 trace!("-------------------");
94 trace!("Frame {}", i);
95 trace!(" return: {:?}", frame.return_place.map(|p| *p));
96 for (i, local) in frame.locals.iter().enumerate() {
97 trace!(" local {}: {:?}", i, local.value);
100 // Let the reported error determine the return code.
105 static DIAGNOSTICS: RefCell<Vec<NonHaltingDiagnostic>> = RefCell::new(Vec::new());
108 /// Schedule a diagnostic for emitting. This function works even if you have no `InterpCx` available.
109 /// The diagnostic will be emitted after the current interpreter step is finished.
110 pub fn register_diagnostic(e: NonHaltingDiagnostic) {
111 DIAGNOSTICS.with(|diagnostics| diagnostics.borrow_mut().push(e));
114 impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
115 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
116 /// Emit all diagnostics that were registed with `register_diagnostics`
117 fn process_diagnostics(&self) {
118 let this = self.eval_context_ref();
119 DIAGNOSTICS.with(|diagnostics| {
120 for e in diagnostics.borrow_mut().drain(..) {
121 use NonHaltingDiagnostic::*;
123 PoppedTrackedPointerTag(item) =>
124 format!("popped tracked tag for item {:?}", item),
125 CreatedAlloc(AllocId(id)) =>
126 format!("created allocation with id {}", id),
128 report_msg(this, "tracking was triggered", msg, None, false);