about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDaniel Bloom <7810950-Daniel.Aaron.Bloom@users.noreply.gitlab.com>2025-05-07 16:29:04 -0700
committerDaniel Bloom <7810950-Daniel.Aaron.Bloom@users.noreply.gitlab.com>2025-06-25 17:06:49 -0700
commit1f1000f4b8480463cb709f3d5c47cdea30c07a26 (patch)
tree0242c37fc7cb6064f60abbd44b6c8379d1c27de5
parent2801f9aaf9b7580d9b230b532b0700709857cc88 (diff)
downloadrust-1f1000f4b8480463cb709f3d5c47cdea30c07a26.tar.gz
rust-1f1000f4b8480463cb709f3d5c47cdea30c07a26.zip
make RefCell unstably const
-rw-r--r--library/core/src/cell.rs98
-rw-r--r--library/coretests/tests/cell.rs72
-rw-r--r--library/coretests/tests/lib.rs3
3 files changed, 138 insertions, 35 deletions
diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs
index a2c1ba835f3..7ce03e3d831 100644
--- a/library/core/src/cell.rs
+++ b/library/core/src/cell.rs
@@ -255,6 +255,7 @@ use crate::fmt::{self, Debug, Display};
 use crate::marker::{PhantomData, PointerLike, Unsize};
 use crate::mem;
 use crate::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn};
+use crate::panic::const_panic;
 use crate::pin::PinCoerceUnsized;
 use crate::ptr::{self, NonNull};
 
