Except for error.rs, the result there looks rather ugly
This commit is contained in:
Ralf Jung 2017-08-10 08:48:38 -07:00
parent 85fd3f8e43
commit 1326aed02c
29 changed files with 2655 additions and 1257 deletions

View File

@ -7,9 +7,7 @@
#[bench]
fn fib(bencher: &mut Bencher) {
bencher.iter(|| {
fibonacci_helper::main();
})
bencher.iter(|| { fibonacci_helper::main(); })
}
#[bench]
@ -19,9 +17,7 @@ fn fib_miri(bencher: &mut Bencher) {
#[bench]
fn fib_iter(bencher: &mut Bencher) {
bencher.iter(|| {
fibonacci_helper_iterative::main();
})
bencher.iter(|| { fibonacci_helper_iterative::main(); })
}
#[bench]

View File

@ -4,9 +4,5 @@ pub fn main() {
}
fn fib(n: usize) -> usize {
if n <= 2 {
1
} else {
fib(n - 1) + fib(n - 2)
}
if n <= 2 { 1 } else { fib(n - 1) + fib(n - 2) }
}

View File

@ -19,9 +19,13 @@ fn find_sysroot() -> String {
let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
match (home, toolchain) {
(Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain),
_ => option_env!("RUST_SYSROOT")
.expect("need to specify RUST_SYSROOT env var or use rustup or multirust")
.to_owned(),
_ => {
option_env!("RUST_SYSROOT")
.expect(
"need to specify RUST_SYSROOT env var or use rustup or multirust",
)
.to_owned()
}
}
}
@ -30,7 +34,7 @@ pub fn run(filename: &str, bencher: &mut Bencher) {
"miri".to_string(),
format!("benches/helpers/{}.rs", filename),
"--sysroot".to_string(),
find_sysroot()
find_sysroot(),
];
let compiler_calls = &mut MiriCompilerCalls(Rc::new(RefCell::new(bencher)));
rustc_driver::run_compiler(args, compiler_calls, None, None);
@ -40,7 +44,7 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls<'a> {
fn build_controller(
&mut self,
_: &Session,
_: &getopts::Matches
_: &getopts::Matches,
) -> driver::CompileController<'a> {
let mut control: driver::CompileController<'a> = driver::CompileController::basic();
@ -51,14 +55,17 @@ fn build_controller(
state.session.abort_if_errors();
let tcx = state.tcx.unwrap();
let (entry_node_id, _) = state.session.entry_fn.borrow()
.expect("no main or start function found");
let (entry_node_id, _) = state.session.entry_fn.borrow().expect(
"no main or start function found",
);
let entry_def_id = tcx.map.local_def_id(entry_node_id);
let memory_size = 100*1024*1024; // 100MB
let memory_size = 100 * 1024 * 1024; // 100MB
let step_limit = 1000_000;
let stack_limit = 100;
bencher.borrow_mut().iter(|| { eval_main(tcx, entry_def_id, memory_size, step_limit, stack_limit); });
bencher.borrow_mut().iter(|| {
eval_main(tcx, entry_def_id, memory_size, step_limit, stack_limit);
});
state.session.abort_if_errors();
});

View File

@ -1,3 +1,2 @@
#[inline(never)]
pub fn main() {
}
pub fn main() {}

View File

