about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2018-09-18 11:01:13 +0200
committerRalf Jung <post@ralfj.de>2018-10-28 11:33:27 +0100
commit02a42382f64d77d4a7121b4f25f537c574639e89 (patch)
tree51c1aeb886e70f894a4cbb8e418ccbed14923514
parent1982f1887ad524951f24c12a6cc7bf05148aec14 (diff)
downloadrust-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.rs102
-rw-r--r--src/librustc_mir/const_eval.rs20
-rw-r--r--src/librustc_mir/transform/const_prop.rs21
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
             },