diff --git a/.travis.yml b/.travis.yml index 4856f1aad5e84..46734f6f1140c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,8 +15,8 @@ script: xargo/build.sh - | # Test plain miri - cargo build && - cargo test && + cargo build --release && + cargo test --release && cargo install - | # Test cargo miri @@ -26,11 +26,11 @@ script: cd .. - | # and run all tests with full mir - MIRI_SYSROOT=~/.xargo/HOST cargo test + MIRI_SYSROOT=~/.xargo/HOST cargo test --release - | # test that the rustc_tests binary compiles cd rustc_tests && - cargo build && + cargo build --release && cd .. notifications: email: diff --git a/Cargo.lock b/Cargo.lock index 66295f0fbced7..e33d99ed4496c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index bfe450c608871..d674cc10d3ec2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ license = "MIT/Apache-2.0" name = "miri" repository = "https://github.com/solson/miri" version = "0.1.0" +build = "build.rs" [[bin]] doc = false diff --git a/build.rs b/build.rs new file mode 100644 index 0000000000000..86ccf3cda1ab5 --- /dev/null +++ b/build.rs @@ -0,0 +1,6 @@ +use std::env; + +fn main() { + // Forward the profile to the main compilation + println!("cargo:rustc-env=PROFILE={}", env::var("PROFILE").unwrap()); +} diff --git a/miri/fn_call.rs b/miri/fn_call.rs index 77aebb9725c69..81db48fe1296b 100644 --- a/miri/fn_call.rs +++ b/miri/fn_call.rs @@ -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 diff --git a/miri/helpers.rs b/miri/helpers.rs index add6558bcc444..3cdabd4e623d6 100644 --- a/miri/helpers.rs +++ b/miri/helpers.rs @@ -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) } } } diff --git a/miri/intrinsic.rs b/miri/intrinsic.rs index 73caf64dbde88..4cdad350b43e6 100644 --- a/miri/intrinsic.rs +++ b/miri/intrinsic.rs @@ -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); diff --git a/miri/lib.rs b/miri/lib.rs index 8f223851b357f..cdccc5a9d4435 100644 --- a/miri/lib.rs +++ b/miri/lib.rs @@ -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(()), } } diff --git a/miri/operator.rs b/miri/operator.rs index a01ba25cd75ea..b6ab72c5dd019 100644 --- a/miri/operator.rs +++ b/miri/operator.rs @@ -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)); } }) } diff --git a/miri/tls.rs b/miri/tls.rs index 87620cd52b29f..6900535dfb89f 100644 --- a/miri/tls.rs +++ b/miri/tls.rs @@ -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)?; diff --git a/src/librustc_mir/Cargo.toml b/src/librustc_mir/Cargo.toml index 78e8cbeecd78f..1ccb9ec0d296a 100644 --- a/src/librustc_mir/Cargo.toml +++ b/src/librustc_mir/Cargo.toml @@ -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" diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index d69e09313c3fe..c3ddeca0e6554 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -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))), } } } diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index 5a334b4db1db7..51f18bccf43fb 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -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> 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()); }, diff --git a/src/librustc_mir/interpret/error.rs b/src/librustc_mir/interpret/error.rs index 7d62d59fcd79d..3b297ed5bd01a 100644 --- a/src/librustc_mir/interpret/error.rs +++ b/src/librustc_mir/interpret/error.rs @@ -1,5 +1,6 @@ use std::error::Error; use std::fmt; + use rustc::mir; use rustc::ty::{FnSig, Ty, layout}; @@ -9,9 +10,25 @@ use super::{ use rustc_const_math::ConstMathErr; use syntax::codemap::Span; +use backtrace::Backtrace; + +#[derive(Debug)] +pub struct EvalError<'tcx> { + pub kind: EvalErrorKind<'tcx>, + pub backtrace: Backtrace, +} + +impl<'tcx> From> for EvalError<'tcx> { + fn from(kind: EvalErrorKind<'tcx>) -> Self { + EvalError { + kind, + backtrace: Backtrace::new(), + } + } +} #[derive(Debug)] -pub enum EvalError<'tcx> { +pub enum EvalErrorKind<'tcx> { /// This variant is used by machines to signal their own errors that do not /// match an existing variant MachineError(Box), @@ -106,8 +123,8 @@ pub type EvalResult<'tcx, T = ()> = Result>; 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", @@ -215,14 +232,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 +248,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" }, diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 16015c4bf3a2c..9f37b3521dcbb 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -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)), } } @@ -415,7 +415,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( @@ -473,7 +473,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(()) } @@ -622,7 +622,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); } } @@ -742,7 +742,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 @@ -870,7 +870,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"); @@ -961,7 +961,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) => { @@ -972,7 +972,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))), } } @@ -983,7 +983,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))), } } @@ -1006,7 +1006,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)) } } } @@ -1025,7 +1025,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)) } } } @@ -1086,7 +1086,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 } }, @@ -1192,7 +1192,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 { @@ -1395,15 +1395,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) @@ -1411,10 +1411,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(()), } @@ -1453,7 +1453,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)), } } @@ -1470,7 +1470,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(), @@ -1491,7 +1491,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?), } } @@ -1655,7 +1655,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) => { @@ -1690,7 +1690,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; @@ -1717,6 +1717,48 @@ 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(); + let mut skip_init = true; + 'frames: for (i, frame) in e.backtrace.frames().iter().enumerate() { + for symbol in frame.symbols() { + if let Some(name) = symbol.name() { + // unmangle the symbol via `to_string` + let name = name.to_string(); + if name.starts_with("miri::after_analysis") { + // don't report initialization gibberish + break 'frames; + } else if name.starts_with("backtrace::capture::Backtrace::new") + // debug mode produces funky symbol names + || name.starts_with("backtrace::capture::{{impl}}::new") { + // don't report backtrace internals + skip_init = false; + continue 'frames; + } + } + } + if skip_init { + continue; + } + write!(trace_text, "{}\n", i).unwrap(); + for symbol in frame.symbols() { + if let Some(name) = symbol.name() { + write!(trace_text, "# {}\n", name).unwrap(); + } else { + write!(trace_text, "# \n").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!("{}", 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() { @@ -1742,13 +1784,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(()) diff --git a/src/librustc_mir/interpret/lvalue.rs b/src/librustc_mir/interpret/lvalue.rs index 5c10d2c195282..8722c96dbecd4 100644 --- a/src/librustc_mir/interpret/lvalue.rs +++ b/src/librustc_mir/interpret/lvalue.rs @@ -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 diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 31e47e706ad33..9c436fb76cccd 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -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, ) -> 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) } diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs index 236e708d96fb8..3b3f82b7a730f 100644 --- a/src/librustc_mir/interpret/mod.rs +++ b/src/librustc_mir/interpret/mod.rs @@ -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::{ diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index c4c0055d20120..a9675d148d664 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -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)); } }; diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index d27f5d03a5442..7ad51a08a4656 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -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; diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs index 3ccc2ee0fb4a3..ce04e1b8d1e06 100644 --- a/src/librustc_mir/interpret/terminator/mod.rs +++ b/src/librustc_mir/interpret/terminator/mod.rs @@ -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); diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index 903a3040fea3d..a1821e58a996f 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -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), } } diff --git a/src/librustc_mir/interpret/validation.rs b/src/librustc_mir/interpret/validation.rs index 7a2f4c796d30a..23ac6bbfcd841 100644 --- a/src/librustc_mir/interpret/validation.rs +++ b/src/librustc_mir/interpret/validation.rs @@ -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 { diff --git a/src/librustc_mir/interpret/value.rs b/src/librustc_mir/interpret/value.rs index c88d1c22dc9f3..163643be01c32 100644 --- a/src/librustc_mir/interpret/value.rs +++ b/src/librustc_mir/interpret/value.rs @@ -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), } } } diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 960b73ee6b243..de0cde265601a 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -20,5 +20,6 @@ extern crate byteorder; #[macro_use] extern crate lazy_static; extern crate regex; +extern crate backtrace; pub mod interpret; diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 7b3ad7d8b78a6..c386e6a528c77 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -13,11 +13,13 @@ macro_rules! eprintln { } } +const MIRI_PATH: &'static str = concat!("target/", env!("PROFILE"), "/miri"); + fn compile_fail(sysroot: &Path, path: &str, target: &str, host: &str, fullmir: bool) { eprintln!("## Running compile-fail tests in {} against miri for target {}", path, target); let mut config = compiletest::default_config(); config.mode = "compile-fail".parse().expect("Invalid mode"); - config.rustc_path = "target/debug/miri".into(); + config.rustc_path = MIRI_PATH.into(); if fullmir { if host != target { // skip fullmir on nonhost @@ -56,7 +58,7 @@ fn miri_pass(path: &str, target: &str, host: &str, fullmir: bool, opt: bool) { config.src_base = PathBuf::from(path); config.target = target.to_owned(); config.host = host.to_owned(); - config.rustc_path = "target/debug/miri".into(); + config.rustc_path = MIRI_PATH.into(); let mut flags = Vec::new(); if fullmir { if host != target {