Open
Description
Ref:
- Miscompile in the GVN transform rust#130853
- Does this code violate the alias model? miri#3921 (comment)
- Maybe Do we have full recursive validity for references? #412 but I don't think it's related. because that's about recursive validity behind a reference that is not accessed at all. This issue is about the validity of really-accessed path.
This is already a fact under {Stacked,Tree}Borrow, though I think it's unfortunate to forbids optimizations on structs containing reference fields. Specifically, optimizing out duplicated-read through double-reference after a function call and caching an (known to be unchanged) pointer indirection are not possible.
fn main() {
let mut s = 0u8;
let borrow = &raw mut s;
unsafe {
let const_borrow: *const *mut u8 = &raw const borrow;
let x: &&u8 = &*const_borrow.cast::<&u8>();
// This can be in another innocent fn, but inline to avoid `static mut`.
{
let y1 = **x;
*borrow = 1; // modify, maybe through some user-provided fn.
let y2 = **x;
assert!(y1 != y2);
}
// An alternative with caching. But compiler is not allowed to
// transform the code above into this.
{
let y: &u8 = *x;
let y1 = *y;
*borrow = 2; // modify
let y2 = *y; // <- Undefined Behavior
assert!(y1 != y2);
}
}
}
Here we can notice that even through x
and *x
is both unchanged and u8: Freeze
, **x
can change via a foreign pointer with write permission. Thus caching one level of indirection *x
is not possible because it would be Disabled
by that foreign write.
Could we do better on this? For this example, maybe we could leave protectors on the access trace of a Frozen
reference argument (when without interior mutability, or course)?