In issue 73987 discusses unsafe { &raw const (*ptr).data } as the (only?) way to get a raw pointer to a field. But it doesn't seem to be raw enough for this situation. We have a mutable reference to a part, and we want a reference to another part.
#![feature(raw_ref_op)]
struct Data { a: i32, b: i32 }
#[repr(transparent)]
struct S { data: Data }
fn main() {
let miri_have_mercy = 0;
let mut s = S { data: Data { a: 4, b: 2 } };
let ptr = &mut s as *mut S;
let data = unsafe { &mut (*ptr).data };
let ra = &mut data.a;
let data = match miri_have_mercy {
0 => unsafe { &raw const (*ptr).data },
1 => unsafe { &raw mut (*ptr).data },
_ => ptr as *const Data,
};
let rb = unsafe { &(*data).b };
println!("{}{}", *ra, *rb);
}
This outputs the correct answer, but has Miri complaining on the *ra in the last line about "Undefined Behavior: trying to reborrow for SharedReadOnly, but parent tag <3686> does not have an appropriate item in the borrow stack". MIRIFLAGS=-Zmiri-track-pointer-tag=3686 has Miri explaining that the so-called raw field access "popped tracked tag for item [Unique for <3686>]". Where is the undefined behavior in this code?
Or how can we legally get to a field's address? Changing the miri_have_mercy constant to pick either of two alternatives makes Miri quiet again, but I know that Miri closes its eyes for some forms of abuse.
note: tracking was triggered
--> <anon>:24:23
|
24 | 0 => unsafe { &raw const (*ptr).data },
| ^^^^^^^^^^^^^^^^^^^^^^ popped tracked tag for item [Unique for <3686>]
|
= note: inside `main` at <anon>:24:23
= note: inside `<fn() as std::ops::FnOnce<()>>::call_once - shim(fn())` at /home/bjorn/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:227:5
= note: inside `std::sys_common::backtrace::__rust_begin_short_backtrace::<fn(), ()>` at /home/bjorn/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys_common/backtrace.rs:125:18
= note: inside closure at /home/bjorn/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:66:18
= note: inside `std::ops::function::impls::<impl std::ops::FnOnce<()> for &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>::call_once` at /home/bjorn/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:259:13
= note: inside `std::panicking::try::do_call::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at /home/bjorn/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:381:40
= note: inside `std::panicking::try::<i32, &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>` at /home/bjorn/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:345:19
= note: inside `std::panic::catch_unwind::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at /home/bjorn/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:382:14
= note: inside `std::rt::lang_start_internal` at /home/bjorn/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:51:25
= note: inside `std::rt::lang_start::<()>` at /home/bjorn/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:65:5
error: Undefined Behavior: trying to reborrow for SharedReadOnly, but parent tag <3686> does not have an appropriate item in the borrow stack
--> <anon>:30:22
|
30 | println!("{}{}", *ra, *rb);
| ^^^ trying to reborrow for SharedReadOnly, but parent tag <3686> does not have an appropriate item in the borrow stack
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
= note: inside `main` at <anon>:30:22
= note: inside `<fn() as std::ops::FnOnce<()>>::call_once - shim(fn())` at /home/bjorn/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:227:5
= note: inside `std::sys_common::backtrace::__rust_begin_short_backtrace::<fn(), ()>` at /home/bjorn/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys_common/backtrace.rs:125:18
= note: inside closure at /home/bjorn/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:66:18
= note: inside `std::ops::function::impls::<impl std::ops::FnOnce<()> for &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>::call_once` at /home/bjorn/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:259:13
= note: inside `std::panicking::try::do_call::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at /home/bjorn/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:381:40
= note: inside `std::panicking::try::<i32, &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>` at /home/bjorn/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:345:19
= note: inside `std::panic::catch_unwind::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at /home/bjorn/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:382:14
= note: inside `std::rt::lang_start_internal` at /home/bjorn/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:51:25
= note: inside `std::rt::lang_start::<()>` at /home/bjorn/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:65:5
error: aborting due to previous error
When doing a reborrow &* and &raw const * are treated identical. They both require SharedReadOnly when the pointee is not frozen. &raw mut * on the other hand requires SharedReadWrite.