rename env var to control ctfe backtraces, and make it usually show the backtrace delayed
The env var is now RUST_CTFE_BACKTRACE. Similar to RUST_BACKTRACE, it usually only prints a backtrace when the error actually surfaces, not when it happens. This makes a difference when we catch errors. As per @oli-obk's request, one can set RUST_CTFE_BACKTRACE=immediate to get the backtrace shown immediately.
This commit is contained in:
parent
1982f1887a
commit
02a42382f6
3 changed files with 87 additions and 56 deletions
|
@ -53,7 +53,7 @@ pub type ConstEvalResult<'tcx> = Result<&'tcx ty::Const<'tcx>, ErrorHandled>;
|
||||||
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
|
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
|
||||||
pub struct ConstEvalErr<'tcx> {
|
pub struct ConstEvalErr<'tcx> {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub error: ::mir::interpret::EvalError<'tcx>,
|
pub error: ::mir::interpret::EvalErrorKind<'tcx, u64>,
|
||||||
pub stacktrace: Vec<FrameInfo>,
|
pub stacktrace: Vec<FrameInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
|
||||||
message: &str,
|
message: &str,
|
||||||
lint_root: Option<ast::NodeId>,
|
lint_root: Option<ast::NodeId>,
|
||||||
) -> Result<DiagnosticBuilder<'tcx>, ErrorHandled> {
|
) -> Result<DiagnosticBuilder<'tcx>, ErrorHandled> {
|
||||||
match self.error.kind {
|
match self.error {
|
||||||
EvalErrorKind::Layout(LayoutError::Unknown(_)) |
|
EvalErrorKind::Layout(LayoutError::Unknown(_)) |
|
||||||
EvalErrorKind::TooGeneric => return Err(ErrorHandled::TooGeneric),
|
EvalErrorKind::TooGeneric => return Err(ErrorHandled::TooGeneric),
|
||||||
EvalErrorKind::Layout(LayoutError::SizeOverflow(_)) |
|
EvalErrorKind::Layout(LayoutError::SizeOverflow(_)) |
|
||||||
|
@ -151,50 +151,74 @@ pub fn struct_error<'a, 'gcx, 'tcx>(
|
||||||
struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
|
struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, RustcEncodable, RustcDecodable)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct EvalError<'tcx> {
|
pub struct EvalError<'tcx> {
|
||||||
pub kind: EvalErrorKind<'tcx, u64>,
|
pub kind: EvalErrorKind<'tcx, u64>,
|
||||||
|
pub backtrace: Option<Box<Backtrace>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> EvalError<'tcx> {
|
||||||
|
pub fn print_backtrace(&mut self) {
|
||||||
|
if let Some(ref mut backtrace) = self.backtrace {
|
||||||
|
error!("{}", print_backtrace(&mut *backtrace));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_backtrace(backtrace: &mut Backtrace) -> String {
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
backtrace.resolve();
|
||||||
|
|
||||||
|
let mut trace_text = "\n\nAn error occurred in miri:\n".to_string();
|
||||||
|
write!(trace_text, "backtrace frames: {}\n", backtrace.frames().len()).unwrap();
|
||||||
|
'frames: for (i, frame) in backtrace.frames().iter().enumerate() {
|
||||||
|
if frame.symbols().is_empty() {
|
||||||
|
write!(trace_text, "{}: no symbols\n", i).unwrap();
|
||||||
|
}
|
||||||
|
for symbol in frame.symbols() {
|
||||||
|
write!(trace_text, "{}: ", i).unwrap();
|
||||||
|
if let Some(name) = symbol.name() {
|
||||||
|
write!(trace_text, "{}\n", name).unwrap();
|
||||||
|
} else {
|
||||||
|
write!(trace_text, "<unknown>\n").unwrap();
|
||||||
|
}
|
||||||
|
write!(trace_text, "\tat ").unwrap();
|
||||||
|
if let Some(file_path) = symbol.filename() {
|
||||||
|
write!(trace_text, "{}", file_path.display()).unwrap();
|
||||||
|
} else {
|
||||||
|
write!(trace_text, "<unknown_file>").unwrap();
|
||||||
|
}
|
||||||
|
if let Some(line) = symbol.lineno() {
|
||||||
|
write!(trace_text, ":{}\n", line).unwrap();
|
||||||
|
} else {
|
||||||
|
write!(trace_text, "\n").unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trace_text
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> From<EvalErrorKind<'tcx, u64>> for EvalError<'tcx> {
|
impl<'tcx> From<EvalErrorKind<'tcx, u64>> for EvalError<'tcx> {
|
||||||
fn from(kind: EvalErrorKind<'tcx, u64>) -> Self {
|
fn from(kind: EvalErrorKind<'tcx, u64>) -> Self {
|
||||||
match env::var("MIRI_BACKTRACE") {
|
let backtrace = match env::var("RUST_CTFE_BACKTRACE") {
|
||||||
Ok(ref val) if !val.is_empty() => {
|
// matching RUST_BACKTRACE, we treat "0" the same as "not present".
|
||||||
let backtrace = Backtrace::new();
|
Ok(ref val) if val != "0" => {
|
||||||
|
let mut backtrace = Backtrace::new_unresolved();
|
||||||
|
|
||||||
use std::fmt::Write;
|
if val == "immediate" {
|
||||||
let mut trace_text = "\n\nAn error occurred in miri:\n".to_string();
|
// Print it now
|
||||||
write!(trace_text, "backtrace frames: {}\n", backtrace.frames().len()).unwrap();
|
error!("{}", print_backtrace(&mut backtrace));
|
||||||
'frames: for (i, frame) in backtrace.frames().iter().enumerate() {
|
None
|
||||||
if frame.symbols().is_empty() {
|
} else {
|
||||||
write!(trace_text, "{}: no symbols\n", i).unwrap();
|
Some(Box::new(backtrace))
|
||||||
}
|
|
||||||
for symbol in frame.symbols() {
|
|
||||||
write!(trace_text, "{}: ", i).unwrap();
|
|
||||||
if let Some(name) = symbol.name() {
|
|
||||||
write!(trace_text, "{}\n", name).unwrap();
|
|
||||||
} else {
|
|
||||||
write!(trace_text, "<unknown>\n").unwrap();
|
|
||||||
}
|
|
||||||
write!(trace_text, "\tat ").unwrap();
|
|
||||||
if let Some(file_path) = symbol.filename() {
|
|
||||||
write!(trace_text, "{}", file_path.display()).unwrap();
|
|
||||||
} else {
|
|
||||||
write!(trace_text, "<unknown_file>").unwrap();
|
|
||||||
}
|
|
||||||
if let Some(line) = symbol.lineno() {
|
|
||||||
write!(trace_text, ":{}\n", line).unwrap();
|
|
||||||
} else {
|
|
||||||
write!(trace_text, "\n").unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
error!("{}", trace_text);
|
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => None,
|
||||||
}
|
};
|
||||||
EvalError {
|
EvalError {
|
||||||
kind,
|
kind,
|
||||||
|
backtrace,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -452,7 +476,13 @@ impl<'tcx, O> EvalErrorKind<'tcx, O> {
|
||||||
|
|
||||||
impl<'tcx> fmt::Display for EvalError<'tcx> {
|
impl<'tcx> fmt::Display for EvalError<'tcx> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{:?}", self.kind)
|
write!(f, "{}", self.kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> fmt::Display for EvalErrorKind<'tcx, u64> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{:?}", self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -513,8 +513,7 @@ pub fn const_field<'a, 'tcx>(
|
||||||
op_to_const(&ecx, field, true)
|
op_to_const(&ecx, field, true)
|
||||||
})();
|
})();
|
||||||
result.map_err(|error| {
|
result.map_err(|error| {
|
||||||
let stacktrace = ecx.generate_stacktrace(None);
|
let err = error_to_const_error(&ecx, error);
|
||||||
let err = ::rustc::mir::interpret::ConstEvalErr { error, stacktrace, span: ecx.tcx.span };
|
|
||||||
err.report_as_error(ecx.tcx, "could not access field of constant");
|
err.report_as_error(ecx.tcx, "could not access field of constant");
|
||||||
ErrorHandled::Reported
|
ErrorHandled::Reported
|
||||||
})
|
})
|
||||||
|
@ -532,6 +531,15 @@ pub fn const_variant_index<'a, 'tcx>(
|
||||||
Ok(ecx.read_discriminant(op)?.1)
|
Ok(ecx.read_discriminant(op)?.1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn error_to_const_error<'a, 'mir, 'tcx>(
|
||||||
|
ecx: &EvalContext<'a, 'mir, 'tcx, CompileTimeInterpreter<'a, 'mir, 'tcx>>,
|
||||||
|
mut error: EvalError<'tcx>
|
||||||
|
) -> ConstEvalErr<'tcx> {
|
||||||
|
error.print_backtrace();
|
||||||
|
let stacktrace = ecx.generate_stacktrace(None);
|
||||||
|
ConstEvalErr { error: error.kind, stacktrace, span: ecx.tcx.span }
|
||||||
|
}
|
||||||
|
|
||||||
fn validate_const<'a, 'tcx>(
|
fn validate_const<'a, 'tcx>(
|
||||||
tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
|
tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
constant: &'tcx ty::Const<'tcx>,
|
constant: &'tcx ty::Const<'tcx>,
|
||||||
|
@ -554,8 +562,7 @@ fn validate_const<'a, 'tcx>(
|
||||||
})();
|
})();
|
||||||
|
|
||||||
val.map_err(|error| {
|
val.map_err(|error| {
|
||||||
let stacktrace = ecx.generate_stacktrace(None);
|
let err = error_to_const_error(&ecx, error);
|
||||||
let err = ::rustc::mir::interpret::ConstEvalErr { error, stacktrace, span: ecx.tcx.span };
|
|
||||||
match err.struct_error(ecx.tcx, "it is undefined behavior to use this value") {
|
match err.struct_error(ecx.tcx, "it is undefined behavior to use this value") {
|
||||||
Ok(mut diag) => {
|
Ok(mut diag) => {
|
||||||
diag.note("The rules on what exactly is undefined behavior aren't clear, \
|
diag.note("The rules on what exactly is undefined behavior aren't clear, \
|
||||||
|
@ -647,8 +654,7 @@ pub fn const_eval_raw_provider<'a, 'tcx>(
|
||||||
}
|
}
|
||||||
op_to_const(&ecx, op, normalize)
|
op_to_const(&ecx, op, normalize)
|
||||||
}).map_err(|error| {
|
}).map_err(|error| {
|
||||||
let stacktrace = ecx.generate_stacktrace(None);
|
let err = error_to_const_error(&ecx, error);
|
||||||
let err = ConstEvalErr { error, stacktrace, span: ecx.tcx.span };
|
|
||||||
// errors in statics are always emitted as fatal errors
|
// errors in statics are always emitted as fatal errors
|
||||||
if tcx.is_static(def_id).is_some() {
|
if tcx.is_static(def_id).is_some() {
|
||||||
let err = err.report_as_error(ecx.tcx, "could not evaluate static initializer");
|
let err = err.report_as_error(ecx.tcx, "could not evaluate static initializer");
|
||||||
|
@ -678,7 +684,7 @@ pub fn const_eval_raw_provider<'a, 'tcx>(
|
||||||
// any other kind of error will be reported to the user as a deny-by-default lint
|
// any other kind of error will be reported to the user as a deny-by-default lint
|
||||||
_ => if let Some(p) = cid.promoted {
|
_ => if let Some(p) = cid.promoted {
|
||||||
let span = tcx.optimized_mir(def_id).promoted[p].span;
|
let span = tcx.optimized_mir(def_id).promoted[p].span;
|
||||||
if let EvalErrorKind::ReferencedConstant = err.error.kind {
|
if let EvalErrorKind::ReferencedConstant = err.error {
|
||||||
err.report_as_error(
|
err.report_as_error(
|
||||||
tcx.at(span),
|
tcx.at(span),
|
||||||
"evaluation of constant expression failed",
|
"evaluation of constant expression failed",
|
||||||
|
|
|
@ -17,13 +17,8 @@ use rustc::mir::{Constant, Location, Place, Mir, Operand, Rvalue, Local};
|
||||||
use rustc::mir::{NullOp, UnOp, StatementKind, Statement, BasicBlock, LocalKind};
|
use rustc::mir::{NullOp, UnOp, StatementKind, Statement, BasicBlock, LocalKind};
|
||||||
use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp, ProjectionElem};
|
use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp, ProjectionElem};
|
||||||
use rustc::mir::visit::{Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext};
|
use rustc::mir::visit::{Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext};
|
||||||
use rustc::mir::interpret::{
|
use rustc::mir::interpret::{EvalErrorKind, Scalar, GlobalId, EvalResult};
|
||||||
ConstEvalErr, EvalErrorKind, Scalar, GlobalId, EvalResult,
|
|
||||||
};
|
|
||||||
use rustc::ty::{TyCtxt, self, Instance};
|
use rustc::ty::{TyCtxt, self, Instance};
|
||||||
use interpret::{self, EvalContext, Value, OpTy, MemoryKind, ScalarMaybeUndef};
|
|
||||||
use const_eval::{CompileTimeInterpreter, eval_promoted, mk_borrowck_eval_cx};
|
|
||||||
use transform::{MirPass, MirSource};
|
|
||||||
use syntax::source_map::{Span, DUMMY_SP};
|
use syntax::source_map::{Span, DUMMY_SP};
|
||||||
use rustc::ty::subst::Substs;
|
use rustc::ty::subst::Substs;
|
||||||
use rustc_data_structures::indexed_vec::IndexVec;
|
use rustc_data_structures::indexed_vec::IndexVec;
|
||||||
|
@ -33,6 +28,10 @@ use rustc::ty::layout::{
|
||||||
HasTyCtxt, TargetDataLayout, HasDataLayout,
|
HasTyCtxt, TargetDataLayout, HasDataLayout,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use interpret::{self, EvalContext, ScalarMaybeUndef, Value, OpTy, MemoryKind};
|
||||||
|
use const_eval::{CompileTimeInterpreter, error_to_const_error, eval_promoted, mk_borrowck_eval_cx};
|
||||||
|
use transform::{MirPass, MirSource};
|
||||||
|
|
||||||
pub struct ConstProp;
|
pub struct ConstProp;
|
||||||
|
|
||||||
impl MirPass for ConstProp {
|
impl MirPass for ConstProp {
|
||||||
|
@ -154,10 +153,9 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
|
||||||
let r = match f(self) {
|
let r = match f(self) {
|
||||||
Ok(val) => Some(val),
|
Ok(val) => Some(val),
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let stacktrace = self.ecx.generate_stacktrace(None);
|
let diagnostic = error_to_const_error(&self.ecx, error);
|
||||||
let diagnostic = ConstEvalErr { span: source_info.span, error, stacktrace };
|
|
||||||
use rustc::mir::interpret::EvalErrorKind::*;
|
use rustc::mir::interpret::EvalErrorKind::*;
|
||||||
match diagnostic.error.kind {
|
match diagnostic.error {
|
||||||
// don't report these, they make no sense in a const prop context
|
// don't report these, they make no sense in a const prop context
|
||||||
| MachineError(_)
|
| MachineError(_)
|
||||||
// at runtime these transformations might make sense
|
// at runtime these transformations might make sense
|
||||||
|
@ -273,10 +271,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
|
||||||
Some((op, c.span))
|
Some((op, c.span))
|
||||||
},
|
},
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let stacktrace = self.ecx.generate_stacktrace(None);
|
let err = error_to_const_error(&self.ecx, error);
|
||||||
let err = ::rustc::mir::interpret::ConstEvalErr {
|
|
||||||
error, stacktrace, span: source_info.span,
|
|
||||||
};
|
|
||||||
err.report_as_error(self.ecx.tcx, "erroneous constant used");
|
err.report_as_error(self.ecx.tcx, "erroneous constant used");
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue