Produce backtraces for miri internals

This commit is contained in:
Oliver Schneider 2017-08-02 16:59:01 +02:00
parent cf318b1f6f
commit 29a3c4d28c
22 changed files with 306 additions and 172 deletions

76
Cargo.lock generated
View File

@ -2,6 +2,7 @@
name = "rustc_miri"
version = "0.1.0"
dependencies = [
"backtrace 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
@ -17,6 +18,29 @@ dependencies = [
"memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.28 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace-sys"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.28 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "byteorder"
version = "1.1.0"
@ -32,6 +56,11 @@ dependencies = [
"serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cfg-if"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "compiletest_rs"
version = "0.2.8"
@ -41,6 +70,15 @@ dependencies = [
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "dbghelp-sys"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "dtoa"
version = "0.4.1"
@ -55,11 +93,25 @@ dependencies = [
"regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "gcc"
version = "0.3.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "itoa"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lazy_static"
version = "0.2.8"
@ -131,6 +183,11 @@ name = "regex-syntax"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc-demangle"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc-serialize"
version = "0.3.24"
@ -221,14 +278,30 @@ name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"
"checksum backtrace 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "72f9b4182546f4b04ebc4ab7f84948953a118bd6021a1b6a6c909e3e94f6be76"
"checksum backtrace-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "afccc5772ba333abccdf60d55200fa3406f8c59dcf54d5f7998c9107d3799c7c"
"checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d"
"checksum cargo_metadata 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "be1057b8462184f634c3a208ee35b0f935cfd94b694b26deadccd98732088d7b"
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
"checksum compiletest_rs 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "617b23d0ed4f57b3bcff6b5fe0a78f0010f1efb636298317665a960b6dbc0533"
"checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850"
"checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90"
"checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b"
"checksum gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)" = "120d07f202dcc3f72859422563522b66fe6463a4c513df062874daad05f85f0a"
"checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf"
"checksum libc 0.2.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb7b49972ee23d8aa1026c365a5b440ba08e35075f18c459980c7395c221ec48"
"checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b"
@ -238,6 +311,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b"
"checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db"
"checksum rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95"
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
"checksum serde 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f7726f29ddf9731b17ff113c461e362c381d9d69433f79de4f3dd572488823e9"
"checksum serde_derive 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cf823e706be268e73e7747b147aa31c8f633ab4ba31f115efb57e5047c3a76dd"
@ -250,3 +324,5 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"

View File

@ -62,7 +62,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
let mir = match self.load_mir(instance.def) {
Ok(mir) => mir,
Err(EvalError::NoMirFor(path)) => {
Err(EvalError{ kind: EvalErrorKind::NoMirFor(path), ..} ) => {
self.call_missing_fn(instance, destination, arg_operands, sig, path)?;
return Ok(true);
},
@ -133,8 +133,8 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
// is called if a `HashMap` is created the regular way.
match self.value_to_primval(args[0], usize)?.to_u64()? {
318 |
511 => return Err(EvalError::Unimplemented("miri does not support random number generators".to_owned())),
id => return Err(EvalError::Unimplemented(format!("miri does not support syscall id {}", id))),
511 => return err!(Unimplemented("miri does not support random number generators".to_owned())),
id => return err!(Unimplemented(format!("miri does not support syscall id {}", id))),
}
}
@ -144,7 +144,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
let symbol_name = self.memory.read_c_str(symbol)?;
let err = format!("bad c unicode symbol: {:?}", symbol_name);
let symbol_name = ::std::str::from_utf8(symbol_name).unwrap_or(&err);
return Err(EvalError::Unimplemented(format!("miri does not support dynamically loading libraries (requested symbol: {})", symbol_name)));
return err!(Unimplemented(format!("miri does not support dynamically loading libraries (requested symbol: {})", symbol_name)));
}
"__rust_maybe_catch_panic" => {
@ -167,7 +167,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
StackPopCleanup::Goto(dest_block),
)?;
let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("Argument to __rust_maybe_catch_panic does not take enough arguments.".to_owned()))?;
let arg_local = self.frame().mir.args_iter().next().ok_or(EvalErrorKind::AbiViolation("Argument to __rust_maybe_catch_panic does not take enough arguments.".to_owned()))?;
let arg_dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
self.write_ptr(arg_dest, data, u8_ptr_ty)?;
@ -179,7 +179,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
}
"__rust_start_panic" => {
return Err(EvalError::Panic);
return err!(Panic);
}
"memcmp" => {
@ -342,7 +342,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
if let Some(result) = result {
self.write_primval(dest, result, dest_ty)?;
} else {
return Err(EvalError::Unimplemented(format!("Unimplemented sysconf name: {}", name)));
return err!(Unimplemented(format!("Unimplemented sysconf name: {}", name)));
}
}
@ -354,13 +354,13 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
let dtor = match args[1].into_ptr(&mut self.memory)?.into_inner_primval() {
PrimVal::Ptr(dtor_ptr) => Some(self.memory.get_fn(dtor_ptr)?),
PrimVal::Bytes(0) => None,
PrimVal::Bytes(_) => return Err(EvalError::ReadBytesAsPointer),
PrimVal::Undef => return Err(EvalError::ReadUndefBytes),
PrimVal::Bytes(_) => return err!(ReadBytesAsPointer),
PrimVal::Undef => return err!(ReadUndefBytes),
};
// Figure out how large a pthread TLS key actually is. This is libc::pthread_key_t.
let key_type = self.operand_ty(&arg_operands[0]).builtin_deref(true, ty::LvaluePreference::NoPreference)
.ok_or(EvalError::AbiViolation("Wrong signature used for pthread_key_create: First argument must be a raw pointer.".to_owned()))?.ty;
.ok_or(EvalErrorKind::AbiViolation("Wrong signature used for pthread_key_create: First argument must be a raw pointer.".to_owned()))?.ty;
let key_size = {
let layout = self.type_layout(key_type)?;
layout.size(&self.tcx.data_layout)
@ -369,7 +369,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
// Create key and write it into the memory where key_ptr wants it
let key = self.memory.create_tls_key(dtor) as u128;
if key_size.bits() < 128 && key >= (1u128 << key_size.bits() as u128) {
return Err(EvalError::OutOfTls);
return err!(OutOfTls);
}
// TODO: Does this need checking for alignment?
self.memory.write_uint(key_ptr.to_ptr()?, key, key_size.bytes())?;
@ -407,7 +407,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
},
_ => {
return Err(EvalError::Unimplemented(format!("can't call C ABI function: {}", link_name)));
return err!(Unimplemented(format!("can't call C ABI function: {}", link_name)));
}
}
@ -452,7 +452,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
let path = path.iter()
.map(|&s| s.to_owned())
.collect();
EvalError::PathNotFound(path)
EvalErrorKind::PathNotFound(path).into()
})
}
@ -467,12 +467,12 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
// In some cases in non-MIR libstd-mode, not having a destination is legit. Handle these early.
match &path[..] {
"std::panicking::rust_panic_with_hook" |
"std::rt::begin_panic_fmt" => return Err(EvalError::Panic),
"std::rt::begin_panic_fmt" => return err!(Panic),
_ => {},
}
let dest_ty = sig.output();
let (dest, dest_block) = destination.ok_or_else(|| EvalError::NoMirFor(path.clone()))?;
let (dest, dest_block) = destination.ok_or_else(|| EvalErrorKind::NoMirFor(path.clone()))?;
if sig.abi == Abi::C {
// An external C function
@ -495,10 +495,10 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
let size = self.value_to_primval(args[0], usize)?.to_u64()?;
let align = self.value_to_primval(args[1], usize)?.to_u64()?;
if size == 0 {
return Err(EvalError::HeapAllocZeroBytes);
return err!(HeapAllocZeroBytes);
}
if !align.is_power_of_two() {
return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align));
return err!(HeapAllocNonPowerOfTwoAlignment(align));
}
let ptr = self.memory.allocate(size, align, Kind::Rust.into())?;
self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?;
@ -507,10 +507,10 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
let size = self.value_to_primval(args[0], usize)?.to_u64()?;
let align = self.value_to_primval(args[1], usize)?.to_u64()?;
if size == 0 {
return Err(EvalError::HeapAllocZeroBytes);
return err!(HeapAllocZeroBytes);
}
if !align.is_power_of_two() {
return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align));
return err!(HeapAllocNonPowerOfTwoAlignment(align));
}
let ptr = self.memory.allocate(size, align, Kind::Rust.into())?;
self.memory.write_repeat(ptr.into(), 0, size)?;
@ -521,10 +521,10 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
let old_size = self.value_to_primval(args[1], usize)?.to_u64()?;
let align = self.value_to_primval(args[2], usize)?.to_u64()?;
if old_size == 0 {
return Err(EvalError::HeapAllocZeroBytes);
return err!(HeapAllocZeroBytes);
}
if !align.is_power_of_two() {
return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align));
return err!(HeapAllocNonPowerOfTwoAlignment(align));
}
self.memory.deallocate(ptr, Some((old_size, align)), Kind::Rust.into())?;
}
@ -535,13 +535,13 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
let new_size = self.value_to_primval(args[3], usize)?.to_u64()?;
let new_align = self.value_to_primval(args[4], usize)?.to_u64()?;
if old_size == 0 || new_size == 0 {
return Err(EvalError::HeapAllocZeroBytes);
return err!(HeapAllocZeroBytes);
}
if !old_align.is_power_of_two() {
return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(old_align));
return err!(HeapAllocNonPowerOfTwoAlignment(old_align));
}
if !new_align.is_power_of_two() {
return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(new_align));
return err!(HeapAllocNonPowerOfTwoAlignment(new_align));
}
let new_ptr = self.memory.reallocate(ptr, old_size, old_align, new_size, new_align, Kind::Rust.into())?;
self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?;
@ -552,15 +552,15 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
"std::io::_print" => {
trace!("Ignoring output. To run programs that print, make sure you have a libstd with full MIR.");
}
"std::thread::Builder::new" => return Err(EvalError::Unimplemented("miri does not support threading".to_owned())),
"std::env::args" => return Err(EvalError::Unimplemented("miri does not support program arguments".to_owned())),
"std::thread::Builder::new" => return err!(Unimplemented("miri does not support threading".to_owned())),
"std::env::args" => return err!(Unimplemented("miri does not support program arguments".to_owned())),
"std::panicking::panicking" |
"std::rt::panicking" => {
// we abort on panic -> `std::rt::panicking` always returns false
let bool = self.tcx.types.bool;
self.write_primval(dest, PrimVal::from_bool(false), bool)?;
}
_ => return Err(EvalError::NoMirFor(path)),
_ => return err!(NoMirFor(path)),
}
// Since we pushed no stack frame, the main loop will act

View File

@ -1,6 +1,6 @@
use rustc_miri::interpret::{
Pointer,
EvalResult, EvalError,
EvalResult,
PrimVal,
EvalContext,
};
@ -48,7 +48,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
// allocation.
if ptr.is_null()? { // NULL pointers must only be offset by 0
return if offset == 0 { Ok(ptr) } else { Err(EvalError::InvalidNullPointerUsage) };
return if offset == 0 { Ok(ptr) } else { err!(InvalidNullPointerUsage) };
}
// FIXME: assuming here that type size is < i64::max_value()
let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64;
@ -59,11 +59,11 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
self.memory.check_bounds(ptr, false)?;
} else if ptr.is_null()? {
// We moved *to* a NULL pointer. That seems wrong, LLVM considers the NULL pointer its own small allocation. Reject this, for now.
return Err(EvalError::InvalidNullPointerUsage);
return err!(InvalidNullPointerUsage);
}
Ok(ptr)
} else {
Err(EvalError::OverflowingMath)
err!(OverflowingMath)
}
}
}

View File

@ -4,7 +4,7 @@ use rustc::ty::layout::Layout;
use rustc::ty::{self, Ty};
use rustc_miri::interpret::{
EvalError, EvalResult,
EvalResult,
Lvalue, LvalueExtra,
PrimVal, PrimValKind, Value, Pointer,
HasMemory,
@ -68,7 +68,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
"assume" => {
let bool = self.tcx.types.bool;
let cond = self.value_to_primval(arg_vals[0], bool)?.to_bool()?;
if !cond { return Err(EvalError::AssumptionNotHeld); }
if !cond { return err!(AssumptionNotHeld); }
}
"atomic_load" |
@ -180,7 +180,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
let kind = self.ty_to_primval_kind(ty)?;
let num = if intrinsic_name.ends_with("_nonzero") {
if num == 0 {
return Err(EvalError::Intrinsic(format!("{} called on 0", intrinsic_name)))
return err!(Intrinsic(format!("{} called on 0", intrinsic_name)))
}
numeric_intrinsic(intrinsic_name.trim_right_matches("_nonzero"), num, kind)?
} else {
@ -423,7 +423,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
let bits = self.type_size(dest_ty)?.expect("intrinsic can't be called on unsized type") as u128 * 8;
let rhs = self.value_to_primval(arg_vals[1], substs.type_at(0))?.to_bytes()?;
if rhs >= bits {
return Err(EvalError::Intrinsic(format!("Overflowing shift by {} in unchecked_shl", rhs)));
return err!(Intrinsic(format!("Overflowing shift by {} in unchecked_shl", rhs)));
}
self.intrinsic_overflowing(mir::BinOp::Shl, &args[0], &args[1], dest, dest_ty)?;
}
@ -432,7 +432,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
let bits = self.type_size(dest_ty)?.expect("intrinsic can't be called on unsized type") as u128 * 8;
let rhs = self.value_to_primval(arg_vals[1], substs.type_at(0))?.to_bytes()?;
if rhs >= bits {
return Err(EvalError::Intrinsic(format!("Overflowing shift by {} in unchecked_shr", rhs)));
return err!(Intrinsic(format!("Overflowing shift by {} in unchecked_shr", rhs)));
}
self.intrinsic_overflowing(mir::BinOp::Shr, &args[0], &args[1], dest, dest_ty)?;
}
@ -440,7 +440,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
"unchecked_div" => {
let rhs = self.value_to_primval(arg_vals[1], substs.type_at(0))?.to_bytes()?;
if rhs == 0 {
return Err(EvalError::Intrinsic(format!("Division by 0 in unchecked_div")));
return err!(Intrinsic(format!("Division by 0 in unchecked_div")));
}
self.intrinsic_overflowing(mir::BinOp::Div, &args[0], &args[1], dest, dest_ty)?;
}
@ -448,7 +448,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
"unchecked_rem" => {
let rhs = self.value_to_primval(arg_vals[1], substs.type_at(0))?.to_bytes()?;
if rhs == 0 {
return Err(EvalError::Intrinsic(format!("Division by 0 in unchecked_rem")));
return err!(Intrinsic(format!("Division by 0 in unchecked_rem")));
}
self.intrinsic_overflowing(mir::BinOp::Rem, &args[0], &args[1], dest, dest_ty)?;
}
@ -489,7 +489,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
}
}
name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))),
name => return err!(Unimplemented(format!("unimplemented intrinsic: {}", name))),
}
self.goto_block(target);

View File

@ -25,6 +25,7 @@ use std::collections::{
BTreeMap,
};
#[macro_use]
extern crate rustc_miri;
pub use rustc_miri::interpret::*;
@ -56,7 +57,7 @@ pub fn eval_main<'a, 'tcx: 'a>(
let mut cleanup_ptr = None; // Pointer to be deallocated when we are done
if !main_mir.return_ty.is_nil() || main_mir.arg_count != 0 {
return Err(EvalError::Unimplemented("miri does not support main functions without `fn()` type signatures".to_owned()));
return err!(Unimplemented("miri does not support main functions without `fn()` type signatures".to_owned()));
}
if let Some(start_id) = start_wrapper {
@ -64,7 +65,7 @@ pub fn eval_main<'a, 'tcx: 'a>(
let start_mir = ecx.load_mir(start_instance.def)?;
if start_mir.arg_count != 3 {
return Err(EvalError::AbiViolation(format!("'start' lang item should have three arguments, but has {}", start_mir.arg_count)));
return err!(AbiViolation(format!("'start' lang item should have three arguments, but has {}", start_mir.arg_count)));
}
// Return value
@ -201,7 +202,7 @@ impl<'tcx> Machine<'tcx> for Evaluator {
use memory::Kind::*;
match m {
// FIXME: This could be allowed, but not for env vars set during miri execution
Env => Err(EvalError::Unimplemented("statics can't refer to env vars".to_owned())),
Env => err!(Unimplemented("statics can't refer to env vars".to_owned())),
_ => Ok(()),
}
}

View File

@ -50,7 +50,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
let result = match (left, right) {
(PrimVal::Bytes(left), PrimVal::Bytes(right)) => left == right,
(PrimVal::Ptr(left), PrimVal::Ptr(right)) => left == right,
(PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes),
(PrimVal::Undef, _) | (_, PrimVal::Undef) => return err!(ReadUndefBytes),
_ => false,
};
Ok(Some((PrimVal::from_bool(result), false)))
@ -59,7 +59,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
let result = match (left, right) {
(PrimVal::Bytes(left), PrimVal::Bytes(right)) => left != right,
(PrimVal::Ptr(left), PrimVal::Ptr(right)) => left != right,
(PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes),
(PrimVal::Undef, _) | (_, PrimVal::Undef) => return err!(ReadUndefBytes),
_ => true,
};
Ok(Some((PrimVal::from_bool(result), false)))
@ -89,7 +89,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
Ok(Some((PrimVal::from_bool(res), false)))
} else {
// Both are pointers, but from different allocations.
Err(EvalError::InvalidPointerMath)
err!(InvalidPointerMath)
}
}
// These work if one operand is a pointer, the other an integer
@ -141,13 +141,13 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
// Case 2: The base address bits are all taken away, i.e., right is all-0 there
(PrimVal::from_u128((left.offset & right) as u128), false)
} else {
return Err(EvalError::ReadPointerAsBytes);
return err!(ReadPointerAsBytes);
}
}
_ => {
let msg = format!("unimplemented binary op on pointer {:?}: {:?}, {:?} ({})", bin_op, left, right, if signed { "signed" } else { "unsigned" });
return Err(EvalError::Unimplemented(msg));
return err!(Unimplemented(msg));
}
})
}

View File

@ -2,7 +2,7 @@ use rustc::{ty, mir};
use super::{
TlsKey, TlsEntry,
EvalResult, EvalError,
EvalResult, EvalErrorKind,
Pointer,
Memory,
Evaluator,
@ -37,7 +37,7 @@ impl<'a, 'tcx: 'a> MemoryExt<'tcx> for Memory<'a, 'tcx, Evaluator> {
trace!("TLS key {} removed", key);
Ok(())
},
None => Err(EvalError::TlsOutOfBounds)
None => err!(TlsOutOfBounds)
}
}
@ -47,7 +47,7 @@ impl<'a, 'tcx: 'a> MemoryExt<'tcx> for Memory<'a, 'tcx, Evaluator> {
trace!("TLS key {} loaded: {:?}", key, data);
Ok(data)
},
None => Err(EvalError::TlsOutOfBounds)
None => err!(TlsOutOfBounds)
}
}
@ -58,7 +58,7 @@ impl<'a, 'tcx: 'a> MemoryExt<'tcx> for Memory<'a, 'tcx, Evaluator> {
*data = new_data;
Ok(())
},
None => Err(EvalError::TlsOutOfBounds)
None => err!(TlsOutOfBounds)
}
}
@ -115,7 +115,7 @@ impl<'a, 'tcx: 'a> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, Evaluator> {
Lvalue::undef(),
StackPopCleanup::None,
)?;
let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("TLS dtor does not take enough arguments.".to_owned()))?;
let arg_local = self.frame().mir.args_iter().next().ok_or(EvalErrorKind::AbiViolation("TLS dtor does not take enough arguments.".to_owned()))?;
let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
let ty = self.tcx.mk_mut_ptr(self.tcx.types.u8);
self.write_ptr(dest, ptr, ty)?;

View File

@ -17,3 +17,4 @@ log = "0.3.6"
log_settings = "0.1.1"
lazy_static = "0.2.8"
regex = "0.2.2"
backtrace = "0.3"

View File

@ -5,7 +5,6 @@ use super::{
PrimVal,
EvalContext,
EvalResult,
EvalError,
MemoryPointer, PointerArithmetic,
Machine,
};
@ -79,12 +78,12 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
TyFloat(FloatTy::F32) => Ok(PrimVal::from_f32(v as f32)),
TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)),
TyChar => Err(EvalError::InvalidChar(v)),
TyChar => err!(InvalidChar(v)),
// No alignment check needed for raw pointers. But we have to truncate to target ptr size.
TyRawPtr(_) => Ok(PrimVal::Bytes(self.memory.truncate_to_ptr(v).0 as u128)),
_ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))),
_ => err!(Unimplemented(format!("int to {:?} cast", ty))),
}
}
@ -99,7 +98,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
TyFloat(FloatTy::F64) => Ok(PrimVal::from_f64(val)),
TyFloat(FloatTy::F32) => Ok(PrimVal::from_f32(val as f32)),
_ => Err(EvalError::Unimplemented(format!("float to {:?} cast", ty))),
_ => err!(Unimplemented(format!("float to {:?} cast", ty))),
}
}
@ -109,8 +108,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
// Casting to a reference or fn pointer is not permitted by rustc, no need to support it here.
TyRawPtr(_) | TyInt(IntTy::Is) | TyUint(UintTy::Us) =>
Ok(PrimVal::Ptr(ptr)),
TyInt(_) | TyUint(_) => Err(EvalError::ReadPointerAsBytes),
_ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))),
TyInt(_) | TyUint(_) => err!(ReadPointerAsBytes),
_ => err!(Unimplemented(format!("ptr to {:?} cast", ty))),
}
}
}

View File

@ -6,7 +6,7 @@ use syntax::ast::Mutability;
use syntax::codemap::Span;
use super::{
EvalResult, EvalError,
EvalResult, EvalError, EvalErrorKind,
Global, GlobalId, Lvalue,
PrimVal,
EvalContext, StackPopCleanup,
@ -87,7 +87,7 @@ struct CompileTimeFunctionEvaluator;
impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
fn into(self) -> EvalError<'tcx> {
EvalError::MachineError(Box::new(self))
EvalErrorKind::MachineError(Box::new(self)).into()
}
}
@ -142,7 +142,7 @@ impl<'tcx> super::Machine<'tcx> for CompileTimeFunctionEvaluator {
}
let mir = match ecx.load_mir(instance.def) {
Ok(mir) => mir,
Err(EvalError::NoMirFor(path)) => {
Err(EvalError{ kind: EvalErrorKind::NoMirFor(path), ..} ) => {
// some simple things like `malloc` might get accepted in the future
return Err(ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path)).into());
},

View File

@ -1,5 +1,7 @@
use std::error::Error;
use std::fmt;
use std::path::{PathBuf, Path};
use rustc::mir;
use rustc::ty::{FnSig, Ty, layout};
@ -11,7 +13,41 @@ use rustc_const_math::ConstMathErr;
use syntax::codemap::Span;
#[derive(Debug)]
pub enum EvalError<'tcx> {
pub struct EvalError<'tcx> {
pub kind: EvalErrorKind<'tcx>,
pub backtrace: Vec<Frame>,
}
impl<'tcx> From<EvalErrorKind<'tcx>> for EvalError<'tcx> {
fn from(kind: EvalErrorKind<'tcx>) -> Self {
let mut backtrace = Vec::new();
use backtrace::{trace, resolve};
trace(|frame| {
resolve(frame.ip(), |symbol| {
backtrace.push(Frame {
function: symbol.name().map(|s| s.to_string()).unwrap_or(String::new()),
file: symbol.filename().unwrap_or(Path::new("")).to_owned(),
line: symbol.lineno().unwrap_or(0),
});
});
true
});
EvalError {
kind,
backtrace,
}
}
}
#[derive(Debug)]
pub struct Frame {
pub function: String,
pub file: PathBuf,
pub line: u32,
}
#[derive(Debug)]
pub enum EvalErrorKind<'tcx> {
/// This variant is used by machines to signal their own errors that do not
/// match an existing variant
MachineError(Box<Error>),
@ -106,8 +142,8 @@ pub type EvalResult<'tcx, T = ()> = Result<T, EvalError<'tcx>>;
impl<'tcx> Error for EvalError<'tcx> {
fn description(&self) -> &str {
use self::EvalError::*;
match *self {
use self::EvalErrorKind::*;
match self.kind {
MachineError(ref inner) => inner.description(),
FunctionPointerTyMismatch(..) =>
"tried to call a function through a function pointer of a different type",
@ -192,7 +228,7 @@ impl<'tcx> Error for EvalError<'tcx> {
TypeNotPrimitive(_) =>
"expected primitive type, got nonprimitive",
ReallocatedWrongMemoryKind(_, _) =>
"tried to reallocate memory from one kind to another",
"tried to EvalErrorKindreallocate memory from one kind to another",
DeallocatedWrongMemoryKind(_, _) =>
"tried to deallocate memory of the wrong kind",
ReallocateNonBasePtr =>
@ -215,14 +251,14 @@ impl<'tcx> Error for EvalError<'tcx> {
"the evaluated program panicked",
ReadFromReturnPointer =>
"tried to read from the return pointer",
EvalError::PathNotFound(_) =>
EvalErrorKind::PathNotFound(_) =>
"a path could not be resolved, maybe the crate is not loaded",
}
}
fn cause(&self) -> Option<&Error> {
use self::EvalError::*;
match *self {
use self::EvalErrorKind::*;
match self.kind {
MachineError(ref inner) => Some(&**inner),
_ => None,
}
@ -231,8 +267,8 @@ impl<'tcx> Error for EvalError<'tcx> {
impl<'tcx> fmt::Display for EvalError<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::EvalError::*;
match *self {
use self::EvalErrorKind::*;
match self.kind {
PointerOutOfBounds { ptr, access, allocation_size } => {
write!(f, "{} at offset {}, outside bounds of allocation {} which has size {}",
if access { "memory access" } else { "pointer computed" },

View File

@ -17,7 +17,7 @@ use syntax::ast::{self, Mutability};
use syntax::abi::Abi;
use super::{
EvalError, EvalResult,
EvalError, EvalResult, EvalErrorKind,
Global, GlobalId, Lvalue, LvalueExtra,
Memory, MemoryPointer, HasMemory,
Kind as MemoryKind,
@ -257,7 +257,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
pub fn load_mir(&self, instance: ty::InstanceDef<'tcx>) -> EvalResult<'tcx, &'tcx mir::Mir<'tcx>> {
trace!("load mir {:?}", instance);
match instance {
ty::InstanceDef::Item(def_id) => self.tcx.maybe_optimized_mir(def_id).ok_or_else(|| EvalError::NoMirFor(self.tcx.item_path_str(def_id))),
ty::InstanceDef::Item(def_id) => self.tcx.maybe_optimized_mir(def_id).ok_or_else(|| EvalErrorKind::NoMirFor(self.tcx.item_path_str(def_id)).into()),
_ => Ok(self.tcx.instance_mir(instance)),
}
}
@ -402,7 +402,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
// TODO(solson): Is this inefficient? Needs investigation.
let ty = self.monomorphize(ty, substs);
ty.layout(self.tcx, ty::ParamEnv::empty(Reveal::All)).map_err(EvalError::Layout)
ty.layout(self.tcx, ty::ParamEnv::empty(Reveal::All)).map_err(|layout| EvalErrorKind::Layout(layout).into())
}
pub fn push_stack_frame(
@ -460,7 +460,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
self.memory.set_cur_frame(cur_frame);
if self.stack.len() > self.stack_limit {
Err(EvalError::StackFrameLimitReached)
err!(StackFrameLimitReached)
} else {
Ok(())
}
@ -609,7 +609,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
// it emits in debug mode) is performance, but it doesn't cost us any performance in miri.
// If, however, the compiler ever starts transforming unchecked intrinsics into unchecked binops,
// we have to go back to just ignoring the overflow here.
return Err(EvalError::OverflowingMath);
return err!(OverflowingMath);
}
}
@ -729,7 +729,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
}
_ => {
return Err(EvalError::Unimplemented(format!(
return err!(Unimplemented(format!(
"can't handle destination layout {:?} when assigning {:?}",
dest_layout,
kind
@ -857,7 +857,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
let discr_val = self.read_discriminant_value(ptr, ty)?;
if let ty::TyAdt(adt_def, _) = ty.sty {
if adt_def.discriminants(self.tcx).all(|v| discr_val != v.to_u128_unchecked()) {
return Err(EvalError::InvalidDiscriminant);
return err!(InvalidDiscriminant);
}
} else {
bug!("rustc only generates Rvalue::Discriminant for enums");
@ -948,7 +948,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
let ty = adt_def.variants[nndiscr as usize].fields[field_index].ty(self.tcx, substs);
Ok(TyAndPacked { ty, packed: nonnull.packed })
},
_ => Err(EvalError::Unimplemented(format!("get_field_ty can't handle enum type: {:?}, {:?}", ty, ty.sty))),
_ => err!(Unimplemented(format!("get_field_ty can't handle enum type: {:?}, {:?}", ty, ty.sty))),
}
}
ty::TyAdt(adt_def, substs) => {
@ -959,7 +959,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
Ok(TyAndPacked { ty: variant_def.fields[field_index].ty(self.tcx, substs), packed: variants.packed }),
Univariant { ref variant, .. } =>
Ok(TyAndPacked { ty: variant_def.fields[field_index].ty(self.tcx, substs), packed: variant.packed }),
_ => Err(EvalError::Unimplemented(format!("get_field_ty can't handle struct type: {:?}, {:?}", ty, ty.sty))),
_ => err!(Unimplemented(format!("get_field_ty can't handle struct type: {:?}, {:?}", ty, ty.sty))),
}
}
@ -970,7 +970,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
ty::TyArray(ref inner, _) => Ok(TyAndPacked { ty: inner, packed: false }),
_ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, {:?}", ty, ty.sty))),
_ => err!(Unimplemented(format!("can't handle type: {:?}, {:?}", ty, ty.sty))),
}
}
@ -993,7 +993,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
UntaggedUnion { .. } => Ok(Size::from_bytes(0)),
_ => {
let msg = format!("get_field_offset: can't handle type: {:?}, with layout: {:?}", ty, layout);
Err(EvalError::Unimplemented(msg))
err!(Unimplemented(msg))
}
}
}
@ -1012,7 +1012,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
UntaggedUnion { .. } => Ok(1),
_ => {
let msg = format!("get_field_count: can't handle type: {:?}, with layout: {:?}", ty, layout);
Err(EvalError::Unimplemented(msg))
err!(Unimplemented(msg))
}
}
}
@ -1073,7 +1073,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
Lvalue::Local { frame, local } => {
// -1 since we don't store the return value
match self.stack[frame].locals[local.index() - 1] {
None => return Err(EvalError::DeadLocal),
None => return err!(DeadLocal),
Some(Value::ByRef { ptr, aligned }) => {
Lvalue::Ptr { ptr, aligned, extra: LvalueExtra::None }
},
@ -1179,7 +1179,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
Lvalue::Global(cid) => {
let dest = self.globals.get_mut(&cid).expect("global should be cached").clone();
if dest.mutable == Mutability::Immutable {
return Err(EvalError::ModifiedConstantMemory);
return err!(ModifiedConstantMemory);
}
let write_dest = |this: &mut Self, val| {
*this.globals.get_mut(&cid).expect("already checked") = Global {
@ -1382,15 +1382,15 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
if variant.fields.len() == 1 {
return self.ty_to_primval_kind(variant.fields[0].ty(self.tcx, substs));
} else {
return Err(EvalError::TypeNotPrimitive(ty));
return err!(TypeNotPrimitive(ty));
}
}
_ => return Err(EvalError::TypeNotPrimitive(ty)),
_ => return err!(TypeNotPrimitive(ty)),
}
}
_ => return Err(EvalError::TypeNotPrimitive(ty)),
_ => return err!(TypeNotPrimitive(ty)),
};
Ok(kind)
@ -1398,10 +1398,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
fn ensure_valid_value(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx> {
match ty.sty {
ty::TyBool if val.to_bytes()? > 1 => Err(EvalError::InvalidBool),
ty::TyBool if val.to_bytes()? > 1 => err!(InvalidBool),
ty::TyChar if ::std::char::from_u32(val.to_bytes()? as u32).is_none()
=> Err(EvalError::InvalidChar(val.to_bytes()? as u32 as u128)),
=> err!(InvalidChar(val.to_bytes()? as u32 as u128)),
_ => Ok(()),
}
@ -1440,7 +1440,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
let c = self.memory.read_uint(ptr.to_ptr()?, 4)? as u32;
match ::std::char::from_u32(c) {
Some(ch) => PrimVal::from_char(ch),
None => return Err(EvalError::InvalidChar(c as u128)),
None => return err!(InvalidChar(c as u128)),
}
}
@ -1457,7 +1457,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
// if we transmute a ptr to an isize, reading it back into a primval shouldn't panic
// Due to read_ptr ignoring the sign, we need to jump around some hoops
match self.memory.read_int(ptr.to_ptr()?, size) {
Err(EvalError::ReadPointerAsBytes) if size == self.memory.pointer_size() =>
Err(EvalError { kind: EvalErrorKind::ReadPointerAsBytes, .. }) if size == self.memory.pointer_size() =>
// Reading as an int failed because we are seeing ptr bytes *and* we are actually reading at ptr size.
// Let's try again, reading a ptr this time.
self.memory.read_ptr(ptr.to_ptr()?)?.into_inner_primval(),
@ -1478,7 +1478,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
// if we transmute a ptr to an usize, reading it back into a primval shouldn't panic
// for consistency's sake, we use the same code as above
match self.memory.read_uint(ptr.to_ptr()?, size) {
Err(EvalError::ReadPointerAsBytes) if size == self.memory.pointer_size() => self.memory.read_ptr(ptr.to_ptr()?)?.into_inner_primval(),
Err(EvalError { kind: EvalErrorKind::ReadPointerAsBytes, .. }) if size == self.memory.pointer_size() => self.memory.read_ptr(ptr.to_ptr()?)?.into_inner_primval(),
other => PrimVal::from_u128(other?),
}
}
@ -1642,7 +1642,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
write!(msg, ":").unwrap();
match self.stack[frame].get_local(local) {
Err(EvalError::DeadLocal) => {
Err(EvalError{ kind: EvalErrorKind::DeadLocal, ..} ) => {
write!(msg, " is dead").unwrap();
}
Err(err) => {
@ -1677,7 +1677,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
{
let mut val = self.globals.get(&cid).expect("global not cached").clone();
if val.mutable == Mutability::Immutable {
return Err(EvalError::ModifiedConstantMemory);
return err!(ModifiedConstantMemory);
}
val.value = f(self, val.value)?;
*self.globals.get_mut(&cid).expect("already checked") = val;
@ -1704,6 +1704,16 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
}
pub fn report(&self, e: &EvalError) {
let mut trace_text = "\n################################\nerror occurred in miri at\n".to_string();
for frame in e.backtrace.iter().skip_while(|frame| frame.function.starts_with("backtrace::")) {
// don't report initialization gibberish
if frame.function == "miri::after_analysis" {
break;
}
write!(trace_text, "# {}\n", frame.function).unwrap();
write!(trace_text, "{}:{}\n", frame.file.display(), frame.line).unwrap();
}
trace!("{}", trace_text);
if let Some(frame) = self.stack().last() {
let block = &frame.mir.basic_blocks()[frame.block];
let span = if frame.stmt < block.statements.len() {
@ -1729,13 +1739,13 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
impl<'tcx> Frame<'tcx> {
pub fn get_local(&self, local: mir::Local) -> EvalResult<'tcx, Value> {
// Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0.
self.locals[local.index() - 1].ok_or(EvalError::DeadLocal)
self.locals[local.index() - 1].ok_or(EvalErrorKind::DeadLocal.into())
}
fn set_local(&mut self, local: mir::Local, value: Value) -> EvalResult<'tcx> {
// Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0.
match self.locals[local.index() - 1] {
None => Err(EvalError::DeadLocal),
None => err!(DeadLocal),
Some(ref mut local) => {
*local = value;
Ok(())

View File

@ -5,7 +5,7 @@ use rustc_data_structures::indexed_vec::Idx;
use syntax::ast::Mutability;
use super::{
EvalError, EvalResult,
EvalResult,
EvalContext,
MemoryPointer,
PrimVal, Value, Pointer,
@ -140,7 +140,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
use rustc::mir::Lvalue::*;
match *lvalue {
// Might allow this in the future, right now there's no way to do this from Rust code anyway
Local(mir::RETURN_POINTER) => Err(EvalError::ReadFromReturnPointer),
Local(mir::RETURN_POINTER) => err!(ReadFromReturnPointer),
// Directly reading a local will always succeed
Local(local) => self.frame().get_local(local).map(Some),
// Directly reading a static will always succeed

View File

@ -9,7 +9,7 @@ use syntax::ast::Mutability;
use rustc::middle::region::CodeExtent;
use super::{
EvalError, EvalResult,
EvalResult, EvalErrorKind,
PrimVal, Pointer,
EvalContext, DynamicLifetime,
Machine,
@ -319,7 +319,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
assert!(align.is_power_of_two());
if self.memory_size - self.memory_usage < size {
return Err(EvalError::OutOfMemory {
return err!(OutOfMemory {
allocation_size: size,
memory_size: self.memory_size,
memory_usage: self.memory_usage,
@ -354,11 +354,11 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
use std::cmp::min;
if ptr.offset != 0 {
return Err(EvalError::ReallocateNonBasePtr);
return err!(ReallocateNonBasePtr);
}
if let Ok(alloc) = self.get(ptr.alloc_id) {
if alloc.kind != kind {
return Err(EvalError::ReallocatedWrongMemoryKind(format!("{:?}", alloc.kind), format!("{:?}", kind)));
return err!(ReallocatedWrongMemoryKind(format!("{:?}", alloc.kind), format!("{:?}", kind)));
}
}
@ -377,12 +377,12 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
kind: Kind<M::MemoryKinds>,
) -> EvalResult<'tcx> {
if ptr.offset != 0 {
return Err(EvalError::DeallocateNonBasePtr);
return err!(DeallocateNonBasePtr);
}
let alloc = match self.alloc_map.remove(&ptr.alloc_id) {
Some(alloc) => alloc,
None => return Err(EvalError::DoubleFree),
None => return err!(DoubleFree),
};
// It is okay for us to still holds locks on deallocation -- for example, we could store data we own
@ -391,14 +391,14 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
// lock by another frame. We *have* to permit deallocation if we hold a read lock.
// TODO: Figure out the exact rules here.
alloc.check_locks(Some(self.cur_frame), 0, alloc.bytes.len() as u64, AccessKind::Read)
.map_err(|lock| EvalError::DeallocatedLockedMemory { ptr, lock })?;
.map_err(|lock| EvalErrorKind::DeallocatedLockedMemory { ptr, lock })?;
if alloc.kind != kind {
return Err(EvalError::DeallocatedWrongMemoryKind(format!("{:?}", alloc.kind), format!("{:?}", kind)));
return err!(DeallocatedWrongMemoryKind(format!("{:?}", alloc.kind), format!("{:?}", kind)));
}
if let Some((size, align)) = size_and_align {
if size != alloc.bytes.len() as u64 || align != alloc.align {
return Err(EvalError::IncorrectAllocationInformation);
return err!(IncorrectAllocationInformation);
}
}
@ -422,7 +422,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
PrimVal::Ptr(ptr) => {
let alloc = self.get(ptr.alloc_id)?;
if alloc.align < align {
return Err(EvalError::AlignmentCheckFailed {
return err!(AlignmentCheckFailed {
has: alloc.align,
required: align,
});
@ -432,16 +432,16 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
PrimVal::Bytes(bytes) => {
let v = ((bytes as u128) % (1 << self.pointer_size())) as u64;
if v == 0 {
return Err(EvalError::InvalidNullPointerUsage);
return err!(InvalidNullPointerUsage);
}
v
},
PrimVal::Undef => return Err(EvalError::ReadUndefBytes),
PrimVal::Undef => return err!(ReadUndefBytes),
};
if offset % align == 0 {
Ok(())
} else {
Err(EvalError::AlignmentCheckFailed {
err!(AlignmentCheckFailed {
has: offset % align,
required: align,
})
@ -452,7 +452,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
let alloc = self.get(ptr.alloc_id)?;
let allocation_size = alloc.bytes.len() as u64;
if ptr.offset > allocation_size {
return Err(EvalError::PointerOutOfBounds { ptr, access, allocation_size });
return err!(PointerOutOfBounds { ptr, access, allocation_size });
}
Ok(())
}
@ -471,7 +471,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
let alloc = self.get(ptr.alloc_id)?;
let frame = self.cur_frame;
alloc.check_locks(Some(frame), ptr.offset, len, access)
.map_err(|lock| EvalError::MemoryLockViolation { ptr, len, frame, access, lock })
.map_err(|lock| EvalErrorKind::MemoryLockViolation { ptr, len, frame, access, lock }.into())
}
#[allow(dead_code)]
@ -488,7 +488,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
// Check if this conflicts with other locks
alloc.check_locks(None, ptr.offset, len, kind)
.map_err(|lock| EvalError::MemoryAcquireConflict { ptr, len, kind, lock })?;
.map_err(|lock| EvalErrorKind::MemoryAcquireConflict { ptr, len, kind, lock })?;
let lifetime = DynamicLifetime { frame, region };
match (alloc.locks.entry(MemoryRange::new(ptr.offset, len)), kind) {
@ -518,17 +518,17 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
WriteLock(ref lft) => {
// Make sure we can release this lock
if lft.frame != cur_frame {
return Err(EvalError::InvalidMemoryLockRelease { ptr, len, frame: cur_frame, lock: lock.clone() });
return err!(InvalidMemoryLockRelease { ptr, len, frame: cur_frame, lock: lock.clone() });
}
if !range.contained_in(ptr.offset, len) {
return Err(EvalError::Unimplemented(format!("miri does not support releasing part of a write-locked region")));
return err!(Unimplemented(format!("miri does not support releasing part of a write-locked region")));
}
// Release it later. We cannot do this now.
remove_list.push(*range);
}
ReadLock(_) => {
// Abort here and bubble the error outwards so that we do not even register a suspension.
return Err(EvalError::InvalidMemoryLockRelease { ptr, len, frame: cur_frame, lock: lock.clone() });
return err!(InvalidMemoryLockRelease { ptr, len, frame: cur_frame, lock: lock.clone() });
},
}
}
@ -591,8 +591,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
match self.alloc_map.get(&id) {
Some(alloc) => Ok(alloc),
None => match self.functions.get(&id) {
Some(_) => Err(EvalError::DerefFunctionPointer),
None => Err(EvalError::DanglingPointerDeref),
Some(_) => err!(DerefFunctionPointer),
None => err!(DanglingPointerDeref),
}
}
}
@ -601,8 +601,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
match self.alloc_map.get_mut(&id) {
Some(alloc) => Ok(alloc),
None => match self.functions.get(&id) {
Some(_) => Err(EvalError::DerefFunctionPointer),
None => Err(EvalError::DanglingPointerDeref),
Some(_) => err!(DerefFunctionPointer),
None => err!(DanglingPointerDeref),
}
}
}
@ -612,20 +612,20 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
if alloc.mutable == Mutability::Mutable {
Ok(alloc)
} else {
Err(EvalError::ModifiedConstantMemory)
err!(ModifiedConstantMemory)
}
}
pub fn get_fn(&self, ptr: MemoryPointer) -> EvalResult<'tcx, ty::Instance<'tcx>> {
if ptr.offset != 0 {
return Err(EvalError::InvalidFunctionPointer);
return err!(InvalidFunctionPointer);
}
debug!("reading fn ptr: {}", ptr.alloc_id);
match self.functions.get(&ptr.alloc_id) {
Some(&fndef) => Ok(fndef),
None => match self.alloc_map.get(&ptr.alloc_id) {
Some(_) => Err(EvalError::ExecuteMemory),
None => Err(EvalError::InvalidFunctionPointer),
Some(_) => err!(ExecuteMemory),
None => err!(InvalidFunctionPointer),
}
}
}
@ -760,7 +760,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
fn get_bytes(&self, ptr: MemoryPointer, size: u64, align: u64) -> EvalResult<'tcx, &[u8]> {
assert_ne!(size, 0);
if self.relocations(ptr, size)?.count() != 0 {
return Err(EvalError::ReadPointerAsBytes);
return err!(ReadPointerAsBytes);
}
self.check_defined(ptr, size)?;
self.get_bytes_unchecked(ptr, size, align)
@ -818,7 +818,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
// mark recursively
mem::replace(relocations, Default::default())
},
None if !self.functions.contains_key(&alloc_id) => return Err(EvalError::DanglingPointerDeref),
None if !self.functions.contains_key(&alloc_id) => return err!(DanglingPointerDeref),
_ => return Ok(()),
};
// recurse into inner allocations
@ -857,7 +857,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
if nonoverlapping {
if (src.offset <= dest.offset && src.offset + size > dest.offset) ||
(dest.offset <= src.offset && dest.offset + size > src.offset) {
return Err(EvalError::Intrinsic(format!("copy_nonoverlapping called on overlapping ranges")));
return err!(Intrinsic(format!("copy_nonoverlapping called on overlapping ranges")));
}
}
ptr::copy(src_bytes, dest_bytes, size as usize);
@ -879,13 +879,13 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
match alloc.bytes[offset..].iter().position(|&c| c == 0) {
Some(size) => {
if self.relocations(ptr, (size + 1) as u64)?.count() != 0 {
return Err(EvalError::ReadPointerAsBytes);
return err!(ReadPointerAsBytes);
}
self.check_defined(ptr, (size + 1) as u64)?;
self.check_locks(ptr, (size + 1) as u64, AccessKind::Read)?;
Ok(&alloc.bytes[offset..offset + size])
},
None => Err(EvalError::UnterminatedCString(ptr)),
None => err!(UnterminatedCString(ptr)),
}
}
@ -987,7 +987,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
match bytes[0] {
0 => Ok(false),
1 => Ok(true),
_ => Err(EvalError::InvalidBool),
_ => err!(InvalidBool),
}
}
@ -1117,7 +1117,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
let overlapping_start = self.relocations(ptr, 0)?.count();
let overlapping_end = self.relocations(ptr.offset(size, self.layout)?, 0)?.count();
if overlapping_start + overlapping_end != 0 {
return Err(EvalError::ReadPointerAsBytes);
return err!(ReadPointerAsBytes);
}
Ok(())
}
@ -1154,7 +1154,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
fn check_defined(&self, ptr: MemoryPointer, size: u64) -> EvalResult<'tcx> {
let alloc = self.get(ptr.alloc_id)?;
if !alloc.undef_mask.is_range_defined(ptr.offset, ptr.offset + size) {
return Err(EvalError::ReadUndefBytes);
return err!(ReadUndefBytes);
}
Ok(())
}
@ -1416,7 +1416,7 @@ pub trait PointerArithmetic : layout::HasDataLayout {
fn signed_offset<'tcx>(self, val: u64, i: i64) -> EvalResult<'tcx, u64> {
let (res, over) = self.overflowing_signed_offset(val, i as i128);
if over {
Err(EvalError::OverflowingMath)
err!(OverflowingMath)
} else {
Ok(res)
}
@ -1425,7 +1425,7 @@ pub trait PointerArithmetic : layout::HasDataLayout {
fn offset<'tcx>(self, val: u64, i: u64) -> EvalResult<'tcx, u64> {
let (res, over) = self.overflowing_offset(val, i);
if over {
Err(EvalError::OverflowingMath)
err!(OverflowingMath)
} else {
Ok(res)
}

View File

@ -1,5 +1,10 @@
//! An interpreter for MIR used in CTFE and by miri
#[macro_export]
macro_rules! err {
($($tt:tt)*) => { Err($crate::interpret::EvalErrorKind::$($tt)*.into()) };
}
mod cast;
mod const_eval;
mod error;
@ -17,6 +22,7 @@ mod value;
pub use self::error::{
EvalError,
EvalResult,
EvalErrorKind,
};
pub use self::eval_context::{

View File

@ -2,7 +2,7 @@ use rustc::mir;
use rustc::ty::Ty;
use super::{
EvalError, EvalResult,
EvalResult,
EvalContext,
Lvalue,
Machine,
@ -173,7 +173,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
if left_kind != right_kind {
let msg = format!("unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
return Err(EvalError::Unimplemented(msg));
return err!(Unimplemented(msg));
}
let val = match (bin_op, left_kind) {
@ -227,7 +227,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
_ => {
let msg = format!("unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
return Err(EvalError::Unimplemented(msg));
return err!(Unimplemented(msg));
}
};
@ -271,7 +271,7 @@ pub fn unary_op<'tcx>(
_ => {
let msg = format!("unimplemented unary op: {:?}, {:?}", un_op, val);
return Err(EvalError::Unimplemented(msg));
return err!(Unimplemented(msg));
}
};

View File

@ -12,7 +12,7 @@ use rustc::ty::layout::Layout;
use rustc::ty::subst::Substs;
use super::{
EvalResult, EvalError,
EvalResult,
EvalContext, StackPopCleanup, TyAndPacked,
Global, GlobalId, Lvalue,
Value, PrimVal,
@ -29,7 +29,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
if self.steps_remaining > 0 {
Ok(())
} else {
Err(EvalError::ExecutionTimeLimitReached)
err!(ExecutionTimeLimitReached)
}
}
@ -123,7 +123,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
StorageLive(ref lvalue) | StorageDead(ref lvalue)=> {
let (frame, local) = match self.eval_lvalue(lvalue)? {
Lvalue::Local{ frame, local } if self.cur_frame() == frame => (frame, local),
_ => return Err(EvalError::Unimplemented("Storage annotations must refer to locals of the topmost stack frame.".to_owned())) // FIXME maybe this should get its own error type
_ => return err!(Unimplemented("Storage annotations must refer to locals of the topmost stack frame.".to_owned())) // FIXME maybe this should get its own error type
};
let old_val = match stmt.kind {
StorageLive(_) => self.stack[frame].storage_live(local)?,
@ -140,7 +140,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
// size of MIR constantly.
Nop => {}
InlineAsm { .. } => return Err(EvalError::InlineAsm),
InlineAsm { .. } => return err!(InlineAsm),
}
self.frame_mut().stmt += 1;

View File

@ -5,7 +5,7 @@ use syntax::codemap::Span;
use syntax::abi::Abi;
use super::{
EvalError, EvalResult,
EvalError, EvalResult, EvalErrorKind,
EvalContext, eval_context, TyAndPacked,
Lvalue,
MemoryPointer,
@ -78,7 +78,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
let real_sig = self.erase_lifetimes(&real_sig);
let real_sig = self.tcx.normalize_associated_type(&real_sig);
if !self.check_sig_compat(sig, real_sig)? {
return Err(EvalError::FunctionPointerTyMismatch(real_sig, sig));
return err!(FunctionPointerTyMismatch(real_sig, sig));
}
},
ref other => bug!("instance def ty: {:?}", other),
@ -88,7 +88,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
ty::TyFnDef(def_id, substs) => (eval_context::resolve(self.tcx, def_id, substs), func_ty.fn_sig(self.tcx)),
_ => {
let msg = format!("can't handle callee of type {:?}", func_ty);
return Err(EvalError::Unimplemented(msg));
return err!(Unimplemented(msg));
}
};
let sig = self.erase_lifetimes(&sig);
@ -121,17 +121,17 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
let index = self.eval_operand_to_primval(index)
.expect("can't eval index")
.to_u64()?;
Err(EvalError::ArrayIndexOutOfBounds(span, len, index))
err!(ArrayIndexOutOfBounds(span, len, index))
},
mir::AssertMessage::Math(ref err) =>
Err(EvalError::Math(terminator.source_info.span, err.clone())),
err!(Math(terminator.source_info.span, err.clone())),
}
}
},
DropAndReplace { .. } => unimplemented!(),
Resume => unimplemented!(),
Unreachable => return Err(EvalError::Unreachable),
Unreachable => return err!(Unreachable),
}
Ok(())
@ -214,11 +214,11 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
ty::InstanceDef::Intrinsic(..) => {
let (ret, target) = match destination {
Some(dest) => dest,
_ => return Err(EvalError::Unreachable),
_ => return err!(Unreachable),
};
let ty = sig.output();
if !eval_context::is_inhabited(self.tcx, ty) {
return Err(EvalError::Unreachable);
return err!(Unreachable);
}
let layout = self.type_layout(ty)?;
M::call_intrinsic(self, instance, arg_operands, ret, ty, layout, target)?;
@ -462,7 +462,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
trace!("read_nonnull_discriminant_value: {:?}, {}, {}", ptr, nndiscr, discr_size);
let not_null = match self.memory.read_uint(ptr, discr_size) {
Ok(0) => false,
Ok(_) | Err(EvalError::ReadPointerAsBytes) => true,
Ok(_) | Err(EvalError{ kind: EvalErrorKind::ReadPointerAsBytes, .. }) => true,
Err(e) => return Err(e),
};
assert!(nndiscr == 0 || nndiscr == 1);

View File

@ -6,7 +6,7 @@ use syntax::codemap::DUMMY_SP;
use syntax::ast::{self, Mutability};
use super::{
EvalResult, EvalError,
EvalResult,
EvalContext, eval_context,
MemoryPointer, Kind,
Value, PrimVal,
@ -82,7 +82,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
// some values don't need to call a drop impl, so the value is null
Value::ByVal(PrimVal::Bytes(0)) => Ok(None),
Value::ByVal(PrimVal::Ptr(drop_fn)) => self.memory.get_fn(drop_fn).map(Some),
_ => Err(EvalError::ReadBytesAsPointer),
_ => err!(ReadBytesAsPointer),
}
}

View File

@ -11,7 +11,7 @@ use rustc::infer::TransNormalize;
use rustc::middle::region::CodeExtent;
use super::{
EvalError, EvalResult,
EvalError, EvalResult, EvalErrorKind,
EvalContext, DynamicLifetime,
AccessKind, LockInfo,
PrimVal, Value,
@ -98,7 +98,7 @@ std::sync::atomic::AtomicBool::get_mut$|\
ValidationOp::Suspend(_) => ValidationMode::Release,
};
match self.validate(query.clone(), mode) {
Err(EvalError::InvalidMemoryLockRelease { lock: LockInfo::ReadLock(_), .. }) => {
Err(EvalError { kind: EvalErrorKind::InvalidMemoryLockRelease { lock: LockInfo::ReadLock(_), .. }, .. }) => {
// HACK: When &x is used while x is already borrowed read-only, AddValidation still
// emits suspension. This code is legit, so just ignore the error *and*
// do NOT register a suspension.
@ -170,7 +170,8 @@ std::sync::atomic::AtomicBool::get_mut$|\
// HACK: If, during releasing, we hit memory we cannot use, we just ignore that.
// This can happen because releases are added before drop elaboration.
// TODO: Fix the MIR so that these releases do not happen.
res @ Err(EvalError::DanglingPointerDeref) | res @ Err(EvalError::ReadUndefBytes) => {
res @ Err(EvalError{ kind: EvalErrorKind::DanglingPointerDeref, ..}) |
res @ Err(EvalError{ kind: EvalErrorKind::ReadUndefBytes, ..}) => {
if let ValidationMode::Release = mode {
return Ok(());
}
@ -207,8 +208,8 @@ std::sync::atomic::AtomicBool::get_mut$|\
Lvalue::Local { frame, local } => {
let res = self.stack[frame].get_local(local);
match (res, mode) {
(Err(EvalError::DeadLocal), ValidationMode::Recover(_)) |
(Err(EvalError::DeadLocal), ValidationMode::Release) |
(Err(EvalError{ kind: EvalErrorKind::DeadLocal, ..}), ValidationMode::Recover(_)) |
(Err(EvalError{ kind: EvalErrorKind::DeadLocal, ..}), ValidationMode::Release) |
(Ok(Value::ByVal(PrimVal::Undef)), ValidationMode::Release) => {
return Ok(());
}
@ -287,7 +288,7 @@ std::sync::atomic::AtomicBool::get_mut$|\
Ok(())
}
TyNever => {
Err(EvalError::ValidationFailure(format!("The empty type is never valid.")))
err!(ValidationFailure(format!("The empty type is never valid.")))
}
TyRef(region, ty::TypeAndMut { ty: pointee_ty, mutbl }) => {
let val = self.read_lvalue(query.lval)?;
@ -370,8 +371,11 @@ std::sync::atomic::AtomicBool::get_mut$|\
// Get variant index for discriminant
let variant_idx = adt.discriminants(self.tcx)
.position(|variant_discr| variant_discr.to_u128_unchecked() == discr)
.ok_or(EvalError::InvalidDiscriminant)?;
.position(|variant_discr| variant_discr.to_u128_unchecked() == discr);
let variant_idx = match variant_idx {
Some(val) => val,
None => return err!(InvalidDiscriminant),
};
let variant = &adt.variants[variant_idx];
if variant.fields.len() > 0 {

View File

@ -4,7 +4,7 @@
use rustc::ty::layout::HasDataLayout;
use super::{
EvalError, EvalResult,
EvalResult,
Memory, MemoryPointer, HasMemory, PointerArithmetic,
Machine,
};
@ -72,7 +72,7 @@ impl<'tcx> Pointer {
Ok(Pointer::from(PrimVal::Bytes(layout.signed_offset(b as u64, i)? as u128)))
},
PrimVal::Ptr(ptr) => ptr.signed_offset(i, layout).map(Pointer::from),
PrimVal::Undef => Err(EvalError::ReadUndefBytes),
PrimVal::Undef => err!(ReadUndefBytes),
}
}
@ -84,7 +84,7 @@ impl<'tcx> Pointer {
Ok(Pointer::from(PrimVal::Bytes(layout.offset(b as u64, i)? as u128)))
},
PrimVal::Ptr(ptr) => ptr.offset(i, layout).map(Pointer::from),
PrimVal::Undef => Err(EvalError::ReadUndefBytes),
PrimVal::Undef => err!(ReadUndefBytes),
}
}
@ -96,7 +96,7 @@ impl<'tcx> Pointer {
Ok(Pointer::from(PrimVal::Bytes(layout.wrapping_signed_offset(b as u64, i) as u128)))
},
PrimVal::Ptr(ptr) => Ok(Pointer::from(ptr.wrapping_signed_offset(i, layout))),
PrimVal::Undef => Err(EvalError::ReadUndefBytes),
PrimVal::Undef => err!(ReadUndefBytes),
}
}
@ -104,7 +104,7 @@ impl<'tcx> Pointer {
match self.primval {
PrimVal::Bytes(b) => Ok(b == 0),
PrimVal::Ptr(_) => Ok(false),
PrimVal::Undef => Err(EvalError::ReadUndefBytes),
PrimVal::Undef => err!(ReadUndefBytes),
}
}
@ -249,16 +249,16 @@ impl<'tcx> PrimVal {
pub fn to_bytes(self) -> EvalResult<'tcx, u128> {
match self {
PrimVal::Bytes(b) => Ok(b),
PrimVal::Ptr(_) => Err(EvalError::ReadPointerAsBytes),
PrimVal::Undef => Err(EvalError::ReadUndefBytes),
PrimVal::Ptr(_) => err!(ReadPointerAsBytes),
PrimVal::Undef => err!(ReadUndefBytes),
}
}
pub fn to_ptr(self) -> EvalResult<'tcx, MemoryPointer> {
match self {
PrimVal::Bytes(_) => Err(EvalError::ReadBytesAsPointer),
PrimVal::Bytes(_) => err!(ReadBytesAsPointer),
PrimVal::Ptr(p) => Ok(p),
PrimVal::Undef => Err(EvalError::ReadUndefBytes),
PrimVal::Undef => err!(ReadUndefBytes),
}
}
@ -324,7 +324,7 @@ impl<'tcx> PrimVal {
match self.to_bytes()? {
0 => Ok(false),
1 => Ok(true),
_ => Err(EvalError::InvalidBool),
_ => err!(InvalidBool),
}
}
}

View File

@ -20,5 +20,6 @@ extern crate byteorder;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate backtrace;
pub mod interpret;