Merge branch 'master' into format-padding
This commit is contained in:
commit
604095fff2
42 changed files with 260 additions and 134 deletions
|
|
@ -39,6 +39,7 @@ directories = { version = "1.0", optional = true }
|
|||
rustc_version = { version = "0.2.3", optional = true }
|
||||
env_logger = "0.6"
|
||||
log = "0.4"
|
||||
shell-escape = "0.1.4"
|
||||
# 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`
|
||||
# for more information.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
+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
|
||||
use this to exclude test cases that will fail under Miri because they do things
|
||||
Miri does not support:
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@ extern crate rustc;
|
|||
extern crate rustc_driver;
|
||||
extern crate test;
|
||||
|
||||
use self::miri::eval_main;
|
||||
use self::rustc_driver::{driver, Compilation};
|
||||
use rustc_driver::{driver, Compilation};
|
||||
use rustc::hir::def_id::LOCAL_CRATE;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use miri::{MiriConfig, eval_main};
|
||||
|
||||
use crate::test::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(|| {
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use std::fs::{self, File};
|
|||
const CARGO_MIRI_HELP: &str = r#"Interprets bin crates and tests in Miri
|
||||
|
||||
Usage:
|
||||
cargo miri [subcommand] [options] [--] [<miri opts>...]
|
||||
cargo miri [subcommand] [options] [--] [<miri opts>...] [--] [<program opts>...]
|
||||
|
||||
Subcommands:
|
||||
run Run binaries (default)
|
||||
|
|
@ -22,8 +22,9 @@ Common options:
|
|||
--features Features to compile for the package
|
||||
-V, --version Print version info and exit
|
||||
|
||||
Other [options] are the same as `cargo rustc`. Everything after the "--" is
|
||||
passed verbatim to Miri.
|
||||
Other [options] are the same as `cargo rustc`. Everything after the first "--" is
|
||||
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
|
||||
it to configure the resource limits
|
||||
|
|
@ -355,11 +356,13 @@ fn in_cargo_miri() {
|
|||
}
|
||||
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
|
||||
.arg("--")
|
||||
.arg("cargo-miri-marker")
|
||||
.args(args);
|
||||
.arg("cargo-miri-marker-begin")
|
||||
.args(args)
|
||||
.arg("cargo-miri-marker-end");
|
||||
let path = std::env::current_exe().expect("current executable path invalid");
|
||||
cmd.env("RUSTC_WRAPPER", path);
|
||||
if verbose {
|
||||
|
|
@ -413,10 +416,19 @@ fn inside_cargo_rustc() {
|
|||
};
|
||||
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
|
||||
// (and remove the marker).
|
||||
let needs_miri = if let Some(pos) = args.iter().position(|arg| arg == "cargo-miri-marker") {
|
||||
args.remove(pos);
|
||||
// See if we can find the cargo-miri markers. Those only get added to the binary we want to
|
||||
// run. They also serve to mark the user-defined arguments, which we have to move all the way to the
|
||||
// end (they get added somewhere in the middle).
|
||||
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
|
||||
} else {
|
||||
false
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ use rustc::ty::TyCtxt;
|
|||
use syntax::ast;
|
||||
use rustc::hir::def_id::LOCAL_CRATE;
|
||||
|
||||
use miri::MiriConfig;
|
||||
|
||||
struct MiriCompilerCalls {
|
||||
default: Box<RustcDefaultCalls>,
|
||||
/// whether we are building for the host
|
||||
|
|
@ -94,9 +96,10 @@ fn after_analysis<'a, 'tcx>(state: &mut CompileState<'a, 'tcx>) {
|
|||
fn visit_item(&mut self, i: &'hir hir::Item) {
|
||||
if let hir::ItemKind::Fn(.., body_id) = i.node {
|
||||
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);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -106,7 +109,8 @@ fn after_analysis<'a, 'tcx>(state: &mut CompileState<'a, 'tcx>) {
|
|||
}
|
||||
state.hir_crate.unwrap().visit_all_item_likes(&mut Visitor(tcx, state));
|
||||
} 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();
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -27,11 +27,11 @@ use rustc_codegen_utils::codegen_backend::CodegenBackend;
|
|||
use rustc::hir::def_id::LOCAL_CRATE;
|
||||
use syntax::ast;
|
||||
|
||||
use miri::MiriConfig;
|
||||
|
||||
struct MiriCompilerCalls {
|
||||
default: Box<RustcDefaultCalls>,
|
||||
|
||||
/// Whether to enforce the validity invariant.
|
||||
validate: bool,
|
||||
miri_config: MiriConfig,
|
||||
}
|
||||
|
||||
impl<'a> CompilerCalls<'a> for MiriCompilerCalls {
|
||||
|
|
@ -79,6 +79,8 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls {
|
|||
odir: &Option<PathBuf>,
|
||||
ofile: &Option<PathBuf>,
|
||||
) -> 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)
|
||||
}
|
||||
fn build_controller(
|
||||
|
|
@ -89,9 +91,9 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls {
|
|||
let this = *self;
|
||||
let mut control = this.default.build_controller(sess, matches);
|
||||
control.after_hir_lowering.callback = Box::new(after_hir_lowering);
|
||||
let validate = this.validate;
|
||||
let miri_config = this.miri_config;
|
||||
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
|
||||
}
|
||||
|
|
@ -107,7 +109,7 @@ fn after_hir_lowering(state: &mut CompileState) {
|
|||
|
||||
fn after_analysis<'a, 'tcx>(
|
||||
state: &mut CompileState<'a, 'tcx>,
|
||||
validate: bool,
|
||||
miri_config: MiriConfig,
|
||||
) {
|
||||
init_late_loggers();
|
||||
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!");
|
||||
|
||||
miri::eval_main(tcx, entry_def_id, validate);
|
||||
miri::eval_main(tcx, entry_def_id, miri_config);
|
||||
|
||||
state.session.abort_if_errors();
|
||||
}
|
||||
|
|
@ -188,34 +190,51 @@ fn find_sysroot() -> String {
|
|||
|
||||
fn main() {
|
||||
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;
|
||||
args.retain(|arg| {
|
||||
match arg.as_str() {
|
||||
"-Zmiri-disable-validation" => {
|
||||
validate = false;
|
||||
false
|
||||
},
|
||||
_ => true
|
||||
let mut rustc_args = vec![];
|
||||
let mut miri_args = vec![];
|
||||
let mut after_dashdash = false;
|
||||
for arg in std::env::args() {
|
||||
if rustc_args.is_empty() {
|
||||
// Very first arg: for rustc
|
||||
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
|
||||
let sysroot_flag = String::from("--sysroot");
|
||||
if !args.contains(&sysroot_flag) {
|
||||
args.push(sysroot_flag);
|
||||
args.push(find_sysroot());
|
||||
if !rustc_args.contains(&sysroot_flag) {
|
||||
rustc_args.push(sysroot_flag);
|
||||
rustc_args.push(find_sysroot());
|
||||
}
|
||||
// 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 || {
|
||||
rustc_driver::run_compiler(&args, Box::new(MiriCompilerCalls {
|
||||
rustc_driver::run_compiler(&rustc_args, Box::new(MiriCompilerCalls {
|
||||
default: Box::new(RustcDefaultCalls),
|
||||
validate,
|
||||
miri_config,
|
||||
}), None, None)
|
||||
});
|
||||
std::process::exit(result as i32);
|
||||
|
|
|
|||
|
|
@ -245,7 +245,13 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a+'mir>: crate::MiriEvalContextExt<'a,
|
|||
this.binop_ignore_overflow(mir::BinOp::Div, a, b, dest)?;
|
||||
},
|
||||
|
||||
"likely" | "unlikely" | "forget" => {}
|
||||
"forget" => {}
|
||||
|
||||
"likely" | "unlikely" => {
|
||||
// These just return their argument
|
||||
let b = this.read_immediate(args[0])?;
|
||||
this.write_immediate(*b, dest)?;
|
||||
}
|
||||
|
||||
"init" => {
|
||||
// Check fast path: we don't want to force an allocation in case the destination is a simple value,
|
||||
|
|
|
|||
57
src/lib.rs
57
src/lib.rs
|
|
@ -57,16 +57,23 @@ pub fn miri_default_args() -> &'static [&'static str] {
|
|||
&["-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
|
||||
pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
main_id: DefId,
|
||||
validate: bool,
|
||||
config: MiriConfig,
|
||||
) -> EvalResult<'tcx, EvalContext<'a, 'mir, 'tcx, Evaluator<'tcx>>> {
|
||||
let mut ecx = EvalContext::new(
|
||||
tcx.at(syntax::source_map::DUMMY_SP),
|
||||
ty::ParamEnv::reveal_all(),
|
||||
Evaluator::new(validate),
|
||||
Evaluator::new(config.validate),
|
||||
);
|
||||
|
||||
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
|
||||
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)?;
|
||||
// Store argc for macOS _NSGetArgc
|
||||
{
|
||||
|
|
@ -130,18 +137,38 @@ pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>(
|
|||
}
|
||||
|
||||
// FIXME: extract main source file path
|
||||
// Third argument (argv): &[b"foo"]
|
||||
const CMD: &str = "running-in-miri\0";
|
||||
// Third argument (argv): Created from config.args
|
||||
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();
|
||||
let raw_str_layout = ecx.layout_of(ecx.tcx.mk_imm_ptr(ecx.tcx.types.u8))?;
|
||||
let cmd_place = ecx.allocate(raw_str_layout, MiriMemoryKind::Env.into());
|
||||
ecx.write_scalar(Scalar::Ptr(cmd), cmd_place.into())?;
|
||||
ecx.memory_mut().mark_immutable(cmd_place.to_ptr()?.alloc_id)?;
|
||||
// For Windows, construct a command string with all the aguments
|
||||
let mut cmd = String::new();
|
||||
for arg in config.args.iter() {
|
||||
if !cmd.is_empty() {
|
||||
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
|
||||
{
|
||||
let argv = cmd_place.ptr;
|
||||
ecx.write_scalar(argv, dest)?;
|
||||
let argv_place = ecx.allocate(dest.layout, MiriMemoryKind::Env.into());
|
||||
ecx.write_scalar(argv, argv_place.into())?;
|
||||
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
|
||||
{
|
||||
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(
|
||||
Size::from_bytes(cmd_utf16.len() as u64 * 2),
|
||||
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>(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
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.
|
||||
let res: EvalResult = (|| {
|
||||
|
|
|
|||
|
|
@ -21,19 +21,19 @@ pub type CallId = u64;
|
|||
pub enum Borrow {
|
||||
/// A unique (mutable) reference.
|
||||
Uniq(Timestamp),
|
||||
/// A shared reference. This is also used by raw pointers, which do not track details
|
||||
/// An aliasing reference. This is also used by raw pointers, which do not track details
|
||||
/// of how or when they were created, hence the timestamp is optional.
|
||||
/// Shr(Some(_)) does NOT mean that the destination of this reference is frozen;
|
||||
/// that depends on the type! Only those parts outside of an `UnsafeCell` are actually
|
||||
/// frozen.
|
||||
Shr(Option<Timestamp>),
|
||||
Alias(Option<Timestamp>),
|
||||
}
|
||||
|
||||
impl Borrow {
|
||||
#[inline(always)]
|
||||
pub fn is_shared(self) -> bool {
|
||||
pub fn is_aliasing(self) -> bool {
|
||||
match self {
|
||||
Borrow::Shr(_) => true,
|
||||
Borrow::Alias(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -49,7 +49,7 @@ impl Borrow {
|
|||
|
||||
impl Default for Borrow {
|
||||
fn default() -> Self {
|
||||
Borrow::Shr(None)
|
||||
Borrow::Alias(None)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -58,10 +58,9 @@ impl Default for Borrow {
|
|||
pub enum BorStackItem {
|
||||
/// Indicates the unique reference that may mutate.
|
||||
Uniq(Timestamp),
|
||||
/// Indicates that the location has been shared. Used for raw pointers, but
|
||||
/// also for shared references. The latter *additionally* get frozen
|
||||
/// when there is no `UnsafeCell`.
|
||||
Shr,
|
||||
/// Indicates that the location has been mutably shared. Used for raw pointers as
|
||||
/// well as for unfrozen shared references.
|
||||
Raw,
|
||||
/// A barrier, tracking the function it belongs to by its index on the call stack
|
||||
FnBarrier(CallId)
|
||||
}
|
||||
|
|
@ -186,19 +185,19 @@ impl<'tcx> Stack {
|
|||
kind: RefKind,
|
||||
) -> Result<Option<usize>, String> {
|
||||
// Exclude unique ref with frozen tag.
|
||||
if let (RefKind::Unique, Borrow::Shr(Some(_))) = (kind, bor) {
|
||||
if let (RefKind::Unique, Borrow::Alias(Some(_))) = (kind, bor) {
|
||||
return Err(format!("Encountered mutable reference with frozen tag ({:?})", bor));
|
||||
}
|
||||
// Checks related to freezing
|
||||
match bor {
|
||||
Borrow::Shr(Some(bor_t)) if kind == RefKind::Frozen => {
|
||||
Borrow::Alias(Some(bor_t)) if kind == RefKind::Frozen => {
|
||||
// We need the location to be frozen. This ensures F3.
|
||||
let frozen = self.frozen_since.map_or(false, |itm_t| itm_t <= bor_t);
|
||||
return if frozen { Ok(None) } else {
|
||||
Err(format!("Location is not frozen long enough"))
|
||||
}
|
||||
}
|
||||
Borrow::Shr(_) if self.frozen_since.is_some() => {
|
||||
Borrow::Alias(_) if self.frozen_since.is_some() => {
|
||||
return Ok(None) // Shared deref to frozen location, looking good
|
||||
}
|
||||
_ => {} // Not sufficient, go on looking.
|
||||
|
|
@ -210,8 +209,8 @@ impl<'tcx> Stack {
|
|||
// Found matching unique item. This satisfies U3.
|
||||
return Ok(Some(idx))
|
||||
}
|
||||
(BorStackItem::Shr, Borrow::Shr(_)) => {
|
||||
// Found matching shared/raw item.
|
||||
(BorStackItem::Raw, Borrow::Alias(_)) => {
|
||||
// Found matching aliasing/raw item.
|
||||
return Ok(Some(idx))
|
||||
}
|
||||
// Go on looking. We ignore barriers! When an `&mut` and an `&` alias,
|
||||
|
|
@ -221,7 +220,7 @@ impl<'tcx> Stack {
|
|||
}
|
||||
}
|
||||
// If we got here, we did not find our item. We have to error to satisfy U3.
|
||||
Err(format!("Borrow being dereferenced ({:?}) does not exist on the stack", bor))
|
||||
Err(format!("Borrow being dereferenced ({:?}) does not exist on the borrow stack", bor))
|
||||
}
|
||||
|
||||
/// Perform an actual memory access using `bor`. We do not know any types here
|
||||
|
|
@ -258,14 +257,15 @@ impl<'tcx> Stack {
|
|||
(BorStackItem::Uniq(itm_t), Borrow::Uniq(bor_t)) if itm_t == bor_t => {
|
||||
// Found matching unique item. Continue after the match.
|
||||
}
|
||||
(BorStackItem::Shr, _) if kind == AccessKind::Read => {
|
||||
// When reading, everything can use a shared item!
|
||||
(BorStackItem::Raw, _) if kind == AccessKind::Read => {
|
||||
// When reading, everything can use a raw item!
|
||||
// We do not want to do this when writing: Writing to an `&mut`
|
||||
// should reaffirm its exclusivity (i.e., make sure it is
|
||||
// on top of the stack). Continue after the match.
|
||||
// on top of the stack).
|
||||
// Continue after the match.
|
||||
}
|
||||
(BorStackItem::Shr, Borrow::Shr(_)) => {
|
||||
// Found matching shared item. Continue after the match.
|
||||
(BorStackItem::Raw, Borrow::Alias(_)) => {
|
||||
// Found matching raw item. Continue after the match.
|
||||
}
|
||||
_ => {
|
||||
// Pop this, go on. This ensures U2.
|
||||
|
|
@ -294,7 +294,7 @@ impl<'tcx> Stack {
|
|||
}
|
||||
// If we got here, we did not find our item.
|
||||
err!(MachineError(format!(
|
||||
"Borrow being accessed ({:?}) does not exist on the stack",
|
||||
"Borrow being accessed ({:?}) does not exist on the borrow stack",
|
||||
bor
|
||||
)))
|
||||
}
|
||||
|
|
@ -309,7 +309,7 @@ impl<'tcx> Stack {
|
|||
// of access (like writing through raw pointers) is permitted.
|
||||
if kind == RefKind::Frozen {
|
||||
let bor_t = match bor {
|
||||
Borrow::Shr(Some(t)) => t,
|
||||
Borrow::Alias(Some(t)) => t,
|
||||
_ => bug!("Creating illegal borrow {:?} for frozen ref", bor),
|
||||
};
|
||||
// It is possible that we already are frozen (e.g. if we just pushed a barrier,
|
||||
|
|
@ -328,12 +328,12 @@ impl<'tcx> Stack {
|
|||
// Push new item to the stack.
|
||||
let itm = match bor {
|
||||
Borrow::Uniq(t) => BorStackItem::Uniq(t),
|
||||
Borrow::Shr(_) => BorStackItem::Shr,
|
||||
Borrow::Alias(_) => BorStackItem::Raw,
|
||||
};
|
||||
if *self.borrows.last().unwrap() == itm {
|
||||
// This is just an optimization, no functional change: Avoid stacking
|
||||
// multiple `Shr` on top of each other.
|
||||
assert!(bor.is_shared());
|
||||
assert!(bor.is_aliasing());
|
||||
trace!("create: Sharing a shared location is a NOP");
|
||||
} else {
|
||||
// This ensures U1.
|
||||
|
|
@ -440,7 +440,7 @@ impl<'tcx> Stacks {
|
|||
_ => false,
|
||||
};
|
||||
if bor_redundant {
|
||||
assert!(new_bor.is_shared(), "A unique reborrow can never be redundant");
|
||||
assert!(new_bor.is_aliasing(), "A unique reborrow can never be redundant");
|
||||
trace!("reborrow is redundant");
|
||||
continue;
|
||||
}
|
||||
|
|
@ -465,7 +465,7 @@ impl AllocationExtra<Borrow, MemoryState> for Stacks {
|
|||
#[inline(always)]
|
||||
fn memory_allocated<'tcx>(size: Size, extra: &MemoryState) -> Self {
|
||||
let stack = Stack {
|
||||
borrows: vec![BorStackItem::Shr],
|
||||
borrows: vec![BorStackItem::Raw],
|
||||
frozen_since: None,
|
||||
};
|
||||
Stacks {
|
||||
|
|
@ -511,7 +511,7 @@ impl<'tcx> Stacks {
|
|||
) {
|
||||
for stack in self.stacks.get_mut().iter_mut(Size::ZERO, size) {
|
||||
assert!(stack.borrows.len() == 1);
|
||||
assert_eq!(stack.borrows.pop().unwrap(), BorStackItem::Shr);
|
||||
assert_eq!(stack.borrows.pop().unwrap(), BorStackItem::Raw);
|
||||
stack.borrows.push(itm);
|
||||
}
|
||||
}
|
||||
|
|
@ -536,7 +536,7 @@ trait EvalContextPrivExt<'a, 'mir, 'tcx: 'a+'mir>: crate::MiriEvalContextExt<'a,
|
|||
let alloc = this.memory().get(ptr.alloc_id)?;
|
||||
alloc.check_bounds(this, ptr, size)?;
|
||||
// Update the stacks.
|
||||
if let Borrow::Shr(Some(_)) = new_bor {
|
||||
if let Borrow::Alias(Some(_)) = new_bor {
|
||||
// Reference that cares about freezing. We need a frozen-sensitive reborrow.
|
||||
this.visit_freeze_sensitive(place, size, |cur_ptr, size, frozen| {
|
||||
let kind = if frozen { RefKind::Frozen } else { RefKind::Raw };
|
||||
|
|
@ -574,7 +574,7 @@ trait EvalContextPrivExt<'a, 'mir, 'tcx: 'a+'mir>: crate::MiriEvalContextExt<'a,
|
|||
let time = this.machine.stacked_borrows.increment_clock();
|
||||
let new_bor = match mutbl {
|
||||
Some(MutMutable) => Borrow::Uniq(time),
|
||||
Some(MutImmutable) => Borrow::Shr(Some(time)),
|
||||
Some(MutImmutable) => Borrow::Alias(Some(time)),
|
||||
None => Borrow::default(),
|
||||
};
|
||||
|
||||
|
|
@ -586,7 +586,7 @@ trait EvalContextPrivExt<'a, 'mir, 'tcx: 'a+'mir>: crate::MiriEvalContextExt<'a,
|
|||
assert!(mutbl == Some(MutMutable), "two-phase shared borrows make no sense");
|
||||
// We immediately share it, to allow read accesses
|
||||
let two_phase_time = this.machine.stacked_borrows.increment_clock();
|
||||
let two_phase_bor = Borrow::Shr(Some(two_phase_time));
|
||||
let two_phase_bor = Borrow::Alias(Some(two_phase_time));
|
||||
this.reborrow(new_place, size, /*fn_barrier*/false, two_phase_bor)?;
|
||||
}
|
||||
|
||||
|
|
@ -651,7 +651,7 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a+'mir>: crate::MiriEvalContextExt<'a,
|
|||
let alloc = this.memory().get(ptr.alloc_id)?;
|
||||
alloc.check_bounds(this, ptr, size)?;
|
||||
// If we got here, we do some checking, *but* we leave the tag unchanged.
|
||||
if let Borrow::Shr(Some(_)) = ptr.tag {
|
||||
if let Borrow::Alias(Some(_)) = ptr.tag {
|
||||
assert_eq!(mutability, Some(MutImmutable));
|
||||
// We need a frozen-sensitive check
|
||||
this.visit_freeze_sensitive(place, size, |cur_ptr, size, frozen| {
|
||||
|
|
|
|||
|
|
@ -37,10 +37,19 @@ def test(name, cmd, stdout_ref, stderr_ref):
|
|||
|
||||
def test_cargo_miri_run():
|
||||
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():
|
||||
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_test()
|
||||
print("TEST SUCCESSFUL!")
|
||||
sys.exit(0)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@ fn main() {
|
|||
let n = <BigEndian as ByteOrder>::read_u32(buf);
|
||||
assert_eq!(n, 0x01020304);
|
||||
println!("{:#010x}", n);
|
||||
eprintln!("standard error");
|
||||
for arg in std::env::args() {
|
||||
eprintln!("{}", arg);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
standard error
|
||||
main
|
||||
|
|
|
|||
3
test-cargo-miri/stderr.ref2
Normal file
3
test-cargo-miri/stderr.ref2
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
main
|
||||
hello world
|
||||
"hello world"
|
||||
11
test-cargo-miri/test.stdout.ref2
Normal file
11
test-cargo-miri/test.stdout.ref2
Normal 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
|
||||
|
||||
|
|
@ -9,5 +9,5 @@ fn main() {
|
|||
retarget(&mut target_alias, target);
|
||||
// now `target_alias` points to the same thing as `target`
|
||||
*target = 13;
|
||||
let _val = *target_alias; //~ ERROR does not exist on the stack
|
||||
let _val = *target_alias; //~ ERROR does not exist on the borrow stack
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::mem;
|
||||
|
||||
pub fn safe(_x: &mut i32, _y: &i32) {} //~ ERROR does not exist on the stack
|
||||
pub fn safe(_x: &mut i32, _y: &i32) {} //~ ERROR does not exist on the borrow stack
|
||||
|
||||
fn main() {
|
||||
let mut x = 0;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ fn demo_mut_advanced_unique(mut our: Box<i32>) -> i32 {
|
|||
unknown_code_2();
|
||||
|
||||
// We know this will return 5
|
||||
*our //~ ERROR does not exist on the stack
|
||||
*our //~ ERROR does not exist on the borrow stack
|
||||
}
|
||||
|
||||
// Now comes the evil context
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@ fn main() {
|
|||
let v1 = safe::as_mut_slice(&v);
|
||||
let _v2 = safe::as_mut_slice(&v);
|
||||
v1[1] = 5;
|
||||
//~^ ERROR does not exist on the stack
|
||||
//~^ ERROR does not exist on the borrow stack
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ mod safe {
|
|||
assert!(mid <= len);
|
||||
|
||||
(from_raw_parts_mut(ptr, len - mid), // BUG: should be "mid" instead of "len - mid"
|
||||
//~^ ERROR does not exist on the stack
|
||||
//~^ ERROR does not exist on the borrow stack
|
||||
from_raw_parts_mut(ptr.offset(mid as isize), len - mid))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ fn main() {
|
|||
let xref = unsafe { &mut *xraw }; // derived from raw, so using raw is still okay...
|
||||
callee(xraw);
|
||||
let _val = *xref; // ...but any use of raw will invalidate our ref.
|
||||
//~^ ERROR: does not exist on the stack
|
||||
//~^ ERROR: does not exist on the borrow stack
|
||||
}
|
||||
|
||||
fn callee(xraw: *mut i32) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ fn main() {
|
|||
let xref = unsafe { &mut *xraw }; // derived from raw, so using raw is still okay...
|
||||
callee(xraw);
|
||||
let _val = *xref; // ...but any use of raw will invalidate our ref.
|
||||
//~^ ERROR: does not exist on the stack
|
||||
//~^ ERROR: does not exist on the borrow stack
|
||||
}
|
||||
|
||||
fn callee(xraw: *mut i32) {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ fn main() {
|
|||
let xref2 = &mut *xref1; // derived from xref1, so using raw is still okay...
|
||||
callee(xref1_sneaky);
|
||||
let _val = *xref2; // ...but any use of it will invalidate our ref.
|
||||
//~^ ERROR: does not exist on the stack
|
||||
//~^ ERROR: does not exist on the borrow stack
|
||||
}
|
||||
|
||||
fn callee(xref1: usize) {
|
||||
|
|
|
|||
|
|
@ -5,5 +5,5 @@ fn main() {
|
|||
let xraw = xref1 as *mut _;
|
||||
let xref2 = unsafe { &mut *xraw };
|
||||
let _val = unsafe { *xraw }; // use the raw again, this invalidates xref2 *even* with the special read except for uniq refs
|
||||
let _illegal = *xref2; //~ ERROR does not exist on the stack
|
||||
let _illegal = *xref2; //~ ERROR does not exist on the borrow stack
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,5 +12,5 @@ fn main() {
|
|||
let _val = *xref; // we can even still use our mutable reference
|
||||
mem::forget(unsafe { ptr::read(xshr) }); // but after reading through the shared ref
|
||||
let _val = *xref; // the mutable one is dead and gone
|
||||
//~^ ERROR does not exist on the stack
|
||||
//~^ ERROR does not exist on the borrow stack
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@ fn main() {
|
|||
let target2 = target as *mut _;
|
||||
drop(&mut *target); // reborrow
|
||||
// Now make sure our ref is still the only one.
|
||||
unsafe { *target2 = 13; } //~ ERROR does not exist on the stack
|
||||
unsafe { *target2 = 13; } //~ ERROR does not exist on the borrow stack
|
||||
let _val = *target;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@ fn main() {
|
|||
// Make sure raw ptr with raw tag cannot mutate frozen location without breaking the shared ref.
|
||||
let r#ref = ⌖ // freeze
|
||||
let ptr = r#ref as *const _ as *mut _; // raw ptr, with raw tag
|
||||
unsafe { *ptr = 42; } //~ ERROR does not exist on the stack
|
||||
unsafe { *ptr = 42; } //~ ERROR does not exist on the borrow stack
|
||||
let _val = *r#ref;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ fn main() {
|
|||
let xref = unsafe { &mut *xraw }; // derived from raw, so using raw is still okay...
|
||||
callee(xraw);
|
||||
let _val = *xref; // ...but any use of raw will invalidate our ref.
|
||||
//~^ ERROR: does not exist on the stack
|
||||
//~^ ERROR: does not exist on the borrow stack
|
||||
}
|
||||
|
||||
fn callee(xraw: *mut i32) {
|
||||
|
|
|
|||
|
|
@ -5,5 +5,5 @@ fn main() {
|
|||
let xref = unsafe { &mut *xraw };
|
||||
let xref_in_mem = Box::new(xref);
|
||||
let _val = unsafe { *xraw }; // invalidate xref
|
||||
let _val = *xref_in_mem; //~ ERROR does not exist on the stack
|
||||
let _val = *xref_in_mem; //~ ERROR does not exist on the borrow stack
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ fn main() {
|
|||
let y: *const i32 = &x;
|
||||
x = 1; // this invalidates y by reactivating the lowermost uniq borrow for this local
|
||||
|
||||
assert_eq!(unsafe { *y }, 1); //~ ERROR does not exist on the stack
|
||||
assert_eq!(unsafe { *y }, 1); //~ ERROR does not exist on the borrow stack
|
||||
|
||||
assert_eq!(x, 1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@ fn main() {
|
|||
let xraw = x as *mut _;
|
||||
let xref = unsafe { &mut *xraw };
|
||||
let _val = unsafe { *xraw }; // invalidate xref
|
||||
foo(xref); //~ ERROR does not exist on the stack
|
||||
foo(xref); //~ ERROR does not exist on the borrow stack
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ fn fun1(x: &mut u8) {
|
|||
|
||||
fn fun2() {
|
||||
// Now we use a pointer we are not allowed to use
|
||||
let _x = unsafe { *PTR }; //~ ERROR does not exist on the stack
|
||||
let _x = unsafe { *PTR }; //~ ERROR does not exist on the borrow stack
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ fn foo(x: &mut (i32, i32)) -> &mut i32 {
|
|||
let xraw = x as *mut (i32, i32);
|
||||
let ret = unsafe { &mut (*xraw).1 };
|
||||
let _val = unsafe { *xraw }; // invalidate xref
|
||||
ret //~ ERROR does not exist on the stack
|
||||
ret //~ ERROR does not exist on the borrow stack
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ fn foo(x: &mut (i32, i32)) -> Option<&mut i32> {
|
|||
let xraw = x as *mut (i32, i32);
|
||||
let ret = Some(unsafe { &mut (*xraw).1 });
|
||||
let _val = unsafe { *xraw }; // invalidate xref
|
||||
ret //~ ERROR does not exist on the stack
|
||||
ret //~ ERROR does not exist on the borrow stack
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ fn foo(x: &mut (i32, i32)) -> (&mut i32,) {
|
|||
let xraw = x as *mut (i32, i32);
|
||||
let ret = (unsafe { &mut (*xraw).1 },);
|
||||
let _val = unsafe { *xraw }; // invalidate xref
|
||||
ret //~ ERROR does not exist on the stack
|
||||
ret //~ ERROR does not exist on the borrow stack
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
|
|||
|
|
@ -10,5 +10,5 @@ fn main() {
|
|||
let _raw: *mut i32 = unsafe { mem::transmute(&mut x[0]) };
|
||||
// `raw` still carries a tag, so we get another pointer to the same location that does not carry a tag
|
||||
let raw = (&mut x[1] as *mut i32).wrapping_offset(-1);
|
||||
unsafe { *raw = 13; } //~ ERROR does not exist on the stack
|
||||
unsafe { *raw = 13; } //~ ERROR does not exist on the borrow stack
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,5 +4,5 @@ fn main() {
|
|||
let mut x = 42;
|
||||
let raw = &mut x as *mut i32 as usize as *mut i32;
|
||||
let _ptr = &mut x;
|
||||
unsafe { *raw = 13; } //~ ERROR does not exist on the stack
|
||||
unsafe { *raw = 13; } //~ ERROR does not exist on the borrow stack
|
||||
}
|
||||
|
|
|
|||
5
tests/run-pass/args.rs
Normal file
5
tests/run-pass/args.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
fn main() {
|
||||
for arg in std::env::args() {
|
||||
println!("{}", arg);
|
||||
}
|
||||
}
|
||||
1
tests/run-pass/args.stdout
Normal file
1
tests/run-pass/args.stdout
Normal file
|
|
@ -0,0 +1 @@
|
|||
args
|
||||
40
tests/run-pass/iter.rs
Normal file
40
tests/run-pass/iter.rs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
fn iter_empty_and_zst() {
|
||||
for _ in Vec::<u32>::new().iter() { // this iterates over a Unique::empty()
|
||||
panic!("We should never be here.");
|
||||
}
|
||||
|
||||
// Iterate over a ZST (uses arith_offset internally)
|
||||
let mut count = 0;
|
||||
for _ in &[(), (), ()] {
|
||||
count += 1;
|
||||
}
|
||||
assert_eq!(count, 3);
|
||||
}
|
||||
|
||||
fn test_iterator_step_by_nth() {
|
||||
let mut it = (0..16).step_by(5);
|
||||
assert_eq!(it.nth(0), Some(0));
|
||||
assert_eq!(it.nth(0), Some(5));
|
||||
assert_eq!(it.nth(0), Some(10));
|
||||
assert_eq!(it.nth(0), Some(15));
|
||||
assert_eq!(it.nth(0), None);
|
||||
}
|
||||
|
||||
fn iter_any() {
|
||||
let f = |x: &u8| { 10u8 == *x };
|
||||
f(&1u8);
|
||||
|
||||
let g = |(), x: &u8| { 10u8 == *x };
|
||||
g((), &1u8);
|
||||
|
||||
let h = |(), (), x: &u8| { 10u8 == *x };
|
||||
h((), (), &1u8);
|
||||
|
||||
[1, 2, 3u8].into_iter().any(|elt| 10 == *elt);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test_iterator_step_by_nth();
|
||||
iter_any();
|
||||
iter_empty_and_zst();
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
pub fn main() {
|
||||
let f = |x: &u8| { 10u8 == *x };
|
||||
f(&1u8);
|
||||
|
||||
let g = |(), x: &u8| { 10u8 == *x };
|
||||
g((), &1u8);
|
||||
|
||||
let h = |(), (), x: &u8| { 10u8 == *x };
|
||||
h((), (), &1u8);
|
||||
|
||||
[1, 2, 3u8].into_iter().any(|elt| 10 == *elt);
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
fn main() {
|
||||
for _ in Vec::<u32>::new().iter() { // this iterates over a Unique::empty()
|
||||
panic!("We should never be here.");
|
||||
}
|
||||
|
||||
// Iterate over a ZST (uses arith_offset internally)
|
||||
let mut count = 0;
|
||||
for _ in &[(), (), ()] {
|
||||
count += 1;
|
||||
}
|
||||
assert_eq!(count, 3);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue