mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-29 10:13:54 +00:00
Rollup merge of #124007 - RalfJung:miri, r=RalfJung
Miri subtree update r? ``@ghost``
This commit is contained in:
commit
cf7900476e
@ -3364,9 +3364,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-build-sysroot"
|
name = "rustc-build-sysroot"
|
||||||
version = "0.4.5"
|
version = "0.4.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a26170e1d79ea32f7ccec3188dd13cfc1f18c82764a9cbc1071667c0f865a4ea"
|
checksum = "a9bf37423495cd3a6a9ef8c75fc4566de0d26de0ab75f36f67a426e58df5768c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
|
@ -451,36 +451,32 @@ Some native rustc `-Z` flags are also very relevant for Miri:
|
|||||||
* `-Zmir-emit-retag` controls whether `Retag` statements are emitted. Miri
|
* `-Zmir-emit-retag` controls whether `Retag` statements are emitted. Miri
|
||||||
enables this per default because it is needed for [Stacked Borrows] and [Tree Borrows].
|
enables this per default because it is needed for [Stacked Borrows] and [Tree Borrows].
|
||||||
|
|
||||||
Moreover, Miri recognizes some environment variables:
|
Moreover, Miri recognizes some environment variables (unless noted otherwise, these are supported
|
||||||
|
by all intended entry points, i.e. `cargo miri` and `./miri {test,run}`):
|
||||||
|
|
||||||
* `MIRI_AUTO_OPS` indicates whether the automatic execution of rustfmt, clippy and toolchain setup
|
* `MIRI_AUTO_OPS` indicates whether the automatic execution of rustfmt, clippy and toolchain setup
|
||||||
should be skipped. If it is set to `no`, they are skipped. This is used to allow automated IDE
|
should be skipped. If it is set to `no`, they are skipped. This is used to allow automated IDE
|
||||||
actions to avoid the auto ops.
|
actions to avoid the auto ops.
|
||||||
* `MIRI_LOG`, `MIRI_BACKTRACE` control logging and backtrace printing during
|
* `MIRI_LOG`, `MIRI_BACKTRACE` control logging and backtrace printing during
|
||||||
Miri executions, also [see "Testing the Miri driver" in `CONTRIBUTING.md`][testing-miri].
|
Miri executions, also [see "Testing the Miri driver" in `CONTRIBUTING.md`][testing-miri].
|
||||||
* `MIRIFLAGS` (recognized by `cargo miri` and the test suite) defines extra
|
* `MIRIFLAGS` defines extra flags to be passed to Miri.
|
||||||
flags to be passed to Miri.
|
* `MIRI_LIB_SRC` defines the directory where Miri expects the sources of the standard library that
|
||||||
* `MIRI_LIB_SRC` defines the directory where Miri expects the sources of the
|
it will build and use for interpretation. This directory must point to the `library` subdirectory
|
||||||
standard library that it will build and use for interpretation. This directory
|
of a `rust-lang/rust` repository checkout.
|
||||||
must point to the `library` subdirectory of a `rust-lang/rust` repository
|
* `MIRI_SYSROOT` indicates the sysroot to use. When using `cargo miri`, this skips the automatic
|
||||||
checkout.
|
setup -- only set this if you do not want to use the automatically created sysroot. When invoking
|
||||||
* `MIRI_SYSROOT` (recognized by `cargo miri` and the Miri driver) indicates the sysroot to use. When
|
`cargo miri setup`, this indicates where the sysroot will be put.
|
||||||
using `cargo miri`, this skips the automatic setup -- only set this if you do not want to use the
|
* `MIRI_TEST_TARGET` (recognized by `./miri {test,run}`) indicates which target
|
||||||
automatically created sysroot. For directly invoking the Miri driver, this variable (or a
|
|
||||||
`--sysroot` flag) is mandatory. When invoking `cargo miri setup`, this indicates where the sysroot
|
|
||||||
will be put.
|
|
||||||
* `MIRI_TEST_TARGET` (recognized by the test suite and the `./miri` script) indicates which target
|
|
||||||
architecture to test against. `miri` and `cargo miri` accept the `--target` flag for the same
|
architecture to test against. `miri` and `cargo miri` accept the `--target` flag for the same
|
||||||
purpose.
|
purpose.
|
||||||
* `MIRI_TEST_THREADS` (recognized by the test suite): set the number of threads to use for running tests.
|
* `MIRI_TEST_THREADS` (recognized by `./miri test`): set the number of threads to use for running tests.
|
||||||
By default the number of cores is used.
|
By default, the number of cores is used.
|
||||||
* `MIRI_NO_STD` (recognized by `cargo miri`) makes sure that the target's sysroot is built without
|
* `MIRI_NO_STD` makes sure that the target's sysroot is built without libstd. This allows testing
|
||||||
libstd. This allows testing and running no_std programs.
|
and running no_std programs. (Miri has a heuristic to detect no-std targets based on the target
|
||||||
(Miri has a heuristic to detect no-std targets based on the target name; this environment variable
|
name; this environment variable is only needed when that heuristic fails.)
|
||||||
is only needed when that heuristic fails.)
|
* `RUSTC_BLESS` (recognized by `./miri test` and `cargo-miri-test/run-test.py`): overwrite all
|
||||||
* `RUSTC_BLESS` (recognized by the test suite and `cargo-miri-test/run-test.py`): overwrite all
|
|
||||||
`stderr` and `stdout` files instead of checking whether the output matches.
|
`stderr` and `stdout` files instead of checking whether the output matches.
|
||||||
* `MIRI_SKIP_UI_CHECKS` (recognized by the test suite): don't check whether the
|
* `MIRI_SKIP_UI_CHECKS` (recognized by `./miri test`): don't check whether the
|
||||||
`stderr` or `stdout` files match the actual output.
|
`stderr` or `stdout` files match the actual output.
|
||||||
|
|
||||||
The following environment variables are *internal* and must not be used by
|
The following environment variables are *internal* and must not be used by
|
||||||
|
@ -194,9 +194,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-build-sysroot"
|
name = "rustc-build-sysroot"
|
||||||
version = "0.4.5"
|
version = "0.4.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a26170e1d79ea32f7ccec3188dd13cfc1f18c82764a9cbc1071667c0f865a4ea"
|
checksum = "a9bf37423495cd3a6a9ef8c75fc4566de0d26de0ab75f36f67a426e58df5768c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
|
@ -18,7 +18,7 @@ directories = "5"
|
|||||||
rustc_version = "0.4"
|
rustc_version = "0.4"
|
||||||
serde_json = "1.0.40"
|
serde_json = "1.0.40"
|
||||||
cargo_metadata = "0.18.0"
|
cargo_metadata = "0.18.0"
|
||||||
rustc-build-sysroot = "0.4.1"
|
rustc-build-sysroot = "0.4.6"
|
||||||
|
|
||||||
# Enable some feature flags that dev-dependencies need but dependencies
|
# Enable some feature flags that dev-dependencies need but dependencies
|
||||||
# do not. This makes `./miri install` after `./miri build` faster.
|
# do not. This makes `./miri install` after `./miri build` faster.
|
||||||
|
@ -172,8 +172,6 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
|
|||||||
// Forward all further arguments (not consumed by `ArgSplitFlagValue`) to cargo.
|
// Forward all further arguments (not consumed by `ArgSplitFlagValue`) to cargo.
|
||||||
cmd.args(args);
|
cmd.args(args);
|
||||||
|
|
||||||
// Let it know where the Miri sysroot lives.
|
|
||||||
cmd.env("MIRI_SYSROOT", miri_sysroot);
|
|
||||||
// Set `RUSTC_WRAPPER` to ourselves. Cargo will prepend that binary to its usual invocation,
|
// Set `RUSTC_WRAPPER` to ourselves. Cargo will prepend that binary to its usual invocation,
|
||||||
// i.e., the first argument is `rustc` -- which is what we use in `main` to distinguish
|
// i.e., the first argument is `rustc` -- which is what we use in `main` to distinguish
|
||||||
// the two codepaths. (That extra argument is why we prefer this over setting `RUSTC`.)
|
// the two codepaths. (That extra argument is why we prefer this over setting `RUSTC`.)
|
||||||
@ -200,10 +198,7 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
|
|||||||
// always applied. However, buggy build scripts (https://github.com/eyre-rs/eyre/issues/84) and
|
// always applied. However, buggy build scripts (https://github.com/eyre-rs/eyre/issues/84) and
|
||||||
// also cargo (https://github.com/rust-lang/cargo/issues/10885) will invoke `rustc` even when
|
// also cargo (https://github.com/rust-lang/cargo/issues/10885) will invoke `rustc` even when
|
||||||
// `RUSTC_WRAPPER` is set, bypassing the wrapper. To make sure everything is coherent, we want
|
// `RUSTC_WRAPPER` is set, bypassing the wrapper. To make sure everything is coherent, we want
|
||||||
// that to be the Miri driver, but acting as rustc, on the target level. (Target, rather than
|
// that to be the Miri driver, but acting as rustc, in host mode.
|
||||||
// host, is needed for cross-interpretation situations.) This is not a perfect emulation of real
|
|
||||||
// rustc (it might be unable to produce binaries since the sysroot is check-only), but it's as
|
|
||||||
// close as we can get, and it's good enough for autocfg.
|
|
||||||
//
|
//
|
||||||
// In `main`, we need the value of `RUSTC` to distinguish RUSTC_WRAPPER invocations from rustdoc
|
// In `main`, we need the value of `RUSTC` to distinguish RUSTC_WRAPPER invocations from rustdoc
|
||||||
// or TARGET_RUNNER invocations, so we canonicalize it here to make it exceedingly unlikely that
|
// or TARGET_RUNNER invocations, so we canonicalize it here to make it exceedingly unlikely that
|
||||||
@ -212,7 +207,10 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
|
|||||||
// bootstrap `rustc` thing in our way! Instead, we have MIRI_HOST_SYSROOT to use for host
|
// bootstrap `rustc` thing in our way! Instead, we have MIRI_HOST_SYSROOT to use for host
|
||||||
// builds.
|
// builds.
|
||||||
cmd.env("RUSTC", fs::canonicalize(find_miri()).unwrap());
|
cmd.env("RUSTC", fs::canonicalize(find_miri()).unwrap());
|
||||||
cmd.env("MIRI_BE_RUSTC", "target"); // we better remember to *unset* this in the other phases!
|
// In case we get invoked as RUSTC without the wrapper, let's be a host rustc. This makes no
|
||||||
|
// sense for cross-interpretation situations, but without the wrapper, this will use the host
|
||||||
|
// sysroot, so asking it to behave like a target build makes even less sense.
|
||||||
|
cmd.env("MIRI_BE_RUSTC", "host"); // we better remember to *unset* this in the other phases!
|
||||||
|
|
||||||
// Set rustdoc to us as well, so we can run doctests.
|
// Set rustdoc to us as well, so we can run doctests.
|
||||||
if let Some(orig_rustdoc) = env::var_os("RUSTDOC") {
|
if let Some(orig_rustdoc) = env::var_os("RUSTDOC") {
|
||||||
@ -220,6 +218,8 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
|
|||||||
}
|
}
|
||||||
cmd.env("RUSTDOC", &cargo_miri_path);
|
cmd.env("RUSTDOC", &cargo_miri_path);
|
||||||
|
|
||||||
|
// Forward some crucial information to our own re-invocations.
|
||||||
|
cmd.env("MIRI_SYSROOT", miri_sysroot);
|
||||||
cmd.env("MIRI_LOCAL_CRATES", local_crates(&metadata));
|
cmd.env("MIRI_LOCAL_CRATES", local_crates(&metadata));
|
||||||
if verbose > 0 {
|
if verbose > 0 {
|
||||||
cmd.env("MIRI_VERBOSE", verbose.to_string()); // This makes the other phases verbose.
|
cmd.env("MIRI_VERBOSE", verbose.to_string()); // This makes the other phases verbose.
|
||||||
@ -412,6 +412,12 @@ pub fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
|
|||||||
// Arguments are treated very differently depending on whether this crate is
|
// Arguments are treated very differently depending on whether this crate is
|
||||||
// for interpretation by Miri, or for use by a build script / proc macro.
|
// for interpretation by Miri, or for use by a build script / proc macro.
|
||||||
if target_crate {
|
if target_crate {
|
||||||
|
if phase != RustcPhase::Setup {
|
||||||
|
// Set the sysroot -- except during setup, where we don't have an existing sysroot yet
|
||||||
|
// and where the bootstrap wrapper adds its own `--sysroot` flag so we can't set ours.
|
||||||
|
cmd.arg("--sysroot").arg(env::var_os("MIRI_SYSROOT").unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
// Forward arguments, but patched.
|
// Forward arguments, but patched.
|
||||||
let emit_flag = "--emit";
|
let emit_flag = "--emit";
|
||||||
// This hack helps bootstrap run standard library tests in Miri. The issue is as follows:
|
// This hack helps bootstrap run standard library tests in Miri. The issue is as follows:
|
||||||
@ -574,6 +580,12 @@ pub fn phase_runner(mut binary_args: impl Iterator<Item = String>, phase: Runner
|
|||||||
cmd.env(name, val);
|
cmd.env(name, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if phase != RunnerPhase::Rustdoc {
|
||||||
|
// Set the sysroot. Not necessary in rustdoc, where we already set the sysroot in
|
||||||
|
// `phase_rustdoc`. rustdoc will forward that flag when invoking rustc (i.e., us), so the
|
||||||
|
// flag is present in `info.args`.
|
||||||
|
cmd.arg("--sysroot").arg(env::var_os("MIRI_SYSROOT").unwrap());
|
||||||
|
}
|
||||||
// Forward rustc arguments.
|
// Forward rustc arguments.
|
||||||
// We need to patch "--extern" filenames because we forced a check-only
|
// We need to patch "--extern" filenames because we forced a check-only
|
||||||
// build without cargo knowing about that: replace `.rlib` suffix by
|
// build without cargo knowing about that: replace `.rlib` suffix by
|
||||||
|
@ -2,6 +2,7 @@ use std::env;
|
|||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::ops::Not;
|
use std::ops::Not;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::process;
|
use std::process;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time;
|
use std::time;
|
||||||
@ -20,10 +21,11 @@ const JOSH_FILTER: &str =
|
|||||||
const JOSH_PORT: &str = "42042";
|
const JOSH_PORT: &str = "42042";
|
||||||
|
|
||||||
impl MiriEnv {
|
impl MiriEnv {
|
||||||
fn build_miri_sysroot(&mut self, quiet: bool) -> Result<()> {
|
/// Returns the location of the sysroot.
|
||||||
if self.sh.var("MIRI_SYSROOT").is_ok() {
|
fn build_miri_sysroot(&mut self, quiet: bool) -> Result<PathBuf> {
|
||||||
|
if let Some(miri_sysroot) = self.sh.var_os("MIRI_SYSROOT") {
|
||||||
// Sysroot already set, use that.
|
// Sysroot already set, use that.
|
||||||
return Ok(());
|
return Ok(miri_sysroot.into());
|
||||||
}
|
}
|
||||||
let manifest_path = path!(self.miri_dir / "cargo-miri" / "Cargo.toml");
|
let manifest_path = path!(self.miri_dir / "cargo-miri" / "Cargo.toml");
|
||||||
let Self { toolchain, cargo_extra_flags, .. } = &self;
|
let Self { toolchain, cargo_extra_flags, .. } = &self;
|
||||||
@ -57,8 +59,8 @@ impl MiriEnv {
|
|||||||
.with_context(|| "`cargo miri setup` failed")?;
|
.with_context(|| "`cargo miri setup` failed")?;
|
||||||
panic!("`cargo miri setup` didn't fail again the 2nd time?");
|
panic!("`cargo miri setup` didn't fail again the 2nd time?");
|
||||||
};
|
};
|
||||||
self.sh.set_var("MIRI_SYSROOT", output);
|
self.sh.set_var("MIRI_SYSROOT", &output);
|
||||||
Ok(())
|
Ok(output.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -505,8 +507,10 @@ impl Command {
|
|||||||
flags.push("--edition=2021".into()); // keep in sync with `tests/ui.rs`.`
|
flags.push("--edition=2021".into()); // keep in sync with `tests/ui.rs`.`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare a sysroot.
|
// Prepare a sysroot, and add it to the flags.
|
||||||
e.build_miri_sysroot(/* quiet */ true)?;
|
let miri_sysroot = e.build_miri_sysroot(/* quiet */ true)?;
|
||||||
|
flags.push("--sysroot".into());
|
||||||
|
flags.push(miri_sysroot.into());
|
||||||
|
|
||||||
// Then run the actual command. Also add MIRIFLAGS.
|
// Then run the actual command. Also add MIRIFLAGS.
|
||||||
let miri_manifest = path!(e.miri_dir / "Cargo.toml");
|
let miri_manifest = path!(e.miri_dir / "Cargo.toml");
|
||||||
|
@ -282,25 +282,6 @@ fn run_compiler(
|
|||||||
callbacks: &mut (dyn rustc_driver::Callbacks + Send),
|
callbacks: &mut (dyn rustc_driver::Callbacks + Send),
|
||||||
using_internal_features: std::sync::Arc<std::sync::atomic::AtomicBool>,
|
using_internal_features: std::sync::Arc<std::sync::atomic::AtomicBool>,
|
||||||
) -> ! {
|
) -> ! {
|
||||||
if target_crate {
|
|
||||||
// Miri needs a custom sysroot for target crates.
|
|
||||||
// If no `--sysroot` is given, the `MIRI_SYSROOT` env var is consulted to find where
|
|
||||||
// that sysroot lives, and that is passed to rustc.
|
|
||||||
let sysroot_flag = "--sysroot";
|
|
||||||
if !args.iter().any(|e| e.starts_with(sysroot_flag)) {
|
|
||||||
// Using the built-in default here would be plain wrong, so we *require*
|
|
||||||
// the env var to make sure things make sense.
|
|
||||||
let miri_sysroot = env::var("MIRI_SYSROOT").unwrap_or_else(|_| {
|
|
||||||
show_error!(
|
|
||||||
"Miri was invoked in 'target' mode without `MIRI_SYSROOT` or `--sysroot` being set"
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
args.push(sysroot_flag.to_owned());
|
|
||||||
args.push(miri_sysroot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't insert `MIRI_DEFAULT_ARGS`, in particular, `--cfg=miri`, if we are building
|
// Don't insert `MIRI_DEFAULT_ARGS`, in particular, `--cfg=miri`, if we are building
|
||||||
// a "host" crate. That may cause procedural macros (and probably build scripts) to
|
// a "host" crate. That may cause procedural macros (and probably build scripts) to
|
||||||
// depend on Miri-only symbols, such as `miri_resolve_frame`:
|
// depend on Miri-only symbols, such as `miri_resolve_frame`:
|
||||||
|
@ -83,6 +83,17 @@ const UNIX_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = {
|
|||||||
("EAGAIN", WouldBlock),
|
("EAGAIN", WouldBlock),
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
// This mapping should match `decode_error_kind` in
|
||||||
|
// <https://github.com/rust-lang/rust/blob/master/library/std/src/sys/pal/windows/mod.rs>.
|
||||||
|
const WINDOWS_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = {
|
||||||
|
use std::io::ErrorKind::*;
|
||||||
|
// FIXME: this is still incomplete.
|
||||||
|
&[
|
||||||
|
("ERROR_ACCESS_DENIED", PermissionDenied),
|
||||||
|
("ERROR_FILE_NOT_FOUND", NotFound),
|
||||||
|
("ERROR_INVALID_PARAMETER", InvalidInput),
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
/// Gets an instance for a path.
|
/// Gets an instance for a path.
|
||||||
///
|
///
|
||||||
@ -749,20 +760,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||||||
}
|
}
|
||||||
throw_unsup_format!("io error {:?} cannot be translated into a raw os error", err_kind)
|
throw_unsup_format!("io error {:?} cannot be translated into a raw os error", err_kind)
|
||||||
} else if target.families.iter().any(|f| f == "windows") {
|
} else if target.families.iter().any(|f| f == "windows") {
|
||||||
// FIXME: we have to finish implementing the Windows equivalent of this.
|
for &(name, kind) in WINDOWS_IO_ERROR_TABLE {
|
||||||
use std::io::ErrorKind::*;
|
if err_kind == kind {
|
||||||
Ok(this.eval_windows(
|
return Ok(this.eval_windows("c", name));
|
||||||
"c",
|
}
|
||||||
match err_kind {
|
}
|
||||||
NotFound => "ERROR_FILE_NOT_FOUND",
|
throw_unsup_format!("io error {:?} cannot be translated into a raw os error", err_kind);
|
||||||
PermissionDenied => "ERROR_ACCESS_DENIED",
|
|
||||||
_ =>
|
|
||||||
throw_unsup_format!(
|
|
||||||
"io error {:?} cannot be translated into a raw os error",
|
|
||||||
err_kind
|
|
||||||
),
|
|
||||||
},
|
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
throw_unsup_format!(
|
throw_unsup_format!(
|
||||||
"converting io::Error into errnum is unsupported for OS {}",
|
"converting io::Error into errnum is unsupported for OS {}",
|
||||||
@ -786,8 +789,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||||||
return Ok(Some(kind));
|
return Ok(Some(kind));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Our table is as complete as the mapping in std, so we are okay with saying "that's a
|
return Ok(None);
|
||||||
// strange one" here.
|
} else if target.families.iter().any(|f| f == "windows") {
|
||||||
|
let errnum = errnum.to_u32()?;
|
||||||
|
for &(name, kind) in WINDOWS_IO_ERROR_TABLE {
|
||||||
|
if errnum == this.eval_windows("c", name).to_u32()? {
|
||||||
|
return Ok(Some(kind));
|
||||||
|
}
|
||||||
|
}
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
} else {
|
} else {
|
||||||
throw_unsup_format!(
|
throw_unsup_format!(
|
||||||
@ -1344,3 +1353,18 @@ pub(crate) fn simd_element_to_bool(elem: ImmTy<'_, Provenance>) -> InterpResult<
|
|||||||
_ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"),
|
_ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check whether an operation that writes to a target buffer was successful.
|
||||||
|
/// Accordingly select return value.
|
||||||
|
/// Local helper function to be used in Windows shims.
|
||||||
|
pub(crate) fn windows_check_buffer_size((success, len): (bool, u64)) -> u32 {
|
||||||
|
if success {
|
||||||
|
// If the function succeeds, the return value is the number of characters stored in the target buffer,
|
||||||
|
// not including the terminating null character.
|
||||||
|
u32::try_from(len.checked_sub(1).unwrap()).unwrap()
|
||||||
|
} else {
|
||||||
|
// If the target buffer was not large enough to hold the data, the return value is the buffer size, in characters,
|
||||||
|
// required to hold the string and its terminating null character.
|
||||||
|
u32::try_from(len).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
#![feature(lint_reasons)]
|
#![feature(lint_reasons)]
|
||||||
#![feature(trait_upcasting)]
|
#![feature(trait_upcasting)]
|
||||||
|
#![feature(absolute_path)]
|
||||||
// Configure clippy and other lints
|
// Configure clippy and other lints
|
||||||
#![allow(
|
#![allow(
|
||||||
clippy::collapsible_else_if,
|
clippy::collapsible_else_if,
|
||||||
|
@ -9,21 +9,7 @@ use rustc_middle::ty::Ty;
|
|||||||
use rustc_target::abi::Size;
|
use rustc_target::abi::Size;
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use helpers::windows_check_buffer_size;
|
||||||
/// Check whether an operation that writes to a target buffer was successful.
|
|
||||||
/// Accordingly select return value.
|
|
||||||
/// Local helper function to be used in Windows shims.
|
|
||||||
fn windows_check_buffer_size((success, len): (bool, u64)) -> u32 {
|
|
||||||
if success {
|
|
||||||
// If the function succeeds, the return value is the number of characters stored in the target buffer,
|
|
||||||
// not including the terminating null character.
|
|
||||||
u32::try_from(len.checked_sub(1).unwrap()).unwrap()
|
|
||||||
} else {
|
|
||||||
// If the target buffer was not large enough to hold the data, the return value is the buffer size, in characters,
|
|
||||||
// required to hold the string and its terminating null character.
|
|
||||||
u32::try_from(len).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct EnvVars<'tcx> {
|
pub struct EnvVars<'tcx> {
|
||||||
@ -164,7 +150,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||||||
let name_ptr = this.read_pointer(name_op)?;
|
let name_ptr = this.read_pointer(name_op)?;
|
||||||
let name = this.read_os_str_from_wide_str(name_ptr)?;
|
let name = this.read_os_str_from_wide_str(name_ptr)?;
|
||||||
Ok(match this.machine.env_vars.map.get(&name) {
|
Ok(match this.machine.env_vars.map.get(&name) {
|
||||||
Some(var_ptr) => {
|
Some(&var_ptr) => {
|
||||||
|
this.set_last_error(Scalar::from_u32(0))?; // make sure this is unambiguously not an error
|
||||||
// The offset is used to strip the "{name}=" part of the string.
|
// The offset is used to strip the "{name}=" part of the string.
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
let name_offset_bytes = u64::try_from(name.len()).unwrap()
|
let name_offset_bytes = u64::try_from(name.len()).unwrap()
|
||||||
@ -375,10 +362,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||||||
|
|
||||||
// If we cannot get the current directory, we return 0
|
// If we cannot get the current directory, we return 0
|
||||||
match env::current_dir() {
|
match env::current_dir() {
|
||||||
Ok(cwd) =>
|
Ok(cwd) => {
|
||||||
|
this.set_last_error(Scalar::from_u32(0))?; // make sure this is unambiguously not an error
|
||||||
return Ok(Scalar::from_u32(windows_check_buffer_size(
|
return Ok(Scalar::from_u32(windows_check_buffer_size(
|
||||||
this.write_path_to_wide_str(&cwd, buf, size, /*truncate*/ false)?,
|
this.write_path_to_wide_str(&cwd, buf, size, /*truncate*/ false)?,
|
||||||
))),
|
)));
|
||||||
|
}
|
||||||
Err(e) => this.set_last_error_from_io_error(e.kind())?,
|
Err(e) => this.set_last_error_from_io_error(e.kind())?,
|
||||||
}
|
}
|
||||||
Ok(Scalar::from_u32(0))
|
Ok(Scalar::from_u32(0))
|
||||||
|
@ -98,6 +98,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||||||
///
|
///
|
||||||
/// If `truncate == true`, then in case `size` is not large enough it *will* write the first
|
/// If `truncate == true`, then in case `size` is not large enough it *will* write the first
|
||||||
/// `size.saturating_sub(1)` many items, followed by a null terminator (if `size > 0`).
|
/// `size.saturating_sub(1)` many items, followed by a null terminator (if `size > 0`).
|
||||||
|
/// The return value is still `(false, length)` in that case.
|
||||||
fn write_os_str_to_wide_str(
|
fn write_os_str_to_wide_str(
|
||||||
&mut self,
|
&mut self,
|
||||||
os_str: &OsStr,
|
os_str: &OsStr,
|
||||||
@ -315,9 +316,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||||||
// We also have to ensure that absolute paths remain absolute.
|
// We also have to ensure that absolute paths remain absolute.
|
||||||
match direction {
|
match direction {
|
||||||
PathConversion::HostToTarget => {
|
PathConversion::HostToTarget => {
|
||||||
// If this start withs a `\`, we add `\\?` so it starts with `\\?\` which is
|
// If the path is `/C:/`, the leading backslash was probably added by the below
|
||||||
// some magic path on Windows that *is* considered absolute.
|
// driver letter handling and we should get rid of it again.
|
||||||
if converted.get(0).copied() == Some(b'\\') {
|
if converted.get(0).copied() == Some(b'\\')
|
||||||
|
&& converted.get(2).copied() == Some(b':')
|
||||||
|
&& converted.get(3).copied() == Some(b'\\')
|
||||||
|
{
|
||||||
|
converted.remove(0);
|
||||||
|
}
|
||||||
|
// If this start withs a `\` but not a `\\`, then for Windows this is a relative
|
||||||
|
// path. But the host path is absolute as it started with `/`. We add `\\?` so
|
||||||
|
// it starts with `\\?\` which is some magic path on Windows that *is*
|
||||||
|
// considered absolute.
|
||||||
|
else if converted.get(0).copied() == Some(b'\\')
|
||||||
|
&& converted.get(1).copied() != Some(b'\\')
|
||||||
|
{
|
||||||
converted.splice(0..0, b"\\\\?".iter().copied());
|
converted.splice(0..0, b"\\\\?".iter().copied());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -332,6 +345,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||||||
// Remove first 3 characters
|
// Remove first 3 characters
|
||||||
converted.splice(0..3, std::iter::empty());
|
converted.splice(0..3, std::iter::empty());
|
||||||
}
|
}
|
||||||
|
// If it starts with a drive letter, convert it to an absolute Unix path.
|
||||||
|
else if converted.get(1).copied() == Some(b':')
|
||||||
|
&& converted.get(2).copied() == Some(b'/')
|
||||||
|
{
|
||||||
|
converted.insert(0, b'/');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Cow::Owned(OsString::from_vec(converted))
|
Cow::Owned(OsString::from_vec(converted))
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
|
use std::ffi::OsStr;
|
||||||
|
use std::io;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
use std::path::{self, Path, PathBuf};
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
use rustc_span::Symbol;
|
use rustc_span::Symbol;
|
||||||
@ -20,6 +23,61 @@ fn is_dyn_sym(name: &str) -> bool {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn win_absolute<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result<PathBuf>> {
|
||||||
|
// We are on Windows so we can simply lte the host do this.
|
||||||
|
return Ok(path::absolute(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[allow(clippy::get_first, clippy::arithmetic_side_effects)]
|
||||||
|
fn win_absolute<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result<PathBuf>> {
|
||||||
|
// We are on Unix, so we need to implement parts of the logic ourselves.
|
||||||
|
let bytes = path.as_os_str().as_encoded_bytes();
|
||||||
|
// If it starts with `//` (these were backslashes but are already converted)
|
||||||
|
// then this is a magic special path, we just leave it unchanged.
|
||||||
|
if bytes.get(0).copied() == Some(b'/') && bytes.get(1).copied() == Some(b'/') {
|
||||||
|
return Ok(Ok(path.into()));
|
||||||
|
};
|
||||||
|
// Special treatment for Windows' magic filenames: they are treated as being relative to `\\.\`.
|
||||||
|
let magic_filenames = &[
|
||||||
|
"CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8",
|
||||||
|
"COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
|
||||||
|
];
|
||||||
|
if magic_filenames.iter().any(|m| m.as_bytes() == bytes) {
|
||||||
|
let mut result: Vec<u8> = br"//./".into();
|
||||||
|
result.extend(bytes);
|
||||||
|
return Ok(Ok(bytes_to_os_str(&result)?.into()));
|
||||||
|
}
|
||||||
|
// Otherwise we try to do something kind of close to what Windows does, but this is probably not
|
||||||
|
// right in all cases. We iterate over the components between `/`, and remove trailing `.`,
|
||||||
|
// except that trailing `..` remain unchanged.
|
||||||
|
let mut result = vec![];
|
||||||
|
let mut bytes = bytes; // the remaining bytes to process
|
||||||
|
loop {
|
||||||
|
let len = bytes.iter().position(|&b| b == b'/').unwrap_or(bytes.len());
|
||||||
|
let mut component = &bytes[..len];
|
||||||
|
if len >= 2 && component[len - 1] == b'.' && component[len - 2] != b'.' {
|
||||||
|
// Strip trailing `.`
|
||||||
|
component = &component[..len - 1];
|
||||||
|
}
|
||||||
|
// Add this component to output.
|
||||||
|
result.extend(component);
|
||||||
|
// Prepare next iteration.
|
||||||
|
if len < bytes.len() {
|
||||||
|
// There's a component after this; add `/` and process remaining bytes.
|
||||||
|
result.push(b'/');
|
||||||
|
bytes = &bytes[len + 1..];
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
// This was the last component and it did not have a trailing `/`.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Let the host `absolute` function do working-dir handling
|
||||||
|
Ok(path::absolute(bytes_to_os_str(&result)?))
|
||||||
|
}
|
||||||
|
|
||||||
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
|
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
|
||||||
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
fn emulate_foreign_item_inner(
|
fn emulate_foreign_item_inner(
|
||||||
@ -111,7 +169,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||||||
|
|
||||||
let written = if handle == -11 || handle == -12 {
|
let written = if handle == -11 || handle == -12 {
|
||||||
// stdout/stderr
|
// stdout/stderr
|
||||||
use std::io::{self, Write};
|
use io::Write;
|
||||||
|
|
||||||
let buf_cont =
|
let buf_cont =
|
||||||
this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(u64::from(n)))?;
|
this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(u64::from(n)))?;
|
||||||
@ -145,6 +203,40 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||||||
dest,
|
dest,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
"GetFullPathNameW" => {
|
||||||
|
let [filename, size, buffer, filepart] =
|
||||||
|
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
|
||||||
|
this.check_no_isolation("`GetFullPathNameW`")?;
|
||||||
|
|
||||||
|
let filename = this.read_pointer(filename)?;
|
||||||
|
let size = this.read_scalar(size)?.to_u32()?;
|
||||||
|
let buffer = this.read_pointer(buffer)?;
|
||||||
|
let filepart = this.read_pointer(filepart)?;
|
||||||
|
|
||||||
|
if !this.ptr_is_null(filepart)? {
|
||||||
|
throw_unsup_format!("GetFullPathNameW: non-null `lpFilePart` is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
let filename = this.read_path_from_wide_str(filename)?;
|
||||||
|
let result = match win_absolute(&filename)? {
|
||||||
|
Err(err) => {
|
||||||
|
this.set_last_error_from_io_error(err.kind())?;
|
||||||
|
Scalar::from_u32(0) // return zero upon failure
|
||||||
|
}
|
||||||
|
Ok(abs_filename) => {
|
||||||
|
this.set_last_error(Scalar::from_u32(0))?; // make sure this is unambiguously not an error
|
||||||
|
Scalar::from_u32(helpers::windows_check_buffer_size(
|
||||||
|
this.write_path_to_wide_str(
|
||||||
|
&abs_filename,
|
||||||
|
buffer,
|
||||||
|
size.into(),
|
||||||
|
/*truncate*/ false,
|
||||||
|
)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.write_scalar(result, dest)?;
|
||||||
|
}
|
||||||
|
|
||||||
// Allocation
|
// Allocation
|
||||||
"HeapAlloc" => {
|
"HeapAlloc" => {
|
||||||
@ -533,6 +625,43 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||||||
this.set_last_error(insufficient_buffer)?;
|
this.set_last_error(insufficient_buffer)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"FormatMessageW" => {
|
||||||
|
let [flags, module, message_id, language_id, buffer, size, arguments] =
|
||||||
|
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
|
||||||
|
|
||||||
|
let flags = this.read_scalar(flags)?.to_u32()?;
|
||||||
|
let _module = this.read_pointer(module)?; // seems to contain a module name
|
||||||
|
let message_id = this.read_scalar(message_id)?;
|
||||||
|
let _language_id = this.read_scalar(language_id)?.to_u32()?;
|
||||||
|
let buffer = this.read_pointer(buffer)?;
|
||||||
|
let size = this.read_scalar(size)?.to_u32()?;
|
||||||
|
let _arguments = this.read_pointer(arguments)?;
|
||||||
|
|
||||||
|
// We only support `FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS`
|
||||||
|
// This also means `arguments` can be ignored.
|
||||||
|
if flags != 4096u32 | 512u32 {
|
||||||
|
throw_unsup_format!("FormatMessageW: unsupported flags {flags:#x}");
|
||||||
|
}
|
||||||
|
|
||||||
|
let error = this.try_errnum_to_io_error(message_id)?;
|
||||||
|
let formatted = match error {
|
||||||
|
Some(err) => format!("{err}"),
|
||||||
|
None => format!("<unknown error in FormatMessageW: {message_id}>"),
|
||||||
|
};
|
||||||
|
let (complete, length) = this.write_os_str_to_wide_str(
|
||||||
|
OsStr::new(&formatted),
|
||||||
|
buffer,
|
||||||
|
size.into(),
|
||||||
|
/*trunacte*/ false,
|
||||||
|
)?;
|
||||||
|
if !complete {
|
||||||
|
// The API docs don't say what happens when the buffer is not big enough...
|
||||||
|
// Let's just bail.
|
||||||
|
throw_unsup_format!("FormatMessageW: buffer not big enough");
|
||||||
|
}
|
||||||
|
// The return value is the number of characters stored *excluding* the null terminator.
|
||||||
|
this.write_int(length.checked_sub(1).unwrap(), dest)?;
|
||||||
|
}
|
||||||
|
|
||||||
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
|
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
|
||||||
// These shims are enabled only when the caller is in the standard library.
|
// These shims are enabled only when the caller is in the standard library.
|
||||||
|
@ -664,54 +664,35 @@ fn convert_float_to_int<'tcx>(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Splits `left`, `right` and `dest` (which must be SIMD vectors)
|
/// Splits `op` (which must be a SIMD vector) into 128-bit chuncks.
|
||||||
/// into 128-bit chuncks.
|
|
||||||
///
|
|
||||||
/// `left`, `right` and `dest` cannot have different types.
|
|
||||||
///
|
///
|
||||||
/// Returns a tuple where:
|
/// Returns a tuple where:
|
||||||
/// * The first element is the number of 128-bit chunks (let's call it `N`).
|
/// * The first element is the number of 128-bit chunks (let's call it `N`).
|
||||||
/// * The second element is the number of elements per chunk (let's call it `M`).
|
/// * The second element is the number of elements per chunk (let's call it `M`).
|
||||||
/// * The third element is the `left` vector split into chunks, i.e, it's
|
/// * The third element is the `op` vector split into chunks, i.e, it's
|
||||||
/// type is `[[T; M]; N]`.
|
/// type is `[[T; M]; N]` where `T` is the element type of `op`.
|
||||||
/// * The fourth element is the `right` vector split into chunks.
|
fn split_simd_to_128bit_chunks<'tcx, P: Projectable<'tcx, Provenance>>(
|
||||||
/// * The fifth element is the `dest` vector split into chunks.
|
|
||||||
fn split_simd_to_128bit_chunks<'tcx>(
|
|
||||||
this: &mut crate::MiriInterpCx<'_, 'tcx>,
|
this: &mut crate::MiriInterpCx<'_, 'tcx>,
|
||||||
left: &OpTy<'tcx, Provenance>,
|
op: &P,
|
||||||
right: &OpTy<'tcx, Provenance>,
|
) -> InterpResult<'tcx, (u64, u64, P)> {
|
||||||
dest: &MPlaceTy<'tcx, Provenance>,
|
let simd_layout = op.layout();
|
||||||
) -> InterpResult<
|
let (simd_len, element_ty) = simd_layout.ty.simd_size_and_type(this.tcx.tcx);
|
||||||
'tcx,
|
|
||||||
(u64, u64, MPlaceTy<'tcx, Provenance>, MPlaceTy<'tcx, Provenance>, MPlaceTy<'tcx, Provenance>),
|
|
||||||
> {
|
|
||||||
assert_eq!(dest.layout, left.layout);
|
|
||||||
assert_eq!(dest.layout, right.layout);
|
|
||||||
|
|
||||||
let (left, left_len) = this.operand_to_simd(left)?;
|
assert_eq!(simd_layout.size.bits() % 128, 0);
|
||||||
let (right, right_len) = this.operand_to_simd(right)?;
|
let num_chunks = simd_layout.size.bits() / 128;
|
||||||
let (dest, dest_len) = this.mplace_to_simd(dest)?;
|
let items_per_chunk = simd_len.checked_div(num_chunks).unwrap();
|
||||||
|
|
||||||
assert_eq!(dest_len, left_len);
|
|
||||||
assert_eq!(dest_len, right_len);
|
|
||||||
|
|
||||||
assert_eq!(dest.layout.size.bits() % 128, 0);
|
|
||||||
let num_chunks = dest.layout.size.bits() / 128;
|
|
||||||
assert_eq!(dest_len.checked_rem(num_chunks), Some(0));
|
|
||||||
let items_per_chunk = dest_len.checked_div(num_chunks).unwrap();
|
|
||||||
|
|
||||||
// Transmute to `[[T; items_per_chunk]; num_chunks]`
|
// Transmute to `[[T; items_per_chunk]; num_chunks]`
|
||||||
let element_layout = left.layout.field(this, 0);
|
let chunked_layout = this
|
||||||
let chunked_layout = this.layout_of(Ty::new_array(
|
.layout_of(Ty::new_array(
|
||||||
this.tcx.tcx,
|
this.tcx.tcx,
|
||||||
Ty::new_array(this.tcx.tcx, element_layout.ty, items_per_chunk),
|
Ty::new_array(this.tcx.tcx, element_ty, items_per_chunk),
|
||||||
num_chunks,
|
num_chunks,
|
||||||
))?;
|
))
|
||||||
let left = left.transmute(chunked_layout, this)?;
|
.unwrap();
|
||||||
let right = right.transmute(chunked_layout, this)?;
|
let chunked_op = op.transmute(chunked_layout, this)?;
|
||||||
let dest = dest.transmute(chunked_layout, this)?;
|
|
||||||
|
|
||||||
Ok((num_chunks, items_per_chunk, left, right, dest))
|
Ok((num_chunks, items_per_chunk, chunked_op))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Horizontaly performs `which` operation on adjacent values of
|
/// Horizontaly performs `which` operation on adjacent values of
|
||||||
@ -731,8 +712,12 @@ fn horizontal_bin_op<'tcx>(
|
|||||||
right: &OpTy<'tcx, Provenance>,
|
right: &OpTy<'tcx, Provenance>,
|
||||||
dest: &MPlaceTy<'tcx, Provenance>,
|
dest: &MPlaceTy<'tcx, Provenance>,
|
||||||
) -> InterpResult<'tcx, ()> {
|
) -> InterpResult<'tcx, ()> {
|
||||||
let (num_chunks, items_per_chunk, left, right, dest) =
|
assert_eq!(left.layout, dest.layout);
|
||||||
split_simd_to_128bit_chunks(this, left, right, dest)?;
|
assert_eq!(right.layout, dest.layout);
|
||||||
|
|
||||||
|
let (num_chunks, items_per_chunk, left) = split_simd_to_128bit_chunks(this, left)?;
|
||||||
|
let (_, _, right) = split_simd_to_128bit_chunks(this, right)?;
|
||||||
|
let (_, _, dest) = split_simd_to_128bit_chunks(this, dest)?;
|
||||||
|
|
||||||
let middle = items_per_chunk / 2;
|
let middle = items_per_chunk / 2;
|
||||||
for i in 0..num_chunks {
|
for i in 0..num_chunks {
|
||||||
@ -779,8 +764,12 @@ fn conditional_dot_product<'tcx>(
|
|||||||
imm: &OpTy<'tcx, Provenance>,
|
imm: &OpTy<'tcx, Provenance>,
|
||||||
dest: &MPlaceTy<'tcx, Provenance>,
|
dest: &MPlaceTy<'tcx, Provenance>,
|
||||||
) -> InterpResult<'tcx, ()> {
|
) -> InterpResult<'tcx, ()> {
|
||||||
let (num_chunks, items_per_chunk, left, right, dest) =
|
assert_eq!(left.layout, dest.layout);
|
||||||
split_simd_to_128bit_chunks(this, left, right, dest)?;
|
assert_eq!(right.layout, dest.layout);
|
||||||
|
|
||||||
|
let (num_chunks, items_per_chunk, left) = split_simd_to_128bit_chunks(this, left)?;
|
||||||
|
let (_, _, right) = split_simd_to_128bit_chunks(this, right)?;
|
||||||
|
let (_, _, dest) = split_simd_to_128bit_chunks(this, dest)?;
|
||||||
|
|
||||||
let element_layout = left.layout.field(this, 0).field(this, 0);
|
let element_layout = left.layout.field(this, 0).field(this, 0);
|
||||||
assert!(items_per_chunk <= 4);
|
assert!(items_per_chunk <= 4);
|
||||||
|
@ -260,7 +260,7 @@ fn test_errors() {
|
|||||||
// Opening a non-existing file should fail with a "not found" error.
|
// Opening a non-existing file should fail with a "not found" error.
|
||||||
assert_eq!(ErrorKind::NotFound, File::open(&path).unwrap_err().kind());
|
assert_eq!(ErrorKind::NotFound, File::open(&path).unwrap_err().kind());
|
||||||
// Make sure we can also format this.
|
// Make sure we can also format this.
|
||||||
format!("{0:?}: {0}", File::open(&path).unwrap_err());
|
format!("{0}: {0:?}", File::open(&path).unwrap_err());
|
||||||
// Removing a non-existing file should fail with a "not found" error.
|
// Removing a non-existing file should fail with a "not found" error.
|
||||||
assert_eq!(ErrorKind::NotFound, remove_file(&path).unwrap_err().kind());
|
assert_eq!(ErrorKind::NotFound, remove_file(&path).unwrap_err().kind());
|
||||||
// Reading the metadata of a non-existing file should fail with a "not found" error.
|
// Reading the metadata of a non-existing file should fail with a "not found" error.
|
||||||
|
@ -1,7 +1,19 @@
|
|||||||
use std::io::IsTerminal;
|
use std::io::{self, IsTerminal};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// We can't really assume that this is truly a terminal, and anyway on Windows Miri will always
|
// We can't really assume that this is truly a terminal, and anyway on Windows Miri will always
|
||||||
// return `false` here, but we can check that the call succeeds.
|
// return `false` here, but we can check that the call succeeds.
|
||||||
std::io::stdout().is_terminal();
|
io::stdout().is_terminal();
|
||||||
|
|
||||||
|
// Ensure we can format `io::Error` created from OS errors
|
||||||
|
// (calls OS-specific error formatting functions).
|
||||||
|
let raw_os_error = if cfg!(unix) {
|
||||||
|
22 // EINVAL (on most Unixes, anyway)
|
||||||
|
} else if cfg!(windows) {
|
||||||
|
87 // ERROR_INVALID_PARAMETER
|
||||||
|
} else {
|
||||||
|
panic!("unsupported OS")
|
||||||
|
};
|
||||||
|
let err = io::Error::from_raw_os_error(raw_os_error);
|
||||||
|
format!("{err}: {err:?}");
|
||||||
}
|
}
|
||||||
|
38
src/tools/miri/tests/pass/shims/path.rs
Normal file
38
src/tools/miri/tests/pass/shims/path.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
//@compile-flags: -Zmiri-disable-isolation
|
||||||
|
#![feature(absolute_path)]
|
||||||
|
use std::path::{absolute, Path};
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn test_absolute(in_: &str, out: &str) {
|
||||||
|
assert_eq!(absolute(in_).unwrap().as_os_str(), Path::new(out).as_os_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if cfg!(unix) {
|
||||||
|
test_absolute("/a/b/c", "/a/b/c");
|
||||||
|
test_absolute("/a/b/c", "/a/b/c");
|
||||||
|
test_absolute("/a//b/c", "/a/b/c");
|
||||||
|
test_absolute("//a/b/c", "//a/b/c");
|
||||||
|
test_absolute("///a/b/c", "/a/b/c");
|
||||||
|
test_absolute("/a/b/c/", "/a/b/c/");
|
||||||
|
test_absolute("/a/./b/../c/.././..", "/a/b/../c/../..");
|
||||||
|
} else if cfg!(windows) {
|
||||||
|
// Test that all these are unchanged
|
||||||
|
test_absolute(r"C:\path\to\file", r"C:\path\to\file");
|
||||||
|
test_absolute(r"C:\path\to\file\", r"C:\path\to\file\");
|
||||||
|
test_absolute(r"\\server\share\to\file", r"\\server\share\to\file");
|
||||||
|
test_absolute(r"\\server.\share.\to\file", r"\\server.\share.\to\file");
|
||||||
|
test_absolute(r"\\.\PIPE\name", r"\\.\PIPE\name");
|
||||||
|
test_absolute(r"\\.\C:\path\to\COM1", r"\\.\C:\path\to\COM1");
|
||||||
|
test_absolute(r"\\?\C:\path\to\file", r"\\?\C:\path\to\file");
|
||||||
|
test_absolute(r"\\?\UNC\server\share\to\file", r"\\?\UNC\server\share\to\file");
|
||||||
|
test_absolute(r"\\?\PIPE\name", r"\\?\PIPE\name");
|
||||||
|
// Verbatim paths are always unchanged, no matter what.
|
||||||
|
test_absolute(r"\\?\path.\to/file..", r"\\?\path.\to/file..");
|
||||||
|
|
||||||
|
test_absolute(r"C:\path..\to.\file.", r"C:\path..\to\file");
|
||||||
|
test_absolute(r"COM1", r"\\.\COM1");
|
||||||
|
} else {
|
||||||
|
panic!("unsupported OS");
|
||||||
|
}
|
||||||
|
}
|
@ -112,6 +112,13 @@ fn run_tests(
|
|||||||
config.program.envs.push(("RUST_BACKTRACE".into(), Some("1".into())));
|
config.program.envs.push(("RUST_BACKTRACE".into(), Some("1".into())));
|
||||||
|
|
||||||
// Add some flags we always want.
|
// Add some flags we always want.
|
||||||
|
config.program.args.push(
|
||||||
|
format!(
|
||||||
|
"--sysroot={}",
|
||||||
|
env::var("MIRI_SYSROOT").expect("MIRI_SYSROOT must be set to run the ui test suite")
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
config.program.args.push("-Dwarnings".into());
|
config.program.args.push("-Dwarnings".into());
|
||||||
config.program.args.push("-Dunused".into());
|
config.program.args.push("-Dunused".into());
|
||||||
config.program.args.push("-Ainternal_features".into());
|
config.program.args.push("-Ainternal_features".into());
|
||||||
@ -296,12 +303,13 @@ fn main() -> Result<()> {
|
|||||||
|
|
||||||
fn run_dep_mode(target: String, mut args: impl Iterator<Item = OsString>) -> Result<()> {
|
fn run_dep_mode(target: String, mut args: impl Iterator<Item = OsString>) -> Result<()> {
|
||||||
let path = args.next().expect("./miri run-dep must be followed by a file name");
|
let path = args.next().expect("./miri run-dep must be followed by a file name");
|
||||||
let config = miri_config(
|
let mut config = miri_config(
|
||||||
&target,
|
&target,
|
||||||
"",
|
"",
|
||||||
Mode::Yolo { rustfix: RustfixMode::Disabled },
|
Mode::Yolo { rustfix: RustfixMode::Disabled },
|
||||||
/* with dependencies */ true,
|
/* with dependencies */ true,
|
||||||
);
|
);
|
||||||
|
config.program.args.clear(); // remove the `--error-format` that ui_test adds by default
|
||||||
let dep_args = config.build_dependencies()?;
|
let dep_args = config.build_dependencies()?;
|
||||||
|
|
||||||
let mut cmd = config.program.build(&config.out_dir);
|
let mut cmd = config.program.build(&config.out_dir);
|
||||||
|
Loading…
Reference in New Issue
Block a user