Merge branch 'master' into appveyor

This commit is contained in:
Ralf Jung 2019-02-11 10:05:29 +01:00 committed by GitHub
commit bab9abefa1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 160 additions and 56 deletions

View File

@ -39,6 +39,7 @@ directories = { version = "1.0", optional = true }
rustc_version = { version = "0.2.3", optional = true } rustc_version = { version = "0.2.3", optional = true }
env_logger = "0.6" env_logger = "0.6"
log = "0.4" log = "0.4"
shell-escape = "0.1.4"
# A noop dependency that changes in the Rust repository, it's a bit of a hack. # A noop dependency that changes in the Rust repository, it's a bit of a hack.
# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust` # See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
# for more information. # for more information.

View File

@ -48,6 +48,13 @@ Now you can run your project in Miri:
3. If you have a binary project, you can run it through Miri using `cargo 3. If you have a binary project, you can run it through Miri using `cargo
+nightly miri run`. +nightly miri run`.
You can pass arguments to Miri after the first `--`, and pass arguments to the
interpreted program or test suite after the second `--`. For example, `cargo
+nightly miri run -- -Zmiri-disable-validation` runs the program without
validation of basic type invariants and references. `cargo +nightly miri test
-- -- filter` passes `filter` to the test suite the same way `cargo test filter`
would.
When running code via `cargo miri`, the `miri` config flag is set. You can When running code via `cargo miri`, the `miri` config flag is set. You can
use this to exclude test cases that will fail under Miri because they do things use this to exclude test cases that will fail under Miri because they do things
Miri does not support: Miri does not support:

View File

@ -4,11 +4,13 @@
extern crate rustc_driver; extern crate rustc_driver;
extern crate test; extern crate test;
use self::miri::eval_main; use rustc_driver::{driver, Compilation};
use self::rustc_driver::{driver, Compilation};
use rustc::hir::def_id::LOCAL_CRATE; use rustc::hir::def_id::LOCAL_CRATE;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use miri::{MiriConfig, eval_main};
use crate::test::Bencher; use crate::test::Bencher;
pub struct MiriCompilerCalls<'a>(Rc<RefCell<&'a mut Bencher>>); pub struct MiriCompilerCalls<'a>(Rc<RefCell<&'a mut Bencher>>);
@ -50,7 +52,8 @@ pub fn run(filename: &str, bencher: &mut Bencher) {
); );
bencher.borrow_mut().iter(|| { bencher.borrow_mut().iter(|| {
eval_main(tcx, entry_def_id, false); let config = MiriConfig { validate: true, args: vec![] };
eval_main(tcx, entry_def_id, config);
}); });
state.session.abort_if_errors(); state.session.abort_if_errors();

View File

