Produce backtraces for miri internals
This commit is contained in:
parent
cf318b1f6f
commit
29a3c4d28c
76
Cargo.lock
generated
76
Cargo.lock
generated
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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(()),
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
10
miri/tls.rs
10
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)?;
|
||||
|
@ -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"
|
||||
|
@ -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))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
},
|
||||
|
@ -1,5 +1,7 @@
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::path::{PathBuf, Path};
|
||||
|
||||
use rustc::mir;
|
||||
use rustc::ty::{FnSig, Ty, layout};
|
||||
|
||||
@ -11,7 +13,41 @@ use rustc_const_math::ConstMathErr;
|
||||
use syntax::codemap::Span;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EvalError<'tcx> {
|
||||
pub struct EvalError<'tcx> {
|
||||
pub kind: EvalErrorKind<'tcx>,
|
||||
pub backtrace: Vec<Frame>,
|
||||
}
|
||||
|
||||
impl<'tcx> From<EvalErrorKind<'tcx>> for EvalError<'tcx> {
|
||||
fn from(kind: EvalErrorKind<'tcx>) -> Self {
|
||||
let mut backtrace = Vec::new();
|
||||
use backtrace::{trace, resolve};
|
||||
trace(|frame| {
|
||||
resolve(frame.ip(), |symbol| {
|
||||
backtrace.push(Frame {
|
||||
function: symbol.name().map(|s| s.to_string()).unwrap_or(String::new()),
|
||||
file: symbol.filename().unwrap_or(Path::new("")).to_owned(),
|
||||
line: symbol.lineno().unwrap_or(0),
|
||||
});
|
||||
});
|
||||
true
|
||||
});
|
||||
EvalError {
|
||||
kind,
|
||||
backtrace,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Frame {
|
||||
pub function: String,
|
||||
pub file: PathBuf,
|
||||
pub line: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EvalErrorKind<'tcx> {
|
||||
/// This variant is used by machines to signal their own errors that do not
|
||||
/// match an existing variant
|
||||
MachineError(Box<Error>),
|
||||
@ -106,8 +142,8 @@ pub type EvalResult<'tcx, T = ()> = Result<T, EvalError<'tcx>>;
|
||||
|
||||
impl<'tcx> Error for EvalError<'tcx> {
|
||||
fn description(&self) -> &str {
|
||||
use self::EvalError::*;
|
||||
match *self {
|
||||
use self::EvalErrorKind::*;
|
||||
match self.kind {
|
||||
MachineError(ref inner) => inner.description(),
|
||||
FunctionPointerTyMismatch(..) =>
|
||||
"tried to call a function through a function pointer of a different type",
|
||||
@ -192,7 +228,7 @@ impl<'tcx> Error for EvalError<'tcx> {
|
||||
TypeNotPrimitive(_) =>
|
||||
"expected primitive type, got nonprimitive",
|
||||
ReallocatedWrongMemoryKind(_, _) =>
|
||||
"tried to reallocate memory from one kind to another",
|
||||
"tried to EvalErrorKindreallocate memory from one kind to another",
|
||||
DeallocatedWrongMemoryKind(_, _) =>
|
||||
"tried to deallocate memory of the wrong kind",
|
||||
ReallocateNonBasePtr =>
|
||||
@ -215,14 +251,14 @@ impl<'tcx> Error for EvalError<'tcx> {
|
||||
"the evaluated program panicked",
|
||||
ReadFromReturnPointer =>
|
||||
"tried to read from the return pointer",
|
||||
EvalError::PathNotFound(_) =>
|
||||
EvalErrorKind::PathNotFound(_) =>
|
||||
"a path could not be resolved, maybe the crate is not loaded",
|
||||
}
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&Error> {
|
||||
use self::EvalError::*;
|
||||
match *self {
|
||||
use self::EvalErrorKind::*;
|
||||
match self.kind {
|
||||
MachineError(ref inner) => Some(&**inner),
|
||||
_ => None,
|
||||
}
|
||||
@ -231,8 +267,8 @@ impl<'tcx> Error for EvalError<'tcx> {
|
||||
|
||||
impl<'tcx> fmt::Display for EvalError<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::EvalError::*;
|
||||
match *self {
|
||||
use self::EvalErrorKind::*;
|
||||
match self.kind {
|
||||
PointerOutOfBounds { ptr, access, allocation_size } => {
|
||||
write!(f, "{} at offset {}, outside bounds of allocation {} which has size {}",
|
||||
if access { "memory access" } else { "pointer computed" },
|
||||
|
@ -17,7 +17,7 @@ use syntax::ast::{self, Mutability};
|
||||
use syntax::abi::Abi;
|
||||
|
||||
use super::{
|
||||
EvalError, EvalResult,
|
||||
EvalError, EvalResult, EvalErrorKind,
|
||||
Global, GlobalId, Lvalue, LvalueExtra,
|
||||
Memory, MemoryPointer, HasMemory,
|
||||
Kind as MemoryKind,
|
||||
@ -257,7 +257,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
pub fn load_mir(&self, instance: ty::InstanceDef<'tcx>) -> EvalResult<'tcx, &'tcx mir::Mir<'tcx>> {
|
||||
trace!("load mir {:?}", instance);
|
||||
match instance {
|
||||
ty::InstanceDef::Item(def_id) => self.tcx.maybe_optimized_mir(def_id).ok_or_else(|| EvalError::NoMirFor(self.tcx.item_path_str(def_id))),
|
||||
ty::InstanceDef::Item(def_id) => self.tcx.maybe_optimized_mir(def_id).ok_or_else(|| EvalErrorKind::NoMirFor(self.tcx.item_path_str(def_id)).into()),
|
||||
_ => Ok(self.tcx.instance_mir(instance)),
|
||||
}
|
||||
}
|
||||
@ -402,7 +402,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
// TODO(solson): Is this inefficient? Needs investigation.
|
||||
let ty = self.monomorphize(ty, substs);
|
||||
|
||||
ty.layout(self.tcx, ty::ParamEnv::empty(Reveal::All)).map_err(EvalError::Layout)
|
||||
ty.layout(self.tcx, ty::ParamEnv::empty(Reveal::All)).map_err(|layout| EvalErrorKind::Layout(layout).into())
|
||||
}
|
||||
|
||||
pub fn push_stack_frame(
|
||||
@ -460,7 +460,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
self.memory.set_cur_frame(cur_frame);
|
||||
|
||||
if self.stack.len() > self.stack_limit {
|
||||
Err(EvalError::StackFrameLimitReached)
|
||||
err!(StackFrameLimitReached)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
@ -609,7 +609,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
// it emits in debug mode) is performance, but it doesn't cost us any performance in miri.
|
||||
// If, however, the compiler ever starts transforming unchecked intrinsics into unchecked binops,
|
||||
// we have to go back to just ignoring the overflow here.
|
||||
return Err(EvalError::OverflowingMath);
|
||||
return err!(OverflowingMath);
|
||||
}
|
||||
}
|
||||
|
||||
@ -729,7 +729,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
}
|
||||
|
||||
_ => {
|
||||
return Err(EvalError::Unimplemented(format!(
|
||||
return err!(Unimplemented(format!(
|
||||
"can't handle destination layout {:?} when assigning {:?}",
|
||||
dest_layout,
|
||||
kind
|
||||
@ -857,7 +857,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
let discr_val = self.read_discriminant_value(ptr, ty)?;
|
||||
if let ty::TyAdt(adt_def, _) = ty.sty {
|
||||
if adt_def.discriminants(self.tcx).all(|v| discr_val != v.to_u128_unchecked()) {
|
||||
return Err(EvalError::InvalidDiscriminant);
|
||||
return err!(InvalidDiscriminant);
|
||||
}
|
||||
} else {
|
||||
bug!("rustc only generates Rvalue::Discriminant for enums");
|
||||
@ -948,7 +948,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
let ty = adt_def.variants[nndiscr as usize].fields[field_index].ty(self.tcx, substs);
|
||||
Ok(TyAndPacked { ty, packed: nonnull.packed })
|
||||
},
|
||||
_ => Err(EvalError::Unimplemented(format!("get_field_ty can't handle enum type: {:?}, {:?}", ty, ty.sty))),
|
||||
_ => err!(Unimplemented(format!("get_field_ty can't handle enum type: {:?}, {:?}", ty, ty.sty))),
|
||||
}
|
||||
}
|
||||
ty::TyAdt(adt_def, substs) => {
|
||||
@ -959,7 +959,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
Ok(TyAndPacked { ty: variant_def.fields[field_index].ty(self.tcx, substs), packed: variants.packed }),
|
||||
Univariant { ref variant, .. } =>
|
||||
Ok(TyAndPacked { ty: variant_def.fields[field_index].ty(self.tcx, substs), packed: variant.packed }),
|
||||
_ => Err(EvalError::Unimplemented(format!("get_field_ty can't handle struct type: {:?}, {:?}", ty, ty.sty))),
|
||||
_ => err!(Unimplemented(format!("get_field_ty can't handle struct type: {:?}, {:?}", ty, ty.sty))),
|
||||
}
|
||||
}
|
||||
|
||||
@ -970,7 +970,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
|
||||
ty::TyArray(ref inner, _) => Ok(TyAndPacked { ty: inner, packed: false }),
|
||||
|
||||
_ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, {:?}", ty, ty.sty))),
|
||||
_ => err!(Unimplemented(format!("can't handle type: {:?}, {:?}", ty, ty.sty))),
|
||||
}
|
||||
}
|
||||
|
||||
@ -993,7 +993,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
UntaggedUnion { .. } => Ok(Size::from_bytes(0)),
|
||||
_ => {
|
||||
let msg = format!("get_field_offset: can't handle type: {:?}, with layout: {:?}", ty, layout);
|
||||
Err(EvalError::Unimplemented(msg))
|
||||
err!(Unimplemented(msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1012,7 +1012,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
UntaggedUnion { .. } => Ok(1),
|
||||
_ => {
|
||||
let msg = format!("get_field_count: can't handle type: {:?}, with layout: {:?}", ty, layout);
|
||||
Err(EvalError::Unimplemented(msg))
|
||||
err!(Unimplemented(msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1073,7 +1073,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
Lvalue::Local { frame, local } => {
|
||||
// -1 since we don't store the return value
|
||||
match self.stack[frame].locals[local.index() - 1] {
|
||||
None => return Err(EvalError::DeadLocal),
|
||||
None => return err!(DeadLocal),
|
||||
Some(Value::ByRef { ptr, aligned }) => {
|
||||
Lvalue::Ptr { ptr, aligned, extra: LvalueExtra::None }
|
||||
},
|
||||
@ -1179,7 +1179,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
Lvalue::Global(cid) => {
|
||||
let dest = self.globals.get_mut(&cid).expect("global should be cached").clone();
|
||||
if dest.mutable == Mutability::Immutable {
|
||||
return Err(EvalError::ModifiedConstantMemory);
|
||||
return err!(ModifiedConstantMemory);
|
||||
}
|
||||
let write_dest = |this: &mut Self, val| {
|
||||
*this.globals.get_mut(&cid).expect("already checked") = Global {
|
||||
@ -1382,15 +1382,15 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
if variant.fields.len() == 1 {
|
||||
return self.ty_to_primval_kind(variant.fields[0].ty(self.tcx, substs));
|
||||
} else {
|
||||
return Err(EvalError::TypeNotPrimitive(ty));
|
||||
return err!(TypeNotPrimitive(ty));
|
||||
}
|
||||
}
|
||||
|
||||
_ => return Err(EvalError::TypeNotPrimitive(ty)),
|
||||
_ => return err!(TypeNotPrimitive(ty)),
|
||||
}
|
||||
}
|
||||
|
||||
_ => return Err(EvalError::TypeNotPrimitive(ty)),
|
||||
_ => return err!(TypeNotPrimitive(ty)),
|
||||
};
|
||||
|
||||
Ok(kind)
|
||||
@ -1398,10 +1398,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
|
||||
fn ensure_valid_value(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx> {
|
||||
match ty.sty {
|
||||
ty::TyBool if val.to_bytes()? > 1 => Err(EvalError::InvalidBool),
|
||||
ty::TyBool if val.to_bytes()? > 1 => err!(InvalidBool),
|
||||
|
||||
ty::TyChar if ::std::char::from_u32(val.to_bytes()? as u32).is_none()
|
||||
=> Err(EvalError::InvalidChar(val.to_bytes()? as u32 as u128)),
|
||||
=> err!(InvalidChar(val.to_bytes()? as u32 as u128)),
|
||||
|
||||
_ => Ok(()),
|
||||
}
|
||||
@ -1440,7 +1440,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
let c = self.memory.read_uint(ptr.to_ptr()?, 4)? as u32;
|
||||
match ::std::char::from_u32(c) {
|
||||
Some(ch) => PrimVal::from_char(ch),
|
||||
None => return Err(EvalError::InvalidChar(c as u128)),
|
||||
None => return err!(InvalidChar(c as u128)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1457,7 +1457,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
// if we transmute a ptr to an isize, reading it back into a primval shouldn't panic
|
||||
// Due to read_ptr ignoring the sign, we need to jump around some hoops
|
||||
match self.memory.read_int(ptr.to_ptr()?, size) {
|
||||
Err(EvalError::ReadPointerAsBytes) if size == self.memory.pointer_size() =>
|
||||
Err(EvalError { kind: EvalErrorKind::ReadPointerAsBytes, .. }) if size == self.memory.pointer_size() =>
|
||||
// Reading as an int failed because we are seeing ptr bytes *and* we are actually reading at ptr size.
|
||||
// Let's try again, reading a ptr this time.
|
||||
self.memory.read_ptr(ptr.to_ptr()?)?.into_inner_primval(),
|
||||
@ -1478,7 +1478,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
// if we transmute a ptr to an usize, reading it back into a primval shouldn't panic
|
||||
// for consistency's sake, we use the same code as above
|
||||
match self.memory.read_uint(ptr.to_ptr()?, size) {
|
||||
Err(EvalError::ReadPointerAsBytes) if size == self.memory.pointer_size() => self.memory.read_ptr(ptr.to_ptr()?)?.into_inner_primval(),
|
||||
Err(EvalError { kind: EvalErrorKind::ReadPointerAsBytes, .. }) if size == self.memory.pointer_size() => self.memory.read_ptr(ptr.to_ptr()?)?.into_inner_primval(),
|
||||
other => PrimVal::from_u128(other?),
|
||||
}
|
||||
}
|
||||
@ -1642,7 +1642,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
write!(msg, ":").unwrap();
|
||||
|
||||
match self.stack[frame].get_local(local) {
|
||||
Err(EvalError::DeadLocal) => {
|
||||
Err(EvalError{ kind: EvalErrorKind::DeadLocal, ..} ) => {
|
||||
write!(msg, " is dead").unwrap();
|
||||
}
|
||||
Err(err) => {
|
||||
@ -1677,7 +1677,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
{
|
||||
let mut val = self.globals.get(&cid).expect("global not cached").clone();
|
||||
if val.mutable == Mutability::Immutable {
|
||||
return Err(EvalError::ModifiedConstantMemory);
|
||||
return err!(ModifiedConstantMemory);
|
||||
}
|
||||
val.value = f(self, val.value)?;
|
||||
*self.globals.get_mut(&cid).expect("already checked") = val;
|
||||
@ -1704,6 +1704,16 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
}
|
||||
|
||||
pub fn report(&self, e: &EvalError) {
|
||||
let mut trace_text = "\n################################\nerror occurred in miri at\n".to_string();
|
||||
for frame in e.backtrace.iter().skip_while(|frame| frame.function.starts_with("backtrace::")) {
|
||||
// don't report initialization gibberish
|
||||
if frame.function == "miri::after_analysis" {
|
||||
break;
|
||||
}
|
||||
write!(trace_text, "# {}\n", frame.function).unwrap();
|
||||
write!(trace_text, "{}:{}\n", frame.file.display(), frame.line).unwrap();
|
||||
}
|
||||
trace!("{}", trace_text);
|
||||
if let Some(frame) = self.stack().last() {
|
||||
let block = &frame.mir.basic_blocks()[frame.block];
|
||||
let span = if frame.stmt < block.statements.len() {
|
||||
@ -1729,13 +1739,13 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
impl<'tcx> Frame<'tcx> {
|
||||
pub fn get_local(&self, local: mir::Local) -> EvalResult<'tcx, Value> {
|
||||
// Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0.
|
||||
self.locals[local.index() - 1].ok_or(EvalError::DeadLocal)
|
||||
self.locals[local.index() - 1].ok_or(EvalErrorKind::DeadLocal.into())
|
||||
}
|
||||
|
||||
fn set_local(&mut self, local: mir::Local, value: Value) -> EvalResult<'tcx> {
|
||||
// Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0.
|
||||
match self.locals[local.index() - 1] {
|
||||
None => Err(EvalError::DeadLocal),
|
||||
None => err!(DeadLocal),
|
||||
Some(ref mut local) => {
|
||||
*local = value;
|
||||
Ok(())
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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::{
|
||||
|
@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,5 +20,6 @@ extern crate byteorder;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate regex;
|
||||
extern crate backtrace;
|
||||
|
||||
pub mod interpret;
|
||||
|
Loading…
x
Reference in New Issue
Block a user