about summary refs log tree commit diff
diff options
context:
space:
mode:
authorOliver Schneider <oli-obk@users.noreply.github.com>2017-08-03 23:02:29 +0200
committerGitHub <noreply@github.com>2017-08-03 23:02:29 +0200
commit726b027ba3d2658d1a7796f46ea404c7299b7c0c (patch)
tree1e6560ab17e318d40817af7acf087c26d5597a9f
parent5aed31812eb8e46fbf1ef8fc13b068ac6bcbb3f9 (diff)
parent2cf394955b576352b972a649ff7711e3e81bfb08 (diff)
downloadrust-726b027ba3d2658d1a7796f46ea404c7299b7c0c.tar.gz
rust-726b027ba3d2658d1a7796f46ea404c7299b7c0c.zip
Merge pull request #280 from solson/archeology
Produce backtraces for miri internals
-rw-r--r--.travis.yml8
-rw-r--r--Cargo.lock76
-rw-r--r--Cargo.toml1
-rw-r--r--build.rs6
-rw-r--r--miri/fn_call.rs54
-rw-r--r--miri/helpers.rs8
-rw-r--r--miri/intrinsic.rs16
-rw-r--r--miri/lib.rs7
-rw-r--r--miri/operator.rs10
-rw-r--r--miri/tls.rs10
-rw-r--r--src/librustc_mir/Cargo.toml1
-rw-r--r--src/librustc_mir/interpret/cast.rs11
-rw-r--r--src/librustc_mir/interpret/const_eval.rs6
-rw-r--r--src/librustc_mir/interpret/error.rs33
-rw-r--r--src/librustc_mir/interpret/eval_context.rs94
-rw-r--r--src/librustc_mir/interpret/lvalue.rs4
-rw-r--r--src/librustc_mir/interpret/memory.rs74
-rw-r--r--src/librustc_mir/interpret/mod.rs6
-rw-r--r--src/librustc_mir/interpret/operator.rs8
-rw-r--r--src/librustc_mir/interpret/step.rs8
-rw-r--r--src/librustc_mir/interpret/terminator/mod.rs18
-rw-r--r--src/librustc_mir/interpret/traits.rs4
-rw-r--r--src/librustc_mir/interpret/validation.rs20
-rw-r--r--src/librustc_mir/interpret/value.rs20
-rw-r--r--src/librustc_mir/lib.rs1
-rw-r--r--tests/compiletest.rs6
26 files changed, 333 insertions, 177 deletions
diff --git a/.travis.yml b/.travis.yml
index 4856f1aad5e..46734f6f114 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 66295f0fbce..e33d99ed449 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)",
@@ -18,6 +19,29 @@ dependencies = [
 ]
 
 [[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"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -33,6 +57,11 @@ dependencies = [
 ]
 
 [[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"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -42,6 +71,15 @@ dependencies = [
 ]
 
 [[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"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -56,11 +94,25 @@ dependencies = [
 ]
 
 [[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"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -132,6 +184,11 @@ 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"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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 bfe450c6088..d674cc10d3e 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 00000000000..86ccf3cda1a
--- /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 77aebb9725c..81db48fe129 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 add6558bcc4..3cdabd4e623 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 73caf64dbde..4cdad350b43 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 8f223851b35..cdccc5a9d44 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 a01ba25cd75..b6ab72c5dd0 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 87620cd52b2..6900535dfb8 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 78e8cbeecd7..1ccb9ec0d29 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 d69e09313c3..c3ddeca0e65 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 5a334b4db1d..51f18bccf43 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<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());
             },
diff --git a/src/librustc_mir/interpret/error.rs b/src/librustc_mir/interpret/error.rs
index 7d62d59fcd7..3b297ed5bd0 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<EvalErrorKind<'tcx>> 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<Error>),
@@ -106,8 +123,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",
@@ -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 16015c4bf3a..9f37b3521dc 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, "# <unknown>\n").unwrap();
+                }
+                if let Some(file_path) = symbol.filename() {
+                    write!(trace_text, "{}", file_path.display()).unwrap();
+                } else {
+                    write!(trace_text, "<unknown_file>").unwrap();
+                }
+                if let Some(line) = symbol.lineno() {
+                    write!(trace_text, ":{}\n", line).unwrap();
+                } else {
+                    write!(trace_text, "\n").unwrap();
+                }
+            }
+        }
+        trace!("{}", 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 5c10d2c1952..8722c96dbec 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 31e47e706ad..9c436fb76cc 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<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)
         }
diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs
index 236e708d96f..3b3f82b7a73 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 c4c0055d201..a9675d148d6 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 d27f5d03a54..7ad51a08a46 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 3ccc2ee0fb4..ce04e1b8d1e 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 903a3040fea..a1821e58a99 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 7a2f4c796d3..23ac6bbfcd8 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 c88d1c22dc9..163643be01c 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 960b73ee6b2..de0cde26560 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 7b3ad7d8b78..c386e6a528c 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 {