diff options
| author | Ralf Jung <post@ralfj.de> | 2018-09-18 11:01:13 +0200 |
|---|---|---|
| committer | Ralf Jung <post@ralfj.de> | 2018-10-28 11:33:27 +0100 |
| commit | 02a42382f64d77d4a7121b4f25f537c574639e89 (patch) | |
| tree | 51c1aeb886e70f894a4cbb8e418ccbed14923514 | |
| parent | 1982f1887ad524951f24c12a6cc7bf05148aec14 (diff) | |
| download | rust-02a42382f64d77d4a7121b4f25f537c574639e89.tar.gz rust-02a42382f64d77d4a7121b4f25f537c574639e89.zip | |
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.
| -rw-r--r-- | src/librustc/mir/interpret/error.rs | 102 | ||||
| -rw-r--r-- | src/librustc_mir/const_eval.rs | 20 | ||||
| -rw-r--r-- | src/librustc_mir/transform/const_prop.rs | 21 |
3 files changed, 87 insertions, 56 deletions
diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index 2445ce334c0..6ce5e3f24ff 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -53,7 +53,7 @@ pub type ConstEvalResult<'tcx> = Result<&'tcx ty::Const<'tcx>, ErrorHandled>; #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct ConstEvalErr<'tcx> { pub span: Span, - pub error: ::mir::interpret::EvalError<'tcx>, + pub error: ::mir::interpret::EvalErrorKind<'tcx, u64>, pub stacktrace: Vec<FrameInfo>, } @@ -112,7 +112,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { message: &str, lint_root: Option<ast::NodeId>, ) -> Result<DiagnosticBuilder<'tcx>, ErrorHandled> { - match self.error.kind { + match self.error { EvalErrorKind::Layout(LayoutError::Unknown(_)) | EvalErrorKind::TooGeneric => return Err(ErrorHandled::TooGeneric), EvalErrorKind::Layout(LayoutError::SizeOverflow(_)) | @@ -151,50 +151,74 @@ pub fn struct_error<'a, 'gcx, 'tcx>( struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg) } -#[derive(Debug, Clone, RustcEncodable, RustcDecodable)] +#[derive(Debug, Clone)] pub struct EvalError<'tcx> { 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> { fn from(kind: EvalErrorKind<'tcx, u64>) -> Self { - match env::var("MIRI_BACKTRACE") { - Ok(ref val) if !val.is_empty() => { - let backtrace = Backtrace::new(); + let backtrace = match env::var("RUST_CTFE_BACKTRACE") { + // matching RUST_BACKTRACE, we treat "0" the same as "not present". + Ok(ref val) if val != "0" => { + let mut backtrace = Backtrace::new_unresolved(); - use std::fmt::Write; - 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(); - } - } + if val == "immediate" { + // Print it now + error!("{}", print_backtrace(&mut backtrace)); + None + } else { + Some(Box::new(backtrace)) } - error!("{}", trace_text); }, - _ => {}, - } + _ => None, + }; EvalError { kind, + backtrace, } } } @@ -452,7 +476,13 @@ impl<'tcx, O> EvalErrorKind<'tcx, O> { impl<'tcx> fmt::Display for EvalError<'tcx> { 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) } } diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index 362fbc4b135..ee610d213e7 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -513,8 +513,7 @@ pub fn const_field<'a, 'tcx>( op_to_const(&ecx, field, true) })(); result.map_err(|error| { - let stacktrace = ecx.generate_stacktrace(None); - let err = ::rustc::mir::interpret::ConstEvalErr { error, stacktrace, span: ecx.tcx.span }; + let err = error_to_const_error(&ecx, error); err.report_as_error(ecx.tcx, "could not access field of constant"); ErrorHandled::Reported }) @@ -532,6 +531,15 @@ pub fn const_variant_index<'a, 'tcx>( 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>( tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, constant: &'tcx ty::Const<'tcx>, @@ -554,8 +562,7 @@ fn validate_const<'a, 'tcx>( })(); val.map_err(|error| { - let stacktrace = ecx.generate_stacktrace(None); - let err = ::rustc::mir::interpret::ConstEvalErr { error, stacktrace, span: ecx.tcx.span }; + let err = error_to_const_error(&ecx, error); match err.struct_error(ecx.tcx, "it is undefined behavior to use this value") { Ok(mut diag) => { 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) }).map_err(|error| { - let stacktrace = ecx.generate_stacktrace(None); - let err = ConstEvalErr { error, stacktrace, span: ecx.tcx.span }; + let err = error_to_const_error(&ecx, error); // errors in statics are always emitted as fatal errors if tcx.is_static(def_id).is_some() { 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 _ => if let Some(p) = cid.promoted { 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( tcx.at(span), "evaluation of constant expression failed", diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 052c6032dec..52de65c184a 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -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::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp, ProjectionElem}; use rustc::mir::visit::{Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext}; -use rustc::mir::interpret::{ - ConstEvalErr, EvalErrorKind, Scalar, GlobalId, EvalResult, -}; +use rustc::mir::interpret::{EvalErrorKind, Scalar, GlobalId, EvalResult}; 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 rustc::ty::subst::Substs; use rustc_data_structures::indexed_vec::IndexVec; @@ -33,6 +28,10 @@ use rustc::ty::layout::{ 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; impl MirPass for ConstProp { @@ -154,10 +153,9 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { let r = match f(self) { Ok(val) => Some(val), Err(error) => { - let stacktrace = self.ecx.generate_stacktrace(None); - let diagnostic = ConstEvalErr { span: source_info.span, error, stacktrace }; + let diagnostic = error_to_const_error(&self.ecx, error); 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 | MachineError(_) // at runtime these transformations might make sense @@ -273,10 +271,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { Some((op, c.span)) }, Err(error) => { - let stacktrace = self.ecx.generate_stacktrace(None); - let err = ::rustc::mir::interpret::ConstEvalErr { - error, stacktrace, span: source_info.span, - }; + let err = error_to_const_error(&self.ecx, error); err.report_as_error(self.ecx.tcx, "erroneous constant used"); None }, |
