Closed shdnx closed 5 days ago
Thanks for the report!
The aliasing model is not recursive, so this seems expected to me -- a &mut Box<T>
is a unique pointer to a Box
, but the inner T
only becomes unique once you actually manifest a Box<T>
.
Also correctly detected if I make the formation of &mut Counter before the method call explicit:
This is because with an explicit &mut counter
, you are doing the equivalent of a write to counter
(i.e. the Box
itself, not the integer inside). An implicit &mut
is a two-phase borrow and does not make such uniqueness assertions.
Also, note that if you want to make the implicit &mut
explicit, the result will look a bit different: counter.increment()
where counter: Box<Counter>
becomes (&mut *counter).increment()
, not (&mut counter).increment()
.
Thank you! I think I see what you mean: having a pointer to Box<T>
and having a pointer to the underlying T
are two different things, and do not alias. Which makes sense because they are two different memory locations.
It then indeed makes sense that a pointer to the Box<T>
is not invalidated by forming a mutable reference to the underlying T
. Except that I imagine that the only way to take a mutable reference to the underlying T
is by first taking a mutable reference to its containing Box
: deref_mut()
takes a &mut self
, and I expect that the *
operator is just syntactic sugar for calling deref_mut()
(or deref()
).
In other words, I'd expect that all of these are equivalent:
let mut counter = Box::new(Counter::new());
let ptr: *mut Box<Counter> = &mut counter;
counter.increment(); // 1
(*counter).increment(); // 2
(&mut *counter).increment(); // 3
counter.deref_mut().increment(); // 4
(&mut counter).deref_mut().increment(); // 5
But according to Miri, 1, 2, and 3 don't invalidate ptr
, but 4 and 5 do.
This is how Deref
and DerefMut
seem to be implemented for Box
:
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized, A: Allocator> Deref for Box<T, A> {
type Target = T;
fn deref(&self) -> &T {
&**self
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized, A: Allocator> DerefMut for Box<T, A> {
fn deref_mut(&mut self) -> &mut T {
&mut **self
}
}
Which either shoots a hole in my expectation that the *
operator is just sugar for deref()
/ deref_mut()
(since then this would be unbounded recursion)... or Box
is just magic?
Yeah Box deref is special, it does not go through DerefMut
(similar to how 1 + 2
does not go through Add
).
Thank you for the information and help! :) I will close this then, since it is not an issue in Miri.
Given:
This is correctly detected as UB:
But if
counter
is aBox<Counter>
instead, then no UB is reported because theincrement()
call seemingly is not detected as creating a&mut Counter
:However, if
ptr
is instead*mut Counter
, then UB is again correctly detected:Also correctly detected if I make the formation of
&mut Counter
before the method call explicit: