Why does raw field pointer access trigger Miri to pop a Unique?

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);
}

(Playground)

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.

For reference:

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

https://github.com/rust-lang/miri/blob/5f1182d04a161915df7b1d001ac7cc77fc7a2047/src/stacked_borrows.rs#L549

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.

Right, but that just means you cannot write to this pointer, which the code is not doing.

This might be another instance of

Yes it was, the weird difference here between &raw const and &raw mut also disappeared.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.