@ -7,9 +7,7 @@
#[bench]
fn noop(bencher: &mut Bencher) {
bencher.iter(|| {
smoke_helper::main();
})
bencher.iter(|| { smoke_helper::main(); })
}
/*

View File

@ -50,29 +50,42 @@ fn main() {
let test = std::env::args().nth(2).map_or(false, |text| text == "test");
let skip = if test { 3 } else { 2 };
let manifest_path_arg = std::env::args().skip(skip).find(|val| val.starts_with("--manifest-path="));
let manifest_path_arg = std::env::args().skip(skip).find(|val| {
val.starts_with("--manifest-path=")
});
let mut metadata = if let Ok(metadata) = cargo_metadata::metadata(manifest_path_arg.as_ref().map(AsRef::as_ref)) {
let mut metadata = if let Ok(metadata) = cargo_metadata::metadata(
manifest_path_arg.as_ref().map(AsRef::as_ref),
)
{
metadata
} else {
let _ = std::io::stderr().write_fmt(format_args!("error: Could not obtain cargo metadata."));
let _ = std::io::stderr().write_fmt(format_args!(
"error: Could not obtain cargo metadata."
));
std::process::exit(101);
};
let manifest_path = manifest_path_arg.map(|arg| PathBuf::from(Path::new(&arg["--manifest-path=".len()..])));
let manifest_path = manifest_path_arg.map(|arg| {
PathBuf::from(Path::new(&arg["--manifest-path=".len()..]))
});
let current_dir = std::env::current_dir();
let package_index = metadata.packages
let package_index = metadata
.packages
.iter()
.position(|package| {
let package_manifest_path = Path::new(&package.manifest_path);
if let Some(ref manifest_path) = manifest_path {
package_manifest_path == manifest_path
} else {
let current_dir = current_dir.as_ref().expect("could not read current directory");
let package_manifest_directory = package_manifest_path.parent()
.expect("could not find parent directory of package manifest");
let current_dir = current_dir.as_ref().expect(
"could not read current directory",
);
let package_manifest_directory = package_manifest_path.parent().expect(
"could not find parent directory of package manifest",
);
package_manifest_directory == current_dir
}
})
@ -80,13 +93,25 @@ fn main() {
let package = metadata.packages.remove(package_index);
for target in package.targets {
let args = std::env::args().skip(skip);
let kind = target.kind.get(0).expect("badly formatted cargo metadata: target::kind is an empty array");
let kind = target.kind.get(0).expect(
"badly formatted cargo metadata: target::kind is an empty array",
);
if test && kind == "test" {
if let Err(code) = process(vec!["--test".to_string(), target.name].into_iter().chain(args)) {
if let Err(code) = process(
vec!["--test".to_string(), target.name].into_iter().chain(
args,
),
)
{
std::process::exit(code);
}
} else if !test && kind == "bin" {
if let Err(code) = process(vec!["--bin".to_string(), target.name].into_iter().chain(args)) {
if let Err(code) = process(
vec!["--bin".to_string(), target.name].into_iter().chain(
args,
),
)
{
std::process::exit(code);
}
}
@ -118,7 +143,11 @@ fn main() {
let mut args: Vec<String> = if std::env::args().any(|s| s == "--sysroot") {
std::env::args().skip(1).collect()
} else {
std::env::args().skip(1).chain(Some("--sysroot".to_owned())).chain(Some(sys_root)).collect()
std::env::args()
.skip(1)
.chain(Some("--sysroot".to_owned()))
.chain(Some(sys_root))
.collect()
};
// this check ensures that dependencies are built but not interpreted and the final crate is
@ -137,9 +166,11 @@ fn main() {
args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-miri""#.to_owned()]);
match command.args(&args).status() {
Ok(exit) => if !exit.success() {
std::process::exit(exit.code().unwrap_or(42));
},
Ok(exit) => {
if !exit.success() {
std::process::exit(exit.code().unwrap_or(42));
}
}
Err(ref e) if miri_enabled => panic!("error during miri run: {:?}", e),
Err(ref e) => panic!("error during rustc call: {:?}", e),
}
@ -147,7 +178,8 @@ fn main() {
}
fn process<I>(old_args: I) -> Result<(), i32>
where I: Iterator<Item = String>
where
I: Iterator<Item = String>,
{
let mut args = vec!["rustc".to_owned()];

View File

@ -16,7 +16,7 @@
use rustc::session::config::{self, Input, ErrorOutputType};
use rustc::hir::{self, itemlikevisit};
use rustc::ty::TyCtxt;
use syntax::ast::{MetaItemKind, NestedMetaItemKind, self};
use syntax::ast::{self, MetaItemKind, NestedMetaItemKind};
use std::path::PathBuf;
struct MiriCompilerCalls(RustcDefaultCalls);
@ -28,9 +28,15 @@ fn early_callback(
sopts: &config::Options,
cfg: &ast::CrateConfig,
descriptions: &rustc_errors::registry::Registry,
output: ErrorOutputType
output: ErrorOutputType,
) -> Compilation {
self.0.early_callback(matches, sopts, cfg, descriptions, output)
self.0.early_callback(
matches,
sopts,
cfg,
descriptions,
output,
)
}
fn no_input(
&mut self,
@ -39,9 +45,16 @@ fn no_input(
cfg: &ast::CrateConfig,
odir: &Option<PathBuf>,
ofile: &Option<PathBuf>,
descriptions: &rustc_errors::registry::Registry
descriptions: &rustc_errors::registry::Registry,
) -> Option<(Input, Option<PathBuf>)> {
self.0.no_input(matches, sopts, cfg, odir, ofile, descriptions)
self.0.no_input(
matches,
sopts,
cfg,
odir,
ofile,
descriptions,
)
}
fn late_callback(
&mut self,
@ -49,11 +62,15 @@ fn late_callback(
sess: &Session,
input: &Input,
odir: &Option<PathBuf>,
ofile: &Option<PathBuf>
ofile: &Option<PathBuf>,
) -> Compilation {
self.0.late_callback(matches, sess, input, odir, ofile)
}
fn build_controller(&mut self, sess: &Session, matches: &getopts::Matches) -> CompileController<'a> {
fn build_controller(
&mut self,
sess: &Session,
matches: &getopts::Matches,
) -> CompileController<'a> {
let mut control = self.0.build_controller(sess, matches);
control.after_hir_lowering.callback = Box::new(after_hir_lowering);
control.after_analysis.callback = Box::new(after_analysis);
@ -66,7 +83,10 @@ fn build_controller(&mut self, sess: &Session, matches: &getopts::Matches) -> Co
}
fn after_hir_lowering(state: &mut CompileState) {
let attr = (String::from("miri"), syntax::feature_gate::AttributeType::Whitelisted);
let attr = (
String::from("miri"),
syntax::feature_gate::AttributeType::Whitelisted,
);
state.session.plugin_attributes.borrow_mut().push(attr);
}
@ -77,13 +97,23 @@ fn after_analysis<'a, 'tcx>(state: &mut CompileState<'a, 'tcx>) {
let limits = resource_limits_from_attributes(state);
if std::env::args().any(|arg| arg == "--test") {
struct Visitor<'a, 'tcx: 'a>(miri::ResourceLimits, TyCtxt<'a, 'tcx, 'tcx>, &'a CompileState<'a, 'tcx>);
struct Visitor<'a, 'tcx: 'a>(
miri::ResourceLimits,
TyCtxt<'a, 'tcx, 'tcx>,
&'a CompileState<'a, 'tcx>
);
impl<'a, 'tcx: 'a, 'hir> itemlikevisit::ItemLikeVisitor<'hir> for Visitor<'a, 'tcx> {
fn visit_item(&mut self, i: &'hir hir::Item) {
if let hir::Item_::ItemFn(_, _, _, _, _, body_id) = i.node {
if i.attrs.iter().any(|attr| attr.name().map_or(false, |n| n == "test")) {
if i.attrs.iter().any(|attr| {
attr.name().map_or(false, |n| n == "test")
})
{
let did = self.1.hir.body_owner_def_id(body_id);
println!("running test: {}", self.1.hir.def_path(did).to_string(self.1));
println!(
"running test: {}",
self.1.hir.def_path(did).to_string(self.1)
);
miri::eval_main(self.1, did, None, self.0);
self.2.session.abort_if_errors();
}
@ -92,11 +122,18 @@ fn visit_item(&mut self, i: &'hir hir::Item) {
fn visit_trait_item(&mut self, _trait_item: &'hir hir::TraitItem) {}
fn visit_impl_item(&mut self, _impl_item: &'hir hir::ImplItem) {}
}
state.hir_crate.unwrap().visit_all_item_likes(&mut Visitor(limits, tcx, state));
state.hir_crate.unwrap().visit_all_item_likes(
&mut Visitor(limits, tcx, state),
);
} else if let Some((entry_node_id, _)) = *state.session.entry_fn.borrow() {
let entry_def_id = tcx.hir.local_def_id(entry_node_id);
let start_wrapper = tcx.lang_items.start_fn().and_then(|start_fn|
if tcx.is_mir_available(start_fn) { Some(start_fn) } else { None });
let start_wrapper = tcx.lang_items.start_fn().and_then(|start_fn| {
if tcx.is_mir_available(start_fn) {
Some(start_fn)
} else {
None
}
});
miri::eval_main(tcx, entry_def_id, start_wrapper, limits);
state.session.abort_if_errors();
@ -112,11 +149,19 @@ fn resource_limits_from_attributes(state: &CompileState) -> miri::ResourceLimits
let extract_int = |lit: &syntax::ast::Lit| -> u128 {
match lit.node {
syntax::ast::LitKind::Int(i, _) => i,
_ => state.session.span_fatal(lit.span, "expected an integer literal"),
_ => {
state.session.span_fatal(
lit.span,
"expected an integer literal",
)
}
}
};
for attr in krate.attrs.iter().filter(|a| a.name().map_or(false, |n| n == "miri")) {
for attr in krate.attrs.iter().filter(|a| {
a.name().map_or(false, |n| n == "miri")
})
{
if let Some(items) = attr.meta_item_list() {
for item in items {
if let NestedMetaItemKind::MetaItem(ref inner) = item.node {
@ -165,7 +210,10 @@ fn init_logger() {
};
let mut builder = env_logger::LogBuilder::new();
builder.format(format).filter(None, log::LogLevelFilter::Info);
builder.format(format).filter(
None,
log::LogLevelFilter::Info,
);
if std::env::var("MIRI_LOG").is_ok() {
builder.parse(&std::env::var("MIRI_LOG").unwrap());
@ -184,9 +232,13 @@ fn find_sysroot() -> String {
let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
match (home, toolchain) {
(Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain),
_ => option_env!("RUST_SYSROOT")
.expect("need to specify RUST_SYSROOT env var or use rustup or multirust")
.to_owned(),
_ => {
option_env!("RUST_SYSROOT")
.expect(
"need to specify RUST_SYSROOT env var or use rustup or multirust",
)
.to_owned()
}
}
}

View File

@ -9,10 +9,7 @@
use rustc_miri::interpret::*;
use super::{
TlsKey,
EvalContext,
};
use super::{TlsKey, EvalContext};
use tls::MemoryExt;
@ -62,13 +59,19 @@ fn eval_fn_call(
let mir = match self.load_mir(instance.def) {
Ok(mir) => mir,
Err(EvalError{ kind: EvalErrorKind::NoMirFor(path), ..} ) => {
self.call_missing_fn(instance, destination, arg_operands, sig, path)?;
Err(EvalError { kind: EvalErrorKind::NoMirFor(path), .. }) => {
self.call_missing_fn(
instance,
destination,
arg_operands,
sig,
path,
)?;
return Ok(true);
},
}
Err(other) => return Err(other),
};
let (return_lvalue, return_to_block) = match destination {
Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)),
None => (Lvalue::undef(), StackPopCleanup::None),
@ -99,7 +102,8 @@ fn call_c_abi(
.unwrap_or(name)
.as_str();
let args_res: EvalResult<Vec<Value>> = arg_operands.iter()
let args_res: EvalResult<Vec<Value>> = arg_operands
.iter()
.map(|arg| self.eval_operand(arg))
.collect();
let args = args_res?;
@ -121,7 +125,11 @@ fn call_c_abi(
"free" => {
let ptr = args[0].into_ptr(&mut self.memory)?;
if !ptr.is_null()? {
self.memory.deallocate(ptr.to_ptr()?, None, MemoryKind::C.into())?;
self.memory.deallocate(
ptr.to_ptr()?,
None,
MemoryKind::C.into(),
)?;
}
}
@ -132,9 +140,16 @@ fn call_c_abi(
// libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)
// is called if a `HashMap` is created the regular way.
match self.value_to_primval(args[0], usize)?.to_u64()? {
318 |
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))),
318 | 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 +159,10 @@ fn call_c_abi(
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!(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 +185,12 @@ fn call_c_abi(
StackPopCleanup::Goto(dest_block),
)?;
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_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)?;
@ -199,14 +222,21 @@ fn call_c_abi(
}
};
self.write_primval(dest, PrimVal::Bytes(result as u128), dest_ty)?;
self.write_primval(
dest,
PrimVal::Bytes(result as u128),
dest_ty,
)?;
}
"memrchr" => {
let ptr = args[0].into_ptr(&mut self.memory)?;
let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8;
let num = self.value_to_primval(args[2], usize)?.to_u64()?;
if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().rev().position(|&c| c == val) {
if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().rev().position(
|&c| c == val,
)
{
let new_ptr = ptr.offset(num - idx as u64 - 1, &self)?;
self.write_ptr(dest, new_ptr, dest_ty)?;
} else {
@ -218,7 +248,10 @@ fn call_c_abi(
let ptr = args[0].into_ptr(&mut self.memory)?;
let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8;
let num = self.value_to_primval(args[2], usize)?.to_u64()?;
if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(|&c| c == val) {
if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(
|&c| c == val,
)
{
let new_ptr = ptr.offset(idx as u64, &self)?;
self.write_ptr(dest, new_ptr, dest_ty)?;
} else {
@ -274,11 +307,19 @@ fn call_c_abi(
}
if let Some((name, value)) = new {
// +1 for the null terminator
let value_copy = self.memory.allocate((value.len() + 1) as u64, 1, MemoryKind::Env.into())?;
let value_copy = self.memory.allocate(
(value.len() + 1) as u64,
1,
MemoryKind::Env.into(),
)?;
self.memory.write_bytes(value_copy.into(), &value)?;
let trailing_zero_ptr = value_copy.offset(value.len() as u64, &self)?.into();
self.memory.write_bytes(trailing_zero_ptr, &[0])?;
if let Some(var) = self.machine_data.env_vars.insert(name.to_owned(), value_copy) {
if let Some(var) = self.machine_data.env_vars.insert(
name.to_owned(),
value_copy,
)
{
self.memory.deallocate(var, None, MemoryKind::Env.into())?;
}
self.write_null(dest, dest_ty)?;
@ -292,17 +333,29 @@ fn call_c_abi(
let buf = args[1].into_ptr(&mut self.memory)?;
let n = self.value_to_primval(args[2], usize)?.to_u64()?;
trace!("Called write({:?}, {:?}, {:?})", fd, buf, n);
let result = if fd == 1 || fd == 2 { // stdout/stderr
let result = if fd == 1 || fd == 2 {
// stdout/stderr
use std::io::{self, Write};
let buf_cont = self.memory.read_bytes(buf, n)?;
let res = if fd == 1 { io::stdout().write(buf_cont) } else { io::stderr().write(buf_cont) };
match res { Ok(n) => n as isize, Err(_) => -1 }
let res = if fd == 1 {
io::stdout().write(buf_cont)
} else {
io::stderr().write(buf_cont)
};
match res {
Ok(n) => n as isize,
Err(_) => -1,
}
} else {
info!("Ignored output to FD {}", fd);
n as isize // pretend it all went well
}; // now result is the value we return back to the program
self.write_primval(dest, PrimVal::Bytes(result as u128), dest_ty)?;
self.write_primval(
dest,
PrimVal::Bytes(result as u128),
dest_ty,
)?;
}
"strlen" => {
@ -328,7 +381,10 @@ fn call_c_abi(
let mut result = None;
for &(path, path_value) in paths {
if let Ok(instance) = self.resolve_path(path) {
let cid = GlobalId { instance, promoted: None };
let cid = GlobalId {
instance,
promoted: None,
};
// compute global if not cached
let val = match self.globals.get(&cid).cloned() {
Some(ptr) => self.value_to_primval(Value::ByRef(ptr), c_int)?.to_u64()?,
@ -343,7 +399,9 @@ fn call_c_abi(
if let Some(result) = result {
self.write_primval(dest, result, dest_ty)?;
} else {
return err!(Unimplemented(format!("Unimplemented sysconf name: {}", name)));
return err!(Unimplemented(
format!("Unimplemented sysconf name: {}", name),
));
}
}
@ -373,7 +431,11 @@ fn call_c_abi(
return err!(OutOfTls);
}
// TODO: Does this need checking for alignment?
self.memory.write_uint(key_ptr.to_ptr()?, key, key_size.bytes())?;
self.memory.write_uint(
key_ptr.to_ptr()?,
key,
key_size.bytes(),
)?;
// Return success (0)
self.write_null(dest, dest_ty)?;
@ -396,7 +458,7 @@ fn call_c_abi(
let key = self.value_to_primval(args[0], usize)?.to_u64()? as TlsKey;
let new_ptr = args[1].into_ptr(&mut self.memory)?;
self.memory.store_tls(key, new_ptr)?;
// Return success (0)
self.write_null(dest, dest_ty)?;
}
@ -405,10 +467,12 @@ fn call_c_abi(
link_name if link_name.starts_with("pthread_") => {
warn!("ignoring C ABI call: {}", link_name);
self.write_null(dest, dest_ty)?;
},
}
_ => {
return err!(Unimplemented(format!("can't call C ABI function: {}", link_name)));
return err!(Unimplemented(
format!("can't call C ABI function: {}", link_name),
));
}
}
@ -425,7 +489,8 @@ fn resolve_path(&self, path: &[&str]) -> EvalResult<'tcx, ty::Instance<'tcx>> {
let cstore = &self.tcx.sess.cstore;
let crates = cstore.crates();
crates.iter()
crates
.iter()
.find(|&&krate| cstore.crate_name(krate) == path[0])
.and_then(|krate| {
let krate = DefId {
@ -450,9 +515,7 @@ fn resolve_path(&self, path: &[&str]) -> EvalResult<'tcx, ty::Instance<'tcx>> {
None
})
.ok_or_else(|| {
let path = path.iter()
.map(|&s| s.to_owned())
.collect();
let path = path.iter().map(|&s| s.to_owned()).collect();
EvalErrorKind::PathNotFound(path).into()
})
}
@ -469,27 +532,36 @@ fn call_missing_fn(
match &path[..] {
"std::panicking::rust_panic_with_hook" |
"std::rt::begin_panic_fmt" => return err!(Panic),
_ => {},
_ => {}
}
let dest_ty = sig.output();
let (dest, dest_block) = destination.ok_or_else(|| EvalErrorKind::NoMirFor(path.clone()))?;
let (dest, dest_block) = destination.ok_or_else(
|| EvalErrorKind::NoMirFor(path.clone()),
)?;
if sig.abi == Abi::C {
// An external C function
// TODO: That functions actually has a similar preamble to what follows here. May make sense to
// unify these two mechanisms for "hooking into missing functions".
self.call_c_abi(instance.def_id(), arg_operands, dest, dest_ty, dest_block)?;
self.call_c_abi(
instance.def_id(),
arg_operands,
dest,
dest_ty,
dest_block,
)?;
return Ok(());
}
let args_res: EvalResult<Vec<Value>> = arg_operands.iter()
let args_res: EvalResult<Vec<Value>> = arg_operands
.iter()
.map(|arg| self.eval_operand(arg))
.collect();
let args = args_res?;
let usize = self.tcx.types.usize;
match &path[..] {
// Allocators are magic. They have no MIR, even when the rest of libstd does.
"alloc::heap::::__rust_alloc" => {
@ -527,7 +599,11 @@ fn call_missing_fn(
if !align.is_power_of_two() {
return err!(HeapAllocNonPowerOfTwoAlignment(align));
}
self.memory.deallocate(ptr, Some((old_size, align)), MemoryKind::Rust.into())?;
self.memory.deallocate(
ptr,
Some((old_size, align)),
MemoryKind::Rust.into(),
)?;
}
"alloc::heap::::__rust_realloc" => {
let ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?;
@ -544,17 +620,32 @@ fn call_missing_fn(
if !new_align.is_power_of_two() {
return err!(HeapAllocNonPowerOfTwoAlignment(new_align));
}
let new_ptr = self.memory.reallocate(ptr, old_size, old_align, new_size, new_align, MemoryKind::Rust.into())?;
let new_ptr = self.memory.reallocate(
ptr,
old_size,
old_align,
new_size,
new_align,
MemoryKind::Rust.into(),
)?;
self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?;
}
// A Rust function is missing, which means we are running with MIR missing for libstd (or other dependencies).
// Still, we can make many things mostly work by "emulating" or ignoring some functions.
"std::io::_print" => {
trace!("Ignoring output. To run programs that print, make sure you have a libstd with full MIR.");
trace!(
"Ignoring output. To run programs that print, make sure you have a libstd with full MIR."
);
}
"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::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

View File

@ -1,9 +1,4 @@
use rustc_miri::interpret::{
Pointer,
EvalResult,
PrimVal,
EvalContext,
};
use rustc_miri::interpret::{Pointer, EvalResult, PrimVal, EvalContext};
use rustc::ty::Ty;
@ -31,7 +26,9 @@ fn wrapping_pointer_offset(
offset: i64,
) -> EvalResult<'tcx, Pointer> {
// 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;
let pointee_size = self.type_size(pointee_ty)?.expect(
"cannot offset a pointer to an unsized type",
) as i64;
let offset = offset.overflowing_mul(pointee_size).0;
ptr.wrapping_signed_offset(offset, self)
}
@ -47,11 +44,18 @@ fn pointer_offset(
// We also consider the NULL pointer its own separate allocation, and all the remaining integers pointers their own
// allocation.
if ptr.is_null()? { // NULL pointers must only be offset by 0
return if offset == 0 { Ok(ptr) } else { err!(InvalidNullPointerUsage) };
if ptr.is_null()? {
// NULL pointers must only be offset by 0
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;
let pointee_size = self.type_size(pointee_ty)?.expect(
"cannot offset a pointer to an unsized type",
) as i64;
return if let Some(offset) = offset.checked_mul(pointee_size) {
let ptr = ptr.signed_offset(offset, self)?;
// Do not do bounds-checking for integers; they can never alias a normal pointer anyway.
@ -64,6 +68,6 @@ fn pointer_offset(
Ok(ptr)
} else {
err!(OverflowingMath)
}
};
}
}

View File

@ -3,13 +3,8 @@
use rustc::ty::layout::Layout;
use rustc::ty::{self, Ty};
use rustc_miri::interpret::{
EvalResult,
Lvalue, LvalueExtra,
PrimVal, PrimValKind, Value, Pointer,
HasMemory,
EvalContext, PtrAndAlign,
};
use rustc_miri::interpret::{EvalResult, Lvalue, LvalueExtra, PrimVal, PrimValKind, Value, Pointer,
HasMemory, EvalContext, PtrAndAlign};
use helpers::EvalContextExt as HelperEvalContextExt;
@ -35,9 +30,8 @@ fn call_intrinsic(
dest_layout: &'tcx Layout,
target: mir::BasicBlock,
) -> EvalResult<'tcx> {
let arg_vals: EvalResult<Vec<Value>> = args.iter()
.map(|arg| self.eval_operand(arg))
.collect();
let arg_vals: EvalResult<Vec<Value>> =
args.iter().map(|arg| self.eval_operand(arg)).collect();
let arg_vals = arg_vals?;
let i32 = self.tcx.types.i32;
let isize = self.tcx.types.isize;
@ -48,15 +42,35 @@ fn call_intrinsic(
let intrinsic_name = &self.tcx.item_name(instance.def_id()).as_str()[..];
match intrinsic_name {
"add_with_overflow" =>
self.intrinsic_with_overflow(mir::BinOp::Add, &args[0], &args[1], dest, dest_ty)?,
"add_with_overflow" => {
self.intrinsic_with_overflow(
mir::BinOp::Add,
&args[0],
&args[1],
dest,
dest_ty,
)?
}
"sub_with_overflow" =>
self.intrinsic_with_overflow(mir::BinOp::Sub, &args[0], &args[1], dest, dest_ty)?,
"mul_with_overflow" =>
self.intrinsic_with_overflow(mir::BinOp::Mul, &args[0], &args[1], dest, dest_ty)?,
"sub_with_overflow" => {
self.intrinsic_with_overflow(
mir::BinOp::Sub,
&args[0],
&args[1],
dest,
dest_ty,
)?
}
"mul_with_overflow" => {
self.intrinsic_with_overflow(
mir::BinOp::Mul,
&args[0],
&args[1],
dest,
dest_ty,
)?
}
"arith_offset" => {
let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64;
@ -68,7 +82,9 @@ fn call_intrinsic(
"assume" => {
let bool = self.tcx.types.bool;
let cond = self.value_to_primval(arg_vals[0], bool)?.to_bool()?;
if !cond { return err!(AssumptionNotHeld); }
if !cond {
return err!(AssumptionNotHeld);
}
}
"atomic_load" |
@ -104,7 +120,11 @@ fn call_intrinsic(
Value::ByValPair(..) => bug!("atomic_xchg doesn't work with nonprimitives"),
};
self.write_primval(dest, old, ty)?;
self.write_primval(Lvalue::from_primval_ptr(ptr), change, ty)?;
self.write_primval(
Lvalue::from_primval_ptr(ptr),
change,
ty,
)?;
}
_ if intrinsic_name.starts_with("atomic_cxchg") => {
@ -121,14 +141,38 @@ fn call_intrinsic(
let (val, _) = self.binary_op(mir::BinOp::Eq, old, ty, expect_old, ty)?;
let dest = self.force_allocation(dest)?.to_ptr()?;
self.write_pair_to_ptr(old, val, dest, dest_ty)?;
self.write_primval(Lvalue::from_primval_ptr(ptr), change, ty)?;
self.write_primval(
Lvalue::from_primval_ptr(ptr),
change,
ty,
)?;
}
"atomic_or" | "atomic_or_acq" | "atomic_or_rel" | "atomic_or_acqrel" | "atomic_or_relaxed" |
"atomic_xor" | "atomic_xor_acq" | "atomic_xor_rel" | "atomic_xor_acqrel" | "atomic_xor_relaxed" |
"atomic_and" | "atomic_and_acq" | "atomic_and_rel" | "atomic_and_acqrel" | "atomic_and_relaxed" |
"atomic_xadd" | "atomic_xadd_acq" | "atomic_xadd_rel" | "atomic_xadd_acqrel" | "atomic_xadd_relaxed" |
"atomic_xsub" | "atomic_xsub_acq" | "atomic_xsub_rel" | "atomic_xsub_acqrel" | "atomic_xsub_relaxed" => {
"atomic_or" |
"atomic_or_acq" |
"atomic_or_rel" |
"atomic_or_acqrel" |
"atomic_or_relaxed" |
"atomic_xor" |
"atomic_xor_acq" |
"atomic_xor_rel" |
"atomic_xor_acqrel" |
"atomic_xor_relaxed" |
"atomic_and" |
"atomic_and_acq" |
"atomic_and_rel" |
"atomic_and_acqrel" |
"atomic_and_relaxed" |
"atomic_xadd" |
"atomic_xadd_acq" |
"atomic_xadd_rel" |
"atomic_xadd_acqrel" |
"atomic_xadd_relaxed" |
"atomic_xsub" |
"atomic_xsub_acq" |
"atomic_xsub_rel" |
"atomic_xsub_acqrel" |
"atomic_xsub_relaxed" => {
let ty = substs.type_at(0);
let ptr = arg_vals[0].into_ptr(&self.memory)?;
let change = self.value_to_primval(arg_vals[1], ty)?;
@ -136,7 +180,9 @@ fn call_intrinsic(
let old = match old {
Value::ByVal(val) => val,
Value::ByRef { .. } => bug!("just read the value, can't be byref"),
Value::ByValPair(..) => bug!("atomic_xadd_relaxed doesn't work with nonprimitives"),
Value::ByValPair(..) => {
bug!("atomic_xadd_relaxed doesn't work with nonprimitives")
}
};
self.write_primval(dest, old, ty)?;
let op = match intrinsic_name.split('_').nth(1).unwrap() {
@ -150,7 +196,7 @@ fn call_intrinsic(
// FIXME: what do atomics do on overflow?
let (val, _) = self.binary_op(op, old, ty, change, ty)?;
self.write_primval(Lvalue::from_primval_ptr(ptr), val, ty)?;
},
}
"breakpoint" => unimplemented!(), // halt miri
@ -165,22 +211,23 @@ fn call_intrinsic(
let elem_align = self.type_align(elem_ty)?;
let src = arg_vals[0].into_ptr(&self.memory)?;
let dest = arg_vals[1].into_ptr(&self.memory)?;
self.memory.copy(src, dest, count * elem_size, elem_align, intrinsic_name.ends_with("_nonoverlapping"))?;
self.memory.copy(
src,
dest,
count * elem_size,
elem_align,
intrinsic_name.ends_with("_nonoverlapping"),
)?;
}
}
"ctpop" |
"cttz" |
"cttz_nonzero" |
"ctlz" |
"ctlz_nonzero" |
"bswap" => {
"ctpop" | "cttz" | "cttz_nonzero" | "ctlz" | "ctlz_nonzero" | "bswap" => {
let ty = substs.type_at(0);
let num = self.value_to_primval(arg_vals[0], ty)?.to_bytes()?;
let kind = self.ty_to_primval_kind(ty)?;
let num = if intrinsic_name.ends_with("_nonzero") {
if num == 0 {
return err!(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 {
@ -196,10 +243,8 @@ fn call_intrinsic(
self.write_primval(dest, PrimVal::Bytes(discr_val), dest_ty)?;
}
"sinf32" | "fabsf32" | "cosf32" |
"sqrtf32" | "expf32" | "exp2f32" |
"logf32" | "log10f32" | "log2f32" |
"floorf32" | "ceilf32" | "truncf32" => {
"sinf32" | "fabsf32" | "cosf32" | "sqrtf32" | "expf32" | "exp2f32" | "logf32" |
"log10f32" | "log2f32" | "floorf32" | "ceilf32" | "truncf32" => {
let f = self.value_to_primval(arg_vals[0], f32)?.to_f32()?;
let f = match intrinsic_name {
"sinf32" => f.sin(),
@ -219,10 +264,8 @@ fn call_intrinsic(
self.write_primval(dest, PrimVal::from_f32(f), dest_ty)?;
}
"sinf64" | "fabsf64" | "cosf64" |
"sqrtf64" | "expf64" | "exp2f64" |
"logf64" | "log10f64" | "log2f64" |
"floorf64" | "ceilf64" | "truncf64" => {
"sinf64" | "fabsf64" | "cosf64" | "sqrtf64" | "expf64" | "exp2f64" | "logf64" |
"log10f64" | "log2f64" | "floorf64" | "ceilf64" | "truncf64" => {
let f = self.value_to_primval(arg_vals[0], f64)?.to_f64()?;
let f = match intrinsic_name {
"sinf64" => f.sin(),
@ -258,9 +301,7 @@ fn call_intrinsic(
self.write_primval(dest, result.0, dest_ty)?;
}
"likely" |
"unlikely" |
"forget" => {}
"likely" | "unlikely" | "forget" => {}
"init" => {
let size = self.type_size(dest_ty)?.expect("cannot zero unsized value");
@ -270,27 +311,35 @@ fn call_intrinsic(
// These writes have no alignment restriction anyway.
this.memory.write_repeat(ptr, 0, size)?;
val
},
}
// TODO(solson): Revisit this, it's fishy to check for Undef here.
Value::ByVal(PrimVal::Undef) => match this.ty_to_primval_kind(dest_ty) {
Ok(_) => Value::ByVal(PrimVal::Bytes(0)),
Err(_) => {
let ptr = this.alloc_ptr_with_substs(dest_ty, substs)?;
let ptr = Pointer::from(PrimVal::Ptr(ptr));
this.memory.write_repeat(ptr, 0, size)?;
Value::by_ref(ptr)
Value::ByVal(PrimVal::Undef) => {
match this.ty_to_primval_kind(dest_ty) {
Ok(_) => Value::ByVal(PrimVal::Bytes(0)),
Err(_) => {
let ptr = this.alloc_ptr_with_substs(dest_ty, substs)?;
let ptr = Pointer::from(PrimVal::Ptr(ptr));
this.memory.write_repeat(ptr, 0, size)?;
Value::by_ref(ptr)
}
}
},
}
Value::ByVal(_) => Value::ByVal(PrimVal::Bytes(0)),
Value::ByValPair(..) =>
Value::ByValPair(PrimVal::Bytes(0), PrimVal::Bytes(0)),
Value::ByValPair(..) => {
Value::ByValPair(PrimVal::Bytes(0), PrimVal::Bytes(0))
}
};
Ok(zero_val)
};
match dest {
Lvalue::Local { frame, local } => self.modify_local(frame, local, init)?,
Lvalue::Ptr { ptr: PtrAndAlign { ptr, aligned: true }, extra: LvalueExtra::None } => self.memory.write_repeat(ptr, 0, size)?,
Lvalue::Ptr { .. } => bug!("init intrinsic tried to write to fat or unaligned ptr target"),
Lvalue::Ptr {
ptr: PtrAndAlign { ptr, aligned: true },
extra: LvalueExtra::None,
} => self.memory.write_repeat(ptr, 0, size)?,
Lvalue::Ptr { .. } => {
bug!("init intrinsic tried to write to fat or unaligned ptr target")
}
}
}
@ -319,7 +368,11 @@ fn call_intrinsic(
let ty = substs.type_at(0);
let env = ty::ParamEnv::empty(Reveal::All);
let needs_drop = ty.needs_drop(self.tcx, env);
self.write_primval(dest, PrimVal::from_bool(needs_drop), dest_ty)?;
self.write_primval(
dest,
PrimVal::from_bool(needs_drop),
dest_ty,
)?;
}
"offset" => {
@ -330,72 +383,124 @@ fn call_intrinsic(
}
"overflowing_sub" => {
self.intrinsic_overflowing(mir::BinOp::Sub, &args[0], &args[1], dest, dest_ty)?;
self.intrinsic_overflowing(
mir::BinOp::Sub,
&args[0],
&args[1],
dest,
dest_ty,
)?;
}
"overflowing_mul" => {
self.intrinsic_overflowing(mir::BinOp::Mul, &args[0], &args[1], dest, dest_ty)?;
self.intrinsic_overflowing(
mir::BinOp::Mul,
&args[0],
&args[1],
dest,
dest_ty,
)?;
}
"overflowing_add" => {
self.intrinsic_overflowing(mir::BinOp::Add, &args[0], &args[1], dest, dest_ty)?;
self.intrinsic_overflowing(
mir::BinOp::Add,
&args[0],
&args[1],
dest,
dest_ty,
)?;
}
"powf32" => {
let f = self.value_to_primval(arg_vals[0], f32)?.to_f32()?;
let f2 = self.value_to_primval(arg_vals[1], f32)?.to_f32()?;
self.write_primval(dest, PrimVal::from_f32(f.powf(f2)), dest_ty)?;
self.write_primval(
dest,
PrimVal::from_f32(f.powf(f2)),
dest_ty,
)?;
}
"powf64" => {
let f = self.value_to_primval(arg_vals[0], f64)?.to_f64()?;
let f2 = self.value_to_primval(arg_vals[1], f64)?.to_f64()?;
self.write_primval(dest, PrimVal::from_f64(f.powf(f2)), dest_ty)?;
self.write_primval(
dest,
PrimVal::from_f64(f.powf(f2)),
dest_ty,
)?;
}
"fmaf32" => {
let a = self.value_to_primval(arg_vals[0], f32)?.to_f32()?;
let b = self.value_to_primval(arg_vals[1], f32)?.to_f32()?;
let c = self.value_to_primval(arg_vals[2], f32)?.to_f32()?;
self.write_primval(dest, PrimVal::from_f32(a * b + c), dest_ty)?;
self.write_primval(
dest,
PrimVal::from_f32(a * b + c),
dest_ty,
)?;
}
"fmaf64" => {
let a = self.value_to_primval(arg_vals[0], f64)?.to_f64()?;
let b = self.value_to_primval(arg_vals[1], f64)?.to_f64()?;
let c = self.value_to_primval(arg_vals[2], f64)?.to_f64()?;
self.write_primval(dest, PrimVal::from_f64(a * b + c), dest_ty)?;
self.write_primval(
dest,
PrimVal::from_f64(a * b + c),
dest_ty,
)?;
}
"powif32" => {
let f = self.value_to_primval(arg_vals[0], f32)?.to_f32()?;
let i = self.value_to_primval(arg_vals[1], i32)?.to_i128()?;
self.write_primval(dest, PrimVal::from_f32(f.powi(i as i32)), dest_ty)?;
self.write_primval(
dest,
PrimVal::from_f32(f.powi(i as i32)),
dest_ty,
)?;
}
"powif64" => {
let f = self.value_to_primval(arg_vals[0], f64)?.to_f64()?;
let i = self.value_to_primval(arg_vals[1], i32)?.to_i128()?;
self.write_primval(dest, PrimVal::from_f64(f.powi(i as i32)), dest_ty)?;
self.write_primval(
dest,
PrimVal::from_f64(f.powi(i as i32)),
dest_ty,
)?;
}
"size_of" => {
let ty = substs.type_at(0);
let size = self.type_size(ty)?.expect("size_of intrinsic called on unsized value") as u128;
let size = self.type_size(ty)?.expect(
"size_of intrinsic called on unsized value",
) as u128;
self.write_primval(dest, PrimVal::from_u128(size), dest_ty)?;
}
"size_of_val" => {
let ty = substs.type_at(0);
let (size, _) = self.size_and_align_of_dst(ty, arg_vals[0])?;
self.write_primval(dest, PrimVal::from_u128(size as u128), dest_ty)?;
self.write_primval(
dest,
PrimVal::from_u128(size as u128),
dest_ty,
)?;
}
"min_align_of_val" |
"align_of_val" => {
let ty = substs.type_at(0);
let (_, align) = self.size_and_align_of_dst(ty, arg_vals[0])?;
self.write_primval(dest, PrimVal::from_u128(align as u128), dest_ty)?;
self.write_primval(
dest,
PrimVal::from_u128(align as u128),
dest_ty,
)?;
}
"type_name" => {
@ -413,61 +518,103 @@ fn call_intrinsic(
"transmute" => {
let src_ty = substs.type_at(0);
let ptr = self.force_allocation(dest)?.to_ptr()?;
self.write_maybe_aligned_mut(/*aligned*/false, |ectx| {
ectx.write_value_to_ptr(arg_vals[0], ptr.into(), src_ty)
})?;
self.write_maybe_aligned_mut(
/*aligned*/
false,
|ectx| {
ectx.write_value_to_ptr(arg_vals[0], ptr.into(), src_ty)
},
)?;
}
"unchecked_shl" => {
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()?;
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!(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)?;
self.intrinsic_overflowing(
mir::BinOp::Shl,
&args[0],
&args[1],
dest,
dest_ty,
)?;
}
"unchecked_shr" => {
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()?;
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!(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)?;
self.intrinsic_overflowing(
mir::BinOp::Shr,
&args[0],
&args[1],
dest,
dest_ty,
)?;
}
"unchecked_div" => {
let rhs = self.value_to_primval(arg_vals[1], substs.type_at(0))?.to_bytes()?;
let rhs = self.value_to_primval(arg_vals[1], substs.type_at(0))?
.to_bytes()?;
if rhs == 0 {
return err!(Intrinsic(format!("Division by 0 in unchecked_div")));
}
self.intrinsic_overflowing(mir::BinOp::Div, &args[0], &args[1], dest, dest_ty)?;
self.intrinsic_overflowing(
mir::BinOp::Div,
&args[0],
&args[1],
dest,
dest_ty,
)?;
}
"unchecked_rem" => {
let rhs = self.value_to_primval(arg_vals[1], substs.type_at(0))?.to_bytes()?;
let rhs = self.value_to_primval(arg_vals[1], substs.type_at(0))?
.to_bytes()?;
if rhs == 0 {
return err!(Intrinsic(format!("Division by 0 in unchecked_rem")));
}
self.intrinsic_overflowing(mir::BinOp::Rem, &args[0], &args[1], dest, dest_ty)?;
self.intrinsic_overflowing(
mir::BinOp::Rem,
&args[0],
&args[1],
dest,
dest_ty,
)?;
}
"uninit" => {
let size = dest_layout.size(&self.tcx.data_layout).bytes();
let uninit = |this: &mut Self, val: Value| {
match val {
Value::ByRef(PtrAndAlign { ptr, .. }) => {
this.memory.mark_definedness(ptr, size, false)?;
Ok(val)
},
_ => Ok(Value::ByVal(PrimVal::Undef)),
let uninit = |this: &mut Self, val: Value| match val {
Value::ByRef(PtrAndAlign { ptr, .. }) => {
this.memory.mark_definedness(ptr, size, false)?;
Ok(val)
}
_ => Ok(Value::ByVal(PrimVal::Undef)),
};
match dest {
Lvalue::Local { frame, local } => self.modify_local(frame, local, uninit)?,
Lvalue::Ptr { ptr: PtrAndAlign { ptr, aligned: true }, extra: LvalueExtra::None } =>
self.memory.mark_definedness(ptr, size, false)?,
Lvalue::Ptr { .. } => bug!("uninit intrinsic tried to write to fat or unaligned ptr target"),
Lvalue::Ptr {
ptr: PtrAndAlign { ptr, aligned: true },
extra: LvalueExtra::None,
} => self.memory.mark_definedness(ptr, size, false)?,
Lvalue::Ptr { .. } => {
bug!("uninit intrinsic tried to write to fat or unaligned ptr target")
}
}
}
@ -476,7 +623,9 @@ fn call_intrinsic(
let ty = substs.type_at(0);
let ty_align = self.type_align(ty)?;
let val_byte = self.value_to_primval(arg_vals[1], u8)?.to_u128()? as u8;
let size = self.type_size(ty)?.expect("write_bytes() type must be sized");
let size = self.type_size(ty)?.expect(
"write_bytes() type must be sized",
);
let ptr = arg_vals[0].into_ptr(&self.memory)?;
let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?;
if count > 0 {
@ -502,7 +651,7 @@ fn call_intrinsic(
fn numeric_intrinsic<'tcx>(
name: &str,
bytes: u128,
kind: PrimValKind
kind: PrimValKind,
) -> EvalResult<'tcx, PrimVal> {
macro_rules! integer_intrinsic {
($method:ident) => ({
@ -527,10 +676,10 @@ macro_rules! integer_intrinsic {
let result_val = match name {
"bswap" => integer_intrinsic!(swap_bytes),
"ctlz" => integer_intrinsic!(leading_zeros),
"ctlz" => integer_intrinsic!(leading_zeros),
"ctpop" => integer_intrinsic!(count_ones),
"cttz" => integer_intrinsic!(trailing_zeros),
_ => bug!("not a numeric intrinsic: {}", name),
"cttz" => integer_intrinsic!(trailing_zeros),
_ => bug!("not a numeric intrinsic: {}", name),
};
Ok(result_val)

View File

@ -20,10 +20,7 @@
use syntax::codemap::Span;
use std::collections::{
HashMap,
BTreeMap,
};
use std::collections::{HashMap, BTreeMap};
#[macro_use]
extern crate rustc_miri;
@ -57,7 +54,10 @@ fn run_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!(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 {
@ -65,7 +65,10 @@ fn run_main<'a, 'tcx: 'a>(
let start_mir = ecx.load_mir(start_instance.def)?;
if start_mir.arg_count != 3 {
return err!(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
@ -90,7 +93,11 @@ fn run_main<'a, 'tcx: 'a>(
let dest = ecx.eval_lvalue(&mir::Lvalue::Local(args.next().unwrap()))?;
let main_ty = main_instance.def.def_ty(ecx.tcx);
let main_ptr_ty = ecx.tcx.mk_fn_ptr(main_ty.fn_sig(ecx.tcx));
ecx.write_value(Value::ByVal(PrimVal::Ptr(main_ptr)), dest, main_ptr_ty)?;
ecx.write_value(
Value::ByVal(PrimVal::Ptr(main_ptr)),
dest,
main_ptr_ty,
)?;
// Second argument (argc): 0
let dest = ecx.eval_lvalue(&mir::Lvalue::Local(args.next().unwrap()))?;
@ -114,7 +121,11 @@ fn run_main<'a, 'tcx: 'a>(
while ecx.step()? {}
ecx.run_tls_dtors()?;
if let Some(cleanup_ptr) = cleanup_ptr {
ecx.memory_mut().deallocate(cleanup_ptr, None, MemoryKind::Stack)?;
ecx.memory_mut().deallocate(
cleanup_ptr,
None,
MemoryKind::Stack,
)?;
}
Ok(())
}

View File

@ -37,20 +37,28 @@ fn ptr_op(
use rustc::mir::BinOp::*;
let usize = PrimValKind::from_uint_size(self.memory.pointer_size());
let isize = PrimValKind::from_int_size(self.memory.pointer_size());
let left_kind = self.ty_to_primval_kind(left_ty)?;
let left_kind = self.ty_to_primval_kind(left_ty)?;
let right_kind = self.ty_to_primval_kind(right_ty)?;
match bin_op {
Offset if left_kind == Ptr && right_kind == usize => {
let pointee_ty = left_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty;
let ptr = self.pointer_offset(left.into(), pointee_ty, right.to_bytes()? as i64)?;
let pointee_ty = left_ty
.builtin_deref(true, ty::LvaluePreference::NoPreference)
.expect("Offset called on non-ptr type")
.ty;
let ptr = self.pointer_offset(
left.into(),
pointee_ty,
right.to_bytes()? as i64,
)?;
Ok(Some((ptr.into_inner_primval(), false)))
},
}
// These work on anything
Eq if left_kind == right_kind => {
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!(ReadUndefBytes),
(PrimVal::Undef, _) |
(_, PrimVal::Undef) => return err!(ReadUndefBytes),
_ => false,
};
Ok(Some((PrimVal::from_bool(result), false)))
@ -59,16 +67,17 @@ fn ptr_op(
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!(ReadUndefBytes),
(PrimVal::Undef, _) |
(_, PrimVal::Undef) => return err!(ReadUndefBytes),
_ => true,
};
Ok(Some((PrimVal::from_bool(result), false)))
}
// These need both pointers to be in the same allocation
Lt | Le | Gt | Ge | Sub
if left_kind == right_kind
&& (left_kind == Ptr || left_kind == usize || left_kind == isize)
&& left.is_ptr() && right.is_ptr() => {
if left_kind == right_kind &&
(left_kind == Ptr || left_kind == usize || left_kind == isize) &&
left.is_ptr() && right.is_ptr() => {
let left = left.to_ptr()?;
let right = right.to_ptr()?;
if left.alloc_id == right.alloc_id {
@ -77,13 +86,15 @@ fn ptr_op(
Le => left.offset <= right.offset,
Gt => left.offset > right.offset,
Ge => left.offset >= right.offset,
Sub => return self.binary_op(
Sub,
PrimVal::Bytes(left.offset as u128),
self.tcx.types.usize,
PrimVal::Bytes(right.offset as u128),
self.tcx.types.usize,
).map(Some),
Sub => {
return self.binary_op(
Sub,
PrimVal::Bytes(left.offset as u128),
self.tcx.types.usize,
PrimVal::Bytes(right.offset as u128),
self.tcx.types.usize,
).map(Some)
}
_ => bug!("We already established it has to be one of these operators."),
};
Ok(Some((PrimVal::from_bool(res), false)))
@ -94,18 +105,28 @@ fn ptr_op(
}
// These work if one operand is a pointer, the other an integer
Add | BitAnd | Sub
if left_kind == right_kind && (left_kind == usize || left_kind == isize)
&& left.is_ptr() && right.is_bytes() => {
if left_kind == right_kind && (left_kind == usize || left_kind == isize) &&
left.is_ptr() && right.is_bytes() => {
// Cast to i128 is fine as we checked the kind to be ptr-sized
self.ptr_int_arithmetic(bin_op, left.to_ptr()?, right.to_bytes()? as i128, left_kind == isize).map(Some)
self.ptr_int_arithmetic(
bin_op,
left.to_ptr()?,
right.to_bytes()? as i128,
left_kind == isize,
).map(Some)
}
Add | BitAnd
if left_kind == right_kind && (left_kind == usize || left_kind == isize)
&& left.is_bytes() && right.is_ptr() => {
if left_kind == right_kind && (left_kind == usize || left_kind == isize) &&
left.is_bytes() && right.is_ptr() => {
// This is a commutative operation, just swap the operands
self.ptr_int_arithmetic(bin_op, right.to_ptr()?, left.to_bytes()? as i128, left_kind == isize).map(Some)
self.ptr_int_arithmetic(
bin_op,
right.to_ptr()?,
left.to_bytes()? as i128,
left_kind == isize,
).map(Some)
}
_ => Ok(None)
_ => Ok(None),
}
}
@ -118,7 +139,7 @@ fn ptr_int_arithmetic(
) -> EvalResult<'tcx, (PrimVal, bool)> {
use rustc::mir::BinOp::*;
fn map_to_primval((res, over) : (MemoryPointer, bool)) -> (PrimVal, bool) {
fn map_to_primval((res, over): (MemoryPointer, bool)) -> (PrimVal, bool) {
(PrimVal::Ptr(res), over)
}

View File

@ -1,21 +1,17 @@
use rustc::{ty, mir};
use super::{
TlsKey, TlsEntry,
EvalResult, EvalErrorKind,
Pointer,
Memory,
Evaluator,
Lvalue,
StackPopCleanup, EvalContext,
};
use super::{TlsKey, TlsEntry, EvalResult, EvalErrorKind, Pointer, Memory, Evaluator, Lvalue,
StackPopCleanup, EvalContext};
pub trait MemoryExt<'tcx> {
fn create_tls_key(&mut self, dtor: Option<ty::Instance<'tcx>>) -> TlsKey;
fn delete_tls_key(&mut self, key: TlsKey) -> EvalResult<'tcx>;
fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, Pointer>;
fn store_tls(&mut self, key: TlsKey, new_data: Pointer) -> EvalResult<'tcx>;
fn fetch_tls_dtor(&mut self, key: Option<TlsKey>) -> EvalResult<'tcx, Option<(ty::Instance<'tcx>, Pointer, TlsKey)>>;
fn fetch_tls_dtor(
&mut self,
key: Option<TlsKey>,
) -> EvalResult<'tcx, Option<(ty::Instance<'tcx>, Pointer, TlsKey)>>;
}
pub trait EvalContextExt<'tcx> {
@ -26,7 +22,13 @@ impl<'a, 'tcx: 'a> MemoryExt<'tcx> for Memory<'a, 'tcx, Evaluator> {
fn create_tls_key(&mut self, dtor: Option<ty::Instance<'tcx>>) -> TlsKey {
let new_key = self.data.next_thread_local;
self.data.next_thread_local += 1;
self.data.thread_local.insert(new_key, TlsEntry { data: Pointer::null(), dtor });
self.data.thread_local.insert(
new_key,
TlsEntry {
data: Pointer::null(),
dtor,
},
);
trace!("New TLS key allocated: {} with dtor {:?}", new_key, dtor);
return new_key;
}
@ -36,9 +38,9 @@ fn delete_tls_key(&mut self, key: TlsKey) -> EvalResult<'tcx> {
Some(_) => {
trace!("TLS key {} removed", key);
Ok(())
},
None => err!(TlsOutOfBounds)
}
}
None => err!(TlsOutOfBounds),
};
}
fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, Pointer> {
@ -46,9 +48,9 @@ fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, Pointer> {
Some(&TlsEntry { data, .. }) => {
trace!("TLS key {} loaded: {:?}", key, data);
Ok(data)
},
None => err!(TlsOutOfBounds)
}
}
None => err!(TlsOutOfBounds),
};
}
fn store_tls(&mut self, key: TlsKey, new_data: Pointer) -> EvalResult<'tcx> {
@ -57,11 +59,11 @@ fn store_tls(&mut self, key: TlsKey, new_data: Pointer) -> EvalResult<'tcx> {
trace!("TLS key {} stored: {:?}", key, new_data);
*data = new_data;
Ok(())
},
None => err!(TlsOutOfBounds)
}
}
None => err!(TlsOutOfBounds),
};
}
/// Returns a dtor, its argument and its index, if one is supposed to run
///
/// An optional destructor function may be associated with each key value.
@ -80,13 +82,18 @@ fn store_tls(&mut self, key: TlsKey, new_data: Pointer) -> EvalResult<'tcx> {
/// with associated destructors, implementations may stop calling destructors,
/// or they may continue calling destructors until no non-NULL values with
/// associated destructors exist, even though this might result in an infinite loop.
fn fetch_tls_dtor(&mut self, key: Option<TlsKey>) -> EvalResult<'tcx, Option<(ty::Instance<'tcx>, Pointer, TlsKey)>> {
fn fetch_tls_dtor(
&mut self,
key: Option<TlsKey>,
) -> EvalResult<'tcx, Option<(ty::Instance<'tcx>, Pointer, TlsKey)>> {
use std::collections::Bound::*;
let start = match key {
Some(key) => Excluded(key),
None => Unbounded,
};
for (&key, &mut TlsEntry { ref mut data, dtor }) in self.data.thread_local.range_mut((start, Unbounded)) {
for (&key, &mut TlsEntry { ref mut data, dtor }) in
self.data.thread_local.range_mut((start, Unbounded))
{
if !data.is_null()? {
if let Some(dtor) = dtor {
let ret = Some((dtor, *data, key));
@ -115,7 +122,9 @@ fn run_tls_dtors(&mut self) -> EvalResult<'tcx> {
Lvalue::undef(),
StackPopCleanup::None,
)?;
let arg_local = self.frame().mir.args_iter().next().ok_or(EvalErrorKind::AbiViolation("TLS dtor does not take enough arguments.".to_owned()))?;
let arg_local = self.frame().mir.args_iter().next().ok_or(
EvalErrorKind::AbiViolation("TLS dtor does not take enough arguments.".to_owned()),
)?;
let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
let ty = self.tcx.mk_mut_ptr(self.tcx.types.u8);
self.write_ptr(dest, ptr, ty)?;

View File

@ -1,20 +1,14 @@
use rustc::ty::{self, Ty};
use syntax::ast::{FloatTy, IntTy, UintTy};
use super::{
PrimVal,
EvalContext,
EvalResult,
MemoryPointer, PointerArithmetic,
Machine,
};
use super::{PrimVal, EvalContext, EvalResult, MemoryPointer, PointerArithmetic, Machine};
impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
pub(super) fn cast_primval(
&self,
val: PrimVal,
src_ty: Ty<'tcx>,
dest_ty: Ty<'tcx>
dest_ty: Ty<'tcx>,
) -> EvalResult<'tcx, PrimVal> {
let src_kind = self.ty_to_primval_kind(src_ty)?;
@ -29,11 +23,11 @@ pub(super) fn cast_primval(
I8 | I16 | I32 | I64 | I128 => {
self.cast_from_signed_int(val.to_i128()?, dest_ty)
},
}
Bool | Char | U8 | U16 | U32 | U64 | U128 | FnPtr | Ptr => {
self.cast_from_int(val.to_u128()?, dest_ty, false)
},
}
}
}
}
@ -43,18 +37,22 @@ fn cast_from_signed_int(&self, val: i128, ty: ty::Ty<'tcx>) -> EvalResult<'tcx,
self.cast_from_int(val as u128, ty, val < 0)
}
fn cast_from_int(&self, v: u128, ty: ty::Ty<'tcx>, negative: bool) -> EvalResult<'tcx, PrimVal> {
fn cast_from_int(
&self,
v: u128,
ty: ty::Ty<'tcx>,
negative: bool,
) -> EvalResult<'tcx, PrimVal> {
use rustc::ty::TypeVariants::*;
match ty.sty {
// Casts to bool are not permitted by rustc, no need to handle them here.
TyInt(IntTy::I8) => Ok(PrimVal::Bytes(v as i128 as i8 as u128)),
TyInt(IntTy::I8) => Ok(PrimVal::Bytes(v as i128 as i8 as u128)),
TyInt(IntTy::I16) => Ok(PrimVal::Bytes(v as i128 as i16 as u128)),
TyInt(IntTy::I32) => Ok(PrimVal::Bytes(v as i128 as i32 as u128)),
TyInt(IntTy::I64) => Ok(PrimVal::Bytes(v as i128 as i64 as u128)),
TyInt(IntTy::I128) => Ok(PrimVal::Bytes(v as u128)),
TyUint(UintTy::U8) => Ok(PrimVal::Bytes(v as u8 as u128)),
TyUint(UintTy::U8) => Ok(PrimVal::Bytes(v as u8 as u128)),
TyUint(UintTy::U16) => Ok(PrimVal::Bytes(v as u16 as u128)),
TyUint(UintTy::U32) => Ok(PrimVal::Bytes(v as u32 as u128)),
TyUint(UintTy::U64) => Ok(PrimVal::Bytes(v as u64 as u128)),
@ -73,9 +71,9 @@ fn cast_from_int(&self, v: u128, ty: ty::Ty<'tcx>, negative: bool) -> EvalResult
}
TyFloat(FloatTy::F64) if negative => Ok(PrimVal::from_f64(v as i128 as f64)),
TyFloat(FloatTy::F64) => Ok(PrimVal::from_f64(v as f64)),
TyFloat(FloatTy::F64) => Ok(PrimVal::from_f64(v as f64)),
TyFloat(FloatTy::F32) if negative => Ok(PrimVal::from_f32(v as i128 as f32)),
TyFloat(FloatTy::F32) => Ok(PrimVal::from_f32(v as f32)),
TyFloat(FloatTy::F32) => Ok(PrimVal::from_f32(v as f32)),
TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)),
TyChar => err!(InvalidChar(v)),
@ -92,7 +90,7 @@ fn cast_from_float(&self, val: f64, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
match ty.sty {
// Casting negative floats to unsigned integers yields zero.
TyUint(_) if val < 0.0 => self.cast_from_int(0, ty, false),
TyInt(_) if val < 0.0 => self.cast_from_int(val as i128 as u128, ty, true),
TyInt(_) if val < 0.0 => self.cast_from_int(val as i128 as u128, ty, true),
TyInt(_) | ty::TyUint(_) => self.cast_from_int(val as u128, ty, false),
@ -106,8 +104,9 @@ fn cast_from_ptr(&self, ptr: MemoryPointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Pr
use rustc::ty::TypeVariants::*;
match ty.sty {
// 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)),
TyRawPtr(_) |
TyInt(IntTy::Is) |
TyUint(UintTy::Us) => Ok(PrimVal::Ptr(ptr)),
TyInt(_) | TyUint(_) => err!(ReadPointerAsBytes),
_ => err!(Unimplemented(format!("ptr to {:?} cast", ty))),
}

View File

@ -5,13 +5,8 @@
use syntax::ast::Mutability;
use syntax::codemap::Span;
use super::{
EvalResult, EvalError, EvalErrorKind,
GlobalId, Lvalue, Value,
PrimVal,
EvalContext, StackPopCleanup, PtrAndAlign,
MemoryKind,
};
use super::{EvalResult, EvalError, EvalErrorKind, GlobalId, Lvalue, Value, PrimVal, EvalContext,
StackPopCleanup, PtrAndAlign, MemoryKind};
use rustc_const_math::ConstInt;
@ -24,22 +19,37 @@ pub fn eval_body_as_primval<'a, 'tcx>(
) -> EvalResult<'tcx, (PrimVal, Ty<'tcx>)> {
let limits = super::ResourceLimits::default();
let mut ecx = EvalContext::<CompileTimeFunctionEvaluator>::new(tcx, limits, (), ());
let cid = GlobalId { instance, promoted: None };
let cid = GlobalId {
instance,
promoted: None,
};
if ecx.tcx.has_attr(instance.def_id(), "linkage") {
return Err(ConstEvalError::NotConst("extern global".to_string()).into());
}
let mir = ecx.load_mir(instance.def)?;
if !ecx.globals.contains_key(&cid) {
let size = ecx.type_size_with_substs(mir.return_ty, instance.substs)?.expect("unsized global");
let size = ecx.type_size_with_substs(mir.return_ty, instance.substs)?
.expect("unsized global");
let align = ecx.type_align_with_substs(mir.return_ty, instance.substs)?;
let ptr = ecx.memory.allocate(size, align, MemoryKind::UninitializedStatic)?;
let ptr = ecx.memory.allocate(
size,
align,
MemoryKind::UninitializedStatic,
)?;
let aligned = !ecx.is_packed(mir.return_ty)?;
ecx.globals.insert(cid, PtrAndAlign { ptr: ptr.into(), aligned });
ecx.globals.insert(
cid,
PtrAndAlign {
ptr: ptr.into(),
aligned,
},
);
let mutable = !mir.return_ty.is_freeze(
ecx.tcx,
ty::ParamEnv::empty(Reveal::All),
mir.span);
ecx.tcx,
ty::ParamEnv::empty(Reveal::All),
mir.span,
);
let mutability = if mutable {
Mutability::Mutable
} else {
@ -77,14 +87,26 @@ pub fn eval_body_as_integer<'a, 'tcx>(
TyInt(IntTy::I32) => ConstInt::I32(prim as i128 as i32),
TyInt(IntTy::I64) => ConstInt::I64(prim as i128 as i64),
TyInt(IntTy::I128) => ConstInt::I128(prim as i128),
TyInt(IntTy::Is) => ConstInt::Isize(ConstIsize::new(prim as i128 as i64, tcx.sess.target.int_type).expect("miri should already have errored")),
TyInt(IntTy::Is) => ConstInt::Isize(
ConstIsize::new(prim as i128 as i64, tcx.sess.target.int_type)
.expect("miri should already have errored"),
),
TyUint(UintTy::U8) => ConstInt::U8(prim as u8),
TyUint(UintTy::U16) => ConstInt::U16(prim as u16),
TyUint(UintTy::U32) => ConstInt::U32(prim as u32),
TyUint(UintTy::U64) => ConstInt::U64(prim as u64),
TyUint(UintTy::U128) => ConstInt::U128(prim),
TyUint(UintTy::Us) => ConstInt::Usize(ConstUsize::new(prim as u64, tcx.sess.target.uint_type).expect("miri should already have errored")),
_ => return Err(ConstEvalError::NeedsRfc("evaluating anything other than isize/usize during typeck".to_string()).into()),
TyUint(UintTy::Us) => ConstInt::Usize(
ConstUsize::new(prim as u64, tcx.sess.target.uint_type)
.expect("miri should already have errored"),
),
_ => {
return Err(
ConstEvalError::NeedsRfc(
"evaluating anything other than isize/usize during typeck".to_string(),
).into(),
)
}
})
}
@ -106,10 +128,14 @@ impl fmt::Display for ConstEvalError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::ConstEvalError::*;
match *self {
NeedsRfc(ref msg) =>
write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg),
NotConst(ref msg) =>
write!(f, "Cannot evaluate within constants: \"{}\"", msg),
NeedsRfc(ref msg) => {
write!(
f,
"\"{}\" needs an rfc before being allowed inside constants",
msg
)
}
NotConst(ref msg) => write!(f, "Cannot evaluate within constants: \"{}\"", msg),
}
}
}
@ -118,10 +144,8 @@ impl Error for ConstEvalError {
fn description(&self) -> &str {
use self::ConstEvalError::*;
match *self {
NeedsRfc(_) =>
"this feature needs an rfc before being allowed inside constants",
NotConst(_) =>
"this feature is not compatible with constant evaluation",
NeedsRfc(_) => "this feature needs an rfc before being allowed inside constants",
NotConst(_) => "this feature is not compatible with constant evaluation",
}
}
@ -143,14 +167,19 @@ fn eval_fn_call<'a>(
_sig: ty::FnSig<'tcx>,
) -> EvalResult<'tcx, bool> {
if !ecx.tcx.is_const_fn(instance.def_id()) {
return Err(ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into());
return Err(
ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(),
);
}
let mir = match ecx.load_mir(instance.def) {
Ok(mir) => mir,
Err(EvalError{ kind: EvalErrorKind::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());
},
return Err(
ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path))
.into(),
);
}
Err(other) => return Err(other),
};
let (return_lvalue, return_to_block) = match destination {
@ -178,7 +207,9 @@ fn call_intrinsic<'a>(
_dest_layout: &'tcx layout::Layout,
_target: mir::BasicBlock,
) -> EvalResult<'tcx> {
Err(ConstEvalError::NeedsRfc("calling intrinsics".to_string()).into())
Err(
ConstEvalError::NeedsRfc("calling intrinsics".to_string()).into(),
)
}
fn try_ptr_op<'a>(
@ -192,7 +223,9 @@ fn try_ptr_op<'a>(
if left.is_bytes() && right.is_bytes() {
Ok(None)
} else {
Err(ConstEvalError::NeedsRfc("Pointer arithmetic or comparison".to_string()).into())
Err(
ConstEvalError::NeedsRfc("Pointer arithmetic or comparison".to_string()).into(),
)
}
}
@ -204,6 +237,8 @@ fn box_alloc<'a>(
_ecx: &mut EvalContext<'a, 'tcx, Self>,
_ty: ty::Ty<'tcx>,
) -> EvalResult<'tcx, PrimVal> {
Err(ConstEvalError::NeedsRfc("Heap allocations via `box` keyword".to_string()).into())
Err(
ConstEvalError::NeedsRfc("Heap allocations via `box` keyword".to_string()).into(),
)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -3,14 +3,7 @@
use rustc::ty::{self, Ty};
use rustc_data_structures::indexed_vec::Idx;
use super::{
EvalResult,
EvalContext,
MemoryPointer,
PrimVal, Value, Pointer,
Machine,
PtrAndAlign,
};
use super::{EvalResult, EvalContext, MemoryPointer, PrimVal, Value, Pointer, Machine, PtrAndAlign};
#[derive(Copy, Clone, Debug)]
pub enum Lvalue {
@ -25,10 +18,7 @@ pub enum Lvalue {
/// An lvalue referring to a value on the stack. Represented by a stack frame index paired with
/// a Mir local index.
Local {
frame: usize,
local: mir::Local,
},
Local { frame: usize, local: mir::Local },
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
@ -57,7 +47,10 @@ pub fn undef() -> Self {
}
pub fn from_primval_ptr(ptr: Pointer) -> Self {
Lvalue::Ptr { ptr: PtrAndAlign { ptr, aligned: true }, extra: LvalueExtra::None }
Lvalue::Ptr {
ptr: PtrAndAlign { ptr, aligned: true },
extra: LvalueExtra::None,
}
}
pub fn from_ptr(ptr: MemoryPointer) -> Self {
@ -87,7 +80,12 @@ pub(super) fn elem_ty_and_len(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) {
ty::TySlice(elem) => {
match self {
Lvalue::Ptr { extra: LvalueExtra::Length(len), .. } => (elem, len),
_ => bug!("elem_ty_and_len of a TySlice given non-slice lvalue: {:?}", self),
_ => {
bug!(
"elem_ty_and_len of a TySlice given non-slice lvalue: {:?}",
self
)
}
}
}
@ -99,7 +97,10 @@ pub(super) fn elem_ty_and_len(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) {
impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
/// Reads a value from the lvalue without going through the intermediate step of obtaining
/// a `miri::Lvalue`
pub fn try_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Option<Value>> {
pub fn try_read_lvalue(
&mut self,
lvalue: &mir::Lvalue<'tcx>,
) -> EvalResult<'tcx, Option<Value>> {
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
@ -109,14 +110,22 @@ pub fn try_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx
// Directly reading a static will always succeed
Static(ref static_) => {
let instance = ty::Instance::mono(self.tcx, static_.def_id);
let cid = GlobalId { instance, promoted: None };
Ok(Some(Value::ByRef(*self.globals.get(&cid).expect("global not cached"))))
},
let cid = GlobalId {
instance,
promoted: None,
};
Ok(Some(Value::ByRef(
*self.globals.get(&cid).expect("global not cached"),
)))
}
Projection(ref proj) => self.try_read_lvalue_projection(proj),
}
}
fn try_read_lvalue_projection(&mut self, proj: &mir::LvalueProjection<'tcx>) -> EvalResult<'tcx, Option<Value>> {
fn try_read_lvalue_projection(
&mut self,
proj: &mir::LvalueProjection<'tcx>,
) -> EvalResult<'tcx, Option<Value>> {
use rustc::mir::ProjectionElem::*;
let base = match self.try_read_lvalue(&proj.base)? {
Some(base) => base,
@ -147,7 +156,10 @@ fn try_read_lvalue_projection(&mut self, proj: &mir::LvalueProjection<'tcx>) ->
}
/// Returns a value and (in case of a ByRef) if we are supposed to use aligned accesses.
pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> {
pub(super) fn eval_and_read_lvalue(
&mut self,
lvalue: &mir::Lvalue<'tcx>,
) -> EvalResult<'tcx, Value> {
// Shortcut for things like accessing a fat pointer's field,
// which would otherwise (in the `eval_lvalue` path) require moving a `ByValPair` to memory
// and returning an `Lvalue::Ptr` to it
@ -164,9 +176,7 @@ pub fn read_lvalue(&self, lvalue: Lvalue) -> EvalResult<'tcx, Value> {
assert_eq!(extra, LvalueExtra::None);
Ok(Value::ByRef(ptr))
}
Lvalue::Local { frame, local } => {
self.stack[frame].get_local(local)
}
Lvalue::Local { frame, local } => self.stack[frame].get_local(local),
}
}
@ -174,11 +184,17 @@ pub fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx
use rustc::mir::Lvalue::*;
let lvalue = match *mir_lvalue {
Local(mir::RETURN_POINTER) => self.frame().return_lvalue,
Local(local) => Lvalue::Local { frame: self.cur_frame(), local },
Local(local) => Lvalue::Local {
frame: self.cur_frame(),
local,
},
Static(ref static_) => {
let instance = ty::Instance::mono(self.tcx, static_.def_id);
let gid = GlobalId { instance, promoted: None };
let gid = GlobalId {
instance,
promoted: None,
};
Lvalue::Ptr {
ptr: *self.globals.get(&gid).expect("uncached global"),
extra: LvalueExtra::None,
@ -209,9 +225,7 @@ pub fn lvalue_field(
let base_layout = self.type_layout(base_ty)?;
use rustc::ty::layout::Layout::*;
let (offset, packed) = match *base_layout {
Univariant { ref variant, .. } => {
(variant.offsets[field_index], variant.packed)
},
Univariant { ref variant, .. } => (variant.offsets[field_index], variant.packed),
General { ref variants, .. } => {
let (_, base_extra) = base.to_ptr_extra_aligned();
@ -249,8 +263,13 @@ pub fn lvalue_field(
ty::TyArray(elem_ty, n) => {
assert!(field < n as u64);
self.type_size(elem_ty)?.expect("array elements are sized") as u64
},
_ => bug!("lvalue_field: got Array layout but non-array type {:?}", base_ty),
}
_ => {
bug!(
"lvalue_field: got Array layout but non-array type {:?}",
base_ty
)
}
};
(Size::from_bytes(field * elem_size), false)
}
@ -267,22 +286,36 @@ pub fn lvalue_field(
// Do not allocate in trivial cases
let (base_ptr, base_extra) = match base {
Lvalue::Ptr { ptr, extra } => (ptr, extra),
Lvalue::Local { frame, local } => match self.stack[frame].get_local(local)? {
// in case the type has a single field, just return the value
Value::ByVal(_) if self.get_field_count(base_ty).map(|c| c == 1).unwrap_or(false) => {
assert_eq!(offset.bytes(), 0, "ByVal can only have 1 non zst field with offset 0");
return Ok(base);
},
Value::ByRef{..} |
Value::ByValPair(..) |
Value::ByVal(_) => self.force_allocation(base)?.to_ptr_extra_aligned(),
},
Lvalue::Local { frame, local } => {
match self.stack[frame].get_local(local)? {
// in case the type has a single field, just return the value
Value::ByVal(_)
if self.get_field_count(base_ty).map(|c| c == 1).unwrap_or(
false,
) => {
assert_eq!(
offset.bytes(),
0,
"ByVal can only have 1 non zst field with offset 0"
);
return Ok(base);
}
Value::ByRef { .. } |
Value::ByValPair(..) |
Value::ByVal(_) => self.force_allocation(base)?.to_ptr_extra_aligned(),
}
}
};
let offset = match base_extra {
LvalueExtra::Vtable(tab) => {
let (_, align) = self.size_and_align_of_dst(base_ty, base_ptr.ptr.to_value_with_vtable(tab))?;
offset.abi_align(Align::from_bytes(align, align).unwrap()).bytes()
let (_, align) = self.size_and_align_of_dst(
base_ty,
base_ptr.ptr.to_value_with_vtable(tab),
)?;
offset
.abi_align(Align::from_bytes(align, align).unwrap())
.bytes()
}
_ => offset.bytes(),
};
@ -299,41 +332,63 @@ pub fn lvalue_field(
} else {
match base_extra {
LvalueExtra::None => bug!("expected fat pointer"),
LvalueExtra::DowncastVariant(..) =>
bug!("Rust doesn't support unsized fields in enum variants"),
LvalueExtra::DowncastVariant(..) => {
bug!("Rust doesn't support unsized fields in enum variants")
}
LvalueExtra::Vtable(_) |
LvalueExtra::Length(_) => {},
LvalueExtra::Length(_) => {}
}
base_extra
};
Ok(Lvalue::Ptr { ptr, extra } )
Ok(Lvalue::Ptr { ptr, extra })
}
pub(super) fn val_to_lvalue(&self, val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Lvalue> {
Ok(match self.tcx.struct_tail(ty).sty {
ty::TyDynamic(..) => {
let (ptr, vtable) = val.into_ptr_vtable_pair(&self.memory)?;
Lvalue::Ptr { ptr: PtrAndAlign { ptr, aligned: true }, extra: LvalueExtra::Vtable(vtable) }
},
Lvalue::Ptr {
ptr: PtrAndAlign { ptr, aligned: true },
extra: LvalueExtra::Vtable(vtable),
}
}
ty::TyStr | ty::TySlice(_) => {
let (ptr, len) = val.into_slice(&self.memory)?;
Lvalue::Ptr { ptr: PtrAndAlign { ptr, aligned: true }, extra: LvalueExtra::Length(len) }
},
Lvalue::Ptr {
ptr: PtrAndAlign { ptr, aligned: true },
extra: LvalueExtra::Length(len),
}
}
_ => Lvalue::from_primval_ptr(val.into_ptr(&self.memory)?),
})
}
pub(super) fn lvalue_index(&mut self, base: Lvalue, outer_ty: Ty<'tcx>, n: u64) -> EvalResult<'tcx, Lvalue> {
pub(super) fn lvalue_index(
&mut self,
base: Lvalue,
outer_ty: Ty<'tcx>,
n: u64,
) -> EvalResult<'tcx, Lvalue> {
// Taking the outer type here may seem odd; it's needed because for array types, the outer type gives away the length.
let base = self.force_allocation(base)?;
let (base_ptr, _) = base.to_ptr_extra_aligned();
let (elem_ty, len) = base.elem_ty_and_len(outer_ty);
let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized");
assert!(n < len, "Tried to access element {} of array/slice with length {}", n, len);
let elem_size = self.type_size(elem_ty)?.expect(
"slice element must be sized",
);
assert!(
n < len,
"Tried to access element {} of array/slice with length {}",
n,
len
);
let ptr = base_ptr.offset(n * elem_size, self.memory.layout)?;
Ok(Lvalue::Ptr { ptr, extra: LvalueExtra::None })
Ok(Lvalue::Ptr {
ptr,
extra: LvalueExtra::None,
})
}
pub(super) fn eval_lvalue_projection(
@ -357,7 +412,8 @@ pub(super) fn eval_lvalue_projection(
use rustc::ty::layout::Layout::*;
let extra = match *base_layout {
General { .. } => LvalueExtra::DowncastVariant(variant),
RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => base_extra,
RawNullablePointer { .. } |
StructWrappedNullablePointer { .. } => base_extra,
_ => bug!("variant downcast on non-aggregate: {:?}", base_layout),
};
(base_ptr, extra)
@ -386,13 +442,19 @@ pub(super) fn eval_lvalue_projection(
return self.lvalue_index(base, base_ty, n);
}
ConstantIndex { offset, min_length, from_end } => {
ConstantIndex {
offset,
min_length,
from_end,
} => {
// FIXME(solson)
let base = self.force_allocation(base)?;
let (base_ptr, _) = base.to_ptr_extra_aligned();
let (elem_ty, n) = base.elem_ty_and_len(base_ty);
let elem_size = self.type_size(elem_ty)?.expect("sequence element must be sized");
let elem_size = self.type_size(elem_ty)?.expect(
"sequence element must be sized",
);
assert!(n >= min_length as u64);
let index = if from_end {
@ -411,7 +473,9 @@ pub(super) fn eval_lvalue_projection(
let (base_ptr, _) = base.to_ptr_extra_aligned();
let (elem_ty, n) = base.elem_ty_and_len(base_ty);
let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized");
let elem_size = self.type_size(elem_ty)?.expect(
"slice element must be sized",
);
assert!(u64::from(from) <= n - u64::from(to));
let ptr = base_ptr.offset(u64::from(from) * elem_size, &self)?;
let extra = LvalueExtra::Length(n - u64::from(to) - u64::from(from));
@ -423,6 +487,9 @@ pub(super) fn eval_lvalue_projection(
}
pub(super) fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> {
self.monomorphize(lvalue.ty(self.mir(), self.tcx).to_ty(self.tcx), self.substs())
self.monomorphize(
lvalue.ty(self.mir(), self.tcx).to_ty(self.tcx),
self.substs(),
)
}
}

View File

@ -2,12 +2,7 @@
//! This separation exists to ensure that no fancy miri features like
//! interpreting common C functions leak into CTFE.
use super::{
EvalResult,
EvalContext,
Lvalue,
PrimVal
};
use super::{EvalResult, EvalContext, Lvalue, PrimVal};
use rustc::{mir, ty};
use syntax::codemap::Span;
@ -76,4 +71,3 @@ fn box_alloc<'a>(
ty: ty::Ty<'tcx>,
) -> EvalResult<'tcx, PrimVal>;
}

File diff suppressed because it is too large Load Diff

View File

@ -20,62 +20,23 @@ macro_rules! err {
mod traits;
mod value;
pub use self::error::{
EvalError,
EvalResult,
EvalErrorKind,
};
pub use self::error::{EvalError, EvalResult, EvalErrorKind};
pub use self::eval_context::{
EvalContext,
Frame,
ResourceLimits,
StackPopCleanup,
DynamicLifetime,
TyAndPacked,
PtrAndAlign,
};
pub use self::eval_context::{EvalContext, Frame, ResourceLimits, StackPopCleanup, DynamicLifetime,
TyAndPacked, PtrAndAlign};
pub use self::lvalue::{
Lvalue,
LvalueExtra,
GlobalId,
};
pub use self::lvalue::{Lvalue, LvalueExtra, GlobalId};
pub use self::memory::{
AllocId,
Memory,
MemoryPointer,
MemoryKind,
HasMemory,
};
pub use self::memory::{AllocId, Memory, MemoryPointer, MemoryKind, HasMemory};
use self::memory::{
PointerArithmetic,
Lock,
AccessKind,
};
use self::memory::{PointerArithmetic, Lock, AccessKind};
use self::range_map::{
RangeMap
};
use self::range_map::RangeMap;
pub use self::value::{
PrimVal,
PrimValKind,
Value,
Pointer,
};
pub use self::value::{PrimVal, PrimValKind, Value, Pointer};
pub use self::const_eval::{
eval_body_as_integer,
eval_body_as_primval,
};
pub use self::const_eval::{eval_body_as_integer, eval_body_as_primval};
pub use self::machine::{
Machine,
};
pub use self::machine::Machine;
pub use self::validation::{
ValidationQuery,
};
pub use self::validation::ValidationQuery;

View File

@ -1,22 +1,10 @@
use rustc::mir;
use rustc::ty::Ty;
use super::{
EvalResult,
EvalContext,
Lvalue,
Machine,
};
use super::{EvalResult, EvalContext, Lvalue, Machine};
use super::value::{
PrimVal,
PrimValKind,
Value,
bytes_to_f32,
bytes_to_f64,
f32_to_bytes,
f64_to_bytes,
};
use super::value::{PrimVal, PrimValKind, Value, bytes_to_f32, bytes_to_f64, f32_to_bytes,
f64_to_bytes};
impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
fn binop_with_overflow(
@ -25,10 +13,10 @@ fn binop_with_overflow(
left: &mir::Operand<'tcx>,
right: &mir::Operand<'tcx>,
) -> EvalResult<'tcx, (PrimVal, bool)> {
let left_ty = self.operand_ty(left);
let right_ty = self.operand_ty(right);
let left_val = self.eval_operand_to_primval(left)?;
let right_val = self.eval_operand_to_primval(right)?;
let left_ty = self.operand_ty(left);
let right_ty = self.operand_ty(right);
let left_val = self.eval_operand_to_primval(left)?;
let right_val = self.eval_operand_to_primval(right)?;
self.binary_op(op, left_val, left_ty, right_val, right_ty)
}
@ -147,7 +135,7 @@ pub fn binary_op(
use rustc::mir::BinOp::*;
use super::PrimValKind::*;
let left_kind = self.ty_to_primval_kind(left_ty)?;
let left_kind = self.ty_to_primval_kind(left_ty)?;
let right_kind = self.ty_to_primval_kind(right_ty)?;
//trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
@ -172,23 +160,30 @@ pub fn binary_op(
}
if left_kind != right_kind {
let msg = format!("unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
let msg = format!(
"unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})",
bin_op,
left,
left_kind,
right,
right_kind
);
return err!(Unimplemented(msg));
}
let val = match (bin_op, left_kind) {
(Eq, F32) => PrimVal::from_bool(bytes_to_f32(l) == bytes_to_f32(r)),
(Ne, F32) => PrimVal::from_bool(bytes_to_f32(l) != bytes_to_f32(r)),
(Lt, F32) => PrimVal::from_bool(bytes_to_f32(l) < bytes_to_f32(r)),
(Lt, F32) => PrimVal::from_bool(bytes_to_f32(l) < bytes_to_f32(r)),
(Le, F32) => PrimVal::from_bool(bytes_to_f32(l) <= bytes_to_f32(r)),
(Gt, F32) => PrimVal::from_bool(bytes_to_f32(l) > bytes_to_f32(r)),
(Gt, F32) => PrimVal::from_bool(bytes_to_f32(l) > bytes_to_f32(r)),
(Ge, F32) => PrimVal::from_bool(bytes_to_f32(l) >= bytes_to_f32(r)),
(Eq, F64) => PrimVal::from_bool(bytes_to_f64(l) == bytes_to_f64(r)),
(Ne, F64) => PrimVal::from_bool(bytes_to_f64(l) != bytes_to_f64(r)),
(Lt, F64) => PrimVal::from_bool(bytes_to_f64(l) < bytes_to_f64(r)),
(Lt, F64) => PrimVal::from_bool(bytes_to_f64(l) < bytes_to_f64(r)),
(Le, F64) => PrimVal::from_bool(bytes_to_f64(l) <= bytes_to_f64(r)),
(Gt, F64) => PrimVal::from_bool(bytes_to_f64(l) > bytes_to_f64(r)),
(Gt, F64) => PrimVal::from_bool(bytes_to_f64(l) > bytes_to_f64(r)),
(Ge, F64) => PrimVal::from_bool(bytes_to_f64(l) >= bytes_to_f64(r)),
(Add, F32) => f32_arithmetic!(+, l, r),
@ -207,15 +202,15 @@ pub fn binary_op(
(Ne, _) => PrimVal::from_bool(l != r),
(Lt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) < (r as i128)),
(Lt, _) => PrimVal::from_bool(l < r),
(Lt, _) => PrimVal::from_bool(l < r),
(Le, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) <= (r as i128)),
(Le, _) => PrimVal::from_bool(l <= r),
(Gt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) > (r as i128)),
(Gt, _) => PrimVal::from_bool(l > r),
(Gt, _) => PrimVal::from_bool(l > r),
(Ge, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) >= (r as i128)),
(Ge, _) => PrimVal::from_bool(l >= r),
(BitOr, _) => PrimVal::Bytes(l | r),
(BitOr, _) => PrimVal::Bytes(l | r),
(BitAnd, _) => PrimVal::Bytes(l & r),
(BitXor, _) => PrimVal::Bytes(l ^ r),
@ -226,7 +221,14 @@ pub fn binary_op(
(Rem, k) if k.is_int() => return int_arithmetic!(k, overflowing_rem, l, r),
_ => {
let msg = format!("unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
let msg = format!(
"unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})",
bin_op,
left,
left_kind,
right,
right_kind
);
return err!(Unimplemented(msg));
}
};
@ -248,19 +250,19 @@ pub fn unary_op<'tcx>(
let result_bytes = match (un_op, val_kind) {
(Not, Bool) => !val.to_bool()? as u128,
(Not, U8) => !(bytes as u8) as u128,
(Not, U8) => !(bytes as u8) as u128,
(Not, U16) => !(bytes as u16) as u128,
(Not, U32) => !(bytes as u32) as u128,
(Not, U64) => !(bytes as u64) as u128,
(Not, U128) => !bytes,
(Not, I8) => !(bytes as i8) as u128,
(Not, I8) => !(bytes as i8) as u128,
(Not, I16) => !(bytes as i16) as u128,
(Not, I32) => !(bytes as i32) as u128,
(Not, I64) => !(bytes as i64) as u128,
(Not, I128) => !(bytes as i128) as u128,
(Neg, I8) => -(bytes as i8) as u128,
(Neg, I8) => -(bytes as i8) as u128,
(Neg, I16) => -(bytes as i16) as u128,
(Neg, I32) => -(bytes as i32) as u128,
(Neg, I64) => -(bytes as i64) as u128,

View File

@ -4,12 +4,12 @@
//! necessary (e.g. when [0,5) is first associated with X, and then [1,2) is mutated).
//! Users must not depend on whether a range is coalesced or not, even though this is observable
//! via the iteration APIs.
use std::collections::{BTreeMap};
use std::collections::BTreeMap;
use std::ops;
#[derive(Clone, Debug)]
pub struct RangeMap<T> {
map: BTreeMap<Range, T>
map: BTreeMap<Range, T>,
}
// The derived `Ord` impl sorts first by the first field, then, if the fields are the same,
@ -31,11 +31,13 @@ fn range(offset: u64, len: u64) -> ops::Range<Range> {
// the range given by the offset into the allocation and the length.
// This is sound if all ranges that intersect with the argument range, are in the
// resulting range of ranges.
let left = Range { // lowest range to include `offset`
let left = Range {
// lowest range to include `offset`
start: 0,
end: offset + 1,
};
let right = Range { // lowest (valid) range not to include `offset+len`
let right = Range {
// lowest (valid) range not to include `offset+len`
start: offset + len,
end: offset + len + 1,
};
@ -45,7 +47,7 @@ fn range(offset: u64, len: u64) -> ops::Range<Range> {
/// Tests if all of [offset, offset+len) are contained in this range.
fn overlaps(&self, offset: u64, len: u64) -> bool {
assert!(len > 0);
offset < self.end && offset+len >= self.start
offset < self.end && offset + len >= self.start
}
}
@ -54,82 +56,122 @@ pub fn new() -> RangeMap<T> {
RangeMap { map: BTreeMap::new() }
}
fn iter_with_range<'a>(&'a self, offset: u64, len: u64) -> impl Iterator<Item=(&'a Range, &'a T)> + 'a {
fn iter_with_range<'a>(
&'a self,
offset: u64,
len: u64,
) -> impl Iterator<Item = (&'a Range, &'a T)> + 'a {
assert!(len > 0);
self.map.range(Range::range(offset, len))
.filter_map(move |(range, data)| {
self.map.range(Range::range(offset, len)).filter_map(
move |(range,
data)| {
if range.overlaps(offset, len) {
Some((range, data))
} else {
None
}
})
},
)
}
pub fn iter<'a>(&'a self, offset: u64, len: u64) -> impl Iterator<Item=&'a T> + 'a {
pub fn iter<'a>(&'a self, offset: u64, len: u64) -> impl Iterator<Item = &'a T> + 'a {
self.iter_with_range(offset, len).map(|(_, data)| data)
}
fn split_entry_at(&mut self, offset: u64) where T: Clone {
fn split_entry_at(&mut self, offset: u64)
where
T: Clone,
{
let range = match self.iter_with_range(offset, 1).next() {
Some((&range, _)) => range,
None => return,
};
assert!(range.start <= offset && range.end > offset, "We got a range that doesn't even contain what we asked for.");
assert!(
range.start <= offset && range.end > offset,
"We got a range that doesn't even contain what we asked for."
);
// There is an entry overlapping this position, see if we have to split it
if range.start < offset {
let data = self.map.remove(&range).unwrap();
let old = self.map.insert(Range { start: range.start, end: offset }, data.clone());
let old = self.map.insert(
Range {
start: range.start,
end: offset,
},
data.clone(),
);
assert!(old.is_none());
let old = self.map.insert(Range { start: offset, end: range.end }, data);
let old = self.map.insert(
Range {
start: offset,
end: range.end,
},
data,
);
assert!(old.is_none());
}
}
pub fn iter_mut_all<'a>(&'a mut self) -> impl Iterator<Item=&'a mut T> + 'a {
pub fn iter_mut_all<'a>(&'a mut self) -> impl Iterator<Item = &'a mut T> + 'a {
self.map.values_mut()
}
/// Provide mutable iteration over everything in the given range. As a side-effect,
/// this will split entries in the map that are only partially hit by the given range,
/// to make sure that when they are mutated, the effect is constrained to the given range.
pub fn iter_mut_with_gaps<'a>(&'a mut self, offset: u64, len: u64) -> impl Iterator<Item=&'a mut T> + 'a
where T: Clone
pub fn iter_mut_with_gaps<'a>(
&'a mut self,
offset: u64,
len: u64,
) -> impl Iterator<Item = &'a mut T> + 'a
where
T: Clone,
{
assert!(len > 0);
// Preparation: Split first and last entry as needed.
self.split_entry_at(offset);
self.split_entry_at(offset+len);
self.split_entry_at(offset + len);
// Now we can provide a mutable iterator
self.map.range_mut(Range::range(offset, len))
.filter_map(move |(&range, data)| {
self.map.range_mut(Range::range(offset, len)).filter_map(
move |(&range, data)| {
if range.overlaps(offset, len) {
assert!(offset <= range.start && offset+len >= range.end, "The splitting went wrong");
assert!(
offset <= range.start && offset + len >= range.end,
"The splitting went wrong"
);
Some(data)
} else {
// Skip this one
None
}
})
},
)
}
/// Provide a mutable iterator over everything in the given range, with the same side-effects as
/// iter_mut_with_gaps. Furthermore, if there are gaps between ranges, fill them with the given default.
/// This is also how you insert.
pub fn iter_mut<'a>(&'a mut self, offset: u64, len: u64) -> impl Iterator<Item=&'a mut T> + 'a
where T: Clone + Default
pub fn iter_mut<'a>(&'a mut self, offset: u64, len: u64) -> impl Iterator<Item = &'a mut T> + 'a
where
T: Clone + Default,
{
// Do a first iteration to collect the gaps
let mut gaps = Vec::new();
let mut last_end = offset;
for (range, _) in self.iter_with_range(offset, len) {
if last_end < range.start {
gaps.push(Range { start: last_end, end: range.start });
gaps.push(Range {
start: last_end,
end: range.start,
});
}
last_end = range.end;
}
if last_end < offset+len {
gaps.push(Range { start: last_end, end: offset+len });
if last_end < offset + len {
gaps.push(Range {
start: last_end,
end: offset + len,
});
}
// Add default for all gaps
@ -143,7 +185,8 @@ pub fn iter_mut<'a>(&'a mut self, offset: u64, len: u64) -> impl Iterator<Item=&
}
pub fn retain<F>(&mut self, mut f: F)
where F: FnMut(&T) -> bool
where
F: FnMut(&T) -> bool,
{
let mut remove = Vec::new();
for (range, data) in self.map.iter() {
@ -164,7 +207,10 @@ mod tests {
/// Query the map at every offset in the range and collect the results.
fn to_vec<T: Copy>(map: &RangeMap<T>, offset: u64, len: u64) -> Vec<T> {
(offset..offset+len).into_iter().map(|i| *map.iter(i, 1).next().unwrap()).collect()
(offset..offset + len)
.into_iter()
.map(|i| *map.iter(i, 1).next().unwrap())
.collect()
}
#[test]
@ -190,10 +236,15 @@ fn gaps() {
// Now request a range that needs three gaps filled
for x in map.iter_mut(10, 10) {
if *x != 42 { *x = 23; }
if *x != 42 {
*x = 23;
}
}
assert_eq!(to_vec(&map, 10, 10), vec![23, 42, 23, 23, 23, 42, 23, 23, 23, 23]);
assert_eq!(
to_vec(&map, 10, 10),
vec![23, 42, 23, 23, 23, 42, 23, 23, 23, 23]
);
assert_eq!(to_vec(&map, 13, 5), vec![23, 23, 42, 23, 23]);
}
}

View File

@ -11,13 +11,8 @@
use rustc::ty::layout::Layout;
use rustc::ty::subst::Substs;
use super::{
EvalResult,
EvalContext, StackPopCleanup, TyAndPacked, PtrAndAlign,
GlobalId, Lvalue,
HasMemory, MemoryKind,
Machine,
};
use super::{EvalResult, EvalContext, StackPopCleanup, TyAndPacked, PtrAndAlign, GlobalId, Lvalue,
HasMemory, MemoryKind, Machine};
use syntax::codemap::Span;
use syntax::ast::Mutability;
@ -52,7 +47,14 @@ pub fn step(&mut self) -> EvalResult<'tcx, bool> {
ecx: self,
mir,
new_constants: &mut new,
}.visit_statement(block, stmt, mir::Location { block, statement_index: stmt_id });
}.visit_statement(
block,
stmt,
mir::Location {
block,
statement_index: stmt_id,
},
);
// if ConstantExtractor added new frames, we don't execute anything here
// but await the next call to step
if new? == 0 {
@ -69,7 +71,14 @@ pub fn step(&mut self) -> EvalResult<'tcx, bool> {
ecx: self,
mir,
new_constants: &mut new,
}.visit_terminator(block, terminator, mir::Location { block, statement_index: stmt_id });
}.visit_terminator(
block,
terminator,
mir::Location {
block,
statement_index: stmt_id,
},
);
// if ConstantExtractor added new frames, we don't execute anything here
// but await the next call to step
if new? == 0 {
@ -85,7 +94,10 @@ fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx> {
match stmt.kind {
Assign(ref lvalue, ref rvalue) => self.eval_rvalue_into_lvalue(rvalue, lvalue)?,
SetDiscriminant { ref lvalue, variant_index } => {
SetDiscriminant {
ref lvalue,
variant_index,
} => {
let dest = self.eval_lvalue(lvalue)?;
let dest_ty = self.lvalue_ty(lvalue);
let dest_layout = self.type_layout(dest_ty)?;
@ -94,7 +106,11 @@ fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx> {
Layout::General { discr, .. } => {
let discr_size = discr.size().bytes();
let dest_ptr = self.force_allocation(dest)?.to_ptr()?;
self.memory.write_uint(dest_ptr, variant_index as u128, discr_size)?
self.memory.write_uint(
dest_ptr,
variant_index as u128,
discr_size,
)?
}
Layout::RawNullablePointer { nndiscr, .. } => {
@ -103,31 +119,57 @@ fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx> {
}
}
Layout::StructWrappedNullablePointer { nndiscr, ref discrfield_source, .. } => {
Layout::StructWrappedNullablePointer {
nndiscr,
ref discrfield_source,
..
} => {
if variant_index as u64 != nndiscr {
let (offset, TyAndPacked { ty, packed }) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield_source)?;
let nonnull = self.force_allocation(dest)?.to_ptr()?.offset(offset.bytes(), &self)?;
let (offset, TyAndPacked { ty, packed }) = self.nonnull_offset_and_ty(
dest_ty,
nndiscr,
discrfield_source,
)?;
let nonnull = self.force_allocation(dest)?.to_ptr()?.offset(
offset.bytes(),
&self,
)?;
trace!("struct wrapped nullable pointer type: {}", ty);
// only the pointer part of a fat pointer is used for this space optimization
let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield");
self.write_maybe_aligned_mut(!packed, |ectx| ectx.memory.write_uint(nonnull, 0, discr_size))?;
let discr_size = self.type_size(ty)?.expect(
"bad StructWrappedNullablePointer discrfield",
);
self.write_maybe_aligned_mut(!packed, |ectx| {
ectx.memory.write_uint(nonnull, 0, discr_size)
})?;
}
},
}
_ => bug!("SetDiscriminant on {} represented as {:#?}", dest_ty, dest_layout),
_ => {
bug!(
"SetDiscriminant on {} represented as {:#?}",
dest_ty,
dest_layout
)
}
}
}
// Mark locals as dead or alive.
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!(Unimplemented("Storage annotations must refer to locals of the topmost stack frame.".to_owned())) // FIXME maybe this should get its own error type
};
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!(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)?,
StorageDead(_) => self.stack[frame].storage_dead(local)?,
_ => bug!("We already checked that we are a storage stmt")
StorageDead(_) => self.stack[frame].storage_dead(local)?,
_ => bug!("We already checked that we are a storage stmt"),
};
self.deallocate_local(old_val)?;
}
@ -171,7 +213,10 @@ fn global_item(
mutability: Mutability,
) -> EvalResult<'tcx, bool> {
let instance = self.resolve_associated_const(def_id, substs);
let cid = GlobalId { instance, promoted: None };
let cid = GlobalId {
instance,
promoted: None,
};
if self.globals.contains_key(&cid) {
return Ok(false);
}
@ -179,22 +224,45 @@ fn global_item(
// FIXME: check that it's `#[linkage = "extern_weak"]`
trace!("Initializing an extern global with NULL");
let ptr_size = self.memory.pointer_size();
let ptr = self.memory.allocate(ptr_size, ptr_size, MemoryKind::UninitializedStatic)?;
let ptr = self.memory.allocate(
ptr_size,
ptr_size,
MemoryKind::UninitializedStatic,
)?;
self.memory.write_usize(ptr, 0)?;
self.memory.mark_static_initalized(ptr.alloc_id, mutability)?;
self.globals.insert(cid, PtrAndAlign { ptr: ptr.into(), aligned: true });
self.globals.insert(
cid,
PtrAndAlign {
ptr: ptr.into(),
aligned: true,
},
);
return Ok(false);
}
let mir = self.load_mir(instance.def)?;
let size = self.type_size_with_substs(mir.return_ty, substs)?.expect("unsized global");
let size = self.type_size_with_substs(mir.return_ty, substs)?.expect(
"unsized global",
);
let align = self.type_align_with_substs(mir.return_ty, substs)?;
let ptr = self.memory.allocate(size, align, MemoryKind::UninitializedStatic)?;
let ptr = self.memory.allocate(
size,
align,
MemoryKind::UninitializedStatic,
)?;
let aligned = !self.is_packed(mir.return_ty)?;
self.globals.insert(cid, PtrAndAlign { ptr: ptr.into(), aligned });
self.globals.insert(
cid,
PtrAndAlign {
ptr: ptr.into(),
aligned,
},
);
let internally_mutable = !mir.return_ty.is_freeze(
self.tcx,
ty::ParamEnv::empty(Reveal::All),
span);
self.tcx,
ty::ParamEnv::empty(Reveal::All),
span,
);
let mutability = if mutability == Mutability::Mutable || internally_mutable {
Mutability::Mutable
} else {
@ -237,7 +305,7 @@ fn try<F: FnOnce(&mut Self) -> EvalResult<'tcx, bool>>(&mut self, f: F) {
// everything ok + a new stackframe
Ok(true) => *self.new_constants = Ok(n + 1),
// constant correctly evaluated, but no new stackframe
Ok(false) => {},
Ok(false) => {}
// constant eval errored
Err(err) => *self.new_constants = Err(err),
}
@ -251,8 +319,15 @@ fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: mir::Loca
// already computed by rustc
mir::Literal::Value { .. } => {}
mir::Literal::Item { def_id, substs } => {
self.try(|this| this.ecx.global_item(def_id, substs, constant.span, Mutability::Immutable));
},
self.try(|this| {
this.ecx.global_item(
def_id,
substs,
constant.span,
Mutability::Immutable,
)
});
}
mir::Literal::Promoted { index } => {
let cid = GlobalId {
instance: self.instance,
@ -263,17 +338,33 @@ fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: mir::Loca
}
let mir = &self.mir.promoted[index];
self.try(|this| {
let size = this.ecx.type_size_with_substs(mir.return_ty, this.instance.substs)?.expect("unsized global");
let align = this.ecx.type_align_with_substs(mir.return_ty, this.instance.substs)?;
let ptr = this.ecx.memory.allocate(size, align, MemoryKind::UninitializedStatic)?;
let size = this.ecx
.type_size_with_substs(mir.return_ty, this.instance.substs)?
.expect("unsized global");
let align = this.ecx.type_align_with_substs(
mir.return_ty,
this.instance.substs,
)?;
let ptr = this.ecx.memory.allocate(
size,
align,
MemoryKind::UninitializedStatic,
)?;
let aligned = !this.ecx.is_packed(mir.return_ty)?;
this.ecx.globals.insert(cid, PtrAndAlign { ptr: ptr.into(), aligned });
this.ecx.globals.insert(
cid,
PtrAndAlign {
ptr: ptr.into(),
aligned,
},
);
trace!("pushing stack frame for {:?}", index);
this.ecx.push_stack_frame(this.instance,
constant.span,
mir,
Lvalue::from_ptr(ptr),
StackPopCleanup::MarkStatic(Mutability::Immutable),
this.ecx.push_stack_frame(
this.instance,
constant.span,
mir,
Lvalue::from_ptr(ptr),
StackPopCleanup::MarkStatic(Mutability::Immutable),
)?;
Ok(true)
});
@ -285,7 +376,7 @@ fn visit_lvalue(
&mut self,
lvalue: &mir::Lvalue<'tcx>,
context: LvalueContext<'tcx>,
location: mir::Location
location: mir::Location,
) {
self.super_lvalue(lvalue, context, location);
if let mir::Lvalue::Static(ref static_) = *lvalue {
@ -295,7 +386,18 @@ fn visit_lvalue(
if let Some(node_item) = self.ecx.tcx.hir.get_if_local(def_id) {
if let hir::map::Node::NodeItem(&hir::Item { ref node, .. }) = node_item {
if let hir::ItemStatic(_, m, _) = *node {
self.try(|this| this.ecx.global_item(def_id, substs, span, if m == hir::MutMutable { Mutability::Mutable } else { Mutability::Immutable }));
self.try(|this| {
this.ecx.global_item(
def_id,
substs,
span,
if m == hir::MutMutable {
Mutability::Mutable
} else {
Mutability::Immutable
},
)
});
return;
} else {
bug!("static def id doesn't point to static");
@ -306,7 +408,18 @@ fn visit_lvalue(
} else {
let def = self.ecx.tcx.describe_def(def_id).expect("static not found");
if let hir::def::Def::Static(_, mutable) = def {
self.try(|this| this.ecx.global_item(def_id, substs, span, if mutable { Mutability::Mutable } else { Mutability::Immutable }));
self.try(|this| {
this.ecx.global_item(
def_id,
substs,
span,
if mutable {
Mutability::Mutable
} else {
Mutability::Immutable
},
)
});
} else {
bug!("static found but isn't a static: {:?}", def);
}

View File

@ -2,29 +2,45 @@
use rustc::ty::{self, Ty};
use syntax::codemap::Span;
use interpret::{
EvalResult,
EvalContext, StackPopCleanup,
Lvalue, LvalueExtra,
PrimVal, Value,
Machine,
};
use interpret::{EvalResult, EvalContext, StackPopCleanup, Lvalue, LvalueExtra, PrimVal, Value,
Machine};
impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
pub(crate) fn drop_lvalue(&mut self, lval: Lvalue, instance: ty::Instance<'tcx>, ty: Ty<'tcx>, span: Span) -> EvalResult<'tcx> {
pub(crate) fn drop_lvalue(
&mut self,
lval: Lvalue,
instance: ty::Instance<'tcx>,
ty: Ty<'tcx>,
span: Span,
) -> EvalResult<'tcx> {
trace!("drop_lvalue: {:#?}", lval);
// We take the address of the object. This may well be unaligned, which is fine for us here.
// However, unaligned accesses will probably make the actual drop implementation fail -- a problem shared
// by rustc.
let val = match self.force_allocation(lval)? {
Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => ptr.ptr.to_value_with_vtable(vtable),
Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => ptr.ptr.to_value_with_len(len),
Lvalue::Ptr { ptr, extra: LvalueExtra::None } => ptr.ptr.to_value(),
Lvalue::Ptr {
ptr,
extra: LvalueExtra::Vtable(vtable),
} => ptr.ptr.to_value_with_vtable(vtable),
Lvalue::Ptr {
ptr,
extra: LvalueExtra::Length(len),
} => ptr.ptr.to_value_with_len(len),
Lvalue::Ptr {
ptr,
extra: LvalueExtra::None,
} => ptr.ptr.to_value(),
_ => bug!("force_allocation broken"),
};
self.drop(val, instance, ty, span)
}
pub(crate) fn drop(&mut self, arg: Value, mut instance: ty::Instance<'tcx>, ty: Ty<'tcx>, span: Span) -> EvalResult<'tcx> {
pub(crate) fn drop(
&mut self,
arg: Value,
mut instance: ty::Instance<'tcx>,
ty: Ty<'tcx>,
span: Span,
) -> EvalResult<'tcx> {
trace!("drop: {:#?}, {:?}, {:?}", arg, ty.sty, instance.def);
if let ty::InstanceDef::DropGlue(_, None) = instance.def {
@ -42,11 +58,11 @@ pub(crate) fn drop(&mut self, arg: Value, mut instance: ty::Instance<'tcx>, ty:
Some(func) => {
instance = func;
self.load_mir(func.def)?
},
}
// no drop fn -> bail out
None => return Ok(()),
}
},
}
_ => self.load_mir(instance.def)?,
};

View File

@ -4,15 +4,8 @@
use syntax::codemap::Span;
use syntax::abi::Abi;
use super::{
EvalError, EvalResult, EvalErrorKind,
EvalContext, eval_context, TyAndPacked, PtrAndAlign,
Lvalue,
MemoryPointer,
PrimVal, Value,
Machine,
HasMemory,
};
use super::{EvalError, EvalResult, EvalErrorKind, EvalContext, eval_context, TyAndPacked,
PtrAndAlign, Lvalue, MemoryPointer, PrimVal, Value, Machine, HasMemory};
use super::eval_context::IntegerExt;
use rustc_data_structures::indexed_vec::Idx;
@ -38,7 +31,12 @@ pub(super) fn eval_terminator(
Goto { target } => self.goto_block(target),
SwitchInt { ref discr, ref values, ref targets, .. } => {
SwitchInt {
ref discr,
ref values,
ref targets,
..
} => {
// FIXME(CTFE): forbid branching
let discr_val = self.eval_operand(discr)?;
let discr_ty = self.operand_ty(discr);
@ -58,7 +56,12 @@ pub(super) fn eval_terminator(
self.goto_block(target_block);
}
Call { ref func, ref args, ref destination, .. } => {
Call {
ref func,
ref args,
ref destination,
..
} => {
let destination = match *destination {
Some((ref lv, target)) => Some((self.eval_lvalue(lv)?, target)),
None => None,
@ -80,22 +83,35 @@ pub(super) fn eval_terminator(
if !self.check_sig_compat(sig, real_sig)? {
return err!(FunctionPointerTyMismatch(real_sig, sig));
}
},
}
ref other => bug!("instance def ty: {:?}", other),
}
(instance, sig)
},
ty::TyFnDef(def_id, substs) => (eval_context::resolve(self.tcx, def_id, substs), func_ty.fn_sig(self.tcx)),
}
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!(Unimplemented(msg));
}
};
let sig = self.erase_lifetimes(&sig);
self.eval_fn_call(fn_def, destination, args, terminator.source_info.span, sig)?;
self.eval_fn_call(
fn_def,
destination,
args,
terminator.source_info.span,
sig,
)?;
}
Drop { ref location, target, .. } => {
Drop {
ref location,
target,
..
} => {
trace!("TerminatorKind::drop: {:?}, {:?}", location, self.substs());
// FIXME(CTFE): forbid drop in const eval
let lval = self.eval_lvalue(location)?;
@ -104,10 +120,21 @@ pub(super) fn eval_terminator(
let ty = eval_context::apply_param_substs(self.tcx, self.substs(), &ty);
let instance = eval_context::resolve_drop_in_place(self.tcx, ty);
self.drop_lvalue(lval, instance, ty, terminator.source_info.span)?;
self.drop_lvalue(
lval,
instance,
ty,
terminator.source_info.span,
)?;
}
Assert { ref cond, expected, ref msg, target, .. } => {
Assert {
ref cond,
expected,
ref msg,
target,
..
} => {
let cond_val = self.eval_operand_to_primval(cond)?.to_bool()?;
if expected == cond_val {
self.goto_block(target);
@ -122,12 +149,13 @@ pub(super) fn eval_terminator(
.expect("can't eval index")
.to_u64()?;
err!(ArrayIndexOutOfBounds(span, len, index))
},
mir::AssertMessage::Math(ref err) =>
err!(Math(terminator.source_info.span, err.clone())),
}
}
mir::AssertMessage::Math(ref err) => {
err!(Math(terminator.source_info.span, err.clone()))
}
};
}
},
}
DropAndReplace { .. } => unimplemented!(),
Resume => unimplemented!(),
@ -144,27 +172,30 @@ fn check_sig_compat(
sig: ty::FnSig<'tcx>,
real_sig: ty::FnSig<'tcx>,
) -> EvalResult<'tcx, bool> {
fn check_ty_compat<'tcx>(
ty: ty::Ty<'tcx>,
real_ty: ty::Ty<'tcx>,
) -> bool {
if ty == real_ty { return true; } // This is actually a fast pointer comparison
fn check_ty_compat<'tcx>(ty: ty::Ty<'tcx>, real_ty: ty::Ty<'tcx>) -> bool {
if ty == real_ty {
return true;
} // This is actually a fast pointer comparison
return match (&ty.sty, &real_ty.sty) {
// Permit changing the pointer type of raw pointers and references as well as
// mutability of raw pointers.
// TODO: Should not be allowed when fat pointers are involved.
(&TypeVariants::TyRawPtr(_), &TypeVariants::TyRawPtr(_)) => true,
(&TypeVariants::TyRef(_, _), &TypeVariants::TyRef(_, _)) =>
ty.is_mutable_pointer() == real_ty.is_mutable_pointer(),
(&TypeVariants::TyRef(_, _), &TypeVariants::TyRef(_, _)) => {
ty.is_mutable_pointer() == real_ty.is_mutable_pointer()
}
// rule out everything else
_ => false
}
_ => false,
};
}
if sig.abi == real_sig.abi &&
sig.variadic == real_sig.variadic &&
if sig.abi == real_sig.abi && sig.variadic == real_sig.variadic &&
sig.inputs_and_output.len() == real_sig.inputs_and_output.len() &&
sig.inputs_and_output.iter().zip(real_sig.inputs_and_output).all(|(ty, real_ty)| check_ty_compat(ty, real_ty)) {
sig.inputs_and_output
.iter()
.zip(real_sig.inputs_and_output)
.all(|(ty, real_ty)| check_ty_compat(ty, real_ty))
{
// Definitely good.
return Ok(true);
}
@ -224,22 +255,15 @@ fn eval_fn_call(
M::call_intrinsic(self, instance, arg_operands, ret, ty, layout, target)?;
self.dump_local(ret);
Ok(())
},
ty::InstanceDef::ClosureOnceShim{..} => {
}
ty::InstanceDef::ClosureOnceShim { .. } => {
let mut args = Vec::new();
for arg in arg_operands {
let arg_val = self.eval_operand(arg)?;
let arg_ty = self.operand_ty(arg);
args.push((arg_val, arg_ty));
}
if M::eval_fn_call(
self,
instance,
destination,
arg_operands,
span,
sig,
)? {
if M::eval_fn_call(self, instance, destination, arg_operands, span, sig)? {
return Ok(());
}
let mut arg_locals = self.frame().mir.args_iter();
@ -250,19 +274,25 @@ fn eval_fn_call(
let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
self.write_value(arg_val, dest, arg_ty)?;
}
},
}
// non capture closure as fn ptr
// need to inject zst ptr for closure object (aka do nothing)
// and need to pack arguments
Abi::Rust => {
trace!("arg_locals: {:?}", self.frame().mir.args_iter().collect::<Vec<_>>());
trace!(
"arg_locals: {:?}",
self.frame().mir.args_iter().collect::<Vec<_>>()
);
trace!("arg_operands: {:?}", arg_operands);
let local = arg_locals.nth(1).unwrap();
for (i, (arg_val, arg_ty)) in args.into_iter().enumerate() {
let dest = self.eval_lvalue(&mir::Lvalue::Local(local).field(mir::Field::new(i), arg_ty))?;
let dest = self.eval_lvalue(&mir::Lvalue::Local(local).field(
mir::Field::new(i),
arg_ty,
))?;
self.write_value(arg_val, dest, arg_ty)?;
}
},
}
_ => bug!("bad ABI for ClosureOnceShim: {:?}", sig.abi),
}
Ok(())
@ -276,27 +306,24 @@ fn eval_fn_call(
}
// Push the stack frame, and potentially be entirely done if the call got hooked
if M::eval_fn_call(
self,
instance,
destination,
arg_operands,
span,
sig,
)? {
if M::eval_fn_call(self, instance, destination, arg_operands, span, sig)? {
return Ok(());
}
// Pass the arguments
let mut arg_locals = self.frame().mir.args_iter();
trace!("ABI: {:?}", sig.abi);
trace!("arg_locals: {:?}", self.frame().mir.args_iter().collect::<Vec<_>>());
trace!(
"arg_locals: {:?}",
self.frame().mir.args_iter().collect::<Vec<_>>()
);
trace!("arg_operands: {:?}", arg_operands);
match sig.abi {
Abi::RustCall => {
assert_eq!(args.len(), 2);
{ // write first argument
{
// write first argument
let first_local = arg_locals.next().unwrap();
let dest = self.eval_lvalue(&mir::Lvalue::Local(first_local))?;
let (arg_val, arg_ty) = args.remove(0);
@ -306,37 +333,58 @@ fn eval_fn_call(
// unpack and write all other args
let (arg_val, arg_ty) = args.remove(0);
let layout = self.type_layout(arg_ty)?;
if let (&ty::TyTuple(fields, _), &Layout::Univariant { ref variant, .. }) = (&arg_ty.sty, layout) {
if let (&ty::TyTuple(fields, _),
&Layout::Univariant { ref variant, .. }) = (&arg_ty.sty, layout)
{
trace!("fields: {:?}", fields);
if self.frame().mir.args_iter().count() == fields.len() + 1 {
let offsets = variant.offsets.iter().map(|s| s.bytes());
match arg_val {
Value::ByRef(PtrAndAlign { ptr, aligned }) => {
assert!(aligned, "Unaligned ByRef-values cannot occur as function arguments");
for ((offset, ty), arg_local) in offsets.zip(fields).zip(arg_locals) {
assert!(
aligned,
"Unaligned ByRef-values cannot occur as function arguments"
);
for ((offset, ty), arg_local) in
offsets.zip(fields).zip(arg_locals)
{
let arg = Value::by_ref(ptr.offset(offset, &self)?);
let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
trace!("writing arg {:?} to {:?} (type: {})", arg, dest, ty);
let dest =
self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
trace!(
"writing arg {:?} to {:?} (type: {})",
arg,
dest,
ty
);
self.write_value(arg, dest, ty)?;
}
},
Value::ByVal(PrimVal::Undef) => {},
}
Value::ByVal(PrimVal::Undef) => {}
other => {
assert_eq!(fields.len(), 1);
let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_locals.next().unwrap()))?;
let dest = self.eval_lvalue(&mir::Lvalue::Local(
arg_locals.next().unwrap(),
))?;
self.write_value(other, dest, fields[0])?;
}
}
} else {
trace!("manual impl of rust-call ABI");
// called a manual impl of a rust-call function
let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_locals.next().unwrap()))?;
let dest = self.eval_lvalue(
&mir::Lvalue::Local(arg_locals.next().unwrap()),
)?;
self.write_value(arg_val, dest, arg_ty)?;
}
} else {
bug!("rust-call ABI tuple argument was {:?}, {:?}", arg_ty, layout);
bug!(
"rust-call ABI tuple argument was {:?}, {:?}",
arg_ty,
layout
);
}
},
}
_ => {
for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) {
let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
@ -345,7 +393,7 @@ fn eval_fn_call(
}
}
Ok(())
},
}
ty::InstanceDef::DropGlue(..) => {
assert_eq!(arg_operands.len(), 1);
assert_eq!(sig.abi, Abi::Rust);
@ -361,7 +409,7 @@ fn eval_fn_call(
_ => bug!("can only deref pointer types"),
};
self.drop(val, instance, pointee_type, span)
},
}
ty::InstanceDef::FnPtrShim(..) => {
trace!("ABI: {}", sig.abi);
let mut args = Vec::new();
@ -370,22 +418,15 @@ fn eval_fn_call(
let arg_ty = self.operand_ty(arg);
args.push((arg_val, arg_ty));
}
if M::eval_fn_call(
self,
instance,
destination,
arg_operands,
span,
sig,
)? {
if M::eval_fn_call(self, instance, destination, arg_operands, span, sig)? {
return Ok(());
}
let arg_locals = self.frame().mir.args_iter();
match sig.abi {
Abi::Rust => {
args.remove(0);
},
Abi::RustCall => {},
}
Abi::RustCall => {}
_ => unimplemented!(),
};
for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) {
@ -393,43 +434,56 @@ fn eval_fn_call(
self.write_value(arg_val, dest, arg_ty)?;
}
Ok(())
},
}
ty::InstanceDef::Virtual(_, idx) => {
let ptr_size = self.memory.pointer_size();
let (_, vtable) = self.eval_operand(&arg_operands[0])?.into_ptr_vtable_pair(&self.memory)?;
let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3), &self)?)?;
let (_, vtable) = self.eval_operand(&arg_operands[0])?.into_ptr_vtable_pair(
&self.memory,
)?;
let fn_ptr = self.memory.read_ptr(
vtable.offset(ptr_size * (idx as u64 + 3), &self)?,
)?;
let instance = self.memory.get_fn(fn_ptr.to_ptr()?)?;
let mut arg_operands = arg_operands.to_vec();
let ty = self.operand_ty(&arg_operands[0]);
let ty = self.get_field_ty(ty, 0)?.ty; // TODO: packed flag is ignored
match arg_operands[0] {
mir::Operand::Consume(ref mut lval) => *lval = lval.clone().field(mir::Field::new(0), ty),
mir::Operand::Consume(ref mut lval) => {
*lval = lval.clone().field(mir::Field::new(0), ty)
}
_ => bug!("virtual call first arg cannot be a constant"),
}
// recurse with concrete function
self.eval_fn_call(
instance,
destination,
&arg_operands,
span,
sig,
)
},
self.eval_fn_call(instance, destination, &arg_operands, span, sig)
}
}
}
pub fn read_discriminant_value(&self, adt_ptr: MemoryPointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u128> {
pub fn read_discriminant_value(
&self,
adt_ptr: MemoryPointer,
adt_ty: Ty<'tcx>,
) -> EvalResult<'tcx, u128> {
use rustc::ty::layout::Layout::*;
let adt_layout = self.type_layout(adt_ty)?;
//trace!("read_discriminant_value {:#?}", adt_layout);
let discr_val = match *adt_layout {
General { discr, .. } | CEnum { discr, signed: false, .. } => {
General { discr, .. } |
CEnum {
discr,
signed: false,
..
} => {
let discr_size = discr.size().bytes();
self.memory.read_uint(adt_ptr, discr_size)?
}
CEnum { discr, signed: true, .. } => {
CEnum {
discr,
signed: true,
..
} => {
let discr_size = discr.size().bytes();
self.memory.read_int(adt_ptr, discr_size)? as u128
}
@ -437,32 +491,62 @@ pub fn read_discriminant_value(&self, adt_ptr: MemoryPointer, adt_ty: Ty<'tcx>)
RawNullablePointer { nndiscr, value } => {
let discr_size = value.size(&self.tcx.data_layout).bytes();
trace!("rawnullablepointer with size {}", discr_size);
self.read_nonnull_discriminant_value(adt_ptr, nndiscr as u128, discr_size)?
self.read_nonnull_discriminant_value(
adt_ptr,
nndiscr as u128,
discr_size,
)?
}
StructWrappedNullablePointer { nndiscr, ref discrfield_source, .. } => {
let (offset, TyAndPacked { ty, packed }) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield_source)?;
StructWrappedNullablePointer {
nndiscr,
ref discrfield_source,
..
} => {
let (offset, TyAndPacked { ty, packed }) = self.nonnull_offset_and_ty(
adt_ty,
nndiscr,
discrfield_source,
)?;
let nonnull = adt_ptr.offset(offset.bytes(), &*self)?;
trace!("struct wrapped nullable pointer type: {}", ty);
// only the pointer part of a fat pointer is used for this space optimization
let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield");
self.read_maybe_aligned(!packed,
|ectx| ectx.read_nonnull_discriminant_value(nonnull, nndiscr as u128, discr_size))?
let discr_size = self.type_size(ty)?.expect(
"bad StructWrappedNullablePointer discrfield",
);
self.read_maybe_aligned(!packed, |ectx| {
ectx.read_nonnull_discriminant_value(nonnull, nndiscr as u128, discr_size)
})?
}
// The discriminant_value intrinsic returns 0 for non-sum types.
Array { .. } | FatPointer { .. } | Scalar { .. } | Univariant { .. } |
Vector { .. } | UntaggedUnion { .. } => 0,
Array { .. } |
FatPointer { .. } |
Scalar { .. } |
Univariant { .. } |
Vector { .. } |
UntaggedUnion { .. } => 0,
};
Ok(discr_val)
}
fn read_nonnull_discriminant_value(&self, ptr: MemoryPointer, nndiscr: u128, discr_size: u64) -> EvalResult<'tcx, u128> {
trace!("read_nonnull_discriminant_value: {:?}, {}, {}", ptr, nndiscr, discr_size);
fn read_nonnull_discriminant_value(
&self,
ptr: MemoryPointer,
nndiscr: u128,
discr_size: u64,
) -> EvalResult<'tcx, u128> {
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{ kind: EvalErrorKind::ReadPointerAsBytes, .. }) => true,
Ok(_) |
Err(EvalError { kind: EvalErrorKind::ReadPointerAsBytes, .. }) => true,
Err(e) => return Err(e),
};
assert!(nndiscr == 0 || nndiscr == 1);

View File

@ -5,16 +5,14 @@
use syntax::codemap::DUMMY_SP;
use syntax::ast::{self, Mutability};
use super::{
EvalResult,
EvalContext, eval_context,
MemoryPointer, MemoryKind,
Value, PrimVal,
Machine,
};
use super::{EvalResult, EvalContext, eval_context, MemoryPointer, MemoryKind, Value, PrimVal,
Machine};
impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
pub(crate) fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> {
pub(crate) fn fulfill_obligation(
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
) -> traits::Vtable<'tcx, ()> {
// Do the initial selection for the obligation. This yields the shallow result we are
// looking for -- that is, what specific impl.
self.tcx.infer_ctxt().enter(|infcx| {
@ -43,15 +41,25 @@ pub(crate) fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> tr
/// The `trait_ref` encodes the erased self type. Hence if we are
/// making an object `Foo<Trait>` from a value of type `Foo<T>`, then
/// `trait_ref` would map `T:Trait`.
pub fn get_vtable(&mut self, ty: Ty<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> EvalResult<'tcx, MemoryPointer> {
pub fn get_vtable(
&mut self,
ty: Ty<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
) -> EvalResult<'tcx, MemoryPointer> {
debug!("get_vtable(trait_ref={:?})", trait_ref);
let size = self.type_size(trait_ref.self_ty())?.expect("can't create a vtable for an unsized type");
let size = self.type_size(trait_ref.self_ty())?.expect(
"can't create a vtable for an unsized type",
);
let align = self.type_align(trait_ref.self_ty())?;
let ptr_size = self.memory.pointer_size();
let methods = ::rustc::traits::get_vtable_methods(self.tcx, trait_ref);
let vtable = self.memory.allocate(ptr_size * (3 + methods.count() as u64), ptr_size, MemoryKind::UninitializedStatic)?;
let vtable = self.memory.allocate(
ptr_size * (3 + methods.count() as u64),
ptr_size,
MemoryKind::UninitializedStatic,
)?;
let drop = eval_context::resolve_drop_in_place(self.tcx, ty);
let drop = self.memory.create_fn_alloc(drop);
@ -71,12 +79,18 @@ pub fn get_vtable(&mut self, ty: Ty<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) ->
}
}
self.memory.mark_static_initalized(vtable.alloc_id, Mutability::Mutable)?;
self.memory.mark_static_initalized(
vtable.alloc_id,
Mutability::Mutable,
)?;
Ok(vtable)
}
pub fn read_drop_type_from_vtable(&self, vtable: MemoryPointer) -> EvalResult<'tcx, Option<ty::Instance<'tcx>>> {
pub fn read_drop_type_from_vtable(
&self,
vtable: MemoryPointer,
) -> EvalResult<'tcx, Option<ty::Instance<'tcx>>> {
// we don't care about the pointee type, we just want a pointer
match self.read_ptr(vtable, self.tcx.mk_nil_ptr())? {
// some values don't need to call a drop impl, so the value is null
@ -86,10 +100,15 @@ pub fn read_drop_type_from_vtable(&self, vtable: MemoryPointer) -> EvalResult<'t
}
}
pub fn read_size_and_align_from_vtable(&self, vtable: MemoryPointer) -> EvalResult<'tcx, (u64, u64)> {
pub fn read_size_and_align_from_vtable(
&self,
vtable: MemoryPointer,
) -> EvalResult<'tcx, (u64, u64)> {
let pointer_size = self.memory.pointer_size();
let size = self.memory.read_usize(vtable.offset(pointer_size, self)?)?;
let align = self.memory.read_usize(vtable.offset(pointer_size * 2, self)?)?;
let align = self.memory.read_usize(
vtable.offset(pointer_size * 2, self)?,
)?;
Ok((size, align))
}
@ -103,8 +122,11 @@ pub(crate) fn resolve_associated_const(
let vtable = self.fulfill_obligation(trait_ref);
if let traits::VtableImpl(vtable_impl) = vtable {
let name = self.tcx.item_name(def_id);
let assoc_const_opt = self.tcx.associated_items(vtable_impl.impl_def_id)
.find(|item| item.kind == ty::AssociatedKind::Const && item.name == name);
let assoc_const_opt = self.tcx.associated_items(vtable_impl.impl_def_id).find(
|item| {
item.kind == ty::AssociatedKind::Const && item.name == name
},
);
if let Some(assoc_const) = assoc_const_opt {
return ty::Instance::new(assoc_const.def_id, vtable_impl.substs);
}

View File

@ -8,14 +8,8 @@
use rustc::traits::Reveal;
use rustc::middle::region::CodeExtent;
use super::{
EvalError, EvalResult, EvalErrorKind,
EvalContext, DynamicLifetime,
AccessKind,
Value,
Lvalue, LvalueExtra,
Machine,
};
use super::{EvalError, EvalResult, EvalErrorKind, EvalContext, DynamicLifetime, AccessKind, Value,
Lvalue, LvalueExtra, Machine};
pub type ValidationQuery<'tcx> = ValidationOperand<'tcx, Lvalue>;
@ -39,7 +33,11 @@ fn acquiring(self) -> bool {
// Validity checks
impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
pub(crate) fn validation_op(&mut self, op: ValidationOp, operand: &ValidationOperand<'tcx, mir::Lvalue<'tcx>>) -> EvalResult<'tcx> {
pub(crate) fn validation_op(
&mut self,
op: ValidationOp,
operand: &ValidationOperand<'tcx, mir::Lvalue<'tcx>>,
) -> EvalResult<'tcx> {
// If mir-emit-validate is set to 0 (i.e., disabled), we may still see validation commands
// because other crates may have been compiled with mir-emit-validate > 0. Ignore those
// commands. This makes mir-emit-validate also a flag to control whether miri will do
@ -73,14 +71,19 @@ pub(crate) fn validation_op(&mut self, op: ValidationOp, operand: &ValidationOpe
// Now test
let name = self.stack[self.cur_frame()].instance.to_string();
if RE.is_match(&name) {
return Ok(())
return Ok(());
}
}
// We need to monomorphize ty *without* erasing lifetimes
let ty = operand.ty.subst(self.tcx, self.substs());
let lval = self.eval_lvalue(&operand.lval)?;
let query = ValidationQuery { lval, ty, re: operand.re, mutbl: operand.mutbl };
let query = ValidationQuery {
lval,
ty,
re: operand.re,
mutbl: operand.mutbl,
};
// Check the mode, and also perform mode-specific operations
let mode = match op {
@ -88,9 +91,14 @@ pub(crate) fn validation_op(&mut self, op: ValidationOp, operand: &ValidationOpe
ValidationOp::Release => ValidationMode::ReleaseUntil(None),
ValidationOp::Suspend(ce) => {
if query.mutbl == MutMutable {
let lft = DynamicLifetime { frame: self.cur_frame(), region: Some(ce) };
let lft = DynamicLifetime {
frame: self.cur_frame(),
region: Some(ce),
};
trace!("Suspending {:?} until {:?}", query, ce);
self.suspended.entry(lft).or_insert_with(Vec::new).push(query.clone());
self.suspended.entry(lft).or_insert_with(Vec::new).push(
query.clone(),
);
}
ValidationMode::ReleaseUntil(Some(ce))
}
@ -101,7 +109,10 @@ pub(crate) fn validation_op(&mut self, op: ValidationOp, operand: &ValidationOpe
pub(crate) fn end_region(&mut self, ce: CodeExtent) -> EvalResult<'tcx> {
self.memory.locks_lifetime_ended(Some(ce));
// Recover suspended lvals
let lft = DynamicLifetime { frame: self.cur_frame(), region: Some(ce) };
let lft = DynamicLifetime {
frame: self.cur_frame(),
region: Some(ce),
};
if let Some(queries) = self.suspended.remove(&lft) {
for query in queries {
trace!("Recovering {:?} from suspension", query);
@ -118,16 +129,19 @@ fn normalize_type_unerased(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
// We copy a bunch of stuff from rustc/infer/mod.rs to be able to tweak its behavior
fn normalize_projections_in<'a, 'gcx, 'tcx, T>(
self_: &InferCtxt<'a, 'gcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
value: &T)
-> T::Lifted
where T: TypeFoldable<'tcx> + ty::Lift<'gcx>
self_: &InferCtxt<'a, 'gcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
value: &T,
) -> T::Lifted
where
T: TypeFoldable<'tcx> + ty::Lift<'gcx>,
{
let mut selcx = traits::SelectionContext::new(self_);
let cause = traits::ObligationCause::dummy();
let traits::Normalized { value: result, obligations } =
traits::normalize(&mut selcx, param_env, cause, value);
let traits::Normalized {
value: result,
obligations,
} = traits::normalize(&mut selcx, param_env, cause, value);
let mut fulfill_cx = traits::FulfillmentContext::new();
@ -139,12 +153,13 @@ fn normalize_projections_in<'a, 'gcx, 'tcx, T>(
}
fn drain_fulfillment_cx_or_panic<'a, 'gcx, 'tcx, T>(
self_: &InferCtxt<'a, 'gcx, 'tcx>,
span: Span,
fulfill_cx: &mut traits::FulfillmentContext<'tcx>,
result: &T)
-> T::Lifted
where T: TypeFoldable<'tcx> + ty::Lift<'gcx>
self_: &InferCtxt<'a, 'gcx, 'tcx>,
span: Span,
fulfill_cx: &mut traits::FulfillmentContext<'tcx>,
result: &T,
) -> T::Lifted
where
T: TypeFoldable<'tcx> + ty::Lift<'gcx>,
{
// In principle, we only need to do this so long as `result`
// contains unbound type parameters. It could be a slight
@ -152,13 +167,23 @@ fn drain_fulfillment_cx_or_panic<'a, 'gcx, 'tcx, T>(
match fulfill_cx.select_all_or_error(self_) {
Ok(()) => { }
Err(errors) => {
span_bug!(span, "Encountered errors `{:?}` resolving bounds after type-checking",
errors);
span_bug!(
span,
"Encountered errors `{:?}` resolving bounds after type-checking",
errors
);
}
}
let result = self_.resolve_type_vars_if_possible(result);
let result = self_.tcx.fold_regions(&result, &mut false, |r, _| match *r { ty::ReVar(_) => self_.tcx.types.re_erased, _ => r });
let result = self_.tcx.fold_regions(
&result,
&mut false,
|r, _| match *r {
ty::ReVar(_) => self_.tcx.types.re_erased,
_ => r,
},
);
match self_.tcx.lift_to_global(&result) {
Some(result) => result,
@ -169,10 +194,11 @@ fn drain_fulfillment_cx_or_panic<'a, 'gcx, 'tcx, T>(
}
trait MyTransNormalize<'gcx>: TypeFoldable<'gcx> {
fn my_trans_normalize<'a, 'tcx>(&self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>)
-> Self;
fn my_trans_normalize<'a, 'tcx>(
&self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Self;
}
macro_rules! items { ($($item:item)+) => ($($item)+) }
@ -200,7 +226,8 @@ fn my_trans_normalize<'a, 'tcx>(&self,
);
fn normalize_associated_type<'a, 'tcx, T>(self_: TyCtxt<'a, 'tcx, 'tcx>, value: &T) -> T
where T: MyTransNormalize<'tcx>
where
T: MyTransNormalize<'tcx>,
{
let param_env = ty::ParamEnv::empty(Reveal::All);
@ -225,12 +252,26 @@ fn validate_variant(
for (idx, field) in variant.fields.iter().enumerate() {
let field_ty = field.ty(self.tcx, subst);
let field_lvalue = self.lvalue_field(query.lval, idx, query.ty, field_ty)?;
self.validate(ValidationQuery { lval: field_lvalue, ty: field_ty, ..query }, mode)?;
self.validate(
ValidationQuery {
lval: field_lvalue,
ty: field_ty,
..query
},
mode,
)?;
}
Ok(())
}
fn validate_ptr(&mut self, val: Value, pointee_ty: Ty<'tcx>, re: Option<CodeExtent>, mutbl: Mutability, mode: ValidationMode) -> EvalResult<'tcx> {
fn validate_ptr(
&mut self,
val: Value,
pointee_ty: Ty<'tcx>,
re: Option<CodeExtent>,
mutbl: Mutability,
mode: ValidationMode,
) -> EvalResult<'tcx> {
// Check alignment and non-NULLness
let (_, align) = self.size_and_align_of_dst(pointee_ty, val)?;
let ptr = val.into_ptr(&self.memory)?;
@ -238,30 +279,39 @@ fn validate_ptr(&mut self, val: Value, pointee_ty: Ty<'tcx>, re: Option<CodeExte
// Recurse
let pointee_lvalue = self.val_to_lvalue(val, pointee_ty)?;
self.validate(ValidationQuery { lval: pointee_lvalue, ty: pointee_ty, re, mutbl }, mode)
self.validate(
ValidationQuery {
lval: pointee_lvalue,
ty: pointee_ty,
re,
mutbl,
},
mode,
)
}
/// Validate the lvalue at the given type. If `acquire` is false, just do a release of all write locks
#[inline]
fn validate(&mut self, query: ValidationQuery<'tcx>, mode: ValidationMode) -> EvalResult<'tcx>
{
fn validate(&mut self, query: ValidationQuery<'tcx>, mode: ValidationMode) -> EvalResult<'tcx> {
match self.try_validate(query, mode) {
// ReleaseUntil(None) of an uninitalized variable is a NOP. This is needed because
// we have to release the return value of a function; due to destination-passing-style
// the callee may directly write there.
// TODO: Ideally we would know whether the destination is already initialized, and only
// release if it is. But of course that can't even always be statically determined.
Err(EvalError{ kind: EvalErrorKind::ReadUndefBytes, ..})
if mode == ValidationMode::ReleaseUntil(None)
=> {
Err(EvalError { kind: EvalErrorKind::ReadUndefBytes, .. })
if mode == ValidationMode::ReleaseUntil(None) => {
return Ok(());
}
res => res,
}
}
fn try_validate(&mut self, mut query: ValidationQuery<'tcx>, mode: ValidationMode) -> EvalResult<'tcx>
{
fn try_validate(
&mut self,
mut query: ValidationQuery<'tcx>,
mode: ValidationMode,
) -> EvalResult<'tcx> {
use rustc::ty::TypeVariants::*;
use rustc::ty::RegionKind::*;
use rustc::ty::AdtKind;
@ -284,12 +334,13 @@ fn try_validate(&mut self, mut query: ValidationQuery<'tcx>, mode: ValidationMod
Lvalue::Local { frame, local } => {
let res = self.stack[frame].get_local(local);
match (res, mode) {
(Err(EvalError{ kind: EvalErrorKind::DeadLocal, ..}), ValidationMode::Recover(_)) => {
(Err(EvalError { kind: EvalErrorKind::DeadLocal, .. }),
ValidationMode::Recover(_)) => {
return Ok(());
}
_ => {},
_ => {}
}
},
}
_ => {}
}
@ -300,12 +351,14 @@ fn try_validate(&mut self, mut query: ValidationQuery<'tcx>, mode: ValidationMod
// just assembles pieces (that each own their memory) together to a larger whole.
// TODO: Currently, we don't acquire locks for padding and discriminants. We should.
let is_owning = match query.ty.sty {
TyInt(_) | TyUint(_) | TyRawPtr(_) |
TyBool | TyFloat(_) | TyChar | TyStr |
TyInt(_) | TyUint(_) | TyRawPtr(_) | TyBool | TyFloat(_) | TyChar | TyStr |
TyRef(..) | TyFnPtr(..) | TyFnDef(..) | TyNever => true,
TyAdt(adt, _) if adt.is_box() => true,
TySlice(_) | TyAdt(_, _) | TyTuple(..) | TyClosure(..) | TyArray(..) | TyDynamic(..) => false,
TyParam(_) | TyInfer(_) | TyProjection(_) | TyAnon(..) | TyError => bug!("I got an incomplete/unnormalized type for validation"),
TySlice(_) | TyAdt(_, _) | TyTuple(..) | TyClosure(..) | TyArray(..) |
TyDynamic(..) => false,
TyParam(_) | TyInfer(_) | TyProjection(_) | TyAnon(..) | TyError => {
bug!("I got an incomplete/unnormalized type for validation")
}
};
if is_owning {
// We need to lock. So we need memory. So we have to force_acquire.
@ -322,7 +375,11 @@ fn try_validate(&mut self, mut query: ValidationQuery<'tcx>, mode: ValidationMod
}
None => {
// The only unsized typ we concider "owning" is TyStr.
assert_eq!(query.ty.sty, TyStr, "Found a surprising unsized owning type");
assert_eq!(
query.ty.sty,
TyStr,
"Found a surprising unsized owning type"
);
// The extra must be the length, in bytes.
match extra {
LvalueExtra::Length(len) => len,
@ -334,20 +391,45 @@ fn try_validate(&mut self, mut query: ValidationQuery<'tcx>, mode: ValidationMod
if len > 0 {
let ptr = ptr.to_ptr()?;
match query.mutbl {
MutImmutable =>
MutImmutable => {
if mode.acquiring() {
self.memory.acquire_lock(ptr, len, query.re, AccessKind::Read)?;
self.memory.acquire_lock(
ptr,
len,
query.re,
AccessKind::Read,
)?;
}
// No releasing of read locks, ever.
MutMutable =>
}
// No releasing of read locks, ever.
MutMutable => {
match mode {
ValidationMode::Acquire =>
self.memory.acquire_lock(ptr, len, query.re, AccessKind::Write)?,
ValidationMode::Recover(ending_ce) =>
self.memory.recover_write_lock(ptr, len, query.re, ending_ce)?,
ValidationMode::ReleaseUntil(suspended_ce) =>
self.memory.suspend_write_lock(ptr, len, query.re, suspended_ce)?,
ValidationMode::Acquire => {
self.memory.acquire_lock(
ptr,
len,
query.re,
AccessKind::Write,
)?
}
ValidationMode::Recover(ending_ce) => {
self.memory.recover_write_lock(
ptr,
len,
query.re,
ending_ce,
)?
}
ValidationMode::ReleaseUntil(suspended_ce) => {
self.memory.suspend_write_lock(
ptr,
len,
query.re,
suspended_ce,
)?
}
}
}
}
}
}
@ -362,10 +444,12 @@ fn try_validate(&mut self, mut query: ValidationQuery<'tcx>, mode: ValidationMod
// TODO: Check if these are valid bool/float/codepoint/UTF-8, respectively (and in particular, not undef).
Ok(())
}
TyNever => {
err!(ValidationFailure(format!("The empty type is never valid.")))
}
TyRef(region, ty::TypeAndMut { ty: pointee_ty, mutbl }) => {
TyNever => err!(ValidationFailure(format!("The empty type is never valid."))),
TyRef(region,
ty::TypeAndMut {
ty: pointee_ty,
mutbl,
}) => {
let val = self.read_lvalue(query.lval)?;
// Sharing restricts our context
if mutbl == MutImmutable {
@ -378,7 +462,7 @@ fn try_validate(&mut self, mut query: ValidationQuery<'tcx>, mode: ValidationMod
ReScope(ce) => query.re = Some(ce),
// It is possible for us to encounter erased lifetimes here because the lifetimes in
// this functions' Subst will be erased.
_ => {},
_ => {}
}
}
self.validate_ptr(val, pointee_ty, query.re, query.mutbl, mode)
@ -388,7 +472,9 @@ fn try_validate(&mut self, mut query: ValidationQuery<'tcx>, mode: ValidationMod
self.validate_ptr(val, query.ty.boxed_ty(), query.re, query.mutbl, mode)
}
TyFnPtr(_sig) => {
let ptr = self.read_lvalue(query.lval)?.into_ptr(&self.memory)?.to_ptr()?;
let ptr = self.read_lvalue(query.lval)?
.into_ptr(&self.memory)?
.to_ptr()?;
self.memory.get_fn(ptr)?;
// TODO: Check if the signature matches (should be the same check as what terminator/mod.rs already does on call?).
Ok(())
@ -403,18 +489,37 @@ fn try_validate(&mut self, mut query: ValidationQuery<'tcx>, mode: ValidationMod
TySlice(elem_ty) => {
let len = match query.lval {
Lvalue::Ptr { extra: LvalueExtra::Length(len), .. } => len,
_ => bug!("acquire_valid of a TySlice given non-slice lvalue: {:?}", query.lval),
_ => {
bug!(
"acquire_valid of a TySlice given non-slice lvalue: {:?}",
query.lval
)
}
};
for i in 0..len {
let inner_lvalue = self.lvalue_index(query.lval, query.ty, i)?;
self.validate(ValidationQuery { lval: inner_lvalue, ty: elem_ty, ..query }, mode)?;
self.validate(
ValidationQuery {
lval: inner_lvalue,
ty: elem_ty,
..query
},
mode,
)?;
}
Ok(())
}
TyArray(elem_ty, len) => {
for i in 0..len {
let inner_lvalue = self.lvalue_index(query.lval, query.ty, i as u64)?;
self.validate(ValidationQuery { lval: inner_lvalue, ty: elem_ty, ..query }, mode)?;
self.validate(
ValidationQuery {
lval: inner_lvalue,
ty: elem_ty,
..query
},
mode,
)?;
}
Ok(())
}
@ -422,7 +527,12 @@ fn try_validate(&mut self, mut query: ValidationQuery<'tcx>, mode: ValidationMod
// Check that this is a valid vtable
let vtable = match query.lval {
Lvalue::Ptr { extra: LvalueExtra::Vtable(vtable), .. } => vtable,
_ => bug!("acquire_valid of a TyDynamic given non-trait-object lvalue: {:?}", query.lval),
_ => {
bug!(
"acquire_valid of a TyDynamic given non-trait-object lvalue: {:?}",
query.lval
)
}
};
self.read_size_and_align_from_vtable(vtable)?;
// TODO: Check that the vtable contains all the function pointers we expect it to have.
@ -433,7 +543,9 @@ fn try_validate(&mut self, mut query: ValidationQuery<'tcx>, mode: ValidationMod
Ok(())
}
TyAdt(adt, subst) => {
if Some(adt.did) == self.tcx.lang_items.unsafe_cell_type() && query.mutbl == MutImmutable {
if Some(adt.did) == self.tcx.lang_items.unsafe_cell_type() &&
query.mutbl == MutImmutable
{
// No locks for shared unsafe cells. Also no other validation, the only field is private anyway.
return Ok(());
}
@ -445,8 +557,9 @@ fn try_validate(&mut self, mut query: ValidationQuery<'tcx>, mode: ValidationMod
let discr = self.read_discriminant_value(ptr, query.ty)?;
// Get variant index for discriminant
let variant_idx = adt.discriminants(self.tcx)
.position(|variant_discr| variant_discr.to_u128_unchecked() == discr);
let variant_idx = adt.discriminants(self.tcx).position(|variant_discr| {
variant_discr.to_u128_unchecked() == discr
});
let variant_idx = match variant_idx {
Some(val) => val,
None => return err!(InvalidDiscriminant),
@ -456,13 +569,22 @@ fn try_validate(&mut self, mut query: ValidationQuery<'tcx>, mode: ValidationMod
if variant.fields.len() > 0 {
// Downcast to this variant, if needed
let lval = if adt.variants.len() > 1 {
self.eval_lvalue_projection(query.lval, query.ty, &mir::ProjectionElem::Downcast(adt, variant_idx))?
self.eval_lvalue_projection(
query.lval,
query.ty,
&mir::ProjectionElem::Downcast(adt, variant_idx),
)?
} else {
query.lval
};
// Recursively validate the fields
self.validate_variant(ValidationQuery { lval, ..query} , variant, subst, mode)
self.validate_variant(
ValidationQuery { lval, ..query },
variant,
subst,
mode,
)
} else {
// No fields, nothing left to check. Downcasting may fail, e.g. in case of a CEnum.
Ok(())
@ -481,20 +603,34 @@ fn try_validate(&mut self, mut query: ValidationQuery<'tcx>, mode: ValidationMod
TyTuple(ref types, _) => {
for (idx, field_ty) in types.iter().enumerate() {
let field_lvalue = self.lvalue_field(query.lval, idx, query.ty, field_ty)?;
self.validate(ValidationQuery { lval: field_lvalue, ty: field_ty, ..query }, mode)?;
self.validate(
ValidationQuery {
lval: field_lvalue,
ty: field_ty,
..query
},
mode,
)?;
}
Ok(())
}
TyClosure(def_id, ref closure_substs) => {
for (idx, field_ty) in closure_substs.upvar_tys(def_id, self.tcx).enumerate() {
let field_lvalue = self.lvalue_field(query.lval, idx, query.ty, field_ty)?;
self.validate(ValidationQuery { lval: field_lvalue, ty: field_ty, ..query }, mode)?;
self.validate(
ValidationQuery {
lval: field_lvalue,
ty: field_ty,
..query
},
mode,
)?;
}
// TODO: Check if the signature matches (should be the same check as what terminator/mod.rs already does on call?).
// Is there other things we can/should check? Like vtable pointers?
Ok(())
}
_ => bug!("We already establishd that this is a type we support.")
_ => bug!("We already establishd that this is a type we support."),
}
}
}

View File

@ -3,12 +3,7 @@
use rustc::ty::layout::HasDataLayout;
use super::{
EvalResult,
Memory, MemoryPointer, HasMemory, PointerArithmetic,
Machine,
PtrAndAlign,
};
use super::{EvalResult, Memory, MemoryPointer, HasMemory, PointerArithmetic, Machine, PtrAndAlign};
pub(super) fn bytes_to_f32(bytes: u128) -> f32 {
f32::from_bits(bytes as u32)
@ -70,8 +65,10 @@ pub fn signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResult<'tcx,
match self.primval {
PrimVal::Bytes(b) => {
assert_eq!(b as u64 as u128, b);
Ok(Pointer::from(PrimVal::Bytes(layout.signed_offset(b as u64, i)? as u128)))
},
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!(ReadUndefBytes),
}
@ -82,8 +79,10 @@ pub fn offset<C: HasDataLayout>(self, i: u64, cx: C) -> EvalResult<'tcx, Self> {
match self.primval {
PrimVal::Bytes(b) => {
assert_eq!(b as u64 as u128, b);
Ok(Pointer::from(PrimVal::Bytes(layout.offset(b as u64, i)? as u128)))
},
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!(ReadUndefBytes),
}
@ -94,8 +93,10 @@ pub fn wrapping_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResu
match self.primval {
PrimVal::Bytes(b) => {
assert_eq!(b as u64 as u128, b);
Ok(Pointer::from(PrimVal::Bytes(layout.wrapping_signed_offset(b as u64, i) as u128)))
},
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!(ReadUndefBytes),
}
@ -158,10 +159,9 @@ pub enum PrimValKind {
I8, I16, I32, I64, I128,
U8, U16, U32, U64, U128,
F32, F64,
Ptr, FnPtr,
Bool,
Char,
Ptr,
FnPtr,
}
impl<'a, 'tcx: 'a> Value {
@ -172,26 +172,35 @@ pub fn by_ref(ptr: Pointer) -> Self {
/// Convert the value into a pointer (or a pointer-sized integer). If the value is a ByRef,
/// this may have to perform a load.
pub fn into_ptr<M: Machine<'tcx>>(&self, mem: &Memory<'a, 'tcx, M>) -> EvalResult<'tcx, Pointer> {
pub fn into_ptr<M: Machine<'tcx>>(
&self,
mem: &Memory<'a, 'tcx, M>,
) -> EvalResult<'tcx, Pointer> {
use self::Value::*;
match *self {
ByRef(PtrAndAlign { ptr, aligned }) => {
mem.read_maybe_aligned(aligned, |mem| mem.read_ptr(ptr.to_ptr()?) )
},
ByVal(ptr) | ByValPair(ptr, _) => Ok(ptr.into()),
mem.read_maybe_aligned(aligned, |mem| mem.read_ptr(ptr.to_ptr()?))
}
ByVal(ptr) |
ByValPair(ptr, _) => Ok(ptr.into()),
}
}
pub(super) fn into_ptr_vtable_pair<M: Machine<'tcx>>(
&self,
mem: &Memory<'a, 'tcx, M>
mem: &Memory<'a, 'tcx, M>,
) -> EvalResult<'tcx, (Pointer, MemoryPointer)> {
use self::Value::*;
match *self {
ByRef(PtrAndAlign { ptr: ref_ptr, aligned }) => {
ByRef(PtrAndAlign {
ptr: ref_ptr,
aligned,
}) => {
mem.read_maybe_aligned(aligned, |mem| {
let ptr = mem.read_ptr(ref_ptr.to_ptr()?)?;
let vtable = mem.read_ptr(ref_ptr.offset(mem.pointer_size(), mem.layout)?.to_ptr()?)?;
let vtable = mem.read_ptr(
ref_ptr.offset(mem.pointer_size(), mem.layout)?.to_ptr()?,
)?;
Ok((ptr, vtable.to_ptr()?))
})
}
@ -203,21 +212,29 @@ pub(super) fn into_ptr_vtable_pair<M: Machine<'tcx>>(
}
}
pub(super) fn into_slice<M: Machine<'tcx>>(&self, mem: &Memory<'a, 'tcx, M>) -> EvalResult<'tcx, (Pointer, u64)> {
pub(super) fn into_slice<M: Machine<'tcx>>(
&self,
mem: &Memory<'a, 'tcx, M>,
) -> EvalResult<'tcx, (Pointer, u64)> {
use self::Value::*;
match *self {
ByRef(PtrAndAlign { ptr: ref_ptr, aligned } ) => {
ByRef(PtrAndAlign {
ptr: ref_ptr,
aligned,
}) => {
mem.read_maybe_aligned(aligned, |mem| {
let ptr = mem.read_ptr(ref_ptr.to_ptr()?)?;
let len = mem.read_usize(ref_ptr.offset(mem.pointer_size(), mem.layout)?.to_ptr()?)?;
let len = mem.read_usize(
ref_ptr.offset(mem.pointer_size(), mem.layout)?.to_ptr()?,
)?;
Ok((ptr, len))
})
},
}
ByValPair(ptr, val) => {
let len = val.to_u128()?;
assert_eq!(len as u64 as u128, len);
Ok((ptr.into(), len as u64))
},
}
ByVal(PrimVal::Undef) => err!(ReadUndefBytes),
ByVal(_) => bug!("expected ptr and length, got {:?}", self),
}
@ -349,7 +366,7 @@ pub fn is_signed_int(self) -> bool {
}
}
pub fn is_float(self) -> bool {
pub fn is_float(self) -> bool {
use self::PrimValKind::*;
match self {
F32 | F64 => true,

View File

@ -16,7 +16,11 @@ 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);
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 = MIRI_PATH.into();
@ -26,7 +30,9 @@ fn compile_fail(sysroot: &Path, path: &str, target: &str, host: &str, fullmir: b
// skip fullmir on nonhost
return;
}
let sysroot = Path::new(&std::env::var("HOME").unwrap()).join(".xargo").join("HOST");
let sysroot = Path::new(&std::env::var("HOME").unwrap())
.join(".xargo")
.join("HOST");
config.target_rustcflags = Some(format!("--sysroot {}", sysroot.to_str().unwrap()));
config.src_base = PathBuf::from(path.to_string());
} else {
@ -50,12 +56,13 @@ fn run_pass(path: &str) {
}
fn miri_pass(path: &str, target: &str, host: &str, fullmir: bool, opt: bool) {
let opt_str = if opt {
" with optimizations"
} else {
""
};
eprintln!("## Running run-pass tests in {} against miri for target {}{}", path, target, opt_str);
let opt_str = if opt { " with optimizations" } else { "" };
eprintln!(
"## Running run-pass tests in {} against miri for target {}{}",
path,
target,
opt_str
);
let mut config = compiletest::default_config();
config.mode = "mir-opt".parse().expect("Invalid mode");
config.src_base = PathBuf::from(path);
@ -68,7 +75,9 @@ fn miri_pass(path: &str, target: &str, host: &str, fullmir: bool, opt: bool) {
// skip fullmir on nonhost
return;
}
let sysroot = Path::new(&std::env::var("HOME").unwrap()).join(".xargo").join("HOST");
let sysroot = Path::new(&std::env::var("HOME").unwrap())
.join(".xargo")
.join("HOST");
flags.push(format!("--sysroot {}", sysroot.to_str().unwrap()));
}
if opt {
@ -99,7 +108,9 @@ fn for_all_targets<F: FnMut(String)>(sysroot: &Path, mut f: F) {
let target_dir = sysroot.join("lib").join("rustlib");
for entry in std::fs::read_dir(target_dir).expect("invalid sysroot") {
let entry = entry.unwrap();
if !is_target_dir(entry.path()) { continue; }
if !is_target_dir(entry.path()) {
continue;
}
let target = entry.file_name().into_string().unwrap();
f(target);
}
@ -125,7 +136,9 @@ fn get_host() -> String {
.expect("rustc not found for -vV")
.stdout;
let host = std::str::from_utf8(&host).expect("sysroot is not utf8");
let host = host.split("\nhost: ").nth(1).expect("no host: part in rustc -vV");
let host = host.split("\nhost: ").nth(1).expect(
"no host: part in rustc -vV",
);
let host = host.split('\n').next().expect("no \n after host");
String::from(host)
}