Auto merge of #123264 - matthiaskrgr:rollup-smyy7j9, r=matthiaskrgr

Rollup of 4 pull requests

Successful merges:

 - #123189 (Log BOLT args in bootstrap `rustc` shim)
 - #123211 (Stop calling visitors `V`)
 - #123242 (pattern analysis: Require enum indices to be contiguous)
 - #123260 (Miri subtree update)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-03-31 10:26:55 +00:00
commit 395f780cd8
28 changed files with 262 additions and 267 deletions

View File

@ -540,19 +540,23 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
}
}
/// Suggest `map[k] = v` => `map.insert(k, v)` and the like.
fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diag<'tcx>, span: Span) {
let Some(adt) = ty.ty_adt_def() else { return };
let did = adt.did();
if self.infcx.tcx.is_diagnostic_item(sym::HashMap, did)
|| self.infcx.tcx.is_diagnostic_item(sym::BTreeMap, did)
{
struct V<'a, 'tcx> {
/// Walks through the HIR, looking for the corresponding span for this error.
/// When it finds it, see if it corresponds to assignment operator whose LHS
/// is an index expr.
struct SuggestIndexOperatorAlternativeVisitor<'a, 'tcx> {
assign_span: Span,
err: &'a mut Diag<'tcx>,
ty: Ty<'tcx>,
suggested: bool,
}
impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> {
impl<'a, 'tcx> Visitor<'tcx> for SuggestIndexOperatorAlternativeVisitor<'a, 'tcx> {
fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
hir::intravisit::walk_stmt(self, stmt);
let expr = match stmt.kind {
@ -645,7 +649,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
let Some(body_id) = hir_map.maybe_body_owned_by(local_def_id) else { return };
let body = self.infcx.tcx.hir().body(body_id);
let mut v = V { assign_span: span, err, ty, suggested: false };
let mut v = SuggestIndexOperatorAlternativeVisitor {
assign_span: span,
err,
ty,
suggested: false,
};
v.visit_body(body);
if !v.suggested {
err.help(format!(

View File

@ -418,8 +418,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
{
if let &hir::ClosureBinder::For { span: for_sp, .. } = binder {
fn span_of_infer(ty: &hir::Ty<'_>) -> Option<Span> {
struct V;
impl<'v> Visitor<'v> for V {
/// Look for `_` anywhere in the signature of a `for<> ||` closure.
/// This is currently disallowed.
struct FindInferInClosureWithBinder;
impl<'v> Visitor<'v> for FindInferInClosureWithBinder {
type Result = ControlFlow<Span>;
fn visit_ty(&mut self, t: &'v hir::Ty<'v>) -> Self::Result {
if matches!(t.kind, hir::TyKind::Infer) {
@ -429,7 +431,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
}
}
}
V.visit_ty(ty).break_value()
FindInferInClosureWithBinder.visit_ty(ty).break_value()
}
let infer_in_rt_sp = match fn_decl.output {

View File

@ -1916,22 +1916,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pat: &'tcx hir::Pat<'tcx>,
ty: Ty<'tcx>,
) {
struct V {
pat_hir_ids: Vec<hir::HirId>,
}
impl<'tcx> Visitor<'tcx> for V {
fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) {
self.pat_hir_ids.push(p.hir_id);
hir::intravisit::walk_pat(self, p);
}
}
if let Err(guar) = ty.error_reported() {
struct OverwritePatternsWithError {
pat_hir_ids: Vec<hir::HirId>,
}
impl<'tcx> Visitor<'tcx> for OverwritePatternsWithError {
fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) {
self.pat_hir_ids.push(p.hir_id);
hir::intravisit::walk_pat(self, p);
}
}
// Override the types everywhere with `err()` to avoid knock on errors.
let err = Ty::new_error(self.tcx, guar);
self.write_ty(hir_id, err);
self.write_ty(pat.hir_id, err);
let mut visitor = V { pat_hir_ids: vec![] };
let mut visitor = OverwritePatternsWithError { pat_hir_ids: vec![] };
hir::intravisit::walk_pat(&mut visitor, pat);
// Mark all the subpatterns as `{type error}` as well. This allows errors for specific
// subpatterns to be silenced.

View File

@ -155,13 +155,13 @@ use std::iter::once;
use smallvec::SmallVec;
use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS};
use rustc_index::bit_set::GrowableBitSet;
use rustc_index::bit_set::{BitSet, GrowableBitSet};
use rustc_index::IndexVec;
use self::Constructor::*;
use self::MaybeInfiniteInt::*;
use self::SliceKind::*;
use crate::index;
use crate::PatCx;
/// Whether we have seen a constructor in the column or not.
@ -920,10 +920,7 @@ pub enum ConstructorSet<Cx: PatCx> {
Struct { empty: bool },
/// This type has the following list of constructors. If `variants` is empty and
/// `non_exhaustive` is false, don't use this; use `NoConstructors` instead.
Variants {
variants: index::IdxContainer<Cx::VariantIdx, VariantVisibility>,
non_exhaustive: bool,
},
Variants { variants: IndexVec<Cx::VariantIdx, VariantVisibility>, non_exhaustive: bool },
/// The type is `&T`.
Ref,
/// The type is a union.
@ -1025,7 +1022,7 @@ impl<Cx: PatCx> ConstructorSet<Cx> {
}
}
ConstructorSet::Variants { variants, non_exhaustive } => {
let mut seen_set = index::IdxSet::new_empty(variants.len());
let mut seen_set = BitSet::new_empty(variants.len());
for idx in seen.iter().filter_map(|c| c.as_variant()) {
seen_set.insert(idx);
}

View File

@ -25,50 +25,9 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
use std::fmt;
#[cfg(feature = "rustc")]
pub mod index {
// Faster version when the indices of variants are `0..variants.len()`.
pub use rustc_index::bit_set::BitSet as IdxSet;
pub use rustc_index::Idx;
pub use rustc_index::IndexVec as IdxContainer;
}
#[cfg(not(feature = "rustc"))]
pub mod index {
// Slower version when the indices of variants are something else.
pub trait Idx: Copy + PartialEq + Eq + std::hash::Hash {}
impl<T: Copy + PartialEq + Eq + std::hash::Hash> Idx for T {}
#[derive(Debug)]
pub struct IdxContainer<K, V>(pub rustc_hash::FxHashMap<K, V>);
impl<K: Idx, V> IdxContainer<K, V> {
pub fn len(&self) -> usize {
self.0.len()
}
pub fn iter_enumerated(&self) -> impl Iterator<Item = (K, &V)> {
self.0.iter().map(|(k, v)| (*k, v))
}
}
impl<V> FromIterator<V> for IdxContainer<usize, V> {
fn from_iter<T: IntoIterator<Item = V>>(iter: T) -> Self {
Self(iter.into_iter().enumerate().collect())
}
}
#[derive(Debug)]
pub struct IdxSet<T>(pub rustc_hash::FxHashSet<T>);
impl<T: Idx> IdxSet<T> {
pub fn new_empty(_len: usize) -> Self {
Self(Default::default())
}
pub fn contains(&self, elem: T) -> bool {
self.0.contains(&elem)
}
pub fn insert(&mut self, elem: T) {
self.0.insert(elem);
}
}
}
// Re-exports to avoid rustc_index version issues.
pub use rustc_index::Idx;
pub use rustc_index::IndexVec;
#[cfg(feature = "rustc")]
use rustc_middle::ty::Ty;
@ -96,7 +55,7 @@ pub trait PatCx: Sized + fmt::Debug {
/// Errors that can abort analysis.
type Error: fmt::Debug;
/// The index of an enum variant.
type VariantIdx: Clone + index::Idx + fmt::Debug;
type VariantIdx: Clone + Idx + fmt::Debug;
/// A string literal
type StrLit: Clone + PartialEq + fmt::Debug;
/// Extra data to store in a match arm.

View File

@ -1128,10 +1128,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
err: &mut Diag<'_>,
) -> bool {
let span = obligation.cause.span;
struct V {
/// Look for the (direct) sub-expr of `?`, and return it if it's a `.` method call.
struct FindMethodSubexprOfTry {
search_span: Span,
}
impl<'v> Visitor<'v> for V {
impl<'v> Visitor<'v> for FindMethodSubexprOfTry {
type Result = ControlFlow<&'v hir::Expr<'v>>;
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result {
if let hir::ExprKind::Match(expr, _arms, hir::MatchSource::TryDesugar(_)) = ex.kind
@ -1149,8 +1150,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) => body_id,
_ => return false,
};
let ControlFlow::Break(expr) =
(V { search_span: span }).visit_body(self.tcx.hir().body(*body_id))
let ControlFlow::Break(expr) = (FindMethodSubexprOfTry { search_span: span })
.visit_body(self.tcx.hir().body(*body_id))
else {
return false;
};

View File

@ -220,6 +220,12 @@ fn main() {
}
}
if env::var_os("RUSTC_BOLT_LINK_FLAGS").is_some() {
if let Some("rustc_driver") = crate_name {
cmd.arg("-Clink-args=-Wl,-q");
}
}
let is_test = args.iter().any(|a| a == "--test");
if verbose > 2 {
let rust_env_vars =
@ -244,12 +250,6 @@ fn main() {
eprintln!("{prefix} libdir: {libdir:?}");
}
if env::var_os("RUSTC_BOLT_LINK_FLAGS").is_some() {
if let Some("rustc_driver") = crate_name {
cmd.arg("-Clink-args=-Wl,-q");
}
}
bin_helpers::maybe_dump(format!("stage{stage}-rustc"), &cmd);
let start = Instant::now();

View File

@ -59,6 +59,7 @@ harness = false
[features]
default = ["stack-cache"]
stack-cache = []
stack-cache-consistency-check = ["stack-cache"]
# Be aware that this file is inside a workspace when used via the
# submodule in the rustc repo. That means there are many cargo features

View File

@ -179,18 +179,27 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
);
}
cmd.env("RUSTC_WRAPPER", &cargo_miri_path);
// There's also RUSTC_WORKSPACE_WRAPPER, which gets in the way of our own wrapping.
if env::var_os("RUSTC_WORKSPACE_WRAPPER").is_some() {
println!(
"WARNING: Ignoring `RUSTC_WORKSPACE_WRAPPER` environment variable, Miri does not support wrapping."
);
}
cmd.env_remove("RUSTC_WORKSPACE_WRAPPER");
// We are going to invoke `MIRI` for everything, not `RUSTC`.
if env::var_os("RUSTC").is_some() && env::var_os("MIRI").is_none() {
println!(
"WARNING: Ignoring `RUSTC` environment variable; set `MIRI` if you want to control the binary used as the driver."
);
}
// Build scripts (and also cargo: https://github.com/rust-lang/cargo/issues/10885) will invoke
// `rustc` even when `RUSTC_WRAPPER` is set. 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 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.
// Ideally we would set RUSTC to some non-existent path, so we can be sure our wrapping is
// 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
// `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
// 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
// or TARGET_RUNNER invocations, so we canonicalize it here to make it exceedingly unlikely that
@ -247,6 +256,16 @@ pub fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
/// Cargo does not give us this information directly, so we need to check
/// various command-line flags.
fn is_runnable_crate() -> bool {
// Determine whether this is cargo invoking rustc to get some infos. Ideally we'd check "is
// there a filename passed to rustc", but that's very hard as we would have to know whether
// e.g. `--print foo` is a booolean flag `--print` followed by filename `foo` or equivalent
// to `--print=foo`. So instead we use this more fragile approach of detecting the presence
// of a "query" flag rather than the absence of a filename.
let info_query = get_arg_flag_value("--print").is_some() || has_arg_flag("-vV");
if info_query {
// Nothing to run.
return false;
}
let is_bin = get_arg_flag_value("--crate-type").as_deref().unwrap_or("bin") == "bin";
let is_test = has_arg_flag("--test");
is_bin || is_test
@ -285,16 +304,9 @@ pub fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
}
}
// phase_cargo_miri set `MIRI_BE_RUSTC` for when build scripts directly invoke the driver;
// however, if we get called back by cargo here, we'll carefully compute the right flags
// ourselves, so we first un-do what the earlier phase did.
env::remove_var("MIRI_BE_RUSTC");
let verbose = std::env::var("MIRI_VERBOSE")
.map_or(0, |verbose| verbose.parse().expect("verbosity flag must be an integer"));
let target_crate = is_target_crate();
// Determine whether this is cargo invoking rustc to get some infos.
let info_query = get_arg_flag_value("--print").is_some() || has_arg_flag("-vV");
let store_json = |info: CrateRunInfo| {
if get_arg_flag_value("--emit").unwrap_or_default().split(',').any(|e| e == "dep-info") {
@ -321,7 +333,7 @@ pub fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
}
};
let runnable_crate = !info_query && is_runnable_crate();
let runnable_crate = is_runnable_crate();
if runnable_crate && target_crate {
assert!(
@ -395,7 +407,7 @@ pub fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
let mut emit_link_hack = false;
// Arguments are treated very differently depending on whether this crate is
// for interpretation by Miri, or for use by a build script / proc macro.
if !info_query && target_crate {
if target_crate {
// Forward arguments, but remove "link" from "--emit" to make this a check-only build.
let emit_flag = "--emit";
while let Some(arg) = args.next() {
@ -429,17 +441,14 @@ pub fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
cmd.arg("-C").arg("panic=abort");
}
} else {
// For host crates (but not when we are just printing some info),
// we might still have to set the sysroot.
if !info_query {
// When we're running `cargo-miri` from `x.py` we need to pass the sysroot explicitly
// due to bootstrap complications.
if let Some(sysroot) = std::env::var_os("MIRI_HOST_SYSROOT") {
cmd.arg("--sysroot").arg(sysroot);
}
// This is a host crate.
// When we're running `cargo-miri` from `x.py` we need to pass the sysroot explicitly
// due to bootstrap complications.
if let Some(sysroot) = std::env::var_os("MIRI_HOST_SYSROOT") {
cmd.arg("--sysroot").arg(sysroot);
}
// For host crates or when we are printing, just forward everything.
// Forward everything.
cmd.args(args);
}
@ -451,9 +460,7 @@ pub fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
// Run it.
if verbose > 0 {
eprintln!(
"[cargo-miri rustc] target_crate={target_crate} runnable_crate={runnable_crate} info_query={info_query}"
);
eprintln!("[cargo-miri rustc] target_crate={target_crate} runnable_crate={runnable_crate}");
}
// Create a stub .rlib file if "link" was requested by cargo.
@ -480,11 +487,6 @@ pub enum RunnerPhase {
}
pub fn phase_runner(mut binary_args: impl Iterator<Item = String>, phase: RunnerPhase) {
// phase_cargo_miri set `MIRI_BE_RUSTC` for when build scripts directly invoke the driver;
// however, if we get called back by cargo here, we'll carefully compute the right flags
// ourselves, so we first un-do what the earlier phase did.
env::remove_var("MIRI_BE_RUSTC");
let verbose = std::env::var("MIRI_VERBOSE")
.map_or(0, |verbose| verbose.parse().expect("verbosity flag must be an integer"));
@ -542,15 +544,13 @@ pub fn phase_runner(mut binary_args: impl Iterator<Item = String>, phase: Runner
// but when we run here, cargo does not interpret the JSON any more. `--json`
// then also needs to be dropped.
let mut args = info.args.into_iter();
let error_format_flag = "--error-format";
let json_flag = "--json";
while let Some(arg) = args.next() {
if arg == "--extern" {
forward_patched_extern_arg(&mut args, &mut cmd);
} else if let Some(suffix) = arg.strip_prefix(error_format_flag) {
} else if let Some(suffix) = arg.strip_prefix("--error-format") {
assert!(suffix.starts_with('='));
// Drop this argument.
} else if let Some(suffix) = arg.strip_prefix(json_flag) {
} else if let Some(suffix) = arg.strip_prefix("--json") {
assert!(suffix.starts_with('='));
// Drop this argument.
} else {
@ -589,13 +589,11 @@ pub fn phase_rustdoc(mut args: impl Iterator<Item = String>) {
let rustdoc = env::var("MIRI_ORIG_RUSTDOC").unwrap_or("rustdoc".to_string());
let mut cmd = Command::new(rustdoc);
let extern_flag = "--extern";
let runtool_flag = "--runtool";
while let Some(arg) = args.next() {
if arg == extern_flag {
if arg == "--extern" {
// Patch --extern arguments to use *.rmeta files, since phase_cargo_rustc only creates stub *.rlib files.
forward_patched_extern_arg(&mut args, &mut cmd);
} else if arg == runtool_flag {
} else if arg == "--runtool" {
// An existing --runtool flag indicates cargo is running in cross-target mode, which we don't support.
// Note that this is only passed when cargo is run with the unstable -Zdoctest-xcompile flag;
// otherwise, we won't be called as rustdoc at all.

View File

@ -101,7 +101,12 @@ pub fn find_miri() -> PathBuf {
}
pub fn miri() -> Command {
Command::new(find_miri())
let mut cmd = Command::new(find_miri());
// We never want to inherit this from the environment.
// However, this is sometimes set in the environment to work around build scripts that don't
// honor RUSTC_WRAPPER. So remove it again in case it is set.
cmd.env_remove("MIRI_BE_RUSTC");
cmd
}
pub fn miri_for_host() -> Command {

View File

@ -22,17 +22,24 @@ if [ "$HOST_TARGET" = i686-pc-windows-msvc ]; then
BASH="C:/Program Files/Git/usr/bin/bash"
fi
# Determine configuration for installed build
echo "Installing release version of Miri"
# Global configuration
export RUSTFLAGS="-D warnings"
export CARGO_INCREMENTAL=0
export CARGO_EXTRA_FLAGS="--locked"
# Determine configuration for installed build
echo "Installing release version of Miri"
./miri install
# Prepare debug build for direct `./miri` invocations
echo "Building debug version of Miri"
echo "Checking various feature flag configurations"
./miri check --no-default-features # make sure this can be built
./miri check --all-features # and this, too
./miri check # and this, too
# `--all-features` is used for the build below, so no extra check needed.
# Prepare debug build for direct `./miri` invocations.
# We enable all features to make sure the Stacked Borrows consistency check runs.
echo "Building debug version of Miri"
export CARGO_EXTRA_FLAGS="$CARGO_EXTRA_FLAGS --all-features"
./miri build --all-targets # the build that all the `./miri test` below will use
endgroup
@ -46,8 +53,8 @@ function run_tests {
fi
## ui test suite
# On the host and on Linux, also stress-test the GC.
if [ -z "${MIRI_TEST_TARGET:-}" ] || [ "$HOST_TARGET" = x86_64-unknown-linux-gnu ]; then
# On the host, also stress-test the GC.
if [ -z "${MIRI_TEST_TARGET:-}" ]; then
MIRIFLAGS="${MIRIFLAGS:-} -Zmiri-provenance-gc=1" ./miri test
else
./miri test
@ -130,6 +137,8 @@ case $HOST_TARGET in
MIRI_TEST_TARGET=aarch64-unknown-linux-gnu run_tests
MIRI_TEST_TARGET=aarch64-apple-darwin run_tests
MIRI_TEST_TARGET=i686-pc-windows-gnu run_tests
MIRI_TEST_TARGET=x86_64-pc-windows-gnu run_tests
MIRI_TEST_TARGET=arm-unknown-linux-gnueabi run_tests
# Some targets are only partially supported.
MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthread-threadname libc-getentropy libc-getrandom libc-misc libc-fs atomic env align num_cpus
MIRI_TEST_TARGET=i686-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthread-threadname libc-getentropy libc-getrandom libc-misc libc-fs atomic env align num_cpus
@ -145,9 +154,7 @@ case $HOST_TARGET in
MIRI_TEST_TARGET=x86_64-pc-windows-msvc run_tests
;;
i686-pc-windows-msvc)
MIRI_TEST_TARGET=arm-unknown-linux-gnueabi run_tests
MIRI_TEST_TARGET=x86_64-unknown-linux-gnu run_tests
MIRI_TEST_TARGET=x86_64-pc-windows-gnu run_tests
;;
*)
echo "FATAL: unknown OS"

View File

@ -479,10 +479,11 @@ impl Command {
Ok(())
}
fn run(dep: bool, flags: Vec<OsString>) -> Result<()> {
fn run(dep: bool, mut flags: Vec<OsString>) -> Result<()> {
let mut e = MiriEnv::new()?;
// Scan for "--target" to overwrite the "MIRI_TEST_TARGET" env var so
// that we set the MIRI_SYSROOT up the right way.
// that we set the MIRI_SYSROOT up the right way. We must make sure that
// MIRI_TEST_TARGET and `--target` are in sync.
use itertools::Itertools;
let target = flags
.iter()
@ -493,33 +494,35 @@ impl Command {
// Found it!
e.sh.set_var("MIRI_TEST_TARGET", target);
} else if let Ok(target) = std::env::var("MIRI_TEST_TARGET") {
// Make sure miri actually uses this target.
let miriflags = e.sh.var("MIRIFLAGS").unwrap_or_default();
e.sh.set_var("MIRIFLAGS", format!("{miriflags} --target {target}"));
// Convert `MIRI_TEST_TARGET` into `--target`.
flags.push("--target".into());
flags.push(target.into());
}
// Scan for "--edition" (we'll set one ourselves if that flag is not present).
// Scan for "--edition", set one ourselves if that flag is not present.
let have_edition =
flags.iter().take_while(|arg| *arg != "--").any(|arg| *arg == "--edition");
if !have_edition {
flags.push("--edition=2021".into()); // keep in sync with `tests/ui.rs`.`
}
// Prepare a sysroot.
e.build_miri_sysroot(/* quiet */ true)?;
// Then run the actual command.
// Then run the actual command. Also add MIRIFLAGS.
let miri_manifest = path!(e.miri_dir / "Cargo.toml");
let miri_flags = e.sh.var("MIRIFLAGS").unwrap_or_default();
let miri_flags = flagsplit(&miri_flags);
let toolchain = &e.toolchain;
let extra_flags = &e.cargo_extra_flags;
let edition_flags = (!have_edition).then_some("--edition=2021"); // keep in sync with `tests/ui.rs`.`
if dep {
cmd!(
e.sh,
"cargo +{toolchain} --quiet test {extra_flags...} --manifest-path {miri_manifest} --test ui -- --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...} {flags...}"
).quiet().run()?;
} else {
cmd!(
e.sh,
"cargo +{toolchain} --quiet run {extra_flags...} --manifest-path {miri_manifest} -- {miri_flags...} {edition_flags...} {flags...}"
"cargo +{toolchain} --quiet run {extra_flags...} --manifest-path {miri_manifest} -- {miri_flags...} {flags...}"
).quiet().run()?;
}
Ok(())

View File

@ -1 +1 @@
cb7c63606e53715f94f3ba04d38e50772e4cd23d
5baf1e13f568b61e121953bf6a3d09faee7dd446

View File

@ -146,7 +146,7 @@ impl<'tcx> Stack {
/// Panics if any of the caching mechanisms have broken,
/// - The StackCache indices don't refer to the parallel items,
/// - There are no Unique items outside of first_unique..last_unique
#[cfg(all(feature = "stack-cache", debug_assertions))]
#[cfg(feature = "stack-cache-consistency-check")]
fn verify_cache_consistency(&self) {
// Only a full cache needs to be valid. Also see the comments in find_granting_cache
// and set_unknown_bottom.
@ -190,7 +190,7 @@ impl<'tcx> Stack {
tag: ProvenanceExtra,
exposed_tags: &FxHashSet<BorTag>,
) -> Result<Option<usize>, ()> {
#[cfg(all(feature = "stack-cache", debug_assertions))]
#[cfg(feature = "stack-cache-consistency-check")]
self.verify_cache_consistency();
let ProvenanceExtra::Concrete(tag) = tag else {
@ -327,7 +327,7 @@ impl<'tcx> Stack {
// This primes the cache for the next access, which is almost always the just-added tag.
self.cache.add(new_idx, new);
#[cfg(debug_assertions)]
#[cfg(feature = "stack-cache-consistency-check")]
self.verify_cache_consistency();
}
@ -410,7 +410,7 @@ impl<'tcx> Stack {
self.unique_range.end = self.unique_range.end.min(disable_start);
}
#[cfg(all(feature = "stack-cache", debug_assertions))]
#[cfg(feature = "stack-cache-consistency-check")]
self.verify_cache_consistency();
Ok(())
@ -465,7 +465,7 @@ impl<'tcx> Stack {
self.unique_range = 0..0;
}
#[cfg(all(feature = "stack-cache", debug_assertions))]
#[cfg(feature = "stack-cache-consistency-check")]
self.verify_cache_consistency();
Ok(())
}

View File

@ -365,7 +365,7 @@ type S = &'static str;
/// Pretty-printing details
///
/// Example:
/// ```
/// ```rust,ignore (private type)
/// DisplayFmtWrapper {
/// top: '>',
/// bot: '<',
@ -393,7 +393,7 @@ struct DisplayFmtWrapper {
/// Formating of the permissions on each range.
///
/// Example:
/// ```
/// ```rust,ignore (private type)
/// DisplayFmtPermission {
/// open: "[",
/// sep: "|",
@ -425,7 +425,7 @@ struct DisplayFmtPermission {
/// Formating of the tree structure.
///
/// Example:
/// ```
/// ```rust,ignore (private type)
/// DisplayFmtPadding {
/// join_middle: "|-",
/// join_last: "'-",
@ -463,7 +463,7 @@ struct DisplayFmtPadding {
/// How to show whether a location has been accessed
///
/// Example:
/// ```
/// ```rust,ignore (private type)
/// DisplayFmtAccess {
/// yes: " ",
/// no: "?",

View File

@ -181,8 +181,12 @@ impl Permission {
pub fn is_initial(&self) -> bool {
self.inner.is_initial()
}
/// Check if `self` is the terminal state of a pointer (is `Disabled`).
pub fn is_disabled(&self) -> bool {
self.inner == Disabled
}
/// Default initial permission of the root of a new tree.
/// Default initial permission of the root of a new tree at inbounds positions.
/// Must *only* be used for the root, this is not in general an "initial" permission!
pub fn new_active() -> Self {
Self { inner: Active }
@ -193,11 +197,17 @@ impl Permission {
Self { inner: Reserved { ty_is_freeze, conflicted: false } }
}
/// Default initial permission of a reborrowed shared reference
/// Default initial permission of a reborrowed shared reference.
pub fn new_frozen() -> Self {
Self { inner: Frozen }
}
/// Default initial permission of the root of a new tre at out-of-bounds positions.
/// Must *only* be used for the root, this is not in general an "initial" permission!
pub fn new_disabled() -> Self {
Self { inner: Disabled }
}
/// Apply the transition to the inner PermissionPriv.
pub fn perform_access(
kind: AccessKind,

View File

@ -42,6 +42,7 @@ pub(super) struct LocationState {
/// protected, that is *not* the case for uninitialized locations. Instead we just have a latent
/// "future initial permission" of `Disabled`, causing UB only if an access is ever actually
/// performed.
/// Note that the tree root is also always initialized, as if the allocation was a write access.
initialized: bool,
/// This pointer's current permission / future initial permission.
permission: Permission,
@ -55,17 +56,18 @@ pub(super) struct LocationState {
}
impl LocationState {
/// Default initial state has never been accessed and has been subjected to no
/// foreign access.
fn new(permission: Permission) -> Self {
/// Constructs a new initial state. It has neither been accessed, nor been subjected
/// to any foreign access yet.
/// The permission is not allowed to be `Active`.
fn new_uninit(permission: Permission) -> Self {
assert!(permission.is_initial() || permission.is_disabled());
Self { permission, initialized: false, latest_foreign_access: None }
}
/// Record that this location was accessed through a child pointer by
/// marking it as initialized
fn with_access(mut self) -> Self {
self.initialized = true;
self
/// Constructs a new initial state. It has not yet been subjected
/// to any foreign access. However, it is already marked as having been accessed.
fn new_init(permission: Permission) -> Self {
Self { permission, initialized: true, latest_foreign_access: None }
}
/// Check if the location has been initialized, i.e. if it has
@ -238,8 +240,10 @@ pub(super) struct Node {
/// If the pointer was reborrowed, it has children.
// FIXME: bench to compare this to FxHashSet and to other SmallVec sizes
pub children: SmallVec<[UniIndex; 4]>,
/// Either `Reserved` or `Frozen`, the permission this tag will be lazily initialized
/// to on the first access.
/// Either `Reserved`, `Frozen`, or `Disabled`, it is the permission this tag will
/// lazily be initialized to on the first access.
/// It is only ever `Disabled` for a tree root, since the root is initialized to `Active` by
/// its own separate mechanism.
default_initial_perm: Permission,
/// Some extra information useful only for debugging purposes
pub debug_info: NodeDebugInfo,
@ -444,12 +448,14 @@ impl<'tree> TreeVisitor<'tree> {
impl Tree {
/// Create a new tree, with only a root pointer.
pub fn new(root_tag: BorTag, size: Size, span: Span) -> Self {
let root_perm = Permission::new_active();
// The root has `Disabled` as the default permission,
// so that any access out of bounds is invalid.
let root_default_perm = Permission::new_disabled();
let mut tag_mapping = UniKeyMap::default();
let root_idx = tag_mapping.insert(root_tag);
let nodes = {
let mut nodes = UniValMap::<Node>::default();
let mut debug_info = NodeDebugInfo::new(root_tag, root_perm, span);
let mut debug_info = NodeDebugInfo::new(root_tag, root_default_perm, span);
// name the root so that all allocations contain one named pointer
debug_info.add_name("root of the allocation");
nodes.insert(
@ -458,7 +464,7 @@ impl Tree {
tag: root_tag,
parent: None,
children: SmallVec::default(),
default_initial_perm: root_perm,
default_initial_perm: root_default_perm,
debug_info,
},
);
@ -466,7 +472,11 @@ impl Tree {
};
let rperms = {
let mut perms = UniValMap::default();
perms.insert(root_idx, LocationState::new(root_perm).with_access());
// We manually set it to `Active` on all in-bounds positions.
// We also ensure that it is initalized, so that no `Active` but
// not yet initialized nodes exist. Essentially, we pretend there
// was a write that initialized these to `Active`.
perms.insert(root_idx, LocationState::new_init(Permission::new_active()));
RangeMap::new(size, perms)
};
Self { root: root_idx, nodes, rperms, tag_mapping }
@ -500,7 +510,7 @@ impl<'tcx> Tree {
// Register new_tag as a child of parent_tag
self.nodes.get_mut(parent_idx).unwrap().children.push(idx);
// Initialize perms
let perm = LocationState::new(default_initial_perm).with_access();
let perm = LocationState::new_init(default_initial_perm);
for (_perms_range, perms) in self.rperms.iter_mut(reborrow_range.start, reborrow_range.size)
{
perms.insert(idx, perm);
@ -599,7 +609,7 @@ impl<'tcx> Tree {
-> Result<ContinueTraversal, TransitionError> {
let NodeAppArgs { node, mut perm, rel_pos } = args;
let old_state = perm.or_insert(LocationState::new(node.default_initial_perm));
let old_state = perm.or_insert(LocationState::new_uninit(node.default_initial_perm));
match old_state.skip_if_known_noop(access_kind, rel_pos) {
ContinueTraversal::SkipChildren => return Ok(ContinueTraversal::SkipChildren),

View File

@ -516,11 +516,11 @@ mod spurious_read {
let source = LocStateProtPair {
xy_rel: RelPosXY::MutuallyForeign,
x: LocStateProt {
state: LocationState::new(Permission::new_frozen()).with_access(),
state: LocationState::new_init(Permission::new_frozen()),
prot: true,
},
y: LocStateProt {
state: LocationState::new(Permission::new_reserved(false)),
state: LocationState::new_uninit(Permission::new_reserved(false)),
prot: true,
},
};

View File

@ -132,7 +132,7 @@ where
/// the associated `UniIndex` from ALL `UniValMap`s.
///
/// Example of such behavior:
/// ```
/// ```rust,ignore (private type can't be doctested)
/// let mut keymap = UniKeyMap::<char>::default();
/// let mut valmap = UniValMap::<char>::default();
/// // Insert 'a' -> _ -> 'A'

View File

@ -1046,7 +1046,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
/// Run the core interpreter loop. Returns only when an interrupt occurs (an error or program
/// termination).
fn run_threads(&mut self) -> InterpResult<'tcx, !> {
let this = self.eval_context_mut();
let this = self.eval_context_mut();
loop {
if CTRL_C_RECEIVED.load(Relaxed) {
this.machine.handle_abnormal_termination();

View File

@ -2,17 +2,11 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "anyhow"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
[[package]]
name = "autocfg"
version = "1.1.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
[[package]]
name = "byteorder"
@ -22,33 +16,33 @@ checksum = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
[[package]]
name = "byteorder"
version = "1.4.3"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cargo-miri-test"
version = "0.1.0"
dependencies = [
"anyhow",
"autocfg",
"byteorder 0.5.3",
"byteorder 1.4.3",
"byteorder 1.5.0",
"cdylib",
"exported_symbol",
"eyre",
"issue_1567",
"issue_1691",
"issue_1705",
"issue_1760",
"issue_rust_86261",
"serde_derive",
"proc-macro2",
"proc_macro_crate",
]
[[package]]
name = "cdylib"
version = "0.1.0"
dependencies = [
"byteorder 1.4.3",
"byteorder 1.5.0",
]
[[package]]
@ -62,11 +56,27 @@ dependencies = [
name = "exported_symbol_dep"
version = "0.1.0"
[[package]]
name = "eyre"
version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
dependencies = [
"indenter",
"once_cell",
]
[[package]]
name = "indenter"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]]
name = "issue_1567"
version = "0.1.0"
dependencies = [
"byteorder 1.4.3",
"byteorder 1.5.0",
]
[[package]]
@ -77,66 +87,44 @@ version = "0.1.0"
name = "issue_1705"
version = "0.1.0"
dependencies = [
"byteorder 1.4.3",
"byteorder 1.5.0",
]
[[package]]
name = "issue_1760"
version = "0.1.0"
[[package]]
name = "issue_rust_86261"
version = "0.1.0"
[[package]]
name = "proc-macro2"
version = "1.0.66"
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "proc-macro2"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
name = "proc_macro_crate"
version = "0.1.0"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde_derive"
version = "1.0.185"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc59dfdcbad1437773485e0367fea4b090a2e0a16d9ffc46af47764536a298ec"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "subcrate"
version = "0.1.0"
dependencies = [
"byteorder 1.4.3",
]
[[package]]
name = "syn"
version = "2.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
"byteorder 1.5.0",
]
[[package]]
name = "unicode-ident"
version = "1.0.6"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"

View File

@ -12,19 +12,21 @@ edition = "2018"
byteorder = "1.0"
cdylib = { path = "cdylib" }
exported_symbol = { path = "exported-symbol" }
proc_macro_crate = { path = "proc-macro-crate" }
issue_1567 = { path = "issue-1567" }
issue_1691 = { path = "issue-1691" }
issue_1705 = { path = "issue-1705" }
issue_1760 = { path = "issue-1760" }
issue_rust_86261 = { path = "issue-rust-86261" }
[dev-dependencies]
byteorder_2 = { package = "byteorder", version = "0.5" } # to test dev-dependencies behave as expected, with renaming
# Not actually used, but exercises some unique code path (`--extern` .so file).
serde_derive = "1.0.185"
# Not actually used, but uses a custom build probe so let's make sure that works.
## More dependencies that we don't actually use, but add just for extra test coverage.
# These use custom build probes, let's make sure they don't explode.
# (Ideally we'd check if the probe was successful, but that's not easily possible.)
anyhow = "1.0"
# proc-macro2 is extra exciting because it is both a host-dependency (of proc_macro_crate above)
# and a target-dependency.
proc-macro2 = "1.0"
eyre = "0.6"
[build-dependencies]
autocfg = "1"

View File

@ -1,8 +0,0 @@
[package]
name = "issue_1760"
version = "0.1.0"
authors = ["Miri Team"]
edition = "2018"
[lib]
proc-macro = true

View File

@ -0,0 +1,13 @@
[package]
# regression test for issue 1760
name = "proc_macro_crate"
version = "0.1.0"
authors = ["Miri Team"]
edition = "2018"
[lib]
proc-macro = true
[dependencies]
# A common dependency of proc macros, let's make sure that works.
proc-macro2 = "1.0"

View File

@ -28,9 +28,9 @@
/// ```
#[no_mangle]
pub fn make_true() -> bool {
proc_macro_crate::use_the_dependency!();
issue_1567::use_the_dependency();
issue_1705::use_the_dependency();
issue_1760::use_the_dependency!();
issue_1691::use_me()
}

View File

@ -54,34 +54,13 @@ fn build_so_for_c_ffi_tests() -> PathBuf {
so_file_path
}
fn test_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> Config {
/// Does *not* set any args or env vars, since it is shared between the test runner and
/// run_dep_mode.
fn miri_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> Config {
// Miri is rustc-like, so we create a default builder for rustc and modify it
let mut program = CommandBuilder::rustc();
program.program = miri_path();
// Add some flags we always want.
program.args.push("-Dwarnings".into());
program.args.push("-Dunused".into());
program.args.push("-Ainternal_features".into());
if let Ok(extra_flags) = env::var("MIRIFLAGS") {
for flag in extra_flags.split_whitespace() {
program.args.push(flag.into());
}
}
program.args.push("-Zui-testing".into());
program.args.push("--target".into());
program.args.push(target.into());
// If we're on linux, and we're testing the extern-so functionality,
// then build the shared object file for testing external C function calls
// and push the relevant compiler flag.
if cfg!(target_os = "linux") && path.starts_with("tests/extern-so/") {
let so_file_path = build_so_for_c_ffi_tests();
let mut flag = std::ffi::OsString::from("-Zmiri-extern-so-file=");
flag.push(so_file_path.into_os_string());
program.args.push(flag);
}
let mut config = Config {
target: Some(target.to_owned()),
stderr_filters: STDERR.clone(),
@ -119,17 +98,38 @@ fn run_tests(
with_dependencies: bool,
tmpdir: &Path,
) -> Result<()> {
let mut config = test_config(target, path, mode, with_dependencies);
let mut config = miri_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())));
// If a test ICEs, we want to see a backtrace.
config.program.envs.push(("RUST_BACKTRACE".into(), Some("1".into())));
// Add some flags we always want.
config.program.args.push("-Dwarnings".into());
config.program.args.push("-Dunused".into());
config.program.args.push("-Ainternal_features".into());
if let Ok(extra_flags) = env::var("MIRIFLAGS") {
for flag in extra_flags.split_whitespace() {
config.program.args.push(flag.into());
}
}
config.program.args.push("-Zui-testing".into());
config.program.args.push("--target".into());
config.program.args.push(target.into());
// If we're on linux, and we're testing the extern-so functionality,
// then build the shared object file for testing external C function calls
// and push the relevant compiler flag.
if cfg!(target_os = "linux") && path.starts_with("tests/extern-so/") {
let so_file_path = build_so_for_c_ffi_tests();
let mut flag = std::ffi::OsString::from("-Zmiri-extern-so-file=");
flag.push(so_file_path.into_os_string());
config.program.args.push(flag);
}
// Handle command-line arguments.
let args = ui_test::Args::test()?;
let default_bless = env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0");
@ -292,13 +292,12 @@ fn main() -> 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 mut config = test_config(
let config = miri_config(
&target,
"",
Mode::Yolo { rustfix: RustfixMode::Disabled },
/* with dependencies */ true,
);
config.program.args.clear(); // We want to give the user full control over flags
let dep_args = config.build_dependencies()?;
let mut cmd = config.program.build(&config.out_dir);