Closed aidanhs closed 2 years ago
Not really related, but since this issue is open: is there a litmus test for converting *mut
to &mut
(i.e. reverse of https://github.com/nikomatsakis/rust-memory-model/blob/master/litmus_tests/ffi_ref_mut_to_star_mut.md) and what invariants that implies?
For example, someone might have multiple aliasing *mut
pointers that they cast to &mut
for the duration of a function for convenience of calling methods on them. The correct solution is probably to make the whole function unsafe, but in practice I believe that this has always been permitted by rust because noalias was only ever added to function parameters - is there a chance rust may want noalias annotations on variables, which could prohibit this casting?
A thought on my original case: rust needs to permit things like
fn y() {
let mut x = vec![1usize, 2, 3];
let p = &mut x[1] as *mut _;
unsafe { ffi(p) };
}
unsafe fn ffi(p: *mut usize) {
*p.offset(-1) = 3;
*p.offset(0) = 2;
*p.offset(1) = 1;
}
for ffi. So creating a pointer from a mutable borrow of a vec should probably give access to the whole vec, which means my initial example should be fine.
The next question for me is how far 'upwards' the permissible access goes. If there are four functions on the stack, each having done a split_at_mut on a slice and passed part of it down, when the final function calls into an ffi function with a pointer to a single element of the slice can the compiler assume anything about which elements of the whole slice are accessed?
Well, it works with a safe pointer:
fn x() -> Vec<usize> {
let mut x = vec![1, 2, 3, 4, 5, 6];
{ // extra block only needed because of lexical lifetimes
let mut p = &mut 0;
for v in x.iter_mut() {
if *v % 2 == 0 {
*p = *v;
}
p = v;
}
}
x
}
fn main() { println!("{:?}", x()); }
The next question for me is how far 'upwards' the permissible access goes. If there are four functions on the stack, each having done a split_at_mut on a slice and passed part of it down, when the final function calls into an ffi function with a pointer to a single element of the slice can the compiler assume anything about which elements of the whole slice are accessed?
That's where the "tootsie pop model" comes into play - if you are passing raw pointers around, we can't second-guess you, but if you wrap a safe interface around it, we will.
This same pattern arose at various points in btree iterators, iirc. It may still.
That's where the "tootsie pop model" comes into play - if you are passing raw pointers around, we can't second-guess you, but if you wrap a safe interface around it, we will.
I admit I've warmed to it since first reading about it, and I think I'd be happy with it subject to having sufficient knobs to twiddle to carefully opt into rustc assumptions. I know this has been mentioned before.
Closing as being (preliminarily) answered by Stacked Borrows. The code given above is fine, in particular since basically the same can be implemented without raw pointers.
Probably covered by the 'key question' in https://github.com/nikomatsakis/rust-memory-model/blob/master/litmus_tests/overlapping_ref_mut_star_mut.md, but this is something I've encountered recently so thought I'd raise it.
Is the compiler allowed to return
vec![1, 2, 3, 4, 5, 6]
(e.g. generating a new vector after the loop and pointer writes) because it cannot see any change occurring through the mutable reference to the overall vec? Or is this something you could get away with because you're mutating the previous elements of the vec (without a direct mutable reference to them)?(in the code that I'm looking at, this is split across 3 different functions for 1) the traversal over a custom tree-like structure, 2) storing pointers into the structure and 3) mutation of pointers based on a condition. So using a function as the maximum boundary of unsafety wouldn't be sufficient to support it)