@@ -781,16 +782,24 @@ impl Display for BorrowMutError {
 #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
 #[track_caller]
 #[cold]
-fn panic_already_borrowed(err: BorrowMutError) -> ! {
-    panic!("{err}")
+const fn panic_already_borrowed(err: BorrowMutError) -> ! {
+    const_panic!(
+        "RefCell already borrowed",
+        "{err}",
+        err: BorrowMutError = err,
+    )
 }
 
 // This ensures the panicking code is outlined from `borrow` for `RefCell`.
 #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
 #[track_caller]
 #[cold]
-fn panic_already_mutably_borrowed(err: BorrowError) -> ! {
-    panic!("{err}")
+const fn panic_already_mutably_borrowed(err: BorrowError) -> ! {
+    const_panic!(
+        "RefCell already mutably borrowed",
+        "{err}",
+        err: BorrowError = err,
+    )
 }
 
 // Positive values represent the number of `Ref` active. Negative values
@@ -810,12 +819,12 @@ type BorrowCounter = isize;
 const UNUSED: BorrowCounter = 0;
 
 #[inline(always)]
-fn is_writing(x: BorrowCounter) -> bool {
+const fn is_writing(x: BorrowCounter) -> bool {
     x < UNUSED
 }
 
 #[inline(always)]
-fn is_reading(x: BorrowCounter) -> bool {
+const fn is_reading(x: BorrowCounter) -> bool {
     x > UNUSED
 }
 
@@ -884,8 +893,9 @@ impl<T> RefCell<T> {
     #[stable(feature = "refcell_replace", since = "1.24.0")]
     #[track_caller]
     #[rustc_confusables("swap")]
-    pub fn replace(&self, t: T) -> T {
-        mem::replace(&mut *self.borrow_mut(), t)
+    #[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
+    pub const fn replace(&self, t: T) -> T {
+        mem::replace(&mut self.borrow_mut(), t)
     }
 
     /// Replaces the wrapped value with a new one computed from `f`, returning
@@ -935,7 +945,8 @@ impl<T> RefCell<T> {
     /// ```
     #[inline]
     #[stable(feature = "refcell_swap", since = "1.24.0")]
-    pub fn swap(&self, other: &Self) {
+    #[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
+    pub const fn swap(&self, other: &Self) {
         mem::swap(&mut *self.borrow_mut(), &mut *other.borrow_mut())
     }
 }
@@ -975,7 +986,8 @@ impl<T: ?Sized> RefCell<T> {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
     #[track_caller]
-    pub fn borrow(&self) -> Ref<'_, T> {
+    #[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
+    pub const fn borrow(&self) -> Ref<'_, T> {
         match self.try_borrow() {
             Ok(b) => b,
             Err(err) => panic_already_mutably_borrowed(err),
@@ -1010,14 +1022,15 @@ impl<T: ?Sized> RefCell<T> {
     #[stable(feature = "try_borrow", since = "1.13.0")]
     #[inline]
     #[cfg_attr(feature = "debug_refcell", track_caller)]
-    pub fn try_borrow(&self) -> Result<Ref<'_, T>, BorrowError> {
+    #[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
+    pub const fn try_borrow(&self) -> Result<Ref<'_, T>, BorrowError> {
         match BorrowRef::new(&self.borrow) {
             Some(b) => {
                 #[cfg(feature = "debug_refcell")]
                 {
                     // `borrowed_at` is always the *first* active borrow
                     if b.borrow.get() == 1 {
-                        self.borrowed_at.set(Some(crate::panic::Location::caller()));
+                        self.borrowed_at.replace(Some(crate::panic::Location::caller()));
                     }
                 }
 
@@ -1071,7 +1084,8 @@ impl<T: ?Sized> RefCell<T> {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
     #[track_caller]
-    pub fn borrow_mut(&self) -> RefMut<'_, T> {
+    #[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
+    pub const fn borrow_mut(&self) -> RefMut<'_, T> {
         match self.try_borrow_mut() {
             Ok(b) => b,
             Err(err) => panic_already_borrowed(err),
@@ -1103,12 +1117,13 @@ impl<T: ?Sized> RefCell<T> {
     #[stable(feature = "try_borrow", since = "1.13.0")]
     #[inline]
     #[cfg_attr(feature = "debug_refcell", track_caller)]
-    pub fn try_borrow_mut(&self) -> Result<RefMut<'_, T>, BorrowMutError> {
+    #[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
+    pub const fn try_borrow_mut(&self) -> Result<RefMut<'_, T>, BorrowMutError> {
         match BorrowRefMut::new(&self.borrow) {
             Some(b) => {
                 #[cfg(feature = "debug_refcell")]
                 {
-                    self.borrowed_at.set(Some(crate::panic::Location::caller()));
+                    self.borrowed_at.replace(Some(crate::panic::Location::caller()));
                 }
 
                 // SAFETY: `BorrowRefMut` guarantees unique access.
@@ -1139,7 +1154,8 @@ impl<T: ?Sized> RefCell<T> {
     #[stable(feature = "cell_as_ptr", since = "1.12.0")]
     #[rustc_as_ptr]
     #[rustc_never_returns_null_ptr]
-    pub fn as_ptr(&self) -> *mut T {
+    #[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
+    pub const fn as_ptr(&self) -> *mut T {
         self.value.get()
     }
 
@@ -1176,7 +1192,8 @@ impl<T: ?Sized> RefCell<T> {
     /// ```
     #[inline]
     #[stable(feature = "cell_get_mut", since = "1.11.0")]
-    pub fn get_mut(&mut self) -> &mut T {
+    #[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
+    pub const fn get_mut(&mut self) -> &mut T {
         self.value.get_mut()
     }
 
@@ -1202,7 +1219,8 @@ impl<T: ?Sized> RefCell<T> {
     /// assert!(c.try_borrow().is_ok());
     /// ```
     #[unstable(feature = "cell_leak", issue = "69099")]
-    pub fn undo_leak(&mut self) -> &mut T {
+    #[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
+    pub const fn undo_leak(&mut self) -> &mut T {
         *self.borrow.get_mut() = UNUSED;
         self.get_mut()
     }
@@ -1236,7 +1254,8 @@ impl<T: ?Sized> RefCell<T> {
     /// ```
     #[stable(feature = "borrow_state", since = "1.37.0")]
     #[inline]
-    pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, BorrowError> {
+    #[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
+    pub const unsafe fn try_borrow_unguarded(&self) -> Result<&T, BorrowError> {
         if !is_writing(self.borrow.get()) {
             // SAFETY: We check that nobody is actively writing now, but it is
             // the caller's responsibility to ensure that nobody writes until
@@ -1400,7 +1419,7 @@ struct BorrowRef<'b> {
 
 impl<'b> BorrowRef<'b> {
     #[inline]
-    fn new(borrow: &'b Cell<BorrowCounter>) -> Option<BorrowRef<'b>> {
+    const fn new(borrow: &'b Cell<BorrowCounter>) -> Option<BorrowRef<'b>> {
         let b = borrow.get().wrapping_add(1);
         if !is_reading(b) {
             // Incrementing borrow can result in a non-reading value (<= 0) in these cases:
@@ -1417,22 +1436,24 @@ impl<'b> BorrowRef<'b> {
             // 1. It was = 0, i.e. it wasn't borrowed, and we are taking the first read borrow
             // 2. It was > 0 and < isize::MAX, i.e. there were read borrows, and isize
             //    is large enough to represent having one more read borrow
-            borrow.set(b);
+            borrow.replace(b);
             Some(BorrowRef { borrow })
         }
     }
 }
 
-impl Drop for BorrowRef<'_> {
+#[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
+impl const Drop for BorrowRef<'_> {
     #[inline]
     fn drop(&mut self) {
         let borrow = self.borrow.get();
         debug_assert!(is_reading(borrow));
-        self.borrow.set(borrow - 1);
+        self.borrow.replace(borrow - 1);
     }
 }
 
-impl Clone for BorrowRef<'_> {
+#[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
+impl const Clone for BorrowRef<'_> {
     #[inline]
     fn clone(&self) -> Self {
         // Since this Ref exists, we know the borrow flag
@@ -1442,7 +1463,7 @@ impl Clone for BorrowRef<'_> {
         // Prevent the borrow counter from overflowing into
         // a writing borrow.
         assert!(borrow != BorrowCounter::MAX);
-        self.borrow.set(borrow + 1);
+        self.borrow.replace(borrow + 1);
         BorrowRef { borrow: self.borrow }
     }
 }
@@ -1463,7 +1484,8 @@ pub struct Ref<'b, T: ?Sized + 'b> {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<T: ?Sized> Deref for Ref<'_, T> {
+#[rustc_const_unstable(feature = "const_deref", issue = "88955")]
+impl<T: ?Sized> const Deref for Ref<'_, T> {
     type Target = T;
 
     #[inline]
@@ -1488,7 +1510,8 @@ impl<'b, T: ?Sized> Ref<'b, T> {
     #[stable(feature = "cell_extras", since = "1.15.0")]
     #[must_use]
     #[inline]
-    pub fn clone(orig: &Ref<'b, T>) -> Ref<'b, T> {
+    #[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
+    pub const fn clone(orig: &Ref<'b, T>) -> Ref<'b, T> {
         Ref { value: orig.value, borrow: orig.borrow.clone() }
     }
 
@@ -1610,7 +1633,8 @@ impl<'b, T: ?Sized> Ref<'b, T> {
     /// assert!(cell.try_borrow_mut().is_err());
     /// ```
     #[unstable(feature = "cell_leak", issue = "69099")]
-    pub fn leak(orig: Ref<'b, T>) -> &'b T {
+    #[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
+    pub const fn leak(orig: Ref<'b, T>) -> &'b T {
         // By forgetting this Ref we ensure that the borrow counter in the RefCell can't go back to
         // UNUSED within the lifetime `'b`. Resetting the reference tracking state would require a
         // unique reference to the borrowed RefCell. No further mutable references can be created
@@ -1776,7 +1800,8 @@ impl<'b, T: ?Sized> RefMut<'b, T> {
     /// assert!(cell.try_borrow_mut().is_err());
     /// ```
     #[unstable(feature = "cell_leak", issue = "69099")]
-    pub fn leak(mut orig: RefMut<'b, T>) -> &'b mut T {
+    #[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
+    pub const fn leak(mut orig: RefMut<'b, T>) -> &'b mut T {
         // By forgetting this BorrowRefMut we ensure that the borrow counter in the RefCell can't
         // go back to UNUSED within the lifetime `'b`. Resetting the reference tracking state would
         // require a unique reference to the borrowed RefCell. No further references can be created
@@ -1792,25 +1817,26 @@ struct BorrowRefMut<'b> {
     borrow: &'b Cell<BorrowCounter>,
 }
 
-impl Drop for BorrowRefMut<'_> {
+#[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
+impl const Drop for BorrowRefMut<'_> {
     #[inline]
     fn drop(&mut self) {
         let borrow = self.borrow.get();
         debug_assert!(is_writing(borrow));
-        self.borrow.set(borrow + 1);
+        self.borrow.replace(borrow + 1);
     }
 }
 
 impl<'b> BorrowRefMut<'b> {
     #[inline]
-    fn new(borrow: &'b Cell<BorrowCounter>) -> Option<BorrowRefMut<'b>> {
+    const fn new(borrow: &'b Cell<BorrowCounter>) -> Option<BorrowRefMut<'b>> {
         // NOTE: Unlike BorrowRefMut::clone, new is called to create the initial
         // mutable reference, and so there must currently be no existing
         // references. Thus, while clone increments the mutable refcount, here
         // we explicitly only allow going from UNUSED to UNUSED - 1.
         match borrow.get() {
             UNUSED => {
-                borrow.set(UNUSED - 1);
+                borrow.replace(UNUSED - 1);
                 Some(BorrowRefMut { borrow })
             }
             _ => None,
@@ -1849,7 +1875,8 @@ pub struct RefMut<'b, T: ?Sized + 'b> {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<T: ?Sized> Deref for RefMut<'_, T> {
+#[rustc_const_unstable(feature = "const_deref", issue = "88955")]
+impl<T: ?Sized> const Deref for RefMut<'_, T> {
     type Target = T;
 
     #[inline]
@@ -1860,7 +1887,8 @@ impl<T: ?Sized> Deref for RefMut<'_, T> {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<T: ?Sized> DerefMut for RefMut<'_, T> {
+#[rustc_const_unstable(feature = "const_deref", issue = "88955")]
+impl<T: ?Sized> const DerefMut for RefMut<'_, T> {
     #[inline]
     fn deref_mut(&mut self) -> &mut T {
         // SAFETY: the value is accessible as long as we hold our borrow.
diff --git a/library/coretests/tests/cell.rs b/library/coretests/tests/cell.rs
index 781a46c3744..c0ac66e5d2b 100644
--- a/library/coretests/tests/cell.rs
+++ b/library/coretests/tests/cell.rs
@@ -1,4 +1,5 @@
 use core::cell::*;
+use core::mem::forget;
 
 #[test]
 fn smoketest_unsafe_cell() {
@@ -477,3 +478,74 @@ fn const_cells() {
         const _: i32 = CELL.into_inner();
     */
 }
+
+#[test]
+fn refcell_borrow() {
+    // Check that `borrow` is usable at compile-time
+    const {
+        let a = RefCell::new(0);
+        assert!(a.try_borrow().is_ok());
+        assert!(a.try_borrow_mut().is_ok());
+        let a_ref = a.borrow();
+        assert!(*a_ref == 0);
+        assert!(a.try_borrow().is_ok());
+        assert!(a.try_borrow_mut().is_err());
+    }
+}
+
+#[test]
+fn refcell_borrow_mut() {
+    // Check that `borrow_mut` is usable at compile-time
+    const {
+        let mut a = RefCell::new(0);
+        {
+            assert!(a.try_borrow().is_ok());
+            assert!(a.try_borrow_mut().is_ok());
+            let mut a_ref = a.borrow_mut();
+            assert!(*a_ref == 0);
+            *a_ref = 10;
+            assert!(*a_ref == 10);
+            assert!(a.try_borrow().is_err());
+            assert!(a.try_borrow_mut().is_err());
+        }
+        assert!(*a.get_mut() == 10);
+    };
+}
+struct NeverDrop;
+impl Drop for NeverDrop {
+    fn drop(&mut self) {
+        panic!("should never be called");
+    }
+}
+
+#[test]
+fn refcell_replace() {
+    // Check that `replace` is usable at compile-time
+    const {
+        let a = RefCell::new(0);
+        assert!(a.replace(10) == 0);
+        let a = a.into_inner();
+        assert!(a == 10);
+
+        let b = RefCell::new(NeverDrop);
+        forget(b.replace(NeverDrop));
+        forget(b)
+    };
+}
+
+#[test]
+fn refcell_swap() {
+    // Check that `swap` is usable at compile-time
+    const {
+        let (a, b) = (RefCell::new(31), RefCell::new(41));
+        a.swap(&b);
+        let (a, b) = (a.into_inner(), b.into_inner());
+        assert!(a == 41);
+        assert!(b == 31);
+
+        let c = RefCell::new(NeverDrop);
+        let d = RefCell::new(NeverDrop);
+        c.swap(&d);
+        forget((c, d));
+    };
+}
diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs
index 5449132413b..b6298883784 100644
--- a/library/coretests/tests/lib.rs
+++ b/library/coretests/tests/lib.rs
@@ -15,8 +15,11 @@
 #![feature(cfg_target_has_reliable_f16_f128)]
 #![feature(char_max_len)]
 #![feature(clone_to_uninit)]
+#![feature(const_deref)]
+#![feature(const_destruct)]
 #![feature(const_eval_select)]
 #![feature(const_float_round_methods)]
+#![feature(const_ref_cell)]
 #![feature(const_trait_impl)]
 #![feature(core_float_math)]
 #![feature(core_intrinsics)]