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