Skip to content

Commit a37bd63

Browse files
committed
add &ReadOnlyCell<T> conversions from &T and &Cell<T>
Is there any practical use for these conversions? I'm not sure :) But they're spiritually similar to the `Cell::from_mut` conversion in the standard library. This makes it possible to write a function taking `&ReadOnlyCell<T>` if you only need to copy the `T` and you don't need the guarantee that it won't change from one read to the next (which it could, if other `&Cell<T>` references exist pointing to the same value). With these conversions in place, the relationships between the different reference types look like this: ``` &mut T / \ coerce Cell::from_mut / \ &T &Cell<T> \ / ReadOnlyCell::from_ref ReadOnlyCell::from_cell \ / &ReadOnlyCell<T> ``` This sort of thing wasn't supported by Miri until recently, but the new Tree Borrows model supports it: rust-lang/unsafe-code-guidelines#303. The new test currently fails Miri by default: ``` $ cargo +nightly miri test test_read_only_cell_conversions ... error: Undefined Behavior: trying to retag from <747397> for SharedReadWrite permission at alloc222352[0x0], but that tag only grants SharedReadOnly permission for this location ``` But it passes with `-Zmiri-tree-borrows`: ``` $ MIRIFLAGS="-Zmiri-tree-borrows" cargo +nightly miri test test_read_only_cell_conversions ... test buffer::tests::test_read_only_cell_conversions ... ok ```
1 parent d89c388 commit a37bd63

File tree

1 file changed

+40
-3
lines changed

1 file changed

+40
-3
lines changed

src/buffer.rs

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -641,9 +641,9 @@ impl<T> Drop for PyBuffer<T> {
641641
/// The data cannot be modified through the reference, but other references may
642642
/// be modifying the data.
643643
#[repr(transparent)]
644-
pub struct ReadOnlyCell<T: Element>(cell::UnsafeCell<T>);
644+
pub struct ReadOnlyCell<T: Copy>(cell::UnsafeCell<T>);
645645

646-
impl<T: Element> ReadOnlyCell<T> {
646+
impl<T: Copy> ReadOnlyCell<T> {
647647
/// Returns a copy of the current value.
648648
#[inline]
649649
pub fn get(&self) -> T {
@@ -655,6 +655,22 @@ impl<T: Element> ReadOnlyCell<T> {
655655
pub fn as_ptr(&self) -> *const T {
656656
self.0.get()
657657
}
658+
659+
/// Converts `&T` to `&ReadOnlyCell<T>`.
660+
#[inline]
661+
pub fn from_ref(t: &T) -> &ReadOnlyCell<T> {
662+
// Safety: ReadOnlyCell is repr(transparent), and &ReadOnlyCell<T> is strictly less capable
663+
// than &T.
664+
unsafe { &*(t as *const T as *const ReadOnlyCell<T>) }
665+
}
666+
667+
/// Converts `&Cell<T>` to `&ReadOnlyCell<T>`.
668+
#[inline]
669+
pub fn from_cell(t: &cell::Cell<T>) -> &ReadOnlyCell<T> {
670+
// Safety: Cell and ReadOnlyCell are both repr(transparent), and &ReadOnlyCell<T> is
671+
// strictly less capable than &Cell<T>.
672+
unsafe { &*(t as *const cell::Cell<T> as *const ReadOnlyCell<T>) }
673+
}
658674
}
659675

660676
macro_rules! impl_element(
@@ -686,7 +702,7 @@ impl_element!(f64, Float);
686702

687703
#[cfg(test)]
688704
mod tests {
689-
use super::PyBuffer;
705+
use super::{PyBuffer, ReadOnlyCell};
690706
use crate::ffi;
691707
use crate::Python;
692708

@@ -942,4 +958,25 @@ mod tests {
942958
assert_eq!(buffer.to_fortran_vec(py).unwrap(), [10.0, 11.0, 12.0, 13.0]);
943959
});
944960
}
961+
962+
#[test]
963+
fn test_read_only_cell_conversions() {
964+
let mut x = 42;
965+
let x_ref_mut = &mut x;
966+
967+
let x_ref = &*x_ref_mut;
968+
let x_rocell1 = ReadOnlyCell::from_ref(x_ref);
969+
let x_rocell2 = ReadOnlyCell::from_ref(x_ref);
970+
assert_eq!(x_rocell1.get(), x_rocell2.get());
971+
assert_eq!(x_rocell1.get(), 42);
972+
973+
let x_cell = std::cell::Cell::from_mut(x_ref_mut);
974+
let x_rocell3 = ReadOnlyCell::from_cell(x_cell);
975+
let x_rocell4 = ReadOnlyCell::from_cell(x_cell);
976+
assert_eq!(x_rocell3.get(), x_rocell4.get());
977+
assert_eq!(x_rocell3.get(), 42);
978+
979+
// Importantly, this doesn't compile, because x_ref and x_cell cannot coexist.
980+
// _ = *x_ref;
981+
}
945982
}

0 commit comments

Comments
 (0)