diff --git a/src/stacked_borrows.rs b/src/stacked_borrows.rs
index 067e3bb8445..6b851cd65b5 100644
--- a/src/stacked_borrows.rs
+++ b/src/stacked_borrows.rs
@@ -62,18 +62,7 @@ pub enum BorStackItem {
/// when there is no `UnsafeCell`.
Shr,
/// A barrier, tracking the function it belongs to by its index on the call stack
- #[allow(dead_code)] // for future use
- FnBarrier(usize)
-}
-
-impl BorStackItem {
- #[inline(always)]
- pub fn is_fn_barrier(self) -> bool {
- match self {
- BorStackItem::FnBarrier(_) => true,
- _ => false,
- }
- }
+ FnBarrier(CallId)
}
/// Extra per-location state
@@ -130,6 +119,10 @@ impl BarrierTracking {
pub fn end_call(&mut self, id: CallId) {
assert!(self.active_calls.remove(&id));
}
+
+ fn is_active(&self, id: CallId) -> bool {
+ self.active_calls.contains(&id)
+ }
}
/// Extra global machine state
@@ -178,7 +171,11 @@ impl<'tcx> Stack {
/// going to read or write.
/// Returns the index of the item we matched, `None` if it was the frozen one.
/// `kind` indicates which kind of reference is being dereferenced.
- fn deref(&self, bor: Borrow, kind: RefKind) -> Result, String> {
+ fn deref(
+ &self,
+ bor: Borrow,
+ kind: RefKind,
+ ) -> Result , String> {
// Exclude unique ref with frozen tag.
if let (RefKind::Unique, Borrow::Shr(Some(_))) = (kind, bor) {
return Err(format!("Encountered mutable reference with frozen tag ({:?})", bor));
@@ -200,7 +197,6 @@ impl<'tcx> Stack {
// If we got here, we have to look for our item in the stack.
for (idx, &itm) in self.borrows.iter().enumerate().rev() {
match (itm, bor) {
- (BorStackItem::FnBarrier(_), _) => break,
(BorStackItem::Uniq(itm_t), Borrow::Uniq(bor_t)) if itm_t == bor_t => {
// Found matching unique item. This satisfies U3.
return Ok(Some(idx))
@@ -209,21 +205,25 @@ impl<'tcx> Stack {
// Found matching shared/raw item.
return Ok(Some(idx))
}
- // Go on looking.
+ // Go on looking. We ignore barriers! When an `&mut` and an `&` alias,
+ // dereferencing the `&` is still possible (to reborrow), but doing
+ // an access is not.
_ => {}
}
}
// If we got here, we did not find our item. We have to error to satisfy U3.
- Err(format!(
- "Borrow being dereferenced ({:?}) does not exist on the stack, or is guarded by a barrier",
- bor
- ))
+ Err(format!("Borrow being dereferenced ({:?}) does not exist on the stack", bor))
}
/// Perform an actual memory access using `bor`. We do not know any types here
/// or whether things should be frozen, but we *do* know if this is reading
/// or writing.
- fn access(&mut self, bor: Borrow, is_write: bool) -> EvalResult<'tcx> {
+ fn access(
+ &mut self,
+ bor: Borrow,
+ is_write: bool,
+ barrier_tracking: &BarrierTracking,
+ ) -> EvalResult<'tcx> {
// Check if we can match the frozen "item".
// Not possible on writes!
if self.is_frozen() {
@@ -240,7 +240,12 @@ impl<'tcx> Stack {
// Pop the stack until we have something matching.
while let Some(&itm) = self.borrows.last() {
match (itm, bor) {
- (BorStackItem::FnBarrier(_), _) => break,
+ (BorStackItem::FnBarrier(call), _) if barrier_tracking.is_active(call) => {
+ return err!(MachineError(format!(
+ "Stopping looking for borrow being accessed ({:?}) because of barrier ({})",
+ bor, call
+ )))
+ }
(BorStackItem::Uniq(itm_t), Borrow::Uniq(bor_t)) if itm_t == bor_t => {
// Found matching unique item.
return Ok(())
@@ -265,7 +270,7 @@ impl<'tcx> Stack {
}
// If we got here, we did not find our item.
err!(MachineError(format!(
- "Borrow being accessed ({:?}) does not exist on the stack, or is guarded by a barrier",
+ "Borrow being accessed ({:?}) does not exist on the stack",
bor
)))
}
@@ -275,18 +280,21 @@ impl<'tcx> Stack {
/// is met: We cannot push `Uniq` onto frozen stacks.
/// `kind` indicates which kind of reference is being created.
fn create(&mut self, bor: Borrow, kind: RefKind) {
- // First, push the item. We do this even if we will later freeze, because we
- // will allow mutation of shared data at the expense of unfreezing.
if self.frozen_since.is_some() {
- // A frozen location, this should be impossible!
- bug!("We should never try pushing to a frozen stack");
+ // A frozen location? Possible if we create a barrier, then push again.
+ assert!(bor.is_shared(), "We should never try creating a unique borrow for a frozen stack");
+ trace!("create: Not doing anything on frozen location");
+ return;
}
- // First, push.
+ // First, push. We do this even if we will later freeze, because we
+ // will allow mutation of shared data at the expense of unfreezing.
let itm = match bor {
Borrow::Uniq(t) => BorStackItem::Uniq(t),
Borrow::Shr(_) => BorStackItem::Shr,
};
if *self.borrows.last().unwrap() == itm {
+ // This is just an optimization, no functional change: Avoid stacking
+ // multiple `Shr` on top of each other.
assert!(bor.is_shared());
trace!("create: Sharing a shared location is a NOP");
} else {
@@ -304,6 +312,21 @@ impl<'tcx> Stack {
self.frozen_since = Some(bor_t);
}
}
+
+ /// Add a barrier
+ fn barrier(&mut self, call: CallId) {
+ let itm = BorStackItem::FnBarrier(call);
+ if *self.borrows.last().unwrap() == itm {
+ // This is just an optimization, no functional change: Avoid stacking
+ // multiple identical barriers on top of each other.
+ // This can happen when a function receives several shared references
+ // that overlap.
+ trace!("barrier: Avoiding redundant extra barrier");
+ } else {
+ trace!("barrier: Pushing barrier for call {}", call);
+ self.borrows.push(itm);
+ }
+ }
}
/// Higher-level per-location operations: deref, access, reborrow.
@@ -330,6 +353,7 @@ impl<'tcx> Stacks {
ptr: Pointer,
size: Size,
is_write: bool,
+ barrier_tracking: &BarrierTracking,
) -> EvalResult<'tcx> {
trace!("{} access of tag {:?}: {:?}, size {}",
if is_write { "read" } else { "write" },
@@ -339,7 +363,7 @@ impl<'tcx> Stacks {
// are no accesses through other references, not even reads.
let mut stacks = self.stacks.borrow_mut();
for stack in stacks.iter_mut(ptr.offset, size) {
- stack.access(ptr.tag, is_write)?;
+ stack.access(ptr.tag, is_write, barrier_tracking)?;
}
Ok(())
}
@@ -350,12 +374,19 @@ impl<'tcx> Stacks {
&self,
ptr: Pointer,
size: Size,
+ mut barrier: Option,
new_bor: Borrow,
new_kind: RefKind,
+ barrier_tracking: &BarrierTracking,
) -> EvalResult<'tcx> {
assert_eq!(new_bor.is_unique(), new_kind == RefKind::Unique);
trace!("reborrow for tag {:?} to {:?} as {:?}: {:?}, size {}",
ptr.tag, new_bor, new_kind, ptr, size.bytes());
+ if new_kind == RefKind::Raw {
+ // No barrier for raw, including `&UnsafeCell`. They can rightfully
+ // alias with `&mut`.
+ barrier = None;
+ }
let mut stacks = self.stacks.borrow_mut();
for stack in stacks.iter_mut(ptr.offset, size) {
// Access source `ptr`, create new ref.
@@ -364,21 +395,25 @@ impl<'tcx> Stacks {
// the stack than the one we come from, just use that.
// IOW, we check if `new_bor` *already* is "derived from" `ptr.tag`.
// This also checks frozenness, if required.
- let bor_redundant = match (ptr_idx, stack.deref(new_bor, new_kind)) {
- // If the new borrow works with the frozen item, or else if it lives
- // above the old one in the stack, our job here is done.
- (_, Ok(None)) => true,
- (Some(ptr_idx), Ok(Some(new_idx))) if new_idx >= ptr_idx => true,
- // Otherwise we need to create a new borrow.
- _ => false,
- };
+ let bor_redundant = barrier.is_none() &&
+ match (ptr_idx, stack.deref(new_bor, new_kind)) {
+ // If the new borrow works with the frozen item, or else if it lives
+ // above the old one in the stack, our job here is done.
+ (_, Ok(None)) => true,
+ (Some(ptr_idx), Ok(Some(new_idx))) if new_idx >= ptr_idx => true,
+ // Otherwise we need to create a new borrow.
+ _ => false,
+ };
if bor_redundant {
assert!(new_bor.is_shared(), "A unique reborrow can never be redundant");
trace!("reborrow is redundant");
continue;
}
// We need to do some actual work.
- stack.access(ptr.tag, new_kind == RefKind::Unique)?;
+ stack.access(ptr.tag, new_kind == RefKind::Unique, barrier_tracking)?;
+ if let Some(call) = barrier {
+ stack.barrier(call);
+ }
stack.create(new_bor, new_kind);
}
Ok(())
@@ -405,7 +440,7 @@ impl AllocationExtra for Stacks {
ptr: Pointer,
size: Size,
) -> EvalResult<'tcx> {
- alloc.extra.access(ptr, size, /*is_write*/false)
+ alloc.extra.access(ptr, size, /*is_write*/false, &*alloc.extra.barrier_tracking.borrow())
}
#[inline(always)]
@@ -414,7 +449,7 @@ impl AllocationExtra for Stacks {
ptr: Pointer,
size: Size,
) -> EvalResult<'tcx> {
- alloc.extra.access(ptr, size, /*is_write*/true)
+ alloc.extra.access(ptr, size, /*is_write*/true, &*alloc.extra.barrier_tracking.borrow())
}
#[inline(always)]
@@ -424,19 +459,18 @@ impl AllocationExtra for Stacks {
size: Size,
) -> EvalResult<'tcx> {
// This is like mutating
- alloc.extra.access(ptr, size, /*is_write*/true)
+ alloc.extra.access(ptr, size, /*is_write*/true, &*alloc.extra.barrier_tracking.borrow())
// FIXME: Error out of there are any barriers?
}
}
impl<'tcx> Stacks {
/// Pushes the first item to the stacks.
- pub fn first_item(
+ pub(crate) fn first_item(
&mut self,
itm: BorStackItem,
size: Size
) {
- assert!(!itm.is_fn_barrier());
for stack in self.stacks.get_mut().iter_mut(Size::ZERO, size) {
assert!(stack.borrows.len() == 1);
assert_eq!(stack.borrows.pop().unwrap(), BorStackItem::Shr);
@@ -466,6 +500,7 @@ pub trait EvalContextExt<'tcx> {
&mut self,
place: MPlaceTy<'tcx, Borrow>,
size: Size,
+ fn_barrier: bool,
new_bor: Borrow
) -> EvalResult<'tcx, Pointer>;
@@ -474,6 +509,7 @@ pub trait EvalContextExt<'tcx> {
&mut self,
ptr: ImmTy<'tcx, Borrow>,
mutbl: Mutability,
+ fn_barrier: bool,
) -> EvalResult<'tcx, Immediate>;
fn retag(
@@ -566,7 +602,7 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> {
place: MPlaceTy<'tcx, Borrow>,
size: Size,
) -> EvalResult<'tcx> {
- self.reborrow(place, size, Borrow::default())?;
+ self.reborrow(place, size, /*fn_barrier*/ false, Borrow::default())?;
Ok(())
}
@@ -574,10 +610,12 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> {
&mut self,
place: MPlaceTy<'tcx, Borrow>,
size: Size,
+ fn_barrier: bool,
new_bor: Borrow
) -> EvalResult<'tcx, Pointer> {
let ptr = place.ptr.to_ptr()?;
let new_ptr = Pointer::new_with_tag(ptr.alloc_id, ptr.offset, new_bor);
+ let barrier = if fn_barrier { Some(self.frame().extra) } else { None };
trace!("reborrow: Creating new reference for {:?} (pointee {}): {:?}",
ptr, place.layout.ty, new_bor);
@@ -589,12 +627,12 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> {
// Reference that cares about freezing. We need a frozen-sensitive reborrow.
self.visit_freeze_sensitive(place, size, |cur_ptr, size, frozen| {
let kind = if frozen { RefKind::Frozen } else { RefKind::Raw };
- alloc.extra.reborrow(cur_ptr, size, new_bor, kind)
+ alloc.extra.reborrow(cur_ptr, size, barrier, new_bor, kind, &*self.memory().extra.borrow())
})?;
} else {
// Just treat this as one big chunk.
let kind = if new_bor.is_unique() { RefKind::Unique } else { RefKind::Raw };
- alloc.extra.reborrow(ptr, size, new_bor, kind)?;
+ alloc.extra.reborrow(ptr, size, barrier, new_bor, kind, &*self.memory().extra.borrow())?;
}
Ok(new_ptr)
}
@@ -603,6 +641,7 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> {
&mut self,
val: ImmTy<'tcx, Borrow>,
mutbl: Mutability,
+ fn_barrier: bool,
) -> EvalResult<'tcx, Immediate> {
// We want a place for where the ptr *points to*, so we get one.
let place = self.ref_to_mplace(val)?;
@@ -622,7 +661,7 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> {
};
// Reborrow.
- let new_ptr = self.reborrow(place, size, new_bor)?;
+ let new_ptr = self.reborrow(place, size, fn_barrier, new_bor)?;
// Return new ptr
let new_place = MemPlace { ptr: Scalar::Ptr(new_ptr), ..*place };
@@ -631,11 +670,9 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> {
fn retag(
&mut self,
- _fn_entry: bool,
+ fn_entry: bool,
place: PlaceTy<'tcx, Borrow>
) -> EvalResult<'tcx> {
- // TODO: Honor `fn_entry`.
-
// We need a visitor to visit all references. However, that requires
// a `MemPlace`, so we have a fast path for reference types that
// avoids allocating.
@@ -648,18 +685,19 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> {
} {
// fast path
let val = self.read_immediate(self.place_to_op(place)?)?;
- let val = self.retag_reference(val, mutbl)?;
+ let val = self.retag_reference(val, mutbl, fn_entry)?;
self.write_immediate(val, place)?;
return Ok(());
}
let place = self.force_allocation(place)?;
- let mut visitor = RetagVisitor { ecx: self };
+ let mut visitor = RetagVisitor { ecx: self, fn_entry };
visitor.visit_value(place)?;
// The actual visitor
struct RetagVisitor<'ecx, 'a, 'mir, 'tcx> {
ecx: &'ecx mut MiriEvalContext<'a, 'mir, 'tcx>,
+ fn_entry: bool,
}
impl<'ecx, 'a, 'mir, 'tcx>
MutValueVisitor<'a, 'mir, 'tcx, Evaluator<'tcx>>
@@ -684,7 +722,7 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> {
_ => return Ok(()), // nothing to do
};
let val = self.ecx.read_immediate(place.into())?;
- let val = self.ecx.retag_reference(val, mutbl)?;
+ let val = self.ecx.retag_reference(val, mutbl, self.fn_entry)?;
self.ecx.write_immediate(val, place.into())?;
Ok(())
}
diff --git a/tests/compile-fail-fullmir/stacked_borrows/aliasing_mut1.rs b/tests/compile-fail-fullmir/stacked_borrows/aliasing_mut1.rs
index e812e13e702..b82901985b7 100644
--- a/tests/compile-fail-fullmir/stacked_borrows/aliasing_mut1.rs
+++ b/tests/compile-fail-fullmir/stacked_borrows/aliasing_mut1.rs
@@ -1,12 +1,17 @@
-// ignore-test validation_op is disabled
-
#![allow(unused_variables)]
-mod safe {
- pub fn safe(x: &mut i32, y: &mut i32) {} //~ ERROR: in conflict with lock WriteLock
-}
+use std::mem;
+
+pub fn safe(x: &mut i32, y: &mut i32) {} //~ ERROR barrier
fn main() {
- let x = &mut 0 as *mut _;
- unsafe { safe::safe(&mut *x, &mut *x) };
+ let mut x = 0;
+ let xraw: *mut i32 = unsafe { mem::transmute(&mut x) };
+ // We need to apply some tricky to be able to call `safe` with two mutable references
+ // with the same tag: We transmute both the fn ptr (to take raw ptrs) and the argument
+ // (to be raw, but still have the unique tag).
+ let safe_raw: fn(x: *mut i32, y: *mut i32) = unsafe {
+ mem::transmute::(safe)
+ };
+ safe_raw(xraw, xraw);
}
diff --git a/tests/compile-fail-fullmir/stacked_borrows/aliasing_mut2.rs b/tests/compile-fail-fullmir/stacked_borrows/aliasing_mut2.rs
index 36ebcc2b4ac..69caddfa8c3 100644
--- a/tests/compile-fail-fullmir/stacked_borrows/aliasing_mut2.rs
+++ b/tests/compile-fail-fullmir/stacked_borrows/aliasing_mut2.rs
@@ -1,12 +1,17 @@
-// ignore-test validation_op is disabled
-
#![allow(unused_variables)]
-mod safe {
- pub fn safe(x: &i32, y: &mut i32) {} //~ ERROR: in conflict with lock ReadLock
-}
+use std::mem;
+
+pub fn safe(x: &i32, y: &mut i32) {} //~ ERROR barrier
fn main() {
- let x = &mut 0 as *mut _;
- unsafe { safe::safe(&*x, &mut *x) };
+ let mut x = 0;
+ let xref = &mut x;
+ let xraw: *mut i32 = unsafe { mem::transmute_copy(&xref) };
+ let xshr = &*xref;
+ // transmute fn ptr around so that we can avoid retagging
+ let safe_raw: fn(x: *const i32, y: *mut i32) = unsafe {
+ mem::transmute::(safe)
+ };
+ safe_raw(xshr, xraw);
}
diff --git a/tests/compile-fail-fullmir/stacked_borrows/aliasing_mut3.rs b/tests/compile-fail-fullmir/stacked_borrows/aliasing_mut3.rs
index ad50fbd61b4..d37f9e63f60 100644
--- a/tests/compile-fail-fullmir/stacked_borrows/aliasing_mut3.rs
+++ b/tests/compile-fail-fullmir/stacked_borrows/aliasing_mut3.rs
@@ -1,12 +1,17 @@
-// ignore-test validation_op is disabled
-
#![allow(unused_variables)]
-mod safe {
- pub fn safe(x: &mut i32, y: &i32) {} //~ ERROR: in conflict with lock WriteLock
-}
+use std::mem;
+
+pub fn safe(x: &mut i32, y: &i32) {} //~ ERROR does not exist on the stack
fn main() {
- let x = &mut 0 as *mut _;
- unsafe { safe::safe(&mut *x, &*x) };
+ let mut x = 0;
+ let xref = &mut x;
+ let xraw: *mut i32 = unsafe { mem::transmute_copy(&xref) };
+ let xshr = &*xref;
+ // transmute fn ptr around so that we can avoid retagging
+ let safe_raw: fn(x: *mut i32, y: *const i32) = unsafe {
+ mem::transmute::(safe)
+ };
+ safe_raw(xraw, xshr);
}
diff --git a/tests/compile-fail-fullmir/stacked_borrows/aliasing_mut4.rs b/tests/compile-fail-fullmir/stacked_borrows/aliasing_mut4.rs
index a0f0a3cf975..bf65d6e2303 100644
--- a/tests/compile-fail-fullmir/stacked_borrows/aliasing_mut4.rs
+++ b/tests/compile-fail-fullmir/stacked_borrows/aliasing_mut4.rs
@@ -1,15 +1,19 @@
-// ignore-test validation_op is disabled
-
#![allow(unused_variables)]
-mod safe {
- use std::cell::Cell;
+use std::mem;
+use std::cell::Cell;
- // Make sure &mut UnsafeCell also has a lock to it
- pub fn safe(x: &mut Cell, y: &i32) {} //~ ERROR: in conflict with lock WriteLock
-}
+// Make sure &mut UnsafeCell also is exclusive
+pub fn safe(x: &i32, y: &mut Cell) {} //~ ERROR barrier
fn main() {
- let x = &mut 0 as *mut _;
- unsafe { safe::safe(&mut *(x as *mut _), &*x) };
+ let mut x = 0;
+ let xref = &mut x;
+ let xraw: *mut i32 = unsafe { mem::transmute_copy(&xref) };
+ let xshr = &*xref;
+ // transmute fn ptr around so that we can avoid retagging
+ let safe_raw: fn(x: *const i32, y: *mut Cell) = unsafe {
+ mem::transmute::), _>(safe)
+ };
+ safe_raw(xshr, xraw as *mut _);
}
diff --git a/tests/compile-fail-fullmir/stacked_borrows/box_exclusive_violation1.rs b/tests/compile-fail-fullmir/stacked_borrows/box_exclusive_violation1.rs
index bd0fec859d8..e8a182779ad 100644
--- a/tests/compile-fail-fullmir/stacked_borrows/box_exclusive_violation1.rs
+++ b/tests/compile-fail-fullmir/stacked_borrows/box_exclusive_violation1.rs
@@ -8,7 +8,7 @@ fn demo_mut_advanced_unique(mut our: Box) -> i32 {
unknown_code_2();
// We know this will return 5
- *our //~ ERROR does not exist on the stack
+ *our
}
// Now comes the evil context
@@ -21,7 +21,7 @@ fn unknown_code_1(x: &i32) { unsafe {
} }
fn unknown_code_2() { unsafe {
- *LEAK = 7;
+ *LEAK = 7; //~ ERROR barrier
} }
fn main() {
diff --git a/tests/compile-fail-fullmir/stacked_borrows/illegal_write1.rs b/tests/compile-fail-fullmir/stacked_borrows/illegal_write1.rs
index b106cc8dc40..d0a23cb4448 100644
--- a/tests/compile-fail-fullmir/stacked_borrows/illegal_write1.rs
+++ b/tests/compile-fail-fullmir/stacked_borrows/illegal_write1.rs
@@ -1,12 +1,9 @@
-fn evil(x: &u32) {
- // mutating shared ref without `UnsafeCell`
- let x : *mut u32 = x as *const _ as *mut _;
- unsafe { *x = 42; }
-}
-
fn main() {
let target = Box::new(42); // has an implicit raw
- let ref_ = &*target;
- evil(ref_); // invalidates shared ref, activates raw
- let _x = *ref_; //~ ERROR is not frozen
+ let xref = &*target;
+ {
+ let x : *mut u32 = xref as *const _ as *mut _;
+ unsafe { *x = 42; } // invalidates shared ref, activates raw
+ }
+ let _x = *xref; //~ ERROR is not frozen
}
diff --git a/tests/compile-fail-fullmir/stacked_borrows/invalidate_against_barrier1.rs b/tests/compile-fail-fullmir/stacked_borrows/invalidate_against_barrier1.rs
new file mode 100644
index 00000000000..fc0dbb9e131
--- /dev/null
+++ b/tests/compile-fail-fullmir/stacked_borrows/invalidate_against_barrier1.rs
@@ -0,0 +1,13 @@
+fn inner(x: *mut i32, _y: &mut i32) {
+ // If `x` and `y` alias, retagging is fine with this... but we really
+ // shouldn't be allowed to use `x` at all because `y` was assumed to be
+ // unique for the duration of this call.
+ let _val = unsafe { *x }; //~ ERROR barrier
+}
+
+fn main() {
+ let mut x = 0;
+ let xraw = &mut x as *mut _;
+ let xref = unsafe { &mut *xraw };
+ inner(xraw, xref);
+}
diff --git a/tests/compile-fail-fullmir/stacked_borrows/invalidate_against_barrier2.rs b/tests/compile-fail-fullmir/stacked_borrows/invalidate_against_barrier2.rs
new file mode 100644
index 00000000000..a080c0958e4
--- /dev/null
+++ b/tests/compile-fail-fullmir/stacked_borrows/invalidate_against_barrier2.rs
@@ -0,0 +1,13 @@
+fn inner(x: *mut i32, _y: &i32) {
+ // If `x` and `y` alias, retagging is fine with this... but we really
+ // shouldn't be allowed to write to `x` at all because `y` was assumed to be
+ // immutable for the duration of this call.
+ unsafe { *x = 0 }; //~ ERROR barrier
+}
+
+fn main() {
+ let mut x = 0;
+ let xraw = &mut x as *mut _;
+ let xref = unsafe { &*xraw };
+ inner(xraw, xref);
+}
diff --git a/tests/compile-fail-fullmir/stacked_borrows/mut_exclusive_violation1.rs b/tests/compile-fail-fullmir/stacked_borrows/mut_exclusive_violation1.rs
index fec699e35bc..3fe6b656742 100644
--- a/tests/compile-fail-fullmir/stacked_borrows/mut_exclusive_violation1.rs
+++ b/tests/compile-fail-fullmir/stacked_borrows/mut_exclusive_violation1.rs
@@ -21,7 +21,7 @@ fn unknown_code_1(x: &i32) { unsafe {
} }
fn unknown_code_2() { unsafe {
- *LEAK = 7; //~ ERROR does not exist on the stack
+ *LEAK = 7; //~ ERROR barrier
} }
fn main() {