Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

miri/diagnostics: don't forget to print_backtrace when ICEing on unexpected errors #115272

Merged
merged 1 commit into from
Aug 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion compiler/rustc_const_eval/src/interpret/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ use super::{
Scalar, StackPopJump,
};
use crate::errors::{self, ErroneousConstUsed};
use crate::fluent_generated as fluent;
use crate::util;
use crate::{fluent_generated as fluent, ReportErrorExt};

pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
/// Stores the `Machine` instance.
Expand Down Expand Up @@ -432,6 +432,27 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
.map_or(CRATE_HIR_ID, |def_id| self.tcx.hir().local_def_id_to_hir_id(def_id))
}

/// Turn the given error into a human-readable string. Expects the string to be printed, so if
/// `RUSTC_CTFE_BACKTRACE` is set this will show a backtrace of the rustc internals that
/// triggered the error.
///
/// This is NOT the preferred way to render an error; use `report` from `const_eval` instead.
/// However, this is useful when error messages appear in ICEs.
pub fn format_error(&self, e: InterpErrorInfo<'tcx>) -> String {
let (e, backtrace) = e.into_parts();
backtrace.print_backtrace();
// FIXME(fee1-dead), HACK: we want to use the error as title therefore we can just extract the
// label and arguments from the InterpError.
let handler = &self.tcx.sess.parse_sess.span_diagnostic;
#[allow(rustc::untranslatable_diagnostic)]
let mut diag = self.tcx.sess.struct_allow("");
let msg = e.diagnostic_message();
e.add_args(handler, &mut diag);
let s = handler.eagerly_translate_to_string(msg, diag.args());
diag.cancel();
s
}

#[inline(always)]
pub(crate) fn stack(&self) -> &[Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>] {
M::stack(self)
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_const_eval/src/interpret/intern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,8 @@ pub fn intern_const_alloc_recursive<
ecx.tcx.sess.delay_span_bug(
ecx.tcx.span,
format!(
"error during interning should later cause validation failure: {error:?}"
"error during interning should later cause validation failure: {}",
ecx.format_error(error),
),
);
}
Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_const_eval/src/interpret/validity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -911,9 +911,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// Complain about any other kind of error -- those are bad because we'd like to
// report them in a way that shows *where* in the value the issue lies.
Err(err) => {
let (err, backtrace) = err.into_parts();
backtrace.print_backtrace();
bug!("Unexpected Undefined Behavior error during validation: {err:?}");
bug!(
"Unexpected Undefined Behavior error during validation: {}",
self.format_error(err)
);
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_mir_transform/src/const_prop_lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
// dedicated error variants should be introduced instead.
assert!(
!error.kind().formatted_string(),
"const-prop encountered formatting error: {error:?}",
"const-prop encountered formatting error: {}",
self.ecx.format_error(error),
);
None
}
Expand Down
65 changes: 27 additions & 38 deletions src/tools/miri/src/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use std::fmt;
use std::fmt::{self, Write};
use std::num::NonZeroU64;

use log::trace;

use rustc_const_eval::ReportErrorExt;
use rustc_errors::DiagnosticMessage;
use rustc_span::{source_map::DUMMY_SP, SpanData, Symbol};
use rustc_target::abi::{Align, Size};
Expand Down Expand Up @@ -271,10 +270,13 @@ pub fn report_error<'tcx, 'mir>(
};
(title, helps)
} else {
#[rustfmt::skip]
let title = match e.kind() {
UndefinedBehavior(UndefinedBehaviorInfo::ValidationError(e)) if matches!(e.kind, ValidationErrorKind::PointerAsInt { .. } | ValidationErrorKind::PartialPointer) =>
bug!("This validation error should be impossible in Miri: {:?}", e.kind),
UndefinedBehavior(UndefinedBehaviorInfo::ValidationError(validation_err))
if matches!(validation_err.kind, ValidationErrorKind::PointerAsInt { .. } | ValidationErrorKind::PartialPointer) =>
{
ecx.handle_ice(); // print interpreter backtrace
bug!("This validation error should be impossible in Miri: {}", ecx.format_error(e));
}
UndefinedBehavior(_) =>
"Undefined Behavior",
ResourceExhaustion(_) =>
Expand All @@ -290,8 +292,10 @@ pub fn report_error<'tcx, 'mir>(
InvalidProgramInfo::Layout(..)
) =>
"post-monomorphization error",
kind =>
bug!("This error should be impossible in Miri: {kind:?}"),
_ => {
ecx.handle_ice(); // print interpreter backtrace
bug!("This error should be impossible in Miri: {}", ecx.format_error(e));
}
};
#[rustfmt::skip]
let helps = match e.kind() {
Expand Down Expand Up @@ -333,30 +337,22 @@ pub fn report_error<'tcx, 'mir>(

let stacktrace = ecx.generate_stacktrace();
let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine);
let (e, backtrace) = e.into_parts();
backtrace.print_backtrace();

// We want to dump the allocation if this is `InvalidUninitBytes`. Since `add_args` consumes
// the `InterpError`, we extract the variables it before that.
let extra = match e {
UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some((alloc_id, access)))) =>
Some((alloc_id, access)),
_ => None,
};

// FIXME(fee1-dead), HACK: we want to use the error as title therefore we can just extract the
// label and arguments from the InterpError.
let e = {
let handler = &ecx.tcx.sess.parse_sess.span_diagnostic;
let mut diag = ecx.tcx.sess.struct_allow("");
let msg = e.diagnostic_message();
e.add_args(handler, &mut diag);
let s = handler.eagerly_translate_to_string(msg, diag.args());
diag.cancel();
s
};
// We want to dump the allocation if this is `InvalidUninitBytes`. Since `format_error` consumes `e`, we compute the outut early.
let mut extra = String::new();
match e.kind() {
UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some((alloc_id, access)))) => {
writeln!(
extra,
"Uninitialized memory occurred at {alloc_id:?}{range:?}, in this allocation:",
range = access.bad,
).unwrap();
writeln!(extra, "{:?}", ecx.dump_alloc(*alloc_id)).unwrap();
}
_ => {}
}

msg.insert(0, e);
msg.insert(0, ecx.format_error(e));

report_msg(
DiagLevel::Error,
Expand All @@ -375,6 +371,8 @@ pub fn report_error<'tcx, 'mir>(
);
}

eprint!("{extra}"); // newlines are already in the string

// Debug-dump all locals.
for (i, frame) in ecx.active_thread_stack().iter().enumerate() {
trace!("-------------------");
Expand All @@ -385,15 +383,6 @@ pub fn report_error<'tcx, 'mir>(
}
}

// Extra output to help debug specific issues.
if let Some((alloc_id, access)) = extra {
eprintln!(
"Uninitialized memory occurred at {alloc_id:?}{range:?}, in this allocation:",
range = access.bad,
);
eprintln!("{:?}", ecx.dump_alloc(alloc_id));
}

None
}

Expand Down