Description
On #181 when discussing whether or not provenance destroying operations can be elided, I brought this code up
extern"C"{
pub fn does_something(param: *mut usize);
}
#[repr(C)]
struct Interesting{
array: [usize;1], // prevents *pointer-interconvertibility* trick under lccc's model
exclusive_access: *mut ()
};
pub fn do_interesting_things(ptr: &mut Interesting){
let x = ptr.exclusive_access;
does_something(&mut ptr.array[0]);
ptr.exclusive_access =x;
}
For which the corresponding rust code would seem to rule out load-store elimination on ptr.exclusive_access
pub unsafe extern"C" fn does_something(ptr: *mut usize){
let ptr = std::transmute(std::transmute<usize>(ptr)) as *mut Interesting;
(*ptr).exclusive_access = ptr as *mut ();
}
While I was told that this code doesn't, replacing the first function with
pub fn do_interesting_things(ptr: &mut Interesting) {
let x = ptr.exclusive_access;
let y = &mut ptr.exclusive_access as *mut _;
does_something(&mut ptr.array[0]);
ptr.exclusive_access = x;
}
This seems odd to me. The fact that y
is created by do_interesting_things
inhibits optimizations accross the black-box does_something
, even though does_something
never recieves it, seems quite a bit off to me. In contrast, this is not permitted by C or C++, at all (if we replace the &mut
in ptr
with either Interesting* restrict
in C, or an analogous compiler-specific extension in C++, to exclude aliasing access on entry to do_interesting_things
).
In my head, pointers are pointers, reguardless of form (noting that references are pointers). It shouldn't be possible to use a pointer that was never recieved by a particular function to validate an operation in that function. This is particularily annoying because its clear through simple data-flow analysis that y
never reaches does_something
, yet it's mere existance is what invalidates the reasoning that ptr.exclusive_access
is not modified.
If this is the case, this is a serious concern of mine, the pointer model in lccc would not permit this, and it is desired that this be the case, but it is intended to permit a superset of operations that Stacked Borrows does (A superset, because some operations that are permitted by C and C++ aren't by stacked borrows presently, one such operation is actually discussed in #256).