mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 23:04:33 +00:00
Auto merge of #112244 - RalfJung:miri, r=oli-obk
update Miri Also adjust Miri's compiletest a little: in pre-subtree days we added `-A unused -Astable-features` to have the Miri toolstate break less often. But nowadays it just causes confusion when Miri CI works in rustc but fails on the Miri side so let's get rid of this difference. r? `@oli-obk`
This commit is contained in:
commit
4e4f0417aa
@ -17,11 +17,11 @@ begingroup "Building Miri"
|
||||
echo "Installing release version of Miri"
|
||||
export RUSTFLAGS="-D warnings"
|
||||
export CARGO_INCREMENTAL=0
|
||||
./miri install # implicitly locked
|
||||
export CARGO_EXTRA_FLAGS="--locked"
|
||||
./miri install
|
||||
|
||||
# Prepare debug build for direct `./miri` invocations
|
||||
echo "Building debug version of Miri"
|
||||
export CARGO_EXTRA_FLAGS="--locked"
|
||||
./miri check --no-default-features # make sure this can be built
|
||||
./miri check --all-features # and this, too
|
||||
./miri build --all-targets # the build that all the `./miri test` below will use
|
||||
@ -39,8 +39,11 @@ function run_tests {
|
||||
## ui test suite
|
||||
./miri test
|
||||
if [ -z "${MIRI_TEST_TARGET+exists}" ]; then
|
||||
# Only for host architecture: tests with optimizations (`-O` is what cargo passes, but crank MIR
|
||||
# optimizations up all the way, too).
|
||||
# Host-only tests: running these on all targets is unlikely to catch more problems and would
|
||||
# cost a lot of CI time.
|
||||
|
||||
# Tests with optimizations (`-O` is what cargo passes, but crank MIR optimizations up all the
|
||||
# way, too).
|
||||
# Optimizations change diagnostics (mostly backtraces), so we don't check
|
||||
# them. Also error locations change so we don't run the failing tests.
|
||||
# We explicitly enable debug-assertions here, they are disabled by -O but we have tests
|
||||
@ -51,6 +54,9 @@ function run_tests {
|
||||
for FILE in tests/many-seeds/*.rs; do
|
||||
MIRI_SEEDS=64 CARGO_EXTRA_FLAGS="$CARGO_EXTRA_FLAGS -q" ./miri many-seeds ./miri run "$FILE"
|
||||
done
|
||||
|
||||
# Check that the benchmarks build and run, but without actually benchmarking.
|
||||
HYPERFINE="bash -c" ./miri bench
|
||||
fi
|
||||
|
||||
## test-cargo-miri
|
||||
@ -75,13 +81,6 @@ function run_tests {
|
||||
unset RUSTC MIRI
|
||||
rm -rf .cargo
|
||||
|
||||
# Ensure that our benchmarks all work, but only on Linux hosts.
|
||||
if [ -z "${MIRI_TEST_TARGET+exists}" ] && [ "$HOST_TARGET" = x86_64-unknown-linux-gnu ] ; then
|
||||
for BENCH in $(ls "bench-cargo-miri"); do
|
||||
cargo miri run --manifest-path bench-cargo-miri/$BENCH/Cargo.toml
|
||||
done
|
||||
fi
|
||||
|
||||
endgroup
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,7 @@ if [ -z "$COMMAND" ]; then
|
||||
fi
|
||||
shift
|
||||
# macOS does not have a useful readlink/realpath so we have to use Python instead...
|
||||
MIRIDIR=$(python3 -c 'import os, sys; print(os.path.dirname(os.path.realpath(sys.argv[1])))' "$0")
|
||||
MIRIDIR=$(python3 -c 'import pathlib, sys; print(pathlib.Path(sys.argv[1]).resolve().parent.as_posix())' "$0")
|
||||
# Used for rustc syncs.
|
||||
JOSH_FILTER=":rev(75dd959a3a40eb5b4574f8d2e23aa6efbeb33573:prefix=src/tools/miri):/src/tools/miri"
|
||||
# Needed for `./miri bench`.
|
||||
@ -184,6 +184,8 @@ many-seeds)
|
||||
exit 0
|
||||
;;
|
||||
bench)
|
||||
# The hyperfine to use
|
||||
HYPERFINE=${HYPERFINE:-hyperfine -w 1 -m 5 --shell=none}
|
||||
# Make sure we have an up-to-date Miri installed
|
||||
"$0" install
|
||||
# Run the requested benchmarks
|
||||
@ -193,7 +195,7 @@ bench)
|
||||
BENCHES=("$@")
|
||||
fi
|
||||
for BENCH in "${BENCHES[@]}"; do
|
||||
hyperfine -w 1 -m 5 --shell=none "cargo +$TOOLCHAIN miri run --manifest-path $MIRIDIR/bench-cargo-miri/$BENCH/Cargo.toml"
|
||||
$HYPERFINE "cargo +$TOOLCHAIN miri run --manifest-path $MIRIDIR/bench-cargo-miri/$BENCH/Cargo.toml"
|
||||
done
|
||||
exit 0
|
||||
;;
|
||||
@ -280,10 +282,9 @@ find_sysroot() {
|
||||
# Run command.
|
||||
case "$COMMAND" in
|
||||
install)
|
||||
# "--locked" to respect the Cargo.lock file if it exists.
|
||||
# Install binaries to the miri toolchain's sysroot so they do not interact with other toolchains.
|
||||
$CARGO install $CARGO_EXTRA_FLAGS --path "$MIRIDIR" --force --locked --root "$SYSROOT" "$@"
|
||||
$CARGO install $CARGO_EXTRA_FLAGS --path "$MIRIDIR"/cargo-miri --force --locked --root "$SYSROOT" "$@"
|
||||
$CARGO install $CARGO_EXTRA_FLAGS --path "$MIRIDIR" --force --root "$SYSROOT" "$@"
|
||||
$CARGO install $CARGO_EXTRA_FLAGS --path "$MIRIDIR"/cargo-miri --force --root "$SYSROOT" "$@"
|
||||
;;
|
||||
check)
|
||||
# Check, and let caller control flags.
|
||||
|
@ -1 +1 @@
|
||||
8b4b20836b832e91aa605a2faf5e2a55190202c8
|
||||
33c3d101280c8eb3cd8af421bfb56a8afcc3881d
|
||||
|
@ -92,7 +92,7 @@ impl HistoryData {
|
||||
{
|
||||
// NOTE: `transition_range` is explicitly absent from the error message, it has no significance
|
||||
// to the user. The meaningful one is `access_range`.
|
||||
self.events.push((Some(span.data()), format!("{this} then transitioned {transition} due to a {rel} {access_kind} at offsets {access_range:?}", rel = if is_foreign { "foreign" } else { "child" })));
|
||||
self.events.push((Some(span.data()), format!("{this} later transitioned to {endpoint} due to a {rel} {access_kind} at offsets {access_range:?}", endpoint = transition.endpoint(), rel = if is_foreign { "foreign" } else { "child" })));
|
||||
self.events.push((None, format!("this corresponds to {}", transition.summary())));
|
||||
}
|
||||
}
|
||||
@ -212,12 +212,13 @@ impl History {
|
||||
|
||||
/// Reconstruct the history relevant to `error_offset` by filtering
|
||||
/// only events whose range contains the offset we are interested in.
|
||||
fn extract_relevant(&self, error_offset: u64) -> Self {
|
||||
fn extract_relevant(&self, error_offset: u64, error_kind: TransitionError) -> Self {
|
||||
History {
|
||||
events: self
|
||||
.events
|
||||
.iter()
|
||||
.filter(|e| e.transition_range.contains(&error_offset))
|
||||
.filter(|e| e.transition.is_relevant(error_kind))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>(),
|
||||
created: self.created,
|
||||
@ -303,7 +304,7 @@ impl TbError<'_> {
|
||||
history.extend(self.accessed_info.history.forget(), "accessed", false);
|
||||
}
|
||||
history.extend(
|
||||
self.conflicting_info.history.extract_relevant(self.error_offset),
|
||||
self.conflicting_info.history.extract_relevant(self.error_offset, self.error_kind),
|
||||
conflicting_tag_name,
|
||||
true,
|
||||
);
|
||||
|
@ -1,6 +1,7 @@
|
||||
use std::cmp::{Ordering, PartialOrd};
|
||||
use std::fmt;
|
||||
|
||||
use crate::borrow_tracker::tree_borrows::diagnostics::TransitionError;
|
||||
use crate::borrow_tracker::tree_borrows::tree::AccessRelatedness;
|
||||
use crate::borrow_tracker::AccessKind;
|
||||
|
||||
@ -115,26 +116,31 @@ mod transition {
|
||||
/// Public interface to the state machine that controls read-write permissions.
|
||||
/// This is the "private `enum`" pattern.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Permission(PermissionPriv);
|
||||
pub struct Permission {
|
||||
inner: PermissionPriv,
|
||||
}
|
||||
|
||||
/// Transition from one permission to the next.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct PermTransition(PermissionPriv, PermissionPriv);
|
||||
pub struct PermTransition {
|
||||
from: PermissionPriv,
|
||||
to: PermissionPriv,
|
||||
}
|
||||
|
||||
impl Permission {
|
||||
/// Default initial permission of the root of a new tree.
|
||||
pub fn new_root() -> Self {
|
||||
Self(Active)
|
||||
Self { inner: Active }
|
||||
}
|
||||
|
||||
/// Default initial permission of a reborrowed mutable reference.
|
||||
pub fn new_unique_2phase(ty_is_freeze: bool) -> Self {
|
||||
Self(Reserved { ty_is_freeze })
|
||||
Self { inner: Reserved { ty_is_freeze } }
|
||||
}
|
||||
|
||||
/// Default initial permission of a reborrowed shared reference
|
||||
pub fn new_frozen() -> Self {
|
||||
Self(Frozen)
|
||||
Self { inner: Frozen }
|
||||
}
|
||||
|
||||
/// Apply the transition to the inner PermissionPriv.
|
||||
@ -144,9 +150,9 @@ impl Permission {
|
||||
old_perm: Self,
|
||||
protected: bool,
|
||||
) -> Option<PermTransition> {
|
||||
let old_state = old_perm.0;
|
||||
let old_state = old_perm.inner;
|
||||
transition::perform_access(kind, rel_pos, old_state, protected)
|
||||
.map(|new_state| PermTransition(old_state, new_state))
|
||||
.map(|new_state| PermTransition { from: old_state, to: new_state })
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,26 +161,27 @@ impl PermTransition {
|
||||
/// should be possible, but the same is not guaranteed by construction of
|
||||
/// transitions inferred by diagnostics. This checks that a transition
|
||||
/// reconstructed by diagnostics is indeed one that could happen.
|
||||
fn is_possible(old: PermissionPriv, new: PermissionPriv) -> bool {
|
||||
old <= new
|
||||
fn is_possible(self) -> bool {
|
||||
self.from <= self.to
|
||||
}
|
||||
|
||||
pub fn from(old: Permission, new: Permission) -> Option<Self> {
|
||||
Self::is_possible(old.0, new.0).then_some(Self(old.0, new.0))
|
||||
pub fn from(from: Permission, to: Permission) -> Option<Self> {
|
||||
let t = Self { from: from.inner, to: to.inner };
|
||||
t.is_possible().then_some(t)
|
||||
}
|
||||
|
||||
pub fn is_noop(self) -> bool {
|
||||
self.0 == self.1
|
||||
self.from == self.to
|
||||
}
|
||||
|
||||
/// Extract result of a transition (checks that the starting point matches).
|
||||
pub fn applied(self, starting_point: Permission) -> Option<Permission> {
|
||||
(starting_point.0 == self.0).then_some(Permission(self.1))
|
||||
(starting_point.inner == self.from).then_some(Permission { inner: self.to })
|
||||
}
|
||||
|
||||
/// Extract starting point of a transition
|
||||
pub fn started(self) -> Permission {
|
||||
Permission(self.0)
|
||||
Permission { inner: self.from }
|
||||
}
|
||||
|
||||
/// Determines whether a transition that occured is compatible with the presence
|
||||
@ -190,10 +197,9 @@ impl PermTransition {
|
||||
/// };
|
||||
/// ```
|
||||
pub fn is_allowed_by_protector(&self) -> bool {
|
||||
let &Self(old, new) = self;
|
||||
assert!(Self::is_possible(old, new));
|
||||
match (old, new) {
|
||||
_ if old == new => true,
|
||||
assert!(self.is_possible());
|
||||
match (self.from, self.to) {
|
||||
_ if self.from == self.to => true,
|
||||
// It is always a protector violation to not be readable anymore
|
||||
(_, Disabled) => false,
|
||||
// In the case of a `Reserved` under a protector, both transitions
|
||||
@ -204,16 +210,9 @@ impl PermTransition {
|
||||
(Reserved { .. }, Active) | (Reserved { .. }, Frozen) => true,
|
||||
// This pointer should have stayed writeable for the whole function
|
||||
(Active, Frozen) => false,
|
||||
_ => unreachable!("Transition from {old:?} to {new:?} should never be possible"),
|
||||
_ => unreachable!("Transition {} should never be possible", self),
|
||||
}
|
||||
}
|
||||
|
||||
/// Composition function: get the transition that can be added after `app` to
|
||||
/// produce `self`.
|
||||
pub fn apply_start(self, app: Self) -> Option<Self> {
|
||||
let new_start = app.applied(Permission(self.0))?;
|
||||
Self::from(new_start, Permission(self.1))
|
||||
}
|
||||
}
|
||||
|
||||
pub mod diagnostics {
|
||||
@ -224,10 +223,10 @@ pub mod diagnostics {
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
PermissionPriv::Reserved { .. } => "Reserved",
|
||||
PermissionPriv::Active => "Active",
|
||||
PermissionPriv::Frozen => "Frozen",
|
||||
PermissionPriv::Disabled => "Disabled",
|
||||
Reserved { .. } => "Reserved",
|
||||
Active => "Active",
|
||||
Frozen => "Frozen",
|
||||
Disabled => "Disabled",
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -235,13 +234,13 @@ pub mod diagnostics {
|
||||
|
||||
impl fmt::Display for PermTransition {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "from {} to {}", self.0, self.1)
|
||||
write!(f, "from {} to {}", self.from, self.to)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Permission {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
write!(f, "{}", self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
@ -251,7 +250,7 @@ pub mod diagnostics {
|
||||
// Make sure there are all of the same length as each other
|
||||
// and also as `diagnostics::DisplayFmtPermission.uninit` otherwise
|
||||
// alignment will be incorrect.
|
||||
match self.0 {
|
||||
match self.inner {
|
||||
Reserved { ty_is_freeze: true } => "Res",
|
||||
Reserved { ty_is_freeze: false } => "Re*",
|
||||
Active => "Act",
|
||||
@ -269,9 +268,9 @@ pub mod diagnostics {
|
||||
/// to have write permissions, because that's what the diagnostics care about
|
||||
/// (otherwise `Reserved -> Frozen` would be considered a noop).
|
||||
pub fn summary(&self) -> &'static str {
|
||||
assert!(Self::is_possible(self.0, self.1));
|
||||
match (self.0, self.1) {
|
||||
(_, Active) => "an activation",
|
||||
assert!(self.is_possible());
|
||||
match (self.from, self.to) {
|
||||
(_, Active) => "the first write to a 2-phase borrowed mutable reference",
|
||||
(_, Frozen) => "a loss of write permissions",
|
||||
(Frozen, Disabled) => "a loss of read permissions",
|
||||
(_, Disabled) => "a loss of read and write permissions",
|
||||
@ -279,6 +278,118 @@ pub mod diagnostics {
|
||||
unreachable!("Transition from {old:?} to {new:?} should never be possible"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines whether `self` is a relevant transition for the error `err`.
|
||||
/// `self` will be a transition that happened to a tag some time before
|
||||
/// that tag caused the error.
|
||||
///
|
||||
/// Irrelevant events:
|
||||
/// - modifications of write permissions when the error is related to read permissions
|
||||
/// (on failed reads and protected `Frozen -> Disabled`, ignore `Reserved -> Active`,
|
||||
/// `Reserved -> Frozen`, and `Active -> Frozen`)
|
||||
/// - all transitions for attempts to deallocate strongly protected tags
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function assumes that its arguments apply to the same location
|
||||
/// and that they were obtained during a normal execution. It will panic otherwise.
|
||||
/// - `err` cannot be a `ProtectedTransition(_)` of a noop transition, as those
|
||||
/// never trigger protectors;
|
||||
/// - all transitions involved in `self` and `err` should be increasing
|
||||
/// (Reserved < Active < Frozen < Disabled);
|
||||
/// - between `self` and `err` the permission should also be increasing,
|
||||
/// so all permissions inside `err` should be greater than `self.1`;
|
||||
/// - `Active` and `Reserved` cannot cause an error due to insufficient permissions,
|
||||
/// so `err` cannot be a `ChildAccessForbidden(_)` of either of them;
|
||||
pub(in super::super) fn is_relevant(&self, err: TransitionError) -> bool {
|
||||
// NOTE: `super::super` is the visibility of `TransitionError`
|
||||
assert!(self.is_possible());
|
||||
if self.is_noop() {
|
||||
return false;
|
||||
}
|
||||
match err {
|
||||
TransitionError::ChildAccessForbidden(insufficient) => {
|
||||
// Show where the permission was gained then lost,
|
||||
// but ignore unrelated permissions.
|
||||
// This eliminates transitions like `Active -> Frozen`
|
||||
// when the error is a failed `Read`.
|
||||
match (self.to, insufficient.inner) {
|
||||
(Frozen, Frozen) => true,
|
||||
(Active, Frozen) => true,
|
||||
(Disabled, Disabled) => true,
|
||||
// A pointer being `Disabled` is a strictly stronger source of
|
||||
// errors than it being `Frozen`. If we try to access a `Disabled`,
|
||||
// then where it became `Frozen` (or `Active`) is the least of our concerns for now.
|
||||
(Active | Frozen, Disabled) => false,
|
||||
|
||||
// `Active` and `Reserved` have all permissions, so a
|
||||
// `ChildAccessForbidden(Reserved | Active)` can never exist.
|
||||
(_, Active) | (_, Reserved { .. }) =>
|
||||
unreachable!("this permission cannot cause an error"),
|
||||
// No transition has `Reserved` as its `.to` unless it's a noop.
|
||||
(Reserved { .. }, _) => unreachable!("self is a noop transition"),
|
||||
// All transitions produced in normal executions (using `apply_access`)
|
||||
// change permissions in the order `Reserved -> Active -> Frozen -> Disabled`.
|
||||
// We assume that the error was triggered on the same location that
|
||||
// the transition `self` applies to, so permissions found must be increasing
|
||||
// in the order `self.from < self.to <= insufficient.inner`
|
||||
(Disabled, Frozen) =>
|
||||
unreachable!("permissions between self and err must be increasing"),
|
||||
}
|
||||
}
|
||||
TransitionError::ProtectedTransition(forbidden) => {
|
||||
assert!(!forbidden.is_noop());
|
||||
// Show how we got to the starting point of the forbidden transition,
|
||||
// but ignore what came before.
|
||||
// This eliminates transitions like `Reserved -> Active`
|
||||
// when the error is a `Frozen -> Disabled`.
|
||||
match (self.to, forbidden.from, forbidden.to) {
|
||||
// We absolutely want to know where it was activated.
|
||||
(Active, Active, Frozen | Disabled) => true,
|
||||
// And knowing where it became Frozen is also important.
|
||||
(Frozen, Frozen, Disabled) => true,
|
||||
// If the error is a transition `Frozen -> Disabled`, then we don't really
|
||||
// care whether before that was `Reserved -> Active -> Frozen` or
|
||||
// `Reserved -> Frozen` or even `Frozen` directly.
|
||||
// The error will only show either
|
||||
// - created as Frozen, then Frozen -> Disabled is forbidden
|
||||
// - created as Reserved, later became Frozen, then Frozen -> Disabled is forbidden
|
||||
// In both cases the `Reserved -> Active` part is inexistant or irrelevant.
|
||||
(Active, Frozen, Disabled) => false,
|
||||
|
||||
// `Reserved -> Frozen` does not trigger protectors.
|
||||
(_, Reserved { .. }, Frozen) =>
|
||||
unreachable!("this transition cannot cause an error"),
|
||||
// No transition has `Reserved` as its `.to` unless it's a noop.
|
||||
(Reserved { .. }, _, _) => unreachable!("self is a noop transition"),
|
||||
(_, Disabled, Disabled) | (_, Frozen, Frozen) | (_, Active, Active) =>
|
||||
unreachable!("err contains a noop transition"),
|
||||
|
||||
// Permissions only evolve in the order `Reserved -> Active -> Frozen -> Disabled`,
|
||||
// so permissions found must be increasing in the order
|
||||
// `self.from < self.to <= forbidden.from < forbidden.to`.
|
||||
(Disabled, Reserved { .. } | Active | Frozen, _)
|
||||
| (Frozen, Reserved { .. } | Active, _)
|
||||
| (Active, Reserved { .. }, _) =>
|
||||
unreachable!("permissions between self and err must be increasing"),
|
||||
(_, Disabled, Reserved { .. } | Active | Frozen)
|
||||
| (_, Frozen, Reserved { .. } | Active)
|
||||
| (_, Active, Reserved { .. }) =>
|
||||
unreachable!("permissions within err must be increasing"),
|
||||
}
|
||||
}
|
||||
// We don't care because protectors evolve independently from
|
||||
// permissions.
|
||||
TransitionError::ProtectedDealloc => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Endpoint of a transition.
|
||||
/// Meant only for diagnostics, use `applied` in non-diagnostics
|
||||
/// code, which also checks that the starting point matches the current state.
|
||||
pub fn endpoint(&self) -> Permission {
|
||||
Permission { inner: self.to }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,8 +97,12 @@ impl MachineStopType for TerminationInfo {
|
||||
}
|
||||
fn add_args(
|
||||
self: Box<Self>,
|
||||
_: &mut dyn FnMut(std::borrow::Cow<'static, str>, rustc_errors::DiagnosticArgValue<'static>),
|
||||
) {}
|
||||
_: &mut dyn FnMut(
|
||||
std::borrow::Cow<'static, str>,
|
||||
rustc_errors::DiagnosticArgValue<'static>,
|
||||
),
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Miri specific diagnostics
|
||||
@ -324,10 +328,9 @@ pub fn report_error<'tcx, 'mir>(
|
||||
// We want to dump the allocation if this is `InvalidUninitBytes`. Since `add_args` consumes
|
||||
// the `InterpError`, we extract the variables it before that.
|
||||
let extra = match e {
|
||||
UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some((alloc_id, access)))) => {
|
||||
Some((alloc_id, access))
|
||||
}
|
||||
_ => None
|
||||
UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some((alloc_id, access)))) =>
|
||||
Some((alloc_id, access)),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// FIXME(fee1-dead), HACK: we want to use the error as title therefore we can just extract the
|
||||
@ -477,7 +480,7 @@ pub fn report_msg<'tcx>(
|
||||
}
|
||||
|
||||
let (mut err, handler) = err.into_diagnostic().unwrap();
|
||||
|
||||
|
||||
// Add backtrace
|
||||
for (idx, frame_info) in stacktrace.iter().enumerate() {
|
||||
let is_local = machine.is_local(frame_info);
|
||||
|
@ -162,9 +162,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||
let instance = this.resolve_path(path, Namespace::ValueNS);
|
||||
let cid = GlobalId { instance, promoted: None };
|
||||
// We don't give a span -- this isn't actually used directly by the program anyway.
|
||||
let const_val = this
|
||||
.eval_global(cid, None)
|
||||
.unwrap_or_else(|err| panic!("failed to evaluate required Rust item: {path:?}\n{err:?}"));
|
||||
let const_val = this.eval_global(cid, None).unwrap_or_else(|err| {
|
||||
panic!("failed to evaluate required Rust item: {path:?}\n{err:?}")
|
||||
});
|
||||
this.read_scalar(&const_val.into())
|
||||
.unwrap_or_else(|err| panic!("failed to read required Rust item: {path:?}\n{err:?}"))
|
||||
}
|
||||
|
@ -51,18 +51,9 @@ fn test_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) ->
|
||||
let mut program = CommandBuilder::rustc();
|
||||
program.program = miri_path();
|
||||
|
||||
let in_rustc_test_suite = option_env!("RUSTC_STAGE").is_some();
|
||||
|
||||
// Add some flags we always want.
|
||||
if in_rustc_test_suite {
|
||||
// Less aggressive warnings to make the rustc toolstate management less painful.
|
||||
// (We often get warnings when e.g. a feature gets stabilized or some lint gets added/improved.)
|
||||
program.args.push("-Astable-features".into());
|
||||
program.args.push("-Aunused".into());
|
||||
} else {
|
||||
program.args.push("-Dwarnings".into());
|
||||
program.args.push("-Dunused".into());
|
||||
}
|
||||
program.args.push("-Dwarnings".into());
|
||||
program.args.push("-Dunused".into());
|
||||
if let Ok(extra_flags) = env::var("MIRIFLAGS") {
|
||||
for flag in extra_flags.split_whitespace() {
|
||||
program.args.push(flag.into());
|
||||
@ -277,7 +268,6 @@ fn run_dep_mode(target: String, mut args: impl Iterator<Item = OsString>) -> Res
|
||||
// the arguments to the interpreted prog
|
||||
cmd.arg("--");
|
||||
cmd.args(args);
|
||||
println!("{cmd:?}");
|
||||
if cmd.spawn()?.wait()?.success() { Ok(()) } else { std::process::exit(1) }
|
||||
}
|
||||
|
||||
|
8
src/tools/miri/tests/fail/intrinsics/unchecked_shl.rs
Normal file
8
src/tools/miri/tests/fail/intrinsics/unchecked_shl.rs
Normal file
@ -0,0 +1,8 @@
|
||||
#![feature(unchecked_math)]
|
||||
|
||||
fn main() {
|
||||
unsafe {
|
||||
let _n = 1i8.unchecked_shl(8);
|
||||
//~^ ERROR: overflowing shift by 8 in `unchecked_shl`
|
||||
}
|
||||
}
|
15
src/tools/miri/tests/fail/intrinsics/unchecked_shl.stderr
Normal file
15
src/tools/miri/tests/fail/intrinsics/unchecked_shl.stderr
Normal file
@ -0,0 +1,15 @@
|
||||
error: Undefined Behavior: overflowing shift by 8 in `unchecked_shl`
|
||||
--> $DIR/unchecked_shl.rs:LL:CC
|
||||
|
|
||||
LL | let _n = 1i8.unchecked_shl(8);
|
||||
| ^^^^^^^^^^^^^^^^^^^^ overflowing shift by 8 in `unchecked_shl`
|
||||
|
|
||||
= 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/unchecked_shl.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: Undefined Behavior: overflowing shift by 64 in `unchecked_shr`
|
||||
--> $DIR/overflowing-unchecked-rsh.rs:LL:CC
|
||||
--> $DIR/unchecked_shr.rs:LL:CC
|
||||
|
|
||||
LL | let _n = 1i64.unchecked_shr(64);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 64 in `unchecked_shr`
|
||||
@ -7,7 +7,7 @@ LL | let _n = 1i64.unchecked_shr(64);
|
||||
= 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/overflowing-unchecked-rsh.rs:LL:CC
|
||||
= note: inside `main` at $DIR/unchecked_shr.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
@ -17,13 +17,13 @@ help: the conflicting tag <TAG> was created here, in the initial state Reserved
|
||||
|
|
||||
LL | let y = unsafe { &mut *(x as *mut u8) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
help: the conflicting tag <TAG> then transitioned from Reserved to Active due to a child write access at offsets [0x0..0x1]
|
||||
help: the conflicting tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x1]
|
||||
--> $DIR/alternate-read-write.rs:LL:CC
|
||||
|
|
||||
LL | *y += 1; // Success
|
||||
| ^^^^^^^
|
||||
= help: this corresponds to an activation
|
||||
help: the conflicting tag <TAG> then transitioned from Active to Frozen due to a foreign read access at offsets [0x0..0x1]
|
||||
= help: this corresponds to the first write to a 2-phase borrowed mutable reference
|
||||
help: the conflicting tag <TAG> later transitioned to Frozen due to a foreign read access at offsets [0x0..0x1]
|
||||
--> $DIR/alternate-read-write.rs:LL:CC
|
||||
|
|
||||
LL | let _val = *x;
|
||||
|
@ -17,19 +17,7 @@ help: the conflicting tag <TAG> was created here, in the initial state Reserved
|
||||
|
|
||||
LL | let rmut = &mut *addr_of_mut!(data[0..6]);
|
||||
| ^^^^
|
||||
help: the conflicting tag <TAG> then transitioned from Reserved to Active due to a child write access at offsets [0x5..0x6]
|
||||
--> $DIR/error-range.rs:LL:CC
|
||||
|
|
||||
LL | rmut[5] += 1;
|
||||
| ^^^^^^^^^^^^
|
||||
= help: this corresponds to an activation
|
||||
help: the conflicting tag <TAG> then transitioned from Active to Frozen due to a foreign read access at offsets [0x5..0x6]
|
||||
--> $DIR/error-range.rs:LL:CC
|
||||
|
|
||||
LL | let _v = data[5];
|
||||
| ^^^^^^^
|
||||
= help: this corresponds to a loss of write permissions
|
||||
help: the conflicting tag <TAG> then transitioned from Frozen to Disabled due to a foreign write access at offsets [0x5..0x6]
|
||||
help: the conflicting tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x5..0x6]
|
||||
--> $DIR/error-range.rs:LL:CC
|
||||
|
|
||||
LL | data[5] = 1;
|
||||
|
@ -17,7 +17,7 @@ help: the conflicting tag <TAG> was created here, in the initial state Reserved
|
||||
|
|
||||
LL | pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ^
|
||||
help: the conflicting tag <TAG> then transitioned from Reserved to Frozen due to a foreign read access at offsets [0x0..0x1]
|
||||
help: the conflicting tag <TAG> later transitioned to Frozen due to a foreign read access at offsets [0x0..0x1]
|
||||
--> RUSTLIB/core/src/ptr/mod.rs:LL:CC
|
||||
|
|
||||
LL | crate::intrinsics::read_via_copy(src)
|
||||
|
@ -11,13 +11,13 @@ help: the accessed tag <TAG> was created here, in the initial state Reserved
|
||||
|
|
||||
LL | let mref = &mut root;
|
||||
| ^^^^^^^^^
|
||||
help: the accessed tag <TAG> then transitioned from Reserved to Active due to a child write access at offsets [0x0..0x1]
|
||||
help: the accessed tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x1]
|
||||
--> $DIR/read-to-local.rs:LL:CC
|
||||
|
|
||||
LL | *ptr = 0; // Write
|
||||
| ^^^^^^^^
|
||||
= help: this corresponds to an activation
|
||||
help: the accessed tag <TAG> then transitioned from Active to Frozen due to a foreign read access at offsets [0x0..0x1]
|
||||
= help: this corresponds to the first write to a 2-phase borrowed mutable reference
|
||||
help: the accessed tag <TAG> later transitioned to Frozen due to a foreign read access at offsets [0x0..0x1]
|
||||
--> $DIR/read-to-local.rs:LL:CC
|
||||
|
|
||||
LL | assert_eq!(root, 0); // Parent Read
|
||||
|
@ -17,12 +17,6 @@ help: the strongly protected tag <TAG> was created here, in the initial state Re
|
||||
|
|
||||
LL | fn inner(x: &mut i32, f: fn(&mut i32)) {
|
||||
| ^
|
||||
help: the strongly protected tag <TAG> then transitioned from Reserved to Active due to a child write access at offsets [0x0..0x4]
|
||||
--> $DIR/strongly-protected.rs:LL:CC
|
||||
|
|
||||
LL | drop(unsafe { Box::from_raw(raw) });
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= help: this corresponds to an activation
|
||||
= note: BACKTRACE (of the first span):
|
||||
= note: inside `std::alloc::dealloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
|
||||
= note: inside `<std::alloc::Global as std::alloc::Allocator>::deallocate` at RUSTLIB/alloc/src/alloc.rs:LL:CC
|
||||
|
@ -18,7 +18,7 @@ LL | | *inner = 42;
|
||||
LL | | n
|
||||
LL | | });
|
||||
| |______^
|
||||
help: the accessed tag <TAG> then transitioned from Reserved to Disabled due to a foreign write access at offsets [0x0..0x8]
|
||||
help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x8]
|
||||
--> $DIR/write-during-2phase.rs:LL:CC
|
||||
|
|
||||
LL | *inner = 42;
|
||||
|
@ -31,7 +31,7 @@ fn main() {
|
||||
}
|
||||
|
||||
fn host_to_target_path(path: String) -> PathBuf {
|
||||
use std::ffi::{c_char, CStr, CString};
|
||||
use std::ffi::{CStr, CString};
|
||||
|
||||
let path = CString::new(path).unwrap();
|
||||
let mut out = Vec::with_capacity(1024);
|
||||
|
Loading…
Reference in New Issue
Block a user