rustfmt
Except for error.rs, the result there looks rather ugly
This commit is contained in:
parent
85fd3f8e43
commit
1326aed02c
@ -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]
|
||||
|
@ -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) }
|
||||
}
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -1,3 +1,2 @@
|
||||
#[inline(never)]
|
||||
pub fn main() {
|
||||
}
|
||||
pub fn main() {}
|
||||
|
@ -7,9 +7,7 @@
|
||||
|
||||
#[bench]
|
||||
fn noop(bencher: &mut Bencher) {
|
||||
bencher.iter(|| {
|
||||
smoke_helper::main();
|
||||
})
|
||||
bencher.iter(|| { smoke_helper::main(); })
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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()];
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
181
miri/fn_call.rs
181
miri/fn_call.rs
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
27
miri/lib.rs
27
miri/lib.rs
@ -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(())
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
57
miri/tls.rs
57
miri/tls.rs
@ -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)?;
|
||||
|
@ -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))),
|
||||
}
|
||||
|
@ -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
@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)?,
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user