Skip to content

Commit

Permalink
Auto merge of #54487 - RalfJung:ctfe-backtrace, r=oli-obk
Browse files Browse the repository at this point in the history
Delayed CTFE backtraces

This renames the env var that controls CTFE backtraces from `MIRI_BACKTRACE` to `RUST_CTFE_BACKTRACE` so that we can use `MIRI_BACKTRACE` in the miri tool to only show backtraces of the main miri execution.

It also makes `RUST_CTFE_BACKTRACE` only show backtraces that actually get rendered as errors, instead of showing them eagerly when the `Err` happens. The current behavior is near useless in miri because it shows about one gazillion backtraces for errors that we later catch and do not care about. However, @oli-obk likes the current behavior for rustc CTFE work so it is still available via `RUST_CTFE_BACKTRACE=immediate`.

NOTE: This is based on top of #53821. Only [the last three commits](oli-obk/rust@sanity_query...RalfJung:ctfe-backtrace) are new.

Fixes #53355
  • Loading branch information
bors committed Oct 28, 2018
2 parents d492c67 + 5903fdb commit 96064eb
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 152 deletions.
42 changes: 0 additions & 42 deletions src/librustc/ich/impls_ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -597,48 +597,6 @@ for ::mir::interpret::EvalErrorKind<'gcx, O> {
required.hash_stable(hcx, hasher);
has.hash_stable(hcx, hasher)
},
MemoryLockViolation {
ptr,
len,
frame,
access,
ref lock,
} => {
ptr.hash_stable(hcx, hasher);
len.hash_stable(hcx, hasher);
frame.hash_stable(hcx, hasher);
access.hash_stable(hcx, hasher);
lock.hash_stable(hcx, hasher)
},
MemoryAcquireConflict {
ptr,
len,
kind,
ref lock,
} => {
ptr.hash_stable(hcx, hasher);
len.hash_stable(hcx, hasher);
kind.hash_stable(hcx, hasher);
lock.hash_stable(hcx, hasher)
},
InvalidMemoryLockRelease {
ptr,
len,
frame,
ref lock,
} => {
ptr.hash_stable(hcx, hasher);
len.hash_stable(hcx, hasher);
frame.hash_stable(hcx, hasher);
lock.hash_stable(hcx, hasher)
},
DeallocatedLockedMemory {
ptr,
ref lock,
} => {
ptr.hash_stable(hcx, hasher);
lock.hash_stable(hcx, hasher)
},
ValidationFailure(ref s) => s.hash_stable(hcx, hasher),
TypeNotPrimitive(ty) => ty.hash_stable(hcx, hasher),
ReallocatedWrongMemoryKind(ref a, ref b) => {
Expand Down
153 changes: 67 additions & 86 deletions src/librustc/mir/interpret/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ use ty::{Ty, layout};
use ty::layout::{Size, Align, LayoutError};
use rustc_target::spec::abi::Abi;

use super::{
Pointer, Lock, AccessKind
};
use super::Pointer;

use backtrace::Backtrace;

Expand Down Expand Up @@ -53,7 +51,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>,
}

Expand Down Expand Up @@ -112,7 +110,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(_)) |
Expand Down Expand Up @@ -151,50 +149,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 {
eprintln!("{}", 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
eprintln!("{}", print_backtrace(&mut backtrace));
None
} else {
Some(Box::new(backtrace))
}
error!("{}", trace_text);
},
_ => {},
}
_ => None,
};
EvalError {
kind,
backtrace,
}
}
}
Expand Down Expand Up @@ -250,29 +272,6 @@ pub enum EvalErrorKind<'tcx, O> {
required: Align,
has: Align,
},
MemoryLockViolation {
ptr: Pointer,
len: u64,
frame: usize,
access: AccessKind,
lock: Lock,
},
MemoryAcquireConflict {
ptr: Pointer,
len: u64,
kind: AccessKind,
lock: Lock,
},
InvalidMemoryLockRelease {
ptr: Pointer,
len: u64,
frame: usize,
lock: Lock,
},
DeallocatedLockedMemory {
ptr: Pointer,
lock: Lock,
},
ValidationFailure(String),
CalledClosureAsFunction,
VtableForArgumentlessMethod,
Expand Down Expand Up @@ -336,16 +335,8 @@ impl<'tcx, O> EvalErrorKind<'tcx, O> {
"pointer offset outside bounds of allocation",
InvalidNullPointerUsage =>
"invalid use of NULL pointer",
MemoryLockViolation { .. } =>
"memory access conflicts with lock",
MemoryAcquireConflict { .. } =>
"new memory lock conflicts with existing lock",
ValidationFailure(..) =>
"type validation failed",
InvalidMemoryLockRelease { .. } =>
"invalid attempt to release write lock",
DeallocatedLockedMemory { .. } =>
"tried to deallocate memory in conflict with a lock",
ReadPointerAsBytes =>
"a raw memory access tried to access part of a pointer value as raw bytes",
ReadBytesAsPointer =>
Expand Down Expand Up @@ -452,7 +443,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)
}
}

Expand All @@ -465,22 +462,6 @@ impl<'tcx, O: fmt::Debug> fmt::Debug for EvalErrorKind<'tcx, O> {
if access { "memory access" } else { "pointer computed" },
ptr.offset.bytes(), ptr.alloc_id, allocation_size.bytes())
},
MemoryLockViolation { ptr, len, frame, access, ref lock } => {
write!(f, "{:?} access by frame {} at {:?}, size {}, is in conflict with lock {:?}",
access, frame, ptr, len, lock)
}
MemoryAcquireConflict { ptr, len, kind, ref lock } => {
write!(f, "new {:?} lock at {:?}, size {}, is in conflict with lock {:?}",
kind, ptr, len, lock)
}
InvalidMemoryLockRelease { ptr, len, frame, ref lock } => {
write!(f, "frame {} tried to release memory write lock at {:?}, size {}, but \
cannot release lock {:?}", frame, ptr, len, lock)
}
DeallocatedLockedMemory { ptr, ref lock } => {
write!(f, "tried to deallocate memory at {:?} in conflict with lock {:?}",
ptr, lock)
}
ValidationFailure(ref err) => {
write!(f, "type validation failed: {}", err)
}
Expand Down
20 changes: 13 additions & 7 deletions src/librustc_mir/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
})
Expand All @@ -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>,
Expand All @@ -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, \
Expand Down Expand Up @@ -654,8 +661,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");
Expand Down Expand Up @@ -685,7 +691,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",
Expand Down
25 changes: 8 additions & 17 deletions src/librustc_mir/transform/const_prop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -185,11 +183,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
| InvalidDiscriminant(..)
| PointerOutOfBounds { .. }
| InvalidNullPointerUsage
| MemoryLockViolation { .. }
| MemoryAcquireConflict { .. }
| ValidationFailure(..)
| InvalidMemoryLockRelease { .. }
| DeallocatedLockedMemory { .. }
| InvalidPointerMath
| ReadUndefBytes(_)
| DeadLocal
Expand Down Expand Up @@ -273,10 +267,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
},
Expand Down

0 comments on commit 96064eb

Please sign in to comment.