@ -10,7 +10,7 @@
const CARGO_MIRI_HELP: &str = r#"Interprets bin crates and tests in Miri const CARGO_MIRI_HELP: &str = r#"Interprets bin crates and tests in Miri
Usage: Usage:
cargo miri [subcommand] [options] [--] [<miri opts>...] cargo miri [subcommand] [options] [--] [<miri opts>...] [--] [<program opts>...]
Subcommands: Subcommands:
run Run binaries (default) run Run binaries (default)
@ -22,8 +22,9 @@
--features Features to compile for the package --features Features to compile for the package
-V, --version Print version info and exit -V, --version Print version info and exit
Other [options] are the same as `cargo rustc`. Everything after the "--" is Other [options] are the same as `cargo rustc`. Everything after the first "--" is
passed verbatim to Miri. passed verbatim to Miri, which will pass everything after the second "--" verbatim
to the interpreted program.
The config flag `miri` is automatically defined for convenience. You can use The config flag `miri` is automatically defined for convenience. You can use
it to configure the resource limits it to configure the resource limits
@ -355,11 +356,13 @@ fn in_cargo_miri() {
} }
cmd.arg(arg); cmd.arg(arg);
} }
// add "--" "-Zcargo-miri-marker" and the remaining user flags // Add "--" (to end the cargo flags), and then the user flags. We add markers around the user flags
// to be able to identify them later.
cmd cmd
.arg("--") .arg("--")
.arg("cargo-miri-marker") .arg("cargo-miri-marker-begin")
.args(args); .args(args)
.arg("cargo-miri-marker-end");
let path = std::env::current_exe().expect("current executable path invalid"); let path = std::env::current_exe().expect("current executable path invalid");
cmd.env("RUSTC_WRAPPER", path); cmd.env("RUSTC_WRAPPER", path);
if verbose { if verbose {
@ -413,10 +416,19 @@ fn inside_cargo_rustc() {
}; };
args.splice(0..0, miri::miri_default_args().iter().map(ToString::to_string)); args.splice(0..0, miri::miri_default_args().iter().map(ToString::to_string));
// see if we have cargo-miri-marker, which means we want to interpret this crate in Miri // See if we can find the cargo-miri markers. Those only get added to the binary we want to
// (and remove the marker). // run. They also serve to mark the user-defined arguments, which we have to move all the way to the
let needs_miri = if let Some(pos) = args.iter().position(|arg| arg == "cargo-miri-marker") { // end (they get added somewhere in the middle).
args.remove(pos); let needs_miri = if let Some(begin) = args.iter().position(|arg| arg == "cargo-miri-marker-begin") {
let end = args.iter().position(|arg| arg == "cargo-miri-marker-end").expect("Cannot find end marker");
// These mark the user arguments. We remove the first and last as they are the markers.
let mut user_args = args.drain(begin..=end);
assert_eq!(user_args.next().unwrap(), "cargo-miri-marker-begin");
assert_eq!(user_args.next_back().unwrap(), "cargo-miri-marker-end");
// Collect the rest and add it back at the end
let mut user_args = user_args.collect::<Vec<String>>();
args.append(&mut user_args);
// Run this in Miri
true true
} else { } else {
false false

View File

@ -25,6 +25,8 @@
use syntax::ast; use syntax::ast;
use rustc::hir::def_id::LOCAL_CRATE; use rustc::hir::def_id::LOCAL_CRATE;
use miri::MiriConfig;
struct MiriCompilerCalls { struct MiriCompilerCalls {
default: Box<RustcDefaultCalls>, default: Box<RustcDefaultCalls>,
/// whether we are building for the host /// whether we are building for the host
@ -94,9 +96,10 @@ impl<'a, 'tcx: 'a, 'hir> itemlikevisit::ItemLikeVisitor<'hir> for Visitor<'a, 't
fn visit_item(&mut self, i: &'hir hir::Item) { fn visit_item(&mut self, i: &'hir hir::Item) {
if let hir::ItemKind::Fn(.., body_id) = i.node { if let hir::ItemKind::Fn(.., body_id) = i.node {
if i.attrs.iter().any(|attr| attr.name() == "test") { if i.attrs.iter().any(|attr| attr.name() == "test") {
let config = MiriConfig { validate: true, args: vec![] };
let did = self.0.hir().body_owner_def_id(body_id); let did = self.0.hir().body_owner_def_id(body_id);
println!("running test: {}", self.0.def_path_debug_str(did)); println!("running test: {}", self.0.def_path_debug_str(did));
miri::eval_main(self.0, did, /*validate*/true); miri::eval_main(self.0, did, config);
self.1.session.abort_if_errors(); self.1.session.abort_if_errors();
} }
} }
@ -106,7 +109,8 @@ fn visit_impl_item(&mut self, _impl_item: &'hir hir::ImplItem) {}
} }
state.hir_crate.unwrap().visit_all_item_likes(&mut Visitor(tcx, state)); state.hir_crate.unwrap().visit_all_item_likes(&mut Visitor(tcx, state));
} else if let Some((entry_def_id, _)) = tcx.entry_fn(LOCAL_CRATE) { } else if let Some((entry_def_id, _)) = tcx.entry_fn(LOCAL_CRATE) {
miri::eval_main(tcx, entry_def_id, /*validate*/true); let config = MiriConfig { validate: true, args: vec![] };
miri::eval_main(tcx, entry_def_id, config);
state.session.abort_if_errors(); state.session.abort_if_errors();
} else { } else {

View File

@ -27,11 +27,11 @@
use rustc::hir::def_id::LOCAL_CRATE; use rustc::hir::def_id::LOCAL_CRATE;
use syntax::ast; use syntax::ast;
use miri::MiriConfig;
struct MiriCompilerCalls { struct MiriCompilerCalls {
default: Box<RustcDefaultCalls>, default: Box<RustcDefaultCalls>,
miri_config: MiriConfig,
/// Whether to enforce the validity invariant.
validate: bool,
} }
impl<'a> CompilerCalls<'a> for MiriCompilerCalls { impl<'a> CompilerCalls<'a> for MiriCompilerCalls {
@ -79,6 +79,8 @@ fn late_callback(
odir: &Option<PathBuf>, odir: &Option<PathBuf>,
ofile: &Option<PathBuf>, ofile: &Option<PathBuf>,
) -> Compilation { ) -> Compilation {
// Called *before* build_controller. Add filename to miri arguments.
self.miri_config.args.insert(0, input.filestem().to_string());
self.default.late_callback(codegen_backend, matches, sess, cstore, input, odir, ofile) self.default.late_callback(codegen_backend, matches, sess, cstore, input, odir, ofile)
} }
fn build_controller( fn build_controller(
@ -89,9 +91,9 @@ fn build_controller(
let this = *self; let this = *self;
let mut control = this.default.build_controller(sess, matches); let mut control = this.default.build_controller(sess, matches);
control.after_hir_lowering.callback = Box::new(after_hir_lowering); control.after_hir_lowering.callback = Box::new(after_hir_lowering);
let validate = this.validate; let miri_config = this.miri_config;
control.after_analysis.callback = control.after_analysis.callback =
Box::new(move |state| after_analysis(state, validate)); Box::new(move |state| after_analysis(state, miri_config.clone()));
control.after_analysis.stop = Compilation::Stop; control.after_analysis.stop = Compilation::Stop;
control control
} }
@ -107,7 +109,7 @@ fn after_hir_lowering(state: &mut CompileState) {
fn after_analysis<'a, 'tcx>( fn after_analysis<'a, 'tcx>(
state: &mut CompileState<'a, 'tcx>, state: &mut CompileState<'a, 'tcx>,
validate: bool, miri_config: MiriConfig,
) { ) {
init_late_loggers(); init_late_loggers();
state.session.abort_if_errors(); state.session.abort_if_errors();
@ -117,7 +119,7 @@ fn after_analysis<'a, 'tcx>(
let (entry_def_id, _) = tcx.entry_fn(LOCAL_CRATE).expect("no main function found!"); let (entry_def_id, _) = tcx.entry_fn(LOCAL_CRATE).expect("no main function found!");
miri::eval_main(tcx, entry_def_id, validate); miri::eval_main(tcx, entry_def_id, miri_config);
state.session.abort_if_errors(); state.session.abort_if_errors();
} }
@ -188,34 +190,51 @@ fn find_sysroot() -> String {
fn main() { fn main() {
init_early_loggers(); init_early_loggers();
let mut args: Vec<String> = std::env::args().collect();
// Parse our own -Z flags and remove them before rustc gets their hand on them. // Parse our arguments and split them across rustc and miri
let mut validate = true; let mut validate = true;
args.retain(|arg| { let mut rustc_args = vec![];
match arg.as_str() { let mut miri_args = vec![];
"-Zmiri-disable-validation" => { let mut after_dashdash = false;
validate = false; for arg in std::env::args() {
false if rustc_args.is_empty() {
}, // Very first arg: for rustc
_ => true rustc_args.push(arg);
} }
}); else if after_dashdash {
// Everything that comes is Miri args
miri_args.push(arg);
} else {
match arg.as_str() {
"-Zmiri-disable-validation" => {
validate = false;
},
"--" => {
after_dashdash = true;
}
_ => {
rustc_args.push(arg);
}
}
}
}
// Determine sysroot and let rustc know about it // Determine sysroot and let rustc know about it
let sysroot_flag = String::from("--sysroot"); let sysroot_flag = String::from("--sysroot");
if !args.contains(&sysroot_flag) { if !rustc_args.contains(&sysroot_flag) {
args.push(sysroot_flag); rustc_args.push(sysroot_flag);
args.push(find_sysroot()); rustc_args.push(find_sysroot());
} }
// Finally, add the default flags all the way in the beginning, but after the binary name. // Finally, add the default flags all the way in the beginning, but after the binary name.
args.splice(1..1, miri::miri_default_args().iter().map(ToString::to_string)); rustc_args.splice(1..1, miri::miri_default_args().iter().map(ToString::to_string));
trace!("rustc arguments: {:?}", args); debug!("rustc arguments: {:?}", rustc_args);
debug!("miri arguments: {:?}", miri_args);
let miri_config = MiriConfig { validate, args: miri_args };
let result = rustc_driver::run(move || { let result = rustc_driver::run(move || {
rustc_driver::run_compiler(&args, Box::new(MiriCompilerCalls { rustc_driver::run_compiler(&rustc_args, Box::new(MiriCompilerCalls {
default: Box::new(RustcDefaultCalls), default: Box::new(RustcDefaultCalls),
validate, miri_config,
}), None, None) }), None, None)
}); });
std::process::exit(result as i32); std::process::exit(result as i32);

View File

@ -57,16 +57,23 @@ pub fn miri_default_args() -> &'static [&'static str] {
&["-Zalways-encode-mir", "-Zmir-emit-retag", "-Zmir-opt-level=0", "--cfg=miri"] &["-Zalways-encode-mir", "-Zmir-emit-retag", "-Zmir-opt-level=0", "--cfg=miri"]
} }
/// Configuration needed to spawn a Miri instance
#[derive(Clone)]
pub struct MiriConfig {
pub validate: bool,
pub args: Vec<String>,
}
// Used by priroda // Used by priroda
pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>( pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>(
tcx: TyCtxt<'a, 'tcx, 'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>,
main_id: DefId, main_id: DefId,
validate: bool, config: MiriConfig,
) -> EvalResult<'tcx, EvalContext<'a, 'mir, 'tcx, Evaluator<'tcx>>> { ) -> EvalResult<'tcx, EvalContext<'a, 'mir, 'tcx, Evaluator<'tcx>>> {
let mut ecx = EvalContext::new( let mut ecx = EvalContext::new(
tcx.at(syntax::source_map::DUMMY_SP), tcx.at(syntax::source_map::DUMMY_SP),
ty::ParamEnv::reveal_all(), ty::ParamEnv::reveal_all(),
Evaluator::new(validate), Evaluator::new(config.validate),
); );
let main_instance = ty::Instance::mono(ecx.tcx.tcx, main_id); let main_instance = ty::Instance::mono(ecx.tcx.tcx, main_id);
@ -120,7 +127,7 @@ pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>(
// Second argument (argc): 1 // Second argument (argc): 1
let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?; let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
let argc = Scalar::from_int(1, dest.layout.size); let argc = Scalar::from_uint(config.args.len() as u128, dest.layout.size);
ecx.write_scalar(argc, dest)?; ecx.write_scalar(argc, dest)?;
// Store argc for macOS _NSGetArgc // Store argc for macOS _NSGetArgc
{ {
@ -130,18 +137,38 @@ pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>(
} }
// FIXME: extract main source file path // FIXME: extract main source file path
// Third argument (argv): &[b"foo"] // Third argument (argv): Created from config.args
const CMD: &str = "running-in-miri\0";
let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?; let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
let cmd = ecx.memory_mut().allocate_static_bytes(CMD.as_bytes()).with_default_tag(); // For Windows, construct a command string with all the aguments
let raw_str_layout = ecx.layout_of(ecx.tcx.mk_imm_ptr(ecx.tcx.types.u8))?; let mut cmd = String::new();
let cmd_place = ecx.allocate(raw_str_layout, MiriMemoryKind::Env.into()); for arg in config.args.iter() {
ecx.write_scalar(Scalar::Ptr(cmd), cmd_place.into())?; if !cmd.is_empty() {
ecx.memory_mut().mark_immutable(cmd_place.to_ptr()?.alloc_id)?; cmd.push(' ');
}
cmd.push_str(&*shell_escape::windows::escape(arg.as_str().into()));
}
cmd.push(std::char::from_u32(0).unwrap()); // don't forget 0 terminator
// Collect the pointers to the individual strings.
let mut argvs = Vec::<Pointer<Borrow>>::new();
for arg in config.args {
// Add 0 terminator
let mut arg = arg.into_bytes();
arg.push(0);
argvs.push(ecx.memory_mut().allocate_static_bytes(arg.as_slice()).with_default_tag());
}
// Make an array with all these pointers, in the Miri memory.
let argvs_layout = ecx.layout_of(ecx.tcx.mk_array(ecx.tcx.mk_imm_ptr(ecx.tcx.types.u8), argvs.len() as u64))?;
let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Env.into());
for (idx, arg) in argvs.into_iter().enumerate() {
let place = ecx.mplace_field(argvs_place, idx as u64)?;
ecx.write_scalar(Scalar::Ptr(arg), place.into())?;
}
ecx.memory_mut().mark_immutable(argvs_place.to_ptr()?.alloc_id)?;
// Write a pointe to that place as the argument.
let argv = argvs_place.ptr;
ecx.write_scalar(argv, dest)?;
// Store argv for macOS _NSGetArgv // Store argv for macOS _NSGetArgv
{ {
let argv = cmd_place.ptr;
ecx.write_scalar(argv, dest)?;
let argv_place = ecx.allocate(dest.layout, MiriMemoryKind::Env.into()); let argv_place = ecx.allocate(dest.layout, MiriMemoryKind::Env.into());
ecx.write_scalar(argv, argv_place.into())?; ecx.write_scalar(argv, argv_place.into())?;
ecx.machine.argv = Some(argv_place.ptr.to_ptr()?); ecx.machine.argv = Some(argv_place.ptr.to_ptr()?);
@ -149,7 +176,7 @@ pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>(
// Store cmdline as UTF-16 for Windows GetCommandLineW // Store cmdline as UTF-16 for Windows GetCommandLineW
{ {
let tcx = &{ecx.tcx.tcx}; let tcx = &{ecx.tcx.tcx};
let cmd_utf16: Vec<u16> = CMD.encode_utf16().collect(); let cmd_utf16: Vec<u16> = cmd.encode_utf16().collect();
let cmd_ptr = ecx.memory_mut().allocate( let cmd_ptr = ecx.memory_mut().allocate(
Size::from_bytes(cmd_utf16.len() as u64 * 2), Size::from_bytes(cmd_utf16.len() as u64 * 2),
Align::from_bytes(2).unwrap(), Align::from_bytes(2).unwrap(),
@ -179,9 +206,9 @@ pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>(
pub fn eval_main<'a, 'tcx: 'a>( pub fn eval_main<'a, 'tcx: 'a>(
tcx: TyCtxt<'a, 'tcx, 'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>,
main_id: DefId, main_id: DefId,
validate: bool, config: MiriConfig,
) { ) {
let mut ecx = create_ecx(tcx, main_id, validate).expect("Couldn't create ecx"); let mut ecx = create_ecx(tcx, main_id, config).expect("Couldn't create ecx");
// Run! The main execution. // Run! The main execution.
let res: EvalResult = (|| { let res: EvalResult = (|| {

View File

@ -37,10 +37,19 @@ def test(name, cmd, stdout_ref, stderr_ref):
def test_cargo_miri_run(): def test_cargo_miri_run():
test("cargo miri run", ["cargo", "miri", "run", "-q"], "stdout.ref", "stderr.ref") test("cargo miri run", ["cargo", "miri", "run", "-q"], "stdout.ref", "stderr.ref")
test("cargo miri run (with arguments)",
["cargo", "miri", "run", "-q", "--", "--", "hello world", '"hello world"'],
"stdout.ref", "stderr.ref2"
)
def test_cargo_miri_test(): def test_cargo_miri_test():
test("cargo miri test", ["cargo", "miri", "test", "-q"], "test.stdout.ref", "test.stderr.ref") test("cargo miri test", ["cargo", "miri", "test", "-q"], "test.stdout.ref", "test.stderr.ref")
test("cargo miri test (with filter)",
["cargo", "miri", "test", "-q", "--", "--", "impl"],
"test.stdout.ref2", "test.stderr.ref"
)
test_cargo_miri_run() test_cargo_miri_run()
test_cargo_miri_test() test_cargo_miri_test()
print("TEST SUCCESSFUL!")
sys.exit(0) sys.exit(0)

View File

@ -9,7 +9,9 @@ fn main() {
let n = <BigEndian as ByteOrder>::read_u32(buf); let n = <BigEndian as ByteOrder>::read_u32(buf);
assert_eq!(n, 0x01020304); assert_eq!(n, 0x01020304);
println!("{:#010x}", n); println!("{:#010x}", n);
eprintln!("standard error"); for arg in std::env::args() {
eprintln!("{}", arg);
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -1 +1 @@
standard error main

View File

@ -0,0 +1,3 @@
main
hello world
"hello world"

View File

@ -0,0 +1,11 @@
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out
running 1 test
test simple ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out

5
tests/run-pass/args.rs Normal file
View File

@ -0,0 +1,5 @@
fn main() {
for arg in std::env::args() {
println!("{}", arg);
}
}

View File

@ -0,0 +1 @@
args