1 use std::cell::RefCell;
3 use rustc_span::DUMMY_SP;
7 /// Details of premature program termination.
8 pub enum TerminationInfo {
10 Abort(Option<String>),
11 UnsupportedInIsolation(String),
14 /// Miri specific diagnostics
15 pub enum NonHaltingDiagnostic {
16 PoppedTrackedPointerTag(Item),
17 CreatedAlloc(AllocId),
20 /// Emit a custom diagnostic without going through the miri-engine machinery
21 pub fn report_error<'tcx, 'mir>(
22 ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
23 mut e: InterpErrorInfo<'tcx>,
28 let (title, msg, help) = match e.kind {
29 MachineStop(info) => {
30 let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
31 use TerminationInfo::*;
32 let (title, msg) = match info {
33 Exit(code) => return Some(*code),
35 ("abnormal termination", format!("the evaluated program aborted execution")),
37 ("abnormal termination", format!("the evaluated program aborted execution: {}", msg)),
38 UnsupportedInIsolation(msg) =>
39 ("unsupported operation", format!("{}", msg)),
41 let help = match info {
42 UnsupportedInIsolation(_) =>
43 Some("pass the flag `-Zmiri-disable-isolation` to disable isolation"),
49 let (title, msg) = match e.kind {
51 ("unsupported operation", e.to_string()),
52 UndefinedBehavior(_) =>
53 ("Undefined Behavior", e.to_string()),
54 ResourceExhaustion(_) =>
55 ("resource exhaustion", e.to_string()),
57 bug!("This error should be impossible in Miri: {}", e),
59 let help = match e.kind {
60 Unsupported(UnsupportedOpInfo::NoMirFor(..)) =>
61 Some("set `MIRI_SYSROOT` to a Miri sysroot, which you can prepare with `cargo miri setup`"),
63 Some("this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support"),
64 UndefinedBehavior(UndefinedBehaviorInfo::UbExperimental(_)) =>
65 Some("this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental"),
66 UndefinedBehavior(_) =>
67 Some("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"),
73 report_msg(ecx, &format!("{}: {}", title, msg), msg, help, true)
76 /// Report an error or note (depending on the `error` argument) at the current frame's current statement.
77 /// Also emits a full stacktrace of the interpreter stack.
78 fn report_msg<'tcx, 'mir>(
79 ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
85 let span = if let Some(frame) = ecx.stack().last() {
86 frame.current_source_info().unwrap().span
90 let mut err = if error {
91 ecx.tcx.sess.struct_span_err(span, title)
93 ecx.tcx.sess.diagnostic().span_note_diag(span, title)
95 err.span_label(span, span_msg);
96 if let Some(help) = help {
100 let frames = ecx.generate_stacktrace(None);
101 // We iterate with indices because we need to look at the next frame (the caller).
102 for idx in 0..frames.len() {
103 let frame_info = &frames[idx];
104 let call_site_is_local = frames
106 .map_or(false, |caller_info| caller_info.instance.def_id().is_local());
107 if call_site_is_local {
108 err.span_note(frame_info.call_site, &frame_info.to_string());
110 err.note(&frame_info.to_string());
116 for (i, frame) in ecx.stack().iter().enumerate() {
117 trace!("-------------------");
118 trace!("Frame {}", i);
119 trace!(" return: {:?}", frame.return_place.map(|p| *p));
120 for (i, local) in frame.locals.iter().enumerate() {
121 trace!(" local {}: {:?}", i, local.value);
124 // Let the reported error determine the return code.
129 static DIAGNOSTICS: RefCell<Vec<NonHaltingDiagnostic>> = RefCell::new(Vec::new());
132 /// Schedule a diagnostic for emitting. This function works even if you have no `InterpCx` available.
133 /// The diagnostic will be emitted after the current interpreter step is finished.
134 pub fn register_diagnostic(e: NonHaltingDiagnostic) {
135 DIAGNOSTICS.with(|diagnostics| diagnostics.borrow_mut().push(e));
138 impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
139 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
140 /// Emit all diagnostics that were registed with `register_diagnostics`
141 fn process_diagnostics(&self) {
142 let this = self.eval_context_ref();
143 DIAGNOSTICS.with(|diagnostics| {
144 for e in diagnostics.borrow_mut().drain(..) {
145 use NonHaltingDiagnostic::*;
147 PoppedTrackedPointerTag(item) =>
148 format!("popped tracked tag for item {:?}", item),
149 CreatedAlloc(AllocId(id)) =>
150 format!("created allocation with id {}", id),
152 report_msg(this, "tracking was triggered", msg, None, false);