1 use std::fmt;
2 use std::num::NonZeroU64;
3
4 use log::trace;
5
6 use rustc_const_eval::ReportErrorExt;
7 use rustc_errors::DiagnosticMessage;
8 use rustc_span::{source_map::DUMMY_SP, SpanData, Symbol};
9 use rustc_target::abi::{Align, Size};
10
11 use crate::borrow_tracker::stacked_borrows::diagnostics::TagHistory;
12 use crate::borrow_tracker::tree_borrows::diagnostics as tree_diagnostics;
13 use crate::*;
14
15 /// Details of premature program termination.
16 pub enum TerminationInfo {
17 Exit {
18 code: i64,
19 leak_check: bool,
20 },
21 Abort(String),
22 UnsupportedInIsolation(String),
23 StackedBorrowsUb {
24 msg: String,
25 help: Option<String>,
26 history: Option<TagHistory>,
27 },
28 TreeBorrowsUb {
29 title: String,
30 details: Vec<String>,
31 history: tree_diagnostics::HistoryData,
32 },
33 Int2PtrWithStrictProvenance,
34 Deadlock,
35 MultipleSymbolDefinitions {
36 link_name: Symbol,
37 first: SpanData,
38 first_crate: Symbol,
39 second: SpanData,
40 second_crate: Symbol,
41 },
42 SymbolShimClashing {
43 link_name: Symbol,
44 span: SpanData,
45 },
46 DataRace {
47 op1: RacingOp,
48 op2: RacingOp,
49 ptr: Pointer,
50 },
51 }
52
53 pub struct RacingOp {
54 pub action: String,
55 pub thread_info: String,
56 pub span: SpanData,
57 }
58
59 impl fmt::Display for TerminationInfo {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 use TerminationInfo::*;
62 match self {
63 Exit { code, .. } => write!(f, "the evaluated program completed with exit code {code}"),
64 Abort(msg) => write!(f, "{msg}"),
65 UnsupportedInIsolation(msg) => write!(f, "{msg}"),
66 Int2PtrWithStrictProvenance =>
67 write!(
68 f,
69 "integer-to-pointer casts and `ptr::from_exposed_addr` are not supported with `-Zmiri-strict-provenance`"
70 ),
71 StackedBorrowsUb { msg, .. } => write!(f, "{msg}"),
72 TreeBorrowsUb { title, .. } => write!(f, "{title}"),
73 Deadlock => write!(f, "the evaluated program deadlocked"),
74 MultipleSymbolDefinitions { link_name, .. } =>
75 write!(f, "multiple definitions of symbol `{link_name}`"),
76 SymbolShimClashing { link_name, .. } =>
77 write!(f, "found `{link_name}` symbol definition that clashes with a built-in shim",),
78 DataRace { ptr, op1, op2 } =>
79 write!(
80 f,
81 "Data race detected between (1) {} on {} and (2) {} on {} at {ptr:?}. (2) just happened here",
82 op1.action, op1.thread_info, op2.action, op2.thread_info
83 ),
84 }
85 }
86 }
87
88 impl fmt::Debug for TerminationInfo {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 write!(f, "{self}")
91 }
92 }
93
94 impl MachineStopType for TerminationInfo {
diagnostic_message(&self) -> DiagnosticMessage95 fn diagnostic_message(&self) -> DiagnosticMessage {
96 self.to_string().into()
97 }
add_args( self: Box<Self>, _: &mut dyn FnMut( std::borrow::Cow<'static, str>, rustc_errors::DiagnosticArgValue<'static>, ), )98 fn add_args(
99 self: Box<Self>,
100 _: &mut dyn FnMut(
101 std::borrow::Cow<'static, str>,
102 rustc_errors::DiagnosticArgValue<'static>,
103 ),
104 ) {
105 }
106 }
107
108 /// Miri specific diagnostics
109 pub enum NonHaltingDiagnostic {
110 /// (new_tag, new_perm, (alloc_id, base_offset, orig_tag))
111 ///
112 /// new_perm is `None` for base tags.
113 CreatedPointerTag(NonZeroU64, Option<String>, Option<(AllocId, AllocRange, ProvenanceExtra)>),
114 /// This `Item` was popped from the borrow stack. The string explains the reason.
115 PoppedPointerTag(Item, String),
116 CreatedCallId(CallId),
117 CreatedAlloc(AllocId, Size, Align, MemoryKind<MiriMemoryKind>),
118 FreedAlloc(AllocId),
119 RejectedIsolatedOp(String),
120 ProgressReport {
121 block_count: u64, // how many basic blocks have been run so far
122 },
123 Int2Ptr {
124 details: bool,
125 },
126 WeakMemoryOutdatedLoad,
127 }
128
129 /// Level of Miri specific diagnostics
130 pub enum DiagLevel {
131 Error,
132 Warning,
133 Note,
134 }
135
136 /// Attempts to prune a stacktrace to omit the Rust runtime, and returns a bool indicating if any
137 /// frames were pruned. If the stacktrace does not have any local frames, we conclude that it must
138 /// be pointing to a problem in the Rust runtime itself, and do not prune it at all.
prune_stacktrace<'tcx>( mut stacktrace: Vec<FrameInfo<'tcx>>, machine: &MiriMachine<'_, 'tcx>, ) -> (Vec<FrameInfo<'tcx>>, bool)139 pub fn prune_stacktrace<'tcx>(
140 mut stacktrace: Vec<FrameInfo<'tcx>>,
141 machine: &MiriMachine<'_, 'tcx>,
142 ) -> (Vec<FrameInfo<'tcx>>, bool) {
143 match machine.backtrace_style {
144 BacktraceStyle::Off => {
145 // Remove all frames marked with `caller_location` -- that attribute indicates we
146 // usually want to point at the caller, not them.
147 stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(machine.tcx));
148 // Retain one frame so that we can print a span for the error itself
149 stacktrace.truncate(1);
150 (stacktrace, false)
151 }
152 BacktraceStyle::Short => {
153 let original_len = stacktrace.len();
154 // Only prune frames if there is at least one local frame. This check ensures that if
155 // we get a backtrace that never makes it to the user code because it has detected a
156 // bug in the Rust runtime, we don't prune away every frame.
157 let has_local_frame = stacktrace.iter().any(|frame| machine.is_local(frame));
158 if has_local_frame {
159 // Remove all frames marked with `caller_location` -- that attribute indicates we
160 // usually want to point at the caller, not them.
161 stacktrace
162 .retain(|frame| !frame.instance.def.requires_caller_location(machine.tcx));
163
164 // This is part of the logic that `std` uses to select the relevant part of a
165 // backtrace. But here, we only look for __rust_begin_short_backtrace, not
166 // __rust_end_short_backtrace because the end symbol comes from a call to the default
167 // panic handler.
168 stacktrace = stacktrace
169 .into_iter()
170 .take_while(|frame| {
171 let def_id = frame.instance.def_id();
172 let path = machine.tcx.def_path_str(def_id);
173 !path.contains("__rust_begin_short_backtrace")
174 })
175 .collect::<Vec<_>>();
176
177 // After we prune frames from the bottom, there are a few left that are part of the
178 // Rust runtime. So we remove frames until we get to a local symbol, which should be
179 // main or a test.
180 // This len check ensures that we don't somehow remove every frame, as doing so breaks
181 // the primary error message.
182 while stacktrace.len() > 1
183 && stacktrace.last().map_or(false, |frame| !machine.is_local(frame))
184 {
185 stacktrace.pop();
186 }
187 }
188 let was_pruned = stacktrace.len() != original_len;
189 (stacktrace, was_pruned)
190 }
191 BacktraceStyle::Full => (stacktrace, false),
192 }
193 }
194
195 /// Emit a custom diagnostic without going through the miri-engine machinery.
196 ///
197 /// Returns `Some` if this was regular program termination with a given exit code and a `bool` indicating whether a leak check should happen; `None` otherwise.
report_error<'tcx, 'mir>( ecx: &InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, e: InterpErrorInfo<'tcx>, ) -> Option<(i64, bool)>198 pub fn report_error<'tcx, 'mir>(
199 ecx: &InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>,
200 e: InterpErrorInfo<'tcx>,
201 ) -> Option<(i64, bool)> {
202 use InterpError::*;
203
204 let mut msg = vec![];
205
206 let (title, helps) = if let MachineStop(info) = e.kind() {
207 let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
208 use TerminationInfo::*;
209 let title = match info {
210 Exit { code, leak_check } => return Some((*code, *leak_check)),
211 Abort(_) => Some("abnormal termination"),
212 UnsupportedInIsolation(_) | Int2PtrWithStrictProvenance =>
213 Some("unsupported operation"),
214 StackedBorrowsUb { .. } | TreeBorrowsUb { .. } | DataRace { .. } =>
215 Some("Undefined Behavior"),
216 Deadlock => Some("deadlock"),
217 MultipleSymbolDefinitions { .. } | SymbolShimClashing { .. } => None,
218 };
219 #[rustfmt::skip]
220 let helps = match info {
221 UnsupportedInIsolation(_) =>
222 vec![
223 (None, format!("pass the flag `-Zmiri-disable-isolation` to disable isolation;")),
224 (None, format!("or pass `-Zmiri-isolation-error=warn` to configure Miri to return an error code from isolated operations (if supported for that operation) and continue with a warning")),
225 ],
226 StackedBorrowsUb { help, history, .. } => {
227 let url = "https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md";
228 msg.extend(help.clone());
229 let mut helps = vec![
230 (None, format!("this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental")),
231 (None, format!("see {url} for further information")),
232 ];
233 if let Some(TagHistory {created, invalidated, protected}) = history.clone() {
234 helps.push((Some(created.1), created.0));
235 if let Some((msg, span)) = invalidated {
236 helps.push((Some(span), msg));
237 }
238 if let Some((protector_msg, protector_span)) = protected {
239 helps.push((Some(protector_span), protector_msg));
240 }
241 }
242 helps
243 },
244 TreeBorrowsUb { title: _, details, history } => {
245 let mut helps = vec![
246 (None, format!("this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental"))
247 ];
248 for m in details {
249 helps.push((None, m.clone()));
250 }
251 for event in history.events.clone() {
252 helps.push(event);
253 }
254 helps
255 }
256 MultipleSymbolDefinitions { first, first_crate, second, second_crate, .. } =>
257 vec![
258 (Some(*first), format!("it's first defined here, in crate `{first_crate}`")),
259 (Some(*second), format!("then it's defined here again, in crate `{second_crate}`")),
260 ],
261 SymbolShimClashing { link_name, span } =>
262 vec![(Some(*span), format!("the `{link_name}` symbol is defined here"))],
263 Int2PtrWithStrictProvenance =>
264 vec![(None, format!("use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead"))],
265 DataRace { op1, .. } =>
266 vec![
267 (Some(op1.span), format!("and (1) occurred earlier here")),
268 (None, format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior")),
269 (None, format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information")),
270 ],
271 _ => vec![],
272 };
273 (title, helps)
274 } else {
275 #[rustfmt::skip]
276 let title = match e.kind() {
277 UndefinedBehavior(_) =>
278 "Undefined Behavior",
279 ResourceExhaustion(_) =>
280 "resource exhaustion",
281 Unsupported(
282 // We list only the ones that can actually happen.
283 UnsupportedOpInfo::Unsupported(_)
284 ) =>
285 "unsupported operation",
286 InvalidProgram(
287 // We list only the ones that can actually happen.
288 InvalidProgramInfo::AlreadyReported(_) |
289 InvalidProgramInfo::Layout(..)
290 ) =>
291 "post-monomorphization error",
292 kind =>
293 bug!("This error should be impossible in Miri: {kind:?}"),
294 };
295 #[rustfmt::skip]
296 let helps = match e.kind() {
297 Unsupported(_) =>
298 vec![(None, format!("this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support"))],
299 UndefinedBehavior(UndefinedBehaviorInfo::AlignmentCheckFailed { .. })
300 if ecx.machine.check_alignment == AlignmentCheck::Symbolic
301 =>
302 vec![
303 (None, format!("this usually indicates that your program performed an invalid operation and caused Undefined Behavior")),
304 (None, format!("but due to `-Zmiri-symbolic-alignment-check`, alignment errors can also be false positives")),
305 ],
306 UndefinedBehavior(_) =>
307 vec![
308 (None, format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior")),
309 (None, format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information")),
310 ],
311 InvalidProgram(
312 InvalidProgramInfo::AlreadyReported(_)
313 ) => {
314 // This got already reported. No point in reporting it again.
315 return None;
316 }
317 _ =>
318 vec![],
319 };
320 (Some(title), helps)
321 };
322
323 let stacktrace = ecx.generate_stacktrace();
324 let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine);
325 let (e, backtrace) = e.into_parts();
326 backtrace.print_backtrace();
327
328 // We want to dump the allocation if this is `InvalidUninitBytes`. Since `add_args` consumes
329 // the `InterpError`, we extract the variables it before that.
330 let extra = match e {
331 UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some((alloc_id, access)))) =>
332 Some((alloc_id, access)),
333 _ => None,
334 };
335
336 // FIXME(fee1-dead), HACK: we want to use the error as title therefore we can just extract the
337 // label and arguments from the InterpError.
338 let e = {
339 let handler = &ecx.tcx.sess.parse_sess.span_diagnostic;
340 let mut diag = ecx.tcx.sess.struct_allow("");
341 let msg = e.diagnostic_message();
342 e.add_args(handler, &mut diag);
343 let s = handler.eagerly_translate_to_string(msg, diag.args());
344 diag.cancel();
345 s
346 };
347
348 msg.insert(0, e);
349
350 report_msg(
351 DiagLevel::Error,
352 if let Some(title) = title { format!("{title}: {}", msg[0]) } else { msg[0].clone() },
353 msg,
354 vec![],
355 helps,
356 &stacktrace,
357 &ecx.machine,
358 );
359
360 // Include a note like `std` does when we omit frames from a backtrace
361 if was_pruned {
362 ecx.tcx.sess.diagnostic().note_without_error(
363 "some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace",
364 );
365 }
366
367 // Debug-dump all locals.
368 for (i, frame) in ecx.active_thread_stack().iter().enumerate() {
369 trace!("-------------------");
370 trace!("Frame {}", i);
371 trace!(" return: {:?}", *frame.return_place);
372 for (i, local) in frame.locals.iter().enumerate() {
373 trace!(" local {}: {:?}", i, local.value);
374 }
375 }
376
377 // Extra output to help debug specific issues.
378 if let Some((alloc_id, access)) = extra {
379 eprintln!(
380 "Uninitialized memory occurred at {alloc_id:?}{range:?}, in this allocation:",
381 range = access.uninit,
382 );
383 eprintln!("{:?}", ecx.dump_alloc(alloc_id));
384 }
385
386 None
387 }
388
report_leaks<'mir, 'tcx>( ecx: &InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, leaks: Vec<(AllocId, MemoryKind<MiriMemoryKind>, Allocation<Provenance, AllocExtra<'tcx>>)>, )389 pub fn report_leaks<'mir, 'tcx>(
390 ecx: &InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>,
391 leaks: Vec<(AllocId, MemoryKind<MiriMemoryKind>, Allocation<Provenance, AllocExtra<'tcx>>)>,
392 ) {
393 let mut any_pruned = false;
394 for (id, kind, mut alloc) in leaks {
395 let Some(backtrace) = alloc.extra.backtrace.take() else {
396 continue;
397 };
398 let (backtrace, pruned) = prune_stacktrace(backtrace, &ecx.machine);
399 any_pruned |= pruned;
400 report_msg(
401 DiagLevel::Error,
402 format!(
403 "memory leaked: {id:?} ({}, size: {:?}, align: {:?}), allocated here:",
404 kind,
405 alloc.size().bytes(),
406 alloc.align.bytes()
407 ),
408 vec![],
409 vec![],
410 vec![],
411 &backtrace,
412 &ecx.machine,
413 );
414 }
415 if any_pruned {
416 ecx.tcx.sess.diagnostic().note_without_error(
417 "some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace",
418 );
419 }
420 }
421
422 /// Report an error or note (depending on the `error` argument) with the given stacktrace.
423 /// Also emits a full stacktrace of the interpreter stack.
424 /// We want to present a multi-line span message for some errors. Diagnostics do not support this
425 /// directly, so we pass the lines as a `Vec<String>` and display each line after the first with an
426 /// additional `span_label` or `note` call.
report_msg<'tcx>( diag_level: DiagLevel, title: String, span_msg: Vec<String>, notes: Vec<(Option<SpanData>, String)>, helps: Vec<(Option<SpanData>, String)>, stacktrace: &[FrameInfo<'tcx>], machine: &MiriMachine<'_, 'tcx>, )427 pub fn report_msg<'tcx>(
428 diag_level: DiagLevel,
429 title: String,
430 span_msg: Vec<String>,
431 notes: Vec<(Option<SpanData>, String)>,
432 helps: Vec<(Option<SpanData>, String)>,
433 stacktrace: &[FrameInfo<'tcx>],
434 machine: &MiriMachine<'_, 'tcx>,
435 ) {
436 let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span);
437 let sess = machine.tcx.sess;
438 let mut err = match diag_level {
439 DiagLevel::Error => sess.struct_span_err(span, title).forget_guarantee(),
440 DiagLevel::Warning => sess.struct_span_warn(span, title),
441 DiagLevel::Note => sess.diagnostic().span_note_diag(span, title),
442 };
443
444 // Show main message.
445 if span != DUMMY_SP {
446 for line in span_msg {
447 err.span_label(span, line);
448 }
449 } else {
450 // Make sure we show the message even when it is a dummy span.
451 for line in span_msg {
452 err.note(line);
453 }
454 err.note("(no span available)");
455 }
456
457 // Show note and help messages.
458 let mut extra_span = false;
459 let notes_len = notes.len();
460 for (span_data, note) in notes {
461 if let Some(span_data) = span_data {
462 err.span_note(span_data.span(), note);
463 extra_span = true;
464 } else {
465 err.note(note);
466 }
467 }
468 let helps_len = helps.len();
469 for (span_data, help) in helps {
470 if let Some(span_data) = span_data {
471 err.span_help(span_data.span(), help);
472 extra_span = true;
473 } else {
474 err.help(help);
475 }
476 }
477 if notes_len + helps_len > 0 {
478 // Add visual separator before backtrace.
479 err.note(if extra_span { "BACKTRACE (of the first span):" } else { "BACKTRACE:" });
480 }
481
482 let (mut err, handler) = err.into_diagnostic().unwrap();
483
484 // Add backtrace
485 for (idx, frame_info) in stacktrace.iter().enumerate() {
486 let is_local = machine.is_local(frame_info);
487 // No span for non-local frames and the first frame (which is the error site).
488 if is_local && idx > 0 {
489 err.eager_subdiagnostic(handler, frame_info.as_note(machine.tcx));
490 } else {
491 let sm = sess.source_map();
492 let span = sm.span_to_embeddable_string(frame_info.span);
493 err.note(format!("{frame_info} at {span}"));
494 }
495 }
496
497 handler.emit_diagnostic(&mut err);
498 }
499
500 impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
emit_diagnostic(&self, e: NonHaltingDiagnostic)501 pub fn emit_diagnostic(&self, e: NonHaltingDiagnostic) {
502 use NonHaltingDiagnostic::*;
503
504 let stacktrace =
505 MiriInterpCx::generate_stacktrace_from_stack(self.threads.active_thread_stack());
506 let (stacktrace, _was_pruned) = prune_stacktrace(stacktrace, self);
507
508 let (title, diag_level) = match &e {
509 RejectedIsolatedOp(_) =>
510 ("operation rejected by isolation".to_string(), DiagLevel::Warning),
511 Int2Ptr { .. } => ("integer-to-pointer cast".to_string(), DiagLevel::Warning),
512 CreatedPointerTag(..)
513 | PoppedPointerTag(..)
514 | CreatedCallId(..)
515 | CreatedAlloc(..)
516 | FreedAlloc(..)
517 | ProgressReport { .. }
518 | WeakMemoryOutdatedLoad => ("tracking was triggered".to_string(), DiagLevel::Note),
519 };
520
521 let msg = match &e {
522 CreatedPointerTag(tag, None, _) => format!("created base tag {tag:?}"),
523 CreatedPointerTag(tag, Some(perm), None) =>
524 format!("created {tag:?} with {perm} derived from unknown tag"),
525 CreatedPointerTag(tag, Some(perm), Some((alloc_id, range, orig_tag))) =>
526 format!(
527 "created tag {tag:?} with {perm} at {alloc_id:?}{range:?} derived from {orig_tag:?}"
528 ),
529 PoppedPointerTag(item, cause) => format!("popped tracked tag for item {item:?}{cause}"),
530 CreatedCallId(id) => format!("function call with id {id}"),
531 CreatedAlloc(AllocId(id), size, align, kind) =>
532 format!(
533 "created {kind} allocation of {size} bytes (alignment {align} bytes) with id {id}",
534 size = size.bytes(),
535 align = align.bytes(),
536 ),
537 FreedAlloc(AllocId(id)) => format!("freed allocation with id {id}"),
538 RejectedIsolatedOp(ref op) =>
539 format!("{op} was made to return an error due to isolation"),
540 ProgressReport { .. } =>
541 format!("progress report: current operation being executed is here"),
542 Int2Ptr { .. } => format!("integer-to-pointer cast"),
543 WeakMemoryOutdatedLoad =>
544 format!("weak memory emulation: outdated value returned from load"),
545 };
546
547 let notes = match &e {
548 ProgressReport { block_count } => {
549 // It is important that each progress report is slightly different, since
550 // identical diagnostics are being deduplicated.
551 vec![(None, format!("so far, {block_count} basic blocks have been executed"))]
552 }
553 _ => vec![],
554 };
555
556 let helps = match &e {
557 Int2Ptr { details: true } =>
558 vec![
559 (
560 None,
561 format!(
562 "This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`,"
563 ),
564 ),
565 (
566 None,
567 format!("which means that Miri might miss pointer bugs in this program."),
568 ),
569 (
570 None,
571 format!(
572 "See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation."
573 ),
574 ),
575 (
576 None,
577 format!(
578 "To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead."
579 ),
580 ),
581 (
582 None,
583 format!(
584 "You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics."
585 ),
586 ),
587 (
588 None,
589 format!(
590 "Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning."
591 ),
592 ),
593 ],
594 _ => vec![],
595 };
596
597 report_msg(diag_level, title, vec![msg], notes, helps, &stacktrace, self);
598 }
599 }
600
601 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
602 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
emit_diagnostic(&self, e: NonHaltingDiagnostic)603 fn emit_diagnostic(&self, e: NonHaltingDiagnostic) {
604 let this = self.eval_context_ref();
605 this.machine.emit_diagnostic(e);
606 }
607
608 /// We had a panic in Miri itself, try to print something useful.
handle_ice(&self)609 fn handle_ice(&self) {
610 eprintln!();
611 eprintln!(
612 "Miri caused an ICE during evaluation. Here's the interpreter backtrace at the time of the panic:"
613 );
614 let this = self.eval_context_ref();
615 let stacktrace = this.generate_stacktrace();
616 report_msg(
617 DiagLevel::Note,
618 "the place in the program where the ICE was triggered".to_string(),
619 vec![],
620 vec![],
621 vec![],
622 &stacktrace,
623 &this.machine,
624 );
625 }
626 }
627