Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delayed CTFE backtraces #54487

Merged
merged 3 commits into from
Oct 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -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");
Expand Down Expand Up @@ -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",
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