Auto merge of #122246 - RalfJung:miri, r=RalfJung

Miri subtree update

r? `@ghost`

`@WaffleLapkin` when this lands, setting `MIRI_TEMP` should not be needed any more on the dev desktops.
This commit is contained in:
bors 2024-03-10 09:40:39 +00:00
commit 3521a2f2f3
36 changed files with 239 additions and 260 deletions

View File

@ -2485,6 +2485,7 @@ dependencies = [
"regex", "regex",
"rustc_version", "rustc_version",
"smallvec", "smallvec",
"tempfile",
"ui_test 0.21.2", "ui_test 0.21.2",
] ]

View File

@ -187,7 +187,7 @@ jobs:
run: RUSTFLAGS="--cap-lints warn" cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r22.12.06 run: RUSTFLAGS="--cap-lints warn" cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r22.12.06
- name: setup bot git name and email - name: setup bot git name and email
run: | run: |
git config --global user.name 'The Miri Conjob Bot' git config --global user.name 'The Miri Cronjob Bot'
git config --global user.email 'miri@cron.bot' git config --global user.email 'miri@cron.bot'
- name: get changes from rustc - name: get changes from rustc
run: ./miri rustc-pull run: ./miri rustc-pull

View File

@ -497,6 +497,7 @@ dependencies = [
"regex", "regex",
"rustc_version", "rustc_version",
"smallvec", "smallvec",
"tempfile",
"ui_test", "ui_test",
] ]

View File

@ -45,6 +45,7 @@ ui_test = "0.21.1"
rustc_version = "0.4" rustc_version = "0.4"
regex = "1.5.5" regex = "1.5.5"
lazy_static = "1.4.0" lazy_static = "1.4.0"
tempfile = "3"
[package.metadata.rust-analyzer] [package.metadata.rust-analyzer]
# This crate uses #[feature(rustc_private)]. # This crate uses #[feature(rustc_private)].
@ -52,7 +53,7 @@ lazy_static = "1.4.0"
rustc_private = true rustc_private = true
[[test]] [[test]]
name = "compiletest" name = "ui"
harness = false harness = false
[features] [features]

View File

@ -318,8 +318,8 @@ environment variable. We first document the most relevant and most commonly used
and `warn-nobacktrace` are the supported actions. The default is to `abort`, and `warn-nobacktrace` are the supported actions. The default is to `abort`,
which halts the machine. Some (but not all) operations also support continuing which halts the machine. Some (but not all) operations also support continuing
execution with a "permission denied" error being returned to the program. execution with a "permission denied" error being returned to the program.
`warn` prints a full backtrace when that happens; `warn-nobacktrace` is less `warn` prints a full backtrace each time that happens; `warn-nobacktrace` is less
verbose. `hide` hides the warning entirely. verbose and shown at most once per operation. `hide` hides the warning entirely.
* `-Zmiri-num-cpus` states the number of available CPUs to be reported by miri. By default, the * `-Zmiri-num-cpus` states the number of available CPUs to be reported by miri. By default, the
number of available CPUs is `1`. Note that this flag does not affect how miri handles threads in number of available CPUs is `1`. Note that this flag does not affect how miri handles threads in
any way. any way.
@ -359,8 +359,6 @@ The remaining flags are for advanced use only, and more likely to change or be r
Some of these are **unsound**, which means they can lead Some of these are **unsound**, which means they can lead
to Miri failing to detect cases of undefined behavior in a program. to Miri failing to detect cases of undefined behavior in a program.
* `-Zmiri-disable-abi-check` disables checking [function ABI]. Using this flag
is **unsound**. This flag is **deprecated**.
* `-Zmiri-disable-alignment-check` disables checking pointer alignment, so you * `-Zmiri-disable-alignment-check` disables checking pointer alignment, so you
can focus on other failures, but it means Miri can miss bugs in your program. can focus on other failures, but it means Miri can miss bugs in your program.
Using this flag is **unsound**. Using this flag is **unsound**.
@ -465,11 +463,7 @@ Moreover, Miri recognizes some environment variables:
* `MIRI_LIB_SRC` defines the directory where Miri expects the sources of the * `MIRI_LIB_SRC` defines the directory where Miri expects the sources of the
standard library that it will build and use for interpretation. This directory standard library that it will build and use for interpretation. This directory
must point to the `library` subdirectory of a `rust-lang/rust` repository must point to the `library` subdirectory of a `rust-lang/rust` repository
checkout. Note that changing files in that directory does not automatically checkout.
trigger a re-build of the standard library; you have to clear the Miri build
cache with `cargo miri clean` or deleting it manually (on Linux, `rm -rf ~/.cache/miri`;
on Windows, `rmdir /S "%LOCALAPPDATA%\rust-lang\miri\cache"`;
and on macOS, `rm -rf ~/Library/Caches/org.rust-lang.miri`).
* `MIRI_SYSROOT` (recognized by `cargo miri` and the Miri driver) indicates the sysroot to use. When * `MIRI_SYSROOT` (recognized by `cargo miri` and the Miri driver) indicates the sysroot to use. When
using `cargo miri`, this skips the automatic setup -- only set this if you do not want to use the using `cargo miri`, this skips the automatic setup -- only set this if you do not want to use the
automatically created sysroot. For directly invoking the Miri driver, this variable (or a automatically created sysroot. For directly invoking the Miri driver, this variable (or a

View File

@ -12,7 +12,7 @@ PLATFORM_SUPPORT_FILE=$(rustc +miri --print sysroot)/share/doc/rust/html/rustc/p
for target in $(python3 ci/scrape-targets.py $PLATFORM_SUPPORT_FILE); do for target in $(python3 ci/scrape-targets.py $PLATFORM_SUPPORT_FILE); do
# Wipe the cache before every build to minimize disk usage # Wipe the cache before every build to minimize disk usage
rm -rf ~/.cache/miri cargo +miri miri clean
if cargo +miri miri setup --target $target 2>&1 | tee failures/$target; then if cargo +miri miri setup --target $target 2>&1 | tee failures/$target; then
# If the build succeeds, delete its output. If we have output, a build failed. # If the build succeeds, delete its output. If we have output, a build failed.
rm $FAILS_DIR/$target rm $FAILS_DIR/$target

View File

@ -510,11 +510,11 @@ impl Command {
let miri_flags = flagsplit(&miri_flags); let miri_flags = flagsplit(&miri_flags);
let toolchain = &e.toolchain; let toolchain = &e.toolchain;
let extra_flags = &e.cargo_extra_flags; let extra_flags = &e.cargo_extra_flags;
let edition_flags = (!have_edition).then_some("--edition=2021"); // keep in sync with `compiletest.rs`.` let edition_flags = (!have_edition).then_some("--edition=2021"); // keep in sync with `tests/ui.rs`.`
if dep { if dep {
cmd!( cmd!(
e.sh, e.sh,
"cargo +{toolchain} --quiet test --test compiletest {extra_flags...} --manifest-path {miri_manifest} -- --miri-run-dep-mode {miri_flags...} {edition_flags...} {flags...}" "cargo +{toolchain} --quiet test {extra_flags...} --manifest-path {miri_manifest} --test ui -- --miri-run-dep-mode {miri_flags...} {edition_flags...} {flags...}"
).quiet().run()?; ).quiet().run()?;
} else { } else {
cmd!( cmd!(
@ -526,37 +526,27 @@ impl Command {
} }
fn fmt(flags: Vec<OsString>) -> Result<()> { fn fmt(flags: Vec<OsString>) -> Result<()> {
use itertools::Itertools;
let e = MiriEnv::new()?; let e = MiriEnv::new()?;
let toolchain = &e.toolchain;
let config_path = path!(e.miri_dir / "rustfmt.toml"); let config_path = path!(e.miri_dir / "rustfmt.toml");
let mut cmd = cmd!( // Collect each rust file in the miri repo.
e.sh, let files = WalkDir::new(&e.miri_dir)
"rustfmt +{toolchain} --edition=2021 --config-path {config_path} --unstable-features --skip-children {flags...}" .into_iter()
); .filter_entry(|entry| {
eprintln!("$ {cmd} ..."); let name = entry.file_name().to_string_lossy();
let ty = entry.file_type();
if ty.is_file() {
name.ends_with(".rs")
} else {
// dir or symlink. skip `target` and `.git`.
&name != "target" && &name != ".git"
}
})
.filter_ok(|item| item.file_type().is_file())
.map_ok(|item| item.into_path());
// Add all the filenames to the command. e.format_files(files, &e.toolchain[..], &config_path, &flags[..])
// FIXME: `rustfmt` will follow the `mod` statements in these files, so we get a bunch of
// duplicate diffs.
for item in WalkDir::new(&e.miri_dir).into_iter().filter_entry(|entry| {
let name = entry.file_name().to_string_lossy();
let ty = entry.file_type();
if ty.is_file() {
name.ends_with(".rs")
} else {
// dir or symlink. skip `target` and `.git`.
&name != "target" && &name != ".git"
}
}) {
let item = item?;
if item.file_type().is_file() {
cmd = cmd.arg(item.into_path());
}
}
// We want our own error message, repeating the command is too much.
cmd.quiet().run().map_err(|_| anyhow!("`rustfmt` failed"))?;
Ok(())
} }
} }

View File

@ -1,7 +1,7 @@
use std::ffi::{OsStr, OsString}; use std::ffi::{OsStr, OsString};
use std::path::PathBuf; use std::path::{Path, PathBuf};
use anyhow::{Context, Result}; use anyhow::{anyhow, Context, Result};
use dunce::canonicalize; use dunce::canonicalize;
use path_macro::path; use path_macro::path;
use xshell::{cmd, Shell}; use xshell::{cmd, Shell};
@ -145,4 +145,48 @@ impl MiriEnv {
.run()?; .run()?;
Ok(()) Ok(())
} }
/// Receives an iterator of files.
/// Will format each file with the miri rustfmt config.
/// Does not recursively format modules.
pub fn format_files(
&self,
files: impl Iterator<Item = Result<PathBuf, walkdir::Error>>,
toolchain: &str,
config_path: &Path,
flags: &[OsString],
) -> anyhow::Result<()> {
use itertools::Itertools;
let mut first = true;
// Format in batches as not all our files fit into Windows' command argument limit.
for batch in &files.chunks(256) {
// Build base command.
let mut cmd = cmd!(
self.sh,
"rustfmt +{toolchain} --edition=2021 --config-path {config_path} --unstable-features --skip-children {flags...}"
);
if first {
// Log an abbreviating command, and only once.
eprintln!("$ {cmd} ...");
first = false;
}
// Add files.
for file in batch {
// Make it a relative path so that on platforms with extremely tight argument
// limits (like Windows), we become immune to someone cloning the repo
// 50 directories deep.
let file = file?;
let file = file.strip_prefix(&self.miri_dir)?;
cmd = cmd.arg(file);
}
// Run rustfmt.
// We want our own error message, repeating the command is too much.
cmd.quiet().run().map_err(|_| anyhow!("`rustfmt` failed"))?;
}
Ok(())
}
} }

View File

@ -1 +1 @@
1a1876c9790f168fb51afa335a7ba3e6fc267d75 4d4bb491b65c300835442f6cb4f34fc9a5685c26

View File

@ -8,6 +8,11 @@
rustc::untranslatable_diagnostic rustc::untranslatable_diagnostic
)] )]
// Some "regular" crates we want to share with rustc
#[macro_use]
extern crate tracing;
// The rustc crates we need
extern crate rustc_data_structures; extern crate rustc_data_structures;
extern crate rustc_driver; extern crate rustc_driver;
extern crate rustc_hir; extern crate rustc_hir;
@ -16,8 +21,6 @@ extern crate rustc_log;
extern crate rustc_metadata; extern crate rustc_metadata;
extern crate rustc_middle; extern crate rustc_middle;
extern crate rustc_session; extern crate rustc_session;
#[macro_use]
extern crate tracing;
use std::env::{self, VarError}; use std::env::{self, VarError};
use std::num::NonZero; use std::num::NonZero;
@ -202,16 +205,12 @@ fn rustc_logger_config() -> rustc_log::LoggerConfig {
// rustc traced, but you can also do `MIRI_LOG=miri=trace,rustc_const_eval::interpret=debug`. // rustc traced, but you can also do `MIRI_LOG=miri=trace,rustc_const_eval::interpret=debug`.
if tracing::Level::from_str(&var).is_ok() { if tracing::Level::from_str(&var).is_ok() {
cfg.filter = Ok(format!( cfg.filter = Ok(format!(
"rustc_middle::mir::interpret={var},rustc_const_eval::interpret={var}" "rustc_middle::mir::interpret={var},rustc_const_eval::interpret={var},miri={var}"
)); ));
} else { } else {
cfg.filter = Ok(var); cfg.filter = Ok(var);
} }
} }
// Enable verbose entry/exit logging by default if MIRI_LOG is set.
if matches!(cfg.verbose_entry_exit, Err(VarError::NotPresent)) {
cfg.verbose_entry_exit = Ok(format!("1"));
}
} }
cfg cfg
@ -342,7 +341,8 @@ fn main() {
// (`install_ice_hook` might change `RUST_BACKTRACE`.) // (`install_ice_hook` might change `RUST_BACKTRACE`.)
let env_snapshot = env::vars_os().collect::<Vec<_>>(); let env_snapshot = env::vars_os().collect::<Vec<_>>();
let args = rustc_driver::args::raw_args(&early_dcx).unwrap_or_else(|_| std::process::exit(rustc_driver::EXIT_FAILURE)); let args = rustc_driver::args::raw_args(&early_dcx)
.unwrap_or_else(|_| std::process::exit(rustc_driver::EXIT_FAILURE));
// If the environment asks us to actually be rustc, then do that. // If the environment asks us to actually be rustc, then do that.
if let Some(crate_kind) = env::var_os("MIRI_BE_RUSTC") { if let Some(crate_kind) = env::var_os("MIRI_BE_RUSTC") {
@ -408,17 +408,11 @@ fn main() {
miri_config.check_alignment = miri::AlignmentCheck::None; miri_config.check_alignment = miri::AlignmentCheck::None;
} else if arg == "-Zmiri-symbolic-alignment-check" { } else if arg == "-Zmiri-symbolic-alignment-check" {
miri_config.check_alignment = miri::AlignmentCheck::Symbolic; miri_config.check_alignment = miri::AlignmentCheck::Symbolic;
} else if arg == "-Zmiri-check-number-validity" {
eprintln!(
"WARNING: the flag `-Zmiri-check-number-validity` no longer has any effect \
since it is now enabled by default"
);
} else if arg == "-Zmiri-disable-abi-check" { } else if arg == "-Zmiri-disable-abi-check" {
eprintln!( eprintln!(
"WARNING: the flag `-Zmiri-disable-abi-check` is deprecated and planned to be removed.\n\ "WARNING: the flag `-Zmiri-disable-abi-check` no longer has any effect; \
If you have a use-case for it, please file an issue." ABI checks cannot be disabled any more"
); );
miri_config.check_abi = false;
} else if arg == "-Zmiri-disable-isolation" { } else if arg == "-Zmiri-disable-isolation" {
if matches!(isolation_enabled, Some(true)) { if matches!(isolation_enabled, Some(true)) {
show_error!( show_error!(
@ -459,8 +453,6 @@ fn main() {
miri_config.collect_leak_backtraces = false; miri_config.collect_leak_backtraces = false;
} else if arg == "-Zmiri-panic-on-unsupported" { } else if arg == "-Zmiri-panic-on-unsupported" {
miri_config.panic_on_unsupported = true; miri_config.panic_on_unsupported = true;
} else if arg == "-Zmiri-tag-raw-pointers" {
eprintln!("WARNING: `-Zmiri-tag-raw-pointers` has no effect; it is enabled by default");
} else if arg == "-Zmiri-strict-provenance" { } else if arg == "-Zmiri-strict-provenance" {
miri_config.provenance_mode = ProvenanceMode::Strict; miri_config.provenance_mode = ProvenanceMode::Strict;
} else if arg == "-Zmiri-permissive-provenance" { } else if arg == "-Zmiri-permissive-provenance" {
@ -476,10 +468,6 @@ fn main() {
"scalar" => RetagFields::OnlyScalar, "scalar" => RetagFields::OnlyScalar,
_ => show_error!("`-Zmiri-retag-fields` can only be `all`, `none`, or `scalar`"), _ => show_error!("`-Zmiri-retag-fields` can only be `all`, `none`, or `scalar`"),
}; };
} else if arg == "-Zmiri-track-raw-pointers" {
eprintln!(
"WARNING: `-Zmiri-track-raw-pointers` has no effect; it is enabled by default"
);
} else if let Some(param) = arg.strip_prefix("-Zmiri-seed=") { } else if let Some(param) = arg.strip_prefix("-Zmiri-seed=") {
if miri_config.seed.is_some() { if miri_config.seed.is_some() {
show_error!("Cannot specify -Zmiri-seed multiple times!"); show_error!("Cannot specify -Zmiri-seed multiple times!");

View File

@ -445,10 +445,13 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
/// Set an active thread and return the id of the thread that was active before. /// Set an active thread and return the id of the thread that was active before.
fn set_active_thread_id(&mut self, id: ThreadId) -> ThreadId { fn set_active_thread_id(&mut self, id: ThreadId) -> ThreadId {
let active_thread_id = self.active_thread; assert!(id.index() < self.threads.len());
self.active_thread = id; info!(
assert!(self.active_thread.index() < self.threads.len()); "---------- Now executing on thread `{}` (previous: `{}`) ----------------------------------------",
active_thread_id self.get_thread_display_name(id),
self.get_thread_display_name(self.active_thread)
);
std::mem::replace(&mut self.active_thread, id)
} }
/// Get the id of the currently active thread. /// Get the id of the currently active thread.
@ -735,6 +738,11 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
for (id, thread) in threads { for (id, thread) in threads {
debug_assert_ne!(self.active_thread, id); debug_assert_ne!(self.active_thread, id);
if thread.state == ThreadState::Enabled { if thread.state == ThreadState::Enabled {
info!(
"---------- Now executing on thread `{}` (previous: `{}`) ----------------------------------------",
self.get_thread_display_name(id),
self.get_thread_display_name(self.active_thread)
);
self.active_thread = id; self.active_thread = id;
break; break;
} }
@ -882,7 +890,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
instance, instance,
start_abi, start_abi,
&[*func_arg], &[*func_arg],
Some(&ret_place.into()), Some(&ret_place),
StackPopCleanup::Root { cleanup: true }, StackPopCleanup::Root { cleanup: true },
)?; )?;

View File

@ -94,8 +94,6 @@ pub struct MiriConfig {
pub unique_is_unique: bool, pub unique_is_unique: bool,
/// Controls alignment checking. /// Controls alignment checking.
pub check_alignment: AlignmentCheck, pub check_alignment: AlignmentCheck,
/// Controls function [ABI](Abi) checking.
pub check_abi: bool,
/// Action for an op requiring communication with the host. /// Action for an op requiring communication with the host.
pub isolated_op: IsolatedOp, pub isolated_op: IsolatedOp,
/// Determines if memory leaks should be ignored. /// Determines if memory leaks should be ignored.
@ -162,7 +160,6 @@ impl Default for MiriConfig {
borrow_tracker: Some(BorrowTrackerMethod::StackedBorrows), borrow_tracker: Some(BorrowTrackerMethod::StackedBorrows),
unique_is_unique: false, unique_is_unique: false,
check_alignment: AlignmentCheck::Int, check_alignment: AlignmentCheck::Int,
check_abi: true,
isolated_op: IsolatedOp::Reject(RejectOpWith::Abort), isolated_op: IsolatedOp::Reject(RejectOpWith::Abort),
ignore_leaks: false, ignore_leaks: false,
forwarded_env_vars: vec![], forwarded_env_vars: vec![],
@ -394,7 +391,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
argv, argv,
Scalar::from_u8(sigpipe).into(), Scalar::from_u8(sigpipe).into(),
], ],
Some(&ret_place.into()), Some(&ret_place),
StackPopCleanup::Root { cleanup: true }, StackPopCleanup::Root { cleanup: true },
)?; )?;
} }
@ -403,7 +400,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
entry_instance, entry_instance,
Abi::Rust, Abi::Rust,
&[argc.into(), argv], &[argc.into(), argv],
Some(&ret_place.into()), Some(&ret_place),
StackPopCleanup::Root { cleanup: true }, StackPopCleanup::Root { cleanup: true },
)?; )?;
} }

View File

@ -1,6 +1,8 @@
use std::cmp; use std::cmp;
use std::collections::BTreeSet;
use std::iter; use std::iter;
use std::num::NonZero; use std::num::NonZero;
use std::sync::Mutex;
use std::time::Duration; use std::time::Duration;
use rustc_apfloat::ieee::{Double, Single}; use rustc_apfloat::ieee::{Double, Single};
@ -387,7 +389,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
let param_env = ty::ParamEnv::reveal_all(); // in Miri this is always the param_env we use... and this.param_env is private. let param_env = ty::ParamEnv::reveal_all(); // in Miri this is always the param_env we use... and this.param_env is private.
let callee_abi = f.ty(*this.tcx, param_env).fn_sig(*this.tcx).abi(); let callee_abi = f.ty(*this.tcx, param_env).fn_sig(*this.tcx).abi();
if this.machine.enforce_abi && callee_abi != caller_abi { if callee_abi != caller_abi {
throw_ub_format!( throw_ub_format!(
"calling a function with ABI {} using caller ABI {}", "calling a function with ABI {} using caller ABI {}",
callee_abi.name(), callee_abi.name(),
@ -399,7 +401,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let mir = this.load_mir(f.def, None)?; let mir = this.load_mir(f.def, None)?;
let dest = match dest { let dest = match dest {
Some(dest) => dest.clone(), Some(dest) => dest.clone(),
None => MPlaceTy::fake_alloc_zst(this.layout_of(mir.return_ty())?).into(), None => MPlaceTy::fake_alloc_zst(this.layout_of(mir.return_ty())?),
}; };
this.push_stack_frame(f, mir, &dest, stack_pop)?; this.push_stack_frame(f, mir, &dest, stack_pop)?;
@ -603,9 +605,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
match reject_with { match reject_with {
RejectOpWith::Abort => isolation_abort_error(op_name), RejectOpWith::Abort => isolation_abort_error(op_name),
RejectOpWith::WarningWithoutBacktrace => { RejectOpWith::WarningWithoutBacktrace => {
this.tcx // This exists to reduce verbosity; make sure we emit the warning at most once per
.dcx() // operation.
.warn(format!("{op_name} was made to return an error due to isolation")); static EMITTED_WARNINGS: Mutex<BTreeSet<String>> = Mutex::new(BTreeSet::new());
let mut emitted_warnings = EMITTED_WARNINGS.lock().unwrap();
if !emitted_warnings.contains(op_name) {
// First time we are seeing this.
emitted_warnings.insert(op_name.to_owned());
this.tcx
.dcx()
.warn(format!("{op_name} was made to return an error due to isolation"));
}
Ok(()) Ok(())
} }
RejectOpWith::Warning => { RejectOpWith::Warning => {
@ -945,7 +956,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
/// Check that the ABI is what we expect. /// Check that the ABI is what we expect.
fn check_abi<'a>(&self, abi: Abi, exp_abi: Abi) -> InterpResult<'a, ()> { fn check_abi<'a>(&self, abi: Abi, exp_abi: Abi) -> InterpResult<'a, ()> {
if self.eval_context_ref().machine.enforce_abi && abi != exp_abi { if abi != exp_abi {
throw_ub_format!( throw_ub_format!(
"calling a function with ABI {} using caller ABI {}", "calling a function with ABI {} using caller ABI {}",
exp_abi.name(), exp_abi.name(),
@ -1091,20 +1102,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
} }
} }
let (val, status) = match src.layout.ty.kind() { let ty::Float(fty) = src.layout.ty.kind() else {
// f32 bug!("float_to_int_checked: non-float input type {}", src.layout.ty)
ty::Float(FloatTy::F32) => };
let (val, status) = match fty {
FloatTy::F16 => unimplemented!("f16_f128"),
FloatTy::F32 =>
float_to_int_inner::<Single>(this, src.to_scalar().to_f32()?, cast_to, round), float_to_int_inner::<Single>(this, src.to_scalar().to_f32()?, cast_to, round),
// f64 FloatTy::F64 =>
ty::Float(FloatTy::F64) =>
float_to_int_inner::<Double>(this, src.to_scalar().to_f64()?, cast_to, round), float_to_int_inner::<Double>(this, src.to_scalar().to_f64()?, cast_to, round),
// Nothing else FloatTy::F128 => unimplemented!("f16_f128"),
_ =>
span_bug!(
this.cur_span(),
"attempted float-to-int conversion with non-float input type {}",
src.layout.ty,
),
}; };
if status.intersects( if status.intersects(

View File

@ -49,8 +49,12 @@
// Needed for rustdoc from bootstrap (with `-Znormalize-docs`). // Needed for rustdoc from bootstrap (with `-Znormalize-docs`).
#![recursion_limit = "256"] #![recursion_limit = "256"]
extern crate either; // the one from rustc // Some "regular" crates we want to share with rustc
extern crate either;
#[macro_use]
extern crate tracing;
// The rustc crates we need
extern crate rustc_apfloat; extern crate rustc_apfloat;
extern crate rustc_ast; extern crate rustc_ast;
extern crate rustc_const_eval; extern crate rustc_const_eval;
@ -63,11 +67,8 @@ extern crate rustc_middle;
extern crate rustc_session; extern crate rustc_session;
extern crate rustc_span; extern crate rustc_span;
extern crate rustc_target; extern crate rustc_target;
#[macro_use] // Linking `rustc_driver` pulls in the required object code as the rest of the rustc crates are
extern crate tracing; // shipped only as rmeta files.
// Necessary to pull in object code as the rest of the rustc crates are shipped only as rmeta
// files.
#[allow(unused_extern_crates)] #[allow(unused_extern_crates)]
extern crate rustc_driver; extern crate rustc_driver;
@ -143,4 +144,7 @@ pub const MIRI_DEFAULT_ARGS: &[&str] = &[
"-Zmir-keep-place-mention", "-Zmir-keep-place-mention",
"-Zmir-opt-level=0", "-Zmir-opt-level=0",
"-Zmir-enable-passes=-CheckAlignment", "-Zmir-enable-passes=-CheckAlignment",
// Deduplicating diagnostics means we miss events when tracking what happens during an
// execution. Let's not do that.
"-Zdeduplicate-diagnostics=no",
]; ];

View File

@ -462,9 +462,6 @@ pub struct MiriMachine<'mir, 'tcx> {
/// Whether to enforce the validity invariant. /// Whether to enforce the validity invariant.
pub(crate) validate: bool, pub(crate) validate: bool,
/// Whether to enforce [ABI](Abi) of function calls.
pub(crate) enforce_abi: bool,
/// The table of file descriptors. /// The table of file descriptors.
pub(crate) file_handler: shims::unix::FileHandler, pub(crate) file_handler: shims::unix::FileHandler,
/// The table of directory descriptors. /// The table of directory descriptors.
@ -643,7 +640,6 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
tls: TlsData::default(), tls: TlsData::default(),
isolated_op: config.isolated_op, isolated_op: config.isolated_op,
validate: config.validate, validate: config.validate,
enforce_abi: config.check_abi,
file_handler: FileHandler::new(config.mute_stdout_stderr), file_handler: FileHandler::new(config.mute_stdout_stderr),
dir_handler: Default::default(), dir_handler: Default::default(),
layouts, layouts,
@ -786,7 +782,6 @@ impl VisitProvenance for MiriMachine<'_, '_> {
tcx: _, tcx: _,
isolated_op: _, isolated_op: _,
validate: _, validate: _,
enforce_abi: _,
clock: _, clock: _,
layouts: _, layouts: _,
static_roots: _, static_roots: _,
@ -934,8 +929,8 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
} }
#[inline(always)] #[inline(always)]
fn enforce_abi(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool { fn enforce_abi(_ecx: &MiriInterpCx<'mir, 'tcx>) -> bool {
ecx.machine.enforce_abi true
} }
#[inline(always)] #[inline(always)]
@ -1338,7 +1333,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
// If we have a borrow tracker, we also have it set up protection so that all reads *and // If we have a borrow tracker, we also have it set up protection so that all reads *and
// writes* during this call are insta-UB. // writes* during this call are insta-UB.
let protected_place = if ecx.machine.borrow_tracker.is_some() { let protected_place = if ecx.machine.borrow_tracker.is_some() {
ecx.protect_place(&place)?.into() ecx.protect_place(place)?
} else { } else {
// No borrow tracker. // No borrow tracker.
place.clone() place.clone()
@ -1447,13 +1442,17 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
if ecx.machine.borrow_tracker.is_some() { if ecx.machine.borrow_tracker.is_some() {
ecx.on_stack_pop(frame)?; ecx.on_stack_pop(frame)?;
} }
// tracing-tree can autoamtically annotate scope changes, but it gets very confused by our
// concurrency and what it prints is just plain wrong. So we print our own information
// instead. (Cc https://github.com/rust-lang/miri/issues/2266)
info!("Leaving {}", ecx.frame().instance);
Ok(()) Ok(())
} }
#[inline(always)] #[inline(always)]
fn after_stack_pop( fn after_stack_pop(
ecx: &mut InterpCx<'mir, 'tcx, Self>, ecx: &mut InterpCx<'mir, 'tcx, Self>,
mut frame: Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>, frame: Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>,
unwinding: bool, unwinding: bool,
) -> InterpResult<'tcx, StackPopJump> { ) -> InterpResult<'tcx, StackPopJump> {
if frame.extra.is_user_relevant { if frame.extra.is_user_relevant {
@ -1463,10 +1462,20 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
// user-relevant frame and restore that here.) // user-relevant frame and restore that here.)
ecx.active_thread_mut().recompute_top_user_relevant_frame(); ecx.active_thread_mut().recompute_top_user_relevant_frame();
} }
let timing = frame.extra.timing.take(); let res = {
let res = ecx.handle_stack_pop_unwind(frame.extra, unwinding); // Move `frame`` into a sub-scope so we control when it will be dropped.
if let Some(profiler) = ecx.machine.profiler.as_ref() { let mut frame = frame;
profiler.finish_recording_interval_event(timing.unwrap()); let timing = frame.extra.timing.take();
let res = ecx.handle_stack_pop_unwind(frame.extra, unwinding);
if let Some(profiler) = ecx.machine.profiler.as_ref() {
profiler.finish_recording_interval_event(timing.unwrap());
}
res
};
// Needs to be done after dropping frame to show up on the right nesting level.
// (Cc https://github.com/rust-lang/miri/issues/2266)
if !ecx.active_thread_stack().is_empty() {
info!("Continuing in {}", ecx.frame().instance);
} }
res res
} }

View File

@ -287,13 +287,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
_ => bug!(), _ => bug!(),
}; };
let float_finite = |x: &ImmTy<'tcx, _>| -> InterpResult<'tcx, bool> { let float_finite = |x: &ImmTy<'tcx, _>| -> InterpResult<'tcx, bool> {
Ok(match x.layout.ty.kind() { let ty::Float(fty) = x.layout.ty.kind() else {
ty::Float(FloatTy::F32) => x.to_scalar().to_f32()?.is_finite(), bug!("float_finite: non-float input type {}", x.layout.ty)
ty::Float(FloatTy::F64) => x.to_scalar().to_f64()?.is_finite(), };
_ => bug!( Ok(match fty {
"`{intrinsic_name}` called with non-float input type {ty:?}", FloatTy::F16 => unimplemented!("f16_f128"),
ty = x.layout.ty, FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(),
), FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(),
FloatTy::F128 => unimplemented!("f16_f128"),
}) })
}; };
match (float_finite(&a)?, float_finite(&b)?) { match (float_finite(&a)?, float_finite(&b)?) {

View File

@ -179,7 +179,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let id = this.init_once_get_id(init_once_op)?; let id = this.init_once_get_id(init_once_op)?;
let flags = this.read_scalar(flags_op)?.to_u32()?; let flags = this.read_scalar(flags_op)?.to_u32()?;
let pending_place = this.deref_pointer(pending_op)?.into(); let pending_place = this.deref_pointer(pending_op)?;
let context = this.read_pointer(context_op)?; let context = this.read_pointer(context_op)?;
if flags != 0 { if flags != 0 {

View File

@ -183,9 +183,9 @@ dependencies = [
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.8.10" version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [ dependencies = [
"libc", "libc",
"wasi 0.11.0+wasi-snapshot-preview1", "wasi 0.11.0+wasi-snapshot-preview1",

View File

@ -1,29 +0,0 @@
//@ignore-target-windows: No libc on Windows
//@compile-flags: -Zmiri-disable-abi-check
//! Unwinding past the top frame of a stack is Undefined Behavior.
#![feature(c_unwind)]
use std::{mem, ptr};
extern "C-unwind" fn thread_start(_null: *mut libc::c_void) -> *mut libc::c_void {
//~^ ERROR: unwinding past the topmost frame of the stack
panic!()
}
fn main() {
unsafe {
let mut native: libc::pthread_t = mem::zeroed();
let attr: libc::pthread_attr_t = mem::zeroed();
// assert_eq!(libc::pthread_attr_init(&mut attr), 0); FIXME: this function is not yet implemented.
// Cast to avoid inserting abort-on-unwind.
let thread_start: extern "C-unwind" fn(*mut libc::c_void) -> *mut libc::c_void =
thread_start;
let thread_start: extern "C" fn(*mut libc::c_void) -> *mut libc::c_void =
mem::transmute(thread_start);
assert_eq!(libc::pthread_create(&mut native, &attr, thread_start, ptr::null_mut()), 0);
assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0);
}
}

View File

@ -1,21 +0,0 @@
WARNING: the flag `-Zmiri-disable-abi-check` is deprecated and planned to be removed.
If you have a use-case for it, please file an issue.
thread '<unnamed>' panicked at $DIR/unwind_top_of_stack.rs:LL:CC:
explicit panic
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: Undefined Behavior: unwinding past the topmost frame of the stack
--> $DIR/unwind_top_of_stack.rs:LL:CC
|
LL | / extern "C-unwind" fn thread_start(_null: *mut libc::c_void) -> *mut libc::c_void {
LL | |
LL | | panic!()
LL | | }
| |_^ unwinding past the topmost frame of the stack
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE on thread `unnamed-ID`:
= note: inside `thread_start` at $DIR/unwind_top_of_stack.rs:LL:CC
error: aborting due to 1 previous error

View File

@ -10,6 +10,14 @@ note: erroneous constant encountered
LL | let _x = UNALIGNED_READ; LL | let _x = UNALIGNED_READ;
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
note: erroneous constant encountered
--> $DIR/const-ub-checks.rs:LL:CC
|
LL | let _x = UNALIGNED_READ;
| ^^^^^^^^^^^^^^
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error: aborting due to 1 previous error error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0080`. For more information about this error, try `rustc --explain E0080`.

View File

@ -10,6 +10,14 @@ note: erroneous constant encountered
LL | println!("{}", FOO); LL | println!("{}", FOO);
| ^^^ | ^^^
note: erroneous constant encountered
--> $DIR/erroneous_const2.rs:LL:CC
|
LL | println!("{}", FOO);
| ^^^
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
note: erroneous constant encountered note: erroneous constant encountered
--> $DIR/erroneous_const2.rs:LL:CC --> $DIR/erroneous_const2.rs:LL:CC
| |

View File

@ -19,7 +19,6 @@ fn main() {
after_call = { after_call = {
Return() Return()
} }
} }
} }

View File

@ -14,7 +14,7 @@ LL | | let _unit: ();
LL | | { LL | | {
LL | | let non_copy = S(42); LL | | let non_copy = S(42);
... | ... |
LL | | LL | | }
LL | | } LL | | }
| |_____^ | |_____^
help: <TAG> is this argument help: <TAG> is this argument

View File

@ -16,7 +16,7 @@ LL | | let _unit: ();
LL | | { LL | | {
LL | | let non_copy = S(42); LL | | let non_copy = S(42);
... | ... |
LL | | LL | | }
LL | | } LL | | }
| |_____^ | |_____^
help: the protected tag <TAG> was created here, in the initial state Reserved help: the protected tag <TAG> was created here, in the initial state Reserved

View File

@ -1,4 +1,3 @@
//@compile-flags: -Zmiri-disable-abi-check
#![feature(c_unwind)] #![feature(c_unwind)]
#[no_mangle] #[no_mangle]

View File

@ -1,5 +1,3 @@
WARNING: the flag `-Zmiri-disable-abi-check` is deprecated and planned to be removed.
If you have a use-case for it, please file an issue.
thread 'main' panicked at $DIR/exported_symbol_bad_unwind1.rs:LL:CC: thread 'main' panicked at $DIR/exported_symbol_bad_unwind1.rs:LL:CC:
explicit panic explicit panic
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

View File

@ -1,12 +0,0 @@
//@compile-flags: -Zmiri-disable-abi-check
// This feature is required to trigger the error using the "C" ABI.
#![feature(c_unwind)]
extern "C" {
fn miri_start_unwind(payload: *mut u8) -> !;
}
fn main() {
unsafe { miri_start_unwind(&mut 0) }
//~^ ERROR: unwinding past a stack frame that does not allow unwinding
}

View File

@ -1,17 +0,0 @@
WARNING: the flag `-Zmiri-disable-abi-check` is deprecated and planned to be removed.
If you have a use-case for it, please file an issue.
error: Undefined Behavior: unwinding past a stack frame that does not allow unwinding
--> $DIR/bad_miri_start_unwind.rs:LL:CC
|
LL | unsafe { miri_start_unwind(&mut 0) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^ unwinding past a stack frame that does not allow unwinding
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at $DIR/bad_miri_start_unwind.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View File

@ -1,6 +1,9 @@
#![feature(c_unwind)] #![feature(c_unwind)]
//! Unwinding when the caller ABI is "C" (without "-unwind") is UB. //! Unwinding when the caller ABI is "C" (without "-unwind") is UB.
// The opposite version (callee does not allow unwinding) is impossible to
// even write: MIR validation catches functions that have `UnwindContinue` but
// are not allowed to unwind.
extern "C-unwind" fn unwind() { extern "C-unwind" fn unwind() {
panic!(); panic!();

View File

@ -219,6 +219,7 @@ fn wait_wake_bitset() {
t.join().unwrap(); t.join().unwrap();
} }
// Crucial test which relies on the SeqCst fences in futex wait/wake.
fn concurrent_wait_wake() { fn concurrent_wait_wake() {
const FREE: i32 = 0; const FREE: i32 = 0;
const HELD: i32 = 1; const HELD: i32 = 1;

View File

@ -7,6 +7,10 @@ use std::sync::{Arc, Barrier, Condvar, Mutex, Once, RwLock};
use std::thread; use std::thread;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
// We are expecting to sleep for 10ms. How long of a sleep we are accepting?
// Even with 1000ms we still see this test fail on macOS runners.
const MAX_SLEEP_TIME_MS: u64 = 2000;
// Check if Rust barriers are working. // Check if Rust barriers are working.
/// This test is taken from the Rust documentation. /// This test is taken from the Rust documentation.
@ -66,7 +70,7 @@ fn check_conditional_variables_timed_wait_timeout() {
let (_guard, timeout) = cvar.wait_timeout(guard, Duration::from_millis(10)).unwrap(); let (_guard, timeout) = cvar.wait_timeout(guard, Duration::from_millis(10)).unwrap();
assert!(timeout.timed_out()); assert!(timeout.timed_out());
let elapsed_time = now.elapsed().as_millis(); let elapsed_time = now.elapsed().as_millis();
assert!(10 <= elapsed_time && elapsed_time <= 1000); assert!(10 <= elapsed_time && elapsed_time <= MAX_SLEEP_TIME_MS.into());
} }
/// Test that signaling a conditional variable when waiting with a timeout works /// Test that signaling a conditional variable when waiting with a timeout works
@ -84,7 +88,8 @@ fn check_conditional_variables_timed_wait_notimeout() {
cvar.notify_one(); cvar.notify_one();
}); });
let (_guard, timeout) = cvar.wait_timeout(guard, Duration::from_millis(1000)).unwrap(); let (_guard, timeout) =
cvar.wait_timeout(guard, Duration::from_millis(MAX_SLEEP_TIME_MS)).unwrap();
assert!(!timeout.timed_out()); assert!(!timeout.timed_out());
handle.join().unwrap(); handle.join().unwrap();
} }
@ -213,20 +218,21 @@ fn check_once() {
fn park_timeout() { fn park_timeout() {
let start = Instant::now(); let start = Instant::now();
thread::park_timeout(Duration::from_millis(200)); thread::park_timeout(Duration::from_millis(10));
// Normally, waiting in park/park_timeout may spuriously wake up early, but we // Normally, waiting in park/park_timeout may spuriously wake up early, but we
// know Miri's timed synchronization primitives do not do that. // know Miri's timed synchronization primitives do not do that.
// We allow much longer sleeps as well since the macOS GHA runners seem very oversubscribed
// and sometimes just pause for 1 second or more.
let elapsed = start.elapsed(); let elapsed = start.elapsed();
assert!((200..2000).contains(&elapsed.as_millis()), "bad sleep time: {elapsed:?}"); assert!(
(10..MAX_SLEEP_TIME_MS.into()).contains(&elapsed.as_millis()),
"bad sleep time: {elapsed:?}"
);
} }
fn park_unpark() { fn park_unpark() {
let t1 = thread::current(); let t1 = thread::current();
let t2 = thread::spawn(move || { let t2 = thread::spawn(move || {
thread::park(); thread::park();
thread::sleep(Duration::from_millis(200)); thread::sleep(Duration::from_millis(10));
t1.unpark(); t1.unpark();
}); });
@ -236,10 +242,11 @@ fn park_unpark() {
thread::park(); thread::park();
// Normally, waiting in park/park_timeout may spuriously wake up early, but we // Normally, waiting in park/park_timeout may spuriously wake up early, but we
// know Miri's timed synchronization primitives do not do that. // know Miri's timed synchronization primitives do not do that.
// We allow much longer sleeps as well since the macOS GHA runners seem very oversubscribed
// and sometimes just pause for 1 second or more.
let elapsed = start.elapsed(); let elapsed = start.elapsed();
assert!((200..2000).contains(&elapsed.as_millis()), "bad sleep time: {elapsed:?}"); assert!(
(10..MAX_SLEEP_TIME_MS.into()).contains(&elapsed.as_millis()),
"bad sleep time: {elapsed:?}"
);
t2.join().unwrap(); t2.join().unwrap();
} }

View File

@ -1,24 +0,0 @@
//@compile-flags: -Zmiri-disable-abi-check
#![feature(core_intrinsics)]
fn main() {
fn foo() {}
extern "C" fn try_fn(ptr: *mut u8) {
assert!(ptr.is_null());
}
extern "Rust" {
fn malloc(size: usize) -> *mut std::ffi::c_void;
}
unsafe {
let _ = malloc(0);
std::mem::transmute::<fn(), extern "C" fn()>(foo)();
std::intrinsics::catch_unwind(
std::mem::transmute::<extern "C" fn(*mut u8), _>(try_fn),
std::ptr::null_mut(),
|_, _| unreachable!(),
);
}
}

View File

@ -1,2 +0,0 @@
WARNING: the flag `-Zmiri-disable-abi-check` is deprecated and planned to be removed.
If you have a use-case for it, please file an issue.

View File

@ -295,7 +295,7 @@ fn test_canonicalize() {
drop(File::create(&path).unwrap()); drop(File::create(&path).unwrap());
let p = canonicalize(format!("{}/./test_file", dir_path.to_string_lossy())).unwrap(); let p = canonicalize(format!("{}/./test_file", dir_path.to_string_lossy())).unwrap();
assert_eq!(p.to_string_lossy().find('.'), None); assert_eq!(p.to_string_lossy().find("/./"), None);
remove_dir_all(&dir_path).unwrap(); remove_dir_all(&dir_path).unwrap();
} }

View File

@ -79,13 +79,6 @@ fn test_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) ->
program.args.push(flag); program.args.push(flag);
} }
// Add a test env var to do environment communication tests.
program.envs.push(("MIRI_ENV_VAR_TEST".into(), Some("0".into())));
// Let the tests know where to store temp files (they might run for a different target, which can make this hard to find).
let miri_temp = env::var_os("MIRI_TEMP").unwrap_or_else(|| env::temp_dir().into());
program.envs.push(("MIRI_TEMP".into(), Some(miri_temp)));
let mut config = Config { let mut config = Config {
target: Some(target.to_owned()), target: Some(target.to_owned()),
stderr_filters: STDERR.clone(), stderr_filters: STDERR.clone(),
@ -116,9 +109,21 @@ fn test_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) ->
config config
} }
fn run_tests(mode: Mode, path: &str, target: &str, with_dependencies: bool) -> Result<()> { fn run_tests(
mode: Mode,
path: &str,
target: &str,
with_dependencies: bool,
tmpdir: &Path,
) -> Result<()> {
let mut config = test_config(target, path, mode, with_dependencies); let mut config = test_config(target, path, mode, with_dependencies);
// Add a test env var to do environment communication tests.
config.program.envs.push(("MIRI_ENV_VAR_TEST".into(), Some("0".into())));
// Let the tests know where to store temp files (they might run for a different target, which can make this hard to find).
config.program.envs.push(("MIRI_TEMP".into(), Some(tmpdir.to_owned().into())));
// Handle command-line arguments. // Handle command-line arguments.
let args = ui_test::Args::test()?; let args = ui_test::Args::test()?;
let default_bless = env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0"); let default_bless = env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0");
@ -211,7 +216,13 @@ enum Dependencies {
use Dependencies::*; use Dependencies::*;
fn ui(mode: Mode, path: &str, target: &str, with_dependencies: Dependencies) -> Result<()> { fn ui(
mode: Mode,
path: &str,
target: &str,
with_dependencies: Dependencies,
tmpdir: &Path,
) -> Result<()> {
let msg = format!("## Running ui tests in {path} against miri for {target}"); let msg = format!("## Running ui tests in {path} against miri for {target}");
eprintln!("{}", msg.green().bold()); eprintln!("{}", msg.green().bold());
@ -219,7 +230,7 @@ fn ui(mode: Mode, path: &str, target: &str, with_dependencies: Dependencies) ->
WithDependencies => true, WithDependencies => true,
WithoutDependencies => false, WithoutDependencies => false,
}; };
run_tests(mode, path, target, with_dependencies) run_tests(mode, path, target, with_dependencies, tmpdir)
} }
fn get_target() -> String { fn get_target() -> String {
@ -230,6 +241,7 @@ fn main() -> Result<()> {
ui_test::color_eyre::install()?; ui_test::color_eyre::install()?;
let target = get_target(); let target = get_target();
let tmpdir = tempfile::Builder::new().prefix("miri-uitest-").tempdir()?;
let mut args = std::env::args_os(); let mut args = std::env::args_os();
@ -240,28 +252,31 @@ fn main() -> Result<()> {
} }
} }
ui(Mode::Pass, "tests/pass", &target, WithoutDependencies)?; ui(Mode::Pass, "tests/pass", &target, WithoutDependencies, tmpdir.path())?;
ui(Mode::Pass, "tests/pass-dep", &target, WithDependencies)?; ui(Mode::Pass, "tests/pass-dep", &target, WithDependencies, tmpdir.path())?;
ui(Mode::Panic, "tests/panic", &target, WithDependencies)?; ui(Mode::Panic, "tests/panic", &target, WithDependencies, tmpdir.path())?;
ui( ui(
Mode::Fail { require_patterns: true, rustfix: RustfixMode::Disabled }, Mode::Fail { require_patterns: true, rustfix: RustfixMode::Disabled },
"tests/fail", "tests/fail",
&target, &target,
WithoutDependencies, WithoutDependencies,
tmpdir.path(),
)?; )?;
ui( ui(
Mode::Fail { require_patterns: true, rustfix: RustfixMode::Disabled }, Mode::Fail { require_patterns: true, rustfix: RustfixMode::Disabled },
"tests/fail-dep", "tests/fail-dep",
&target, &target,
WithDependencies, WithDependencies,
tmpdir.path(),
)?; )?;
if cfg!(target_os = "linux") { if cfg!(target_os = "linux") {
ui(Mode::Pass, "tests/extern-so/pass", &target, WithoutDependencies)?; ui(Mode::Pass, "tests/extern-so/pass", &target, WithoutDependencies, tmpdir.path())?;
ui( ui(
Mode::Fail { require_patterns: true, rustfix: RustfixMode::Disabled }, Mode::Fail { require_patterns: true, rustfix: RustfixMode::Disabled },
"tests/extern-so/fail", "tests/extern-so/fail",
&target, &target,
WithoutDependencies, WithoutDependencies,
tmpdir.path(),
)?; )?;
} }