Auto merge of #97027 - cuviper:yesalias-refcell, r=thomcc
Use pointers in `cell::{Ref,RefMut}` to avoid `noalias`
When `Ref` and `RefMut` were based on references, they would get LLVM `noalias` attributes that were incorrect, because that alias guarantee is only true until the guard drops. A `&RefCell` on the same value can get a new borrow that aliases the previous guard, possibly leading to miscompilation. Using `NonNull` pointers in `Ref` and `RefCell` avoids `noalias`.
Fixes the library side of #63787, but we still might want to explore language solutions there.
This commit is contained in:
commit
4d6992bc18
4 changed files with 103 additions and 38 deletions
|
|
@ -7,15 +7,15 @@
|
|||
</Expand>
|
||||
</Type>
|
||||
<Type Name="core::cell::Ref<*>">
|
||||
<DisplayString>{value}</DisplayString>
|
||||
<DisplayString>{value.pointer}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem>value</ExpandedItem>
|
||||
<ExpandedItem>value.pointer</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="core::cell::RefMut<*>">
|
||||
<DisplayString>{value}</DisplayString>
|
||||
<DisplayString>{value.pointer}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem>value</ExpandedItem>
|
||||
<ExpandedItem>value.pointer</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="core::cell::RefCell<*>">
|
||||
|
|
|
|||
14
src/test/codegen/noalias-refcell.rs
Normal file
14
src/test/codegen/noalias-refcell.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
// compile-flags: -O -C no-prepopulate-passes -Z mutable-noalias=yes
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
|
||||
// Make sure that none of the arguments get a `noalias` attribute, because
|
||||
// the `RefCell` might alias writes after either `Ref`/`RefMut` is dropped.
|
||||
|
||||
// CHECK-LABEL: @maybe_aliased(
|
||||
// CHECK-NOT: noalias
|
||||
// CHECK-SAME: %_refcell
|
||||
#[no_mangle]
|
||||
pub unsafe fn maybe_aliased(_: Ref<'_, i32>, _: RefMut<'_, i32>, _refcell: &RefCell<i32>) {}
|
||||
36
src/test/ui/issues/issue-63787.rs
Normal file
36
src/test/ui/issues/issue-63787.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// run-pass
|
||||
// compile-flags: -O
|
||||
|
||||
// Make sure that `Ref` and `RefMut` do not make false promises about aliasing,
|
||||
// because once they drop, their reference/pointer can alias other writes.
|
||||
|
||||
// Adapted from comex's proof of concept:
|
||||
// https://github.com/rust-lang/rust/issues/63787#issuecomment-523588164
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::ops::Deref;
|
||||
|
||||
pub fn break_if_r_is_noalias(rc: &RefCell<i32>, r: impl Deref<Target = i32>) -> i32 {
|
||||
let ptr1 = &*r as *const i32;
|
||||
let a = *r;
|
||||
drop(r);
|
||||
*rc.borrow_mut() = 2;
|
||||
let r2 = rc.borrow();
|
||||
let ptr2 = &*r2 as *const i32;
|
||||
if ptr2 != ptr1 {
|
||||
panic!();
|
||||
}
|
||||
// If LLVM knows the pointers are the same, and if `r` was `noalias`,
|
||||
// then it may replace this with `a + a`, ignoring the earlier write.
|
||||
a + *r2
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut rc = RefCell::new(1);
|
||||
let res = break_if_r_is_noalias(&rc, rc.borrow());
|
||||
assert_eq!(res, 3);
|
||||
|
||||
*rc.get_mut() = 1;
|
||||
let res = break_if_r_is_noalias(&rc, rc.borrow_mut());
|
||||
assert_eq!(res, 3);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue