Here is the simplest code I could mimick a strange Miri error for. It may look hacky, but it's a lot simpler than the original BTreeMap code already.
use std::ptr::NonNull;
struct S {
a: i32,
b: i32,
}
struct Holder {
s: S,
}
struct Proxy<'a> {
ref_a: &'a mut i32,
ptr_s: NonNull<S>,
}
fn point_me_to(s: &mut S) -> NonNull<S> {
unsafe { NonNull::new_unchecked(s as *mut _) }
}
impl S {
fn gimme_an_a(&mut self) -> &mut i32 {
&mut self.a
}
}
impl Holder {
fn make_proxy(&mut self) -> Proxy {
let ref_s = &mut self.s;
let ptr_s = point_me_to(ref_s);
let ptr_s = unsafe { NonNull::new_unchecked(ref_s as *mut _) };
Proxy { ref_a: ref_s.gimme_an_a(), ptr_s }
}
}
impl Proxy<'_> {
fn change(mut self) {
*self.ref_a *= 5;
unsafe {
self.ptr_s.as_mut().b *= 7;
}
}
}
fn main() {
let mut h = Holder {
s: S { a: 2, b: 3 },
};
let p = h.make_proxy();
p.change();
assert_eq!(h.s.a, 10);
assert_eq!(h.s.b, 21);
}
Building gives an obvious warning, but that's not what my question is about. Remove the 2nd definition of ptr_s
, which is the same as the 1st one inlined anyway, and the warning is gone. But then running with Miri starts complaining: "Undefined Behavior: no item granting read access to tag found in borrow stack.". Regardless of how ptr_s
is obtained, the code seems to run fine without Miri's scrutiny, not just this example, but plenty of btree tests mocking about with similar code, so I'm pretty sure that point_me_to
is not returning a plainly wrong pointer. So what is wrong about point_me_to
?
It doesn't happen when I replace the NonNull with a raw pointer, and not when I cut out the Holder level from the example.