-
Notifications
You must be signed in to change notification settings - Fork 349
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Stacked borrows: Mutable reference pops stack when created in some cases #2082
Comments
This code: I suspect you're saying "sometimes" because sometimes you're reborrowing other pointers. |
What i find confusing and astonishing, is that fn main() {
let mut queue = Queue::new();
let sender = unsafe { queue.get_sender() };
let queue = queue.ref_mut();
queue.recv();
sender.send();
} does not produce an error. I thought that it would desugar to this: fn main() {
let mut queue = Queue::new();
let sender = unsafe { Queue::get_sender(&queue) };
let queue = Queue::ref_mut(&mut queue);
Queue::recv(queue);
Sender::send(&sender);
} But in fact this code also gives the miri error that the code with @eivindbergem you are not allowed to simultaneously have an |
I don't think that can be it just on its own. Consider that EDIT: @y86-dev beat me to it More info: I think it must be two phase borrows, because the MIR for the method call looks like _5 = &mut _1;
Retag([2phase] _5);
_4 = Queue::ref_mut(move _5) -> bb3; instead of for the mutable borrow: _6 = &mut _1;
Retag(_6);
_5 = &mut (*_6);
Retag([2phase] _5);
_4 = Queue::ref_mut(move _5) -> bb3; |
I'm still trying to wrap my head around stacked borrows, so I thought I could get away with this, but I guess not. I don't actually need the queue to be mutable, but I want the queue to be member of another struct that is mutable. Because of static allocation, I can't wrap it in an |
I am guessing that you are using some kind of synchronization wrapper, so you could just do it like this: pub struct QueueAndData {
data: SyncWrapper<Data>,
queue: Queue,
}
impl QueueAndData {
pub fn do_foo(&self) {
self.data.lock().do_foo();
}
pub fn do_foobar(&self) {
self.data.lock().do_foo();
self.queue.do_bar();
}
}
pub static QUEUE_AND_DATA: QueueAndData = ...; You just need to be careful, that queue is now no longer synced with the SyncWrapper, but i guess that queue is doing some syncing of its own. But as you mentioned, you could also declare two statics which might be simpler at the definition site. Another solution would be to make |
Thanks for the help! I'll close this as the issue is on my end. |
@oli-obk (and anyone else) do you think that the rules for two phase borrows should be adjusted? Because in this case just taking We could also add a lint in miri, when a two phase borrow is reserved (i have no idea, if this information is available in MIR), but there are conflicting shared borrows similar to how the borrow checker does it. |
I am not sure what the issue is here exactly, but we should make sure we know why two phase borrows allow this code and whether that is sound. If it is sound (because the borrow is delayed to the actual use site), then I'm wondering why we can't do something similar for the plain mutable borrow. |
Two-phase borrows are basically treated like (tagged) raw pointers by Miri. They allow all sorts of funky aliasing so Stacked Borrows can't really do any better. Maybe future aliasing models can... |
Miri implements Stacked Borrows as intended here. So closing in favor of rust-lang/unsafe-code-guidelines#85. |
I'm working on a statically allocated MPSC queue for embedded systems, and I've run into a miri issue. From my understanding of stacked borrows, reading from a mutable reference shouldn't pop from the stack. However, in my case the stack is popped when creating the reference, but only in some cases.
I've created a minimal example shown below.
Shared code:
This is fine:
And this is also fine:
But this is not:
Error (with tag tracking):
The text was updated successfully, but these errors were encountered: