diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 0c38cb10a2320..8f837327ddb15 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -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) => { diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index 2445ce334c0e7..8e025f0d029b7 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -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; @@ -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, } @@ -112,7 +110,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> { message: &str, lint_root: Option, ) -> Result, ErrorHandled> { - match self.error.kind { + match self.error { EvalErrorKind::Layout(LayoutError::Unknown(_)) | EvalErrorKind::TooGeneric => return Err(ErrorHandled::TooGeneric), EvalErrorKind::Layout(LayoutError::SizeOverflow(_)) | @@ -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>, +} + +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, "\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, "").unwrap(); + } + if let Some(line) = symbol.lineno() { + write!(trace_text, ":{}\n", line).unwrap(); + } else { + write!(trace_text, "\n").unwrap(); + } + } + } + trace_text } impl<'tcx> From> 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, "\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, "").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, } } } @@ -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, @@ -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 => @@ -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) } } @@ -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) } diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index d92001704285a..73cb203809419 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, \ @@ -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"); @@ -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", diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 052c6032dec09..8ee009db023f0 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 @@ -185,11 +183,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { | InvalidDiscriminant(..) | PointerOutOfBounds { .. } | InvalidNullPointerUsage - | MemoryLockViolation { .. } - | MemoryAcquireConflict { .. } | ValidationFailure(..) - | InvalidMemoryLockRelease { .. } - | DeallocatedLockedMemory { .. } | InvalidPointerMath | ReadUndefBytes(_) | DeadLocal @@ -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 },