]> git.lizzy.rs Git - rust.git/blob - src/diagnostics.rs
Make the non-halting diagnostic scheme independent of `InterpError`
[rust.git] / src / diagnostics.rs
1 use rustc_mir::interpret::InterpErrorInfo;
2 use std::cell::RefCell;
3
4 use crate::*;
5
6 pub enum NonHaltingDiagnostic {
7     PoppedTrackedPointerTag(Item),
8 }
9
10 pub fn report_err<'tcx, 'mir>(
11     ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
12     mut e: InterpErrorInfo<'tcx>,
13 ) -> Option<i64> {
14     // Special treatment for some error kinds
15     let msg = match e.kind {
16         InterpError::MachineStop(ref info) => {
17             let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
18             match info {
19                 TerminationInfo::Exit(code) => return Some(*code),
20                 TerminationInfo::Abort => format!("the evaluated program aborted execution"),
21             }
22         }
23         err_unsup!(NoMirFor(..)) => format!(
24             "{}. Did you set `MIRI_SYSROOT` to a Miri-enabled sysroot? You can prepare one with `cargo miri setup`.",
25             e
26         ),
27         InterpError::InvalidProgram(_) => bug!("This error should be impossible in Miri: {}", e),
28         _ => e.to_string(),
29     };
30     e.print_backtrace();
31     report_msg(ecx, msg, true)
32 }
33
34 pub fn report_msg<'tcx, 'mir>(
35     ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
36     msg: String,
37     error: bool,
38 ) -> Option<i64> {
39     if let Some(frame) = ecx.stack().last() {
40         let span = frame.current_source_info().unwrap().span;
41
42         let mut err = if error {
43             let msg = format!("Miri evaluation error: {}", msg);
44             ecx.tcx.sess.struct_span_err(span, msg.as_str())
45         } else {
46             ecx.tcx.sess.diagnostic().span_note_diag(span, msg.as_str())
47         };
48         let frames = ecx.generate_stacktrace(None);
49         err.span_label(span, msg);
50         // We iterate with indices because we need to look at the next frame (the caller).
51         for idx in 0..frames.len() {
52             let frame_info = &frames[idx];
53             let call_site_is_local = frames
54                 .get(idx + 1)
55                 .map_or(false, |caller_info| caller_info.instance.def_id().is_local());
56             if call_site_is_local {
57                 err.span_note(frame_info.call_site, &frame_info.to_string());
58             } else {
59                 err.note(&frame_info.to_string());
60             }
61         }
62         err.emit();
63     } else {
64         ecx.tcx.sess.err(&msg);
65     }
66
67     for (i, frame) in ecx.stack().iter().enumerate() {
68         trace!("-------------------");
69         trace!("Frame {}", i);
70         trace!("    return: {:?}", frame.return_place.map(|p| *p));
71         for (i, local) in frame.locals.iter().enumerate() {
72             trace!("    local {}: {:?}", i, local.value);
73         }
74     }
75     // Let the reported error determine the return code.
76     return None;
77 }
78
79 thread_local! {
80     static ECX: RefCell<Vec<NonHaltingDiagnostic>> = RefCell::new(Vec::new());
81 }
82
83 pub fn register_err(e: NonHaltingDiagnostic) {
84     ECX.with(|ecx| ecx.borrow_mut().push(e));
85 }
86
87 impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
88 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
89     fn process_errors(&self) {
90         let this = self.eval_context_ref();
91         ECX.with(|ecx| {
92             for e in ecx.borrow_mut().drain(..) {
93                 let msg = match e {
94                     NonHaltingDiagnostic::PoppedTrackedPointerTag(item) =>
95                         format!("popped tracked tag for item {:?}", item),
96                 };
97                 report_msg(this, msg, false);
98             }
99         });
100     }
101 }