]> git.lizzy.rs Git - rust.git/blob - src/diagnostics.rs
c46ff5354dec3c749bb1fc0fb8ba0ca3b33b16b0
[rust.git] / src / diagnostics.rs
1 use std::cell::RefCell;
2
3 use rustc_span::DUMMY_SP;
4
5 use crate::*;
6
7 /// Details of premature program termination.
8 pub enum TerminationInfo {
9     Exit(i64),
10     Abort(Option<String>),
11     UnsupportedInIsolation(String),
12 }
13
14 /// Miri specific diagnostics
15 pub enum NonHaltingDiagnostic {
16     PoppedTrackedPointerTag(Item),
17     CreatedAlloc(AllocId),
18 }
19
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>,
24 ) -> Option<i64> {
25     use InterpError::*;
26
27     e.print_backtrace();
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),
34                 Abort(None) =>
35                     ("abnormal termination", format!("the evaluated program aborted execution")),
36                 Abort(Some(msg)) =>
37                     ("abnormal termination", format!("the evaluated program aborted execution: {}", msg)),
38                 UnsupportedInIsolation(msg) =>
39                     ("unsupported operation", format!("{}", msg)),
40             };
41             let help = match info {
42                 UnsupportedInIsolation(_) =>
43                     Some("pass the flag `-Zmiri-disable-isolation` to disable isolation"),
44                 _ => None,
45             };
46             (title, msg, help)
47         }
48         _ => {
49             let (title, msg) = match e.kind {
50                 Unsupported(_) =>
51                     ("unsupported operation", e.to_string()),
52                 UndefinedBehavior(_) =>
53                     ("Undefined Behavior", e.to_string()),
54                 ResourceExhaustion(_) =>
55                     ("resource exhaustion", e.to_string()),
56                 _ =>
57                     bug!("This error should be impossible in Miri: {}", e),
58             };
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`"),
62                 Unsupported(_) =>
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"),
68                 _ => None,
69             };
70             (title, msg, help)
71         }
72     };
73     report_msg(ecx, &format!("{}: {}", title, msg), msg, help, true)
74 }
75
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>>,
80     title: &str,
81     span_msg: String,
82     help: Option<&str>,
83     error: bool,
84 ) -> Option<i64> {
85     let span = if let Some(frame) = ecx.stack().last() {
86         frame.current_source_info().unwrap().span
87     } else {
88         DUMMY_SP
89     };
90     let mut err = if error {
91         ecx.tcx.sess.struct_span_err(span, title)
92     } else {
93         ecx.tcx.sess.diagnostic().span_note_diag(span, title)
94     };
95     err.span_label(span, span_msg);
96     if let Some(help) = help {
97         err.help(help);
98     }
99     // Add backtrace
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
105             .get(idx + 1)
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());
109         } else {
110             err.note(&frame_info.to_string());
111         }
112     }
113
114     err.emit();
115
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);
122         }
123     }
124     // Let the reported error determine the return code.
125     return None;
126 }
127
128 thread_local! {
129     static DIAGNOSTICS: RefCell<Vec<NonHaltingDiagnostic>> = RefCell::new(Vec::new());
130 }
131
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));
136 }
137
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::*;
146                 let msg = match e {
147                     PoppedTrackedPointerTag(item) =>
148                         format!("popped tracked tag for item {:?}", item),
149                     CreatedAlloc(AllocId(id)) =>
150                         format!("created allocation with id {}", id),
151                 };
152                 report_msg(this, "tracking was triggered", msg, None, false);
153             }
154         });
155     }
156 }