189 lines
5.3 KiB
Rust
189 lines
5.3 KiB
Rust
// compile-flags: -Zmiri-tag-raw-pointers
|
|
use std::ptr;
|
|
|
|
// Test various stacked-borrows-related things.
|
|
fn main() {
|
|
read_does_not_invalidate1();
|
|
read_does_not_invalidate2();
|
|
mut_raw_then_mut_shr();
|
|
mut_shr_then_mut_raw();
|
|
mut_raw_mut();
|
|
partially_invalidate_mut();
|
|
drop_after_sharing();
|
|
direct_mut_to_const_raw();
|
|
two_raw();
|
|
shr_and_raw();
|
|
disjoint_mutable_subborrows();
|
|
raw_ref_to_part();
|
|
array_casts();
|
|
}
|
|
|
|
// Make sure that reading from an `&mut` does, like reborrowing to `&`,
|
|
// NOT invalidate other reborrows.
|
|
fn read_does_not_invalidate1() {
|
|
fn foo(x: &mut (i32, i32)) -> &i32 {
|
|
let xraw = x as *mut (i32, i32);
|
|
let ret = unsafe { &(*xraw).1 };
|
|
let _val = x.1; // we just read, this does NOT invalidate the reborrows.
|
|
ret
|
|
}
|
|
assert_eq!(*foo(&mut (1, 2)), 2);
|
|
}
|
|
// Same as above, but this time we first create a raw, then read from `&mut`
|
|
// and then freeze from the raw.
|
|
fn read_does_not_invalidate2() {
|
|
fn foo(x: &mut (i32, i32)) -> &i32 {
|
|
let xraw = x as *mut (i32, i32);
|
|
let _val = x.1; // we just read, this does NOT invalidate the raw reborrow.
|
|
let ret = unsafe { &(*xraw).1 };
|
|
ret
|
|
}
|
|
assert_eq!(*foo(&mut (1, 2)), 2);
|
|
}
|
|
|
|
// Escape a mut to raw, then share the same mut and use the share, then the raw.
|
|
// That should work.
|
|
fn mut_raw_then_mut_shr() {
|
|
let mut x = 2;
|
|
let xref = &mut x;
|
|
let xraw = &mut *xref as *mut _;
|
|
let xshr = &*xref;
|
|
assert_eq!(*xshr, 2);
|
|
unsafe { *xraw = 4; }
|
|
assert_eq!(x, 4);
|
|
}
|
|
|
|
// Create first a shared reference and then a raw pointer from a `&mut`
|
|
// should permit mutation through that raw pointer.
|
|
fn mut_shr_then_mut_raw() {
|
|
let xref = &mut 2;
|
|
let _xshr = &*xref;
|
|
let xraw = xref as *mut _;
|
|
unsafe { *xraw = 3; }
|
|
assert_eq!(*xref, 3);
|
|
}
|
|
|
|
// Ensure that if we derive from a mut a raw, and then from that a mut,
|
|
// and then read through the original mut, that does not invalidate the raw.
|
|
// This shows that the read-exception for `&mut` applies even if the `Shr` item
|
|
// on the stack is not at the top.
|
|
fn mut_raw_mut() {
|
|
let mut x = 2;
|
|
{
|
|
let xref1 = &mut x;
|
|
let xraw = xref1 as *mut _;
|
|
let _xref2 = unsafe { &mut *xraw };
|
|
let _val = *xref1;
|
|
unsafe { *xraw = 4; }
|
|
// we can now use both xraw and xref1, for reading
|
|
assert_eq!(*xref1, 4);
|
|
assert_eq!(unsafe { *xraw }, 4);
|
|
assert_eq!(*xref1, 4);
|
|
assert_eq!(unsafe { *xraw }, 4);
|
|
// we cannot use xref2; see `compile-fail/stacked-borows/illegal_read4.rs`
|
|
}
|
|
assert_eq!(x, 4);
|
|
}
|
|
|
|
fn partially_invalidate_mut() {
|
|
let data = &mut (0u8, 0u8);
|
|
let reborrow = &mut *data as *mut (u8, u8);
|
|
let shard = unsafe { &mut (*reborrow).0 };
|
|
data.1 += 1; // the deref overlaps with `shard`, but that is ok; the access does not overlap.
|
|
*shard += 1; // so we can still use `shard`.
|
|
assert_eq!(*data, (1, 1));
|
|
}
|
|
|
|
// Make sure that we can handle the situation where a loaction is frozen when being dropped.
|
|
fn drop_after_sharing() {
|
|
let x = String::from("hello!");
|
|
let _len = x.len();
|
|
}
|
|
|
|
// Make sure that coercing &mut T to *const T produces a writeable pointer.
|
|
fn direct_mut_to_const_raw() {
|
|
// TODO: This is currently disabled, waiting on a decision on <https://github.com/rust-lang/rust/issues/56604>
|
|
/*let x = &mut 0;
|
|
let y: *const i32 = x;
|
|
unsafe { *(y as *mut i32) = 1; }
|
|
assert_eq!(*x, 1);
|
|
*/
|
|
}
|
|
|
|
// Make sure that we can create two raw pointers from a mutable reference and use them both.
|
|
fn two_raw() { unsafe {
|
|
let x = &mut 0;
|
|
let y1 = x as *mut _;
|
|
let y2 = x as *mut _;
|
|
*y1 += 2;
|
|
*y2 += 1;
|
|
} }
|
|
|
|
// Make sure that creating a *mut does not invalidate existing shared references.
|
|
fn shr_and_raw() { unsafe {
|
|
use std::mem;
|
|
let x = &mut 0;
|
|
let y1: &i32 = mem::transmute(&*x); // launder lifetimes
|
|
let y2 = x as *mut _;
|
|
let _val = *y1;
|
|
*y2 += 1;
|
|
} }
|
|
|
|
fn disjoint_mutable_subborrows() {
|
|
struct Foo {
|
|
a: String,
|
|
b: Vec<u32>,
|
|
}
|
|
|
|
unsafe fn borrow_field_a<'a>(this:*mut Foo) -> &'a mut String {
|
|
&mut (*this).a
|
|
}
|
|
|
|
unsafe fn borrow_field_b<'a>(this:*mut Foo) -> &'a mut Vec<u32> {
|
|
&mut (*this).b
|
|
}
|
|
|
|
let mut foo = Foo {
|
|
a: "hello".into(),
|
|
b: vec![0,1,2],
|
|
};
|
|
|
|
let ptr = &mut foo as *mut Foo;
|
|
|
|
let a = unsafe{ borrow_field_a(ptr) };
|
|
let b = unsafe{ borrow_field_b(ptr) };
|
|
b.push(4);
|
|
a.push_str(" world");
|
|
eprintln!("{:?} {:?}", a, b);
|
|
}
|
|
|
|
fn raw_ref_to_part() {
|
|
struct Part {
|
|
_lame: i32,
|
|
}
|
|
|
|
#[repr(C)]
|
|
struct Whole {
|
|
part: Part,
|
|
extra: i32,
|
|
}
|
|
|
|
let it = Box::new(Whole { part: Part { _lame: 0 }, extra: 42 });
|
|
let whole = ptr::addr_of_mut!(*Box::leak(it));
|
|
let part = unsafe { ptr::addr_of_mut!((*whole).part) };
|
|
let typed = unsafe { &mut *(part as *mut Whole) };
|
|
assert!(typed.extra == 42);
|
|
drop(unsafe { Box::from_raw(whole) });
|
|
}
|
|
|
|
/// When casting an array reference to a raw element ptr, that should cover the whole array.
|
|
fn array_casts() {
|
|
let mut x: [usize; 2] = [0, 0];
|
|
let p = &mut x as *mut usize;
|
|
unsafe { *p.add(1) = 1; }
|
|
|
|
let x: [usize; 2] = [0, 1];
|
|
let p = &x as *const usize;
|
|
assert_eq!(unsafe { *p.add(1) }, 1);
|
|
}
|