From e11911c60e531789196227872c51d5a085b91ac5 Mon Sep 17 00:00:00 2001 From: Daniel Bloom <7810950-Daniel.Aaron.Bloom@users.noreply.gitlab.com> Date: Wed, 7 May 2025 16:29:04 -0700 Subject: [PATCH] make RefCell unstably const --- library/core/src/cell.rs | 120 +++++++++++++++++++++----------- library/core/src/lib.rs | 1 + library/coretests/tests/cell.rs | 72 +++++++++++++++++++ library/coretests/tests/lib.rs | 3 + 4 files changed, 154 insertions(+), 42 deletions(-) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index c7657350a0d9a..1180bd81eedd8 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}; @@ -787,16 +788,24 @@ impl Display for BorrowMutError { #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] #[track_caller] #[cold] -fn panic_already_borrowed(err: BorrowMutError) -> ! { - panic!("already borrowed: {:?}", err) +const fn panic_already_borrowed(err: BorrowMutError) -> ! { + const_panic!( + "already borrowed", + "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!("already mutably borrowed: {:?}", err) +const fn panic_already_mutably_borrowed(err: BorrowError) -> ! { + const_panic!( + "already mutably borrowed", + "already mutably borrowed: {err:?}", + err: BorrowError = err, + ) } // Positive values represent the number of `Ref` active. Negative values @@ -816,12 +825,12 @@ type BorrowFlag = isize; const UNUSED: BorrowFlag = 0; #[inline(always)] -fn is_writing(x: BorrowFlag) -> bool { +const fn is_writing(x: BorrowFlag) -> bool { x < UNUSED } #[inline(always)] -fn is_reading(x: BorrowFlag) -> bool { +const fn is_reading(x: BorrowFlag) -> bool { x > UNUSED } @@ -890,8 +899,9 @@ impl RefCell { #[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 @@ -941,7 +951,8 @@ impl RefCell { /// ``` #[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()) } } @@ -981,7 +992,8 @@ impl RefCell { #[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), @@ -1016,14 +1028,15 @@ impl RefCell { #[stable(feature = "try_borrow", since = "1.13.0")] #[inline] #[cfg_attr(feature = "debug_refcell", track_caller)] - pub fn try_borrow(&self) -> Result, BorrowError> { + #[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")] + pub const fn try_borrow(&self) -> Result, 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())); } } @@ -1077,7 +1090,8 @@ impl RefCell { #[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), @@ -1109,12 +1123,13 @@ impl RefCell { #[stable(feature = "try_borrow", since = "1.13.0")] #[inline] #[cfg_attr(feature = "debug_refcell", track_caller)] - pub fn try_borrow_mut(&self) -> Result, BorrowMutError> { + #[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")] + pub const fn try_borrow_mut(&self) -> Result, 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. @@ -1145,7 +1160,8 @@ impl RefCell { #[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() } @@ -1182,7 +1198,8 @@ impl RefCell { /// ``` #[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() } @@ -1208,7 +1225,8 @@ impl RefCell { /// 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() } @@ -1242,7 +1260,8 @@ impl RefCell { /// ``` #[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 @@ -1406,7 +1425,8 @@ struct BorrowRef<'b> { impl<'b> BorrowRef<'b> { #[inline] - fn new(borrow: &'b Cell) -> Option> { + #[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")] + const fn new(borrow: &'b Cell) -> Option> { let b = borrow.get().wrapping_add(1); if !is_reading(b) { // Incrementing borrow can result in a non-reading value (<= 0) in these cases: @@ -1423,33 +1443,41 @@ 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 }) } } + + /// FIXME(const-hack): `Clone` is not a `const_trait`, so work around that by making our own method + #[inline] + #[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")] + const fn clone(&self) -> Self { + // Since this Ref exists, we know the borrow flag + // is a reading borrow. + let borrow = self.borrow.get(); + debug_assert!(is_reading(borrow)); + // Prevent the borrow counter from overflowing into + // a writing borrow. + assert!(borrow != BorrowFlag::MAX); + self.borrow.replace(borrow + 1); + BorrowRef { borrow: self.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<'_> { #[inline] fn clone(&self) -> Self { - // Since this Ref exists, we know the borrow flag - // is a reading borrow. - let borrow = self.borrow.get(); - debug_assert!(is_reading(borrow)); - // Prevent the borrow counter from overflowing into - // a writing borrow. - assert!(borrow != BorrowFlag::MAX); - self.borrow.set(borrow + 1); - BorrowRef { borrow: self.borrow } + self.clone() } } @@ -1469,7 +1497,8 @@ pub struct Ref<'b, T: ?Sized + 'b> { } #[stable(feature = "rust1", since = "1.0.0")] -impl Deref for Ref<'_, T> { +#[rustc_const_unstable(feature = "const_deref", issue = "88955")] +impl const Deref for Ref<'_, T> { type Target = T; #[inline] @@ -1494,7 +1523,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() } } @@ -1616,7 +1646,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 @@ -1782,7 +1813,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 @@ -1798,25 +1830,27 @@ struct BorrowRefMut<'b> { borrow: &'b Cell, } -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) -> Option> { + #[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")] + const fn new(borrow: &'b Cell) -> Option> { // 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, @@ -1855,7 +1889,8 @@ pub struct RefMut<'b, T: ?Sized + 'b> { } #[stable(feature = "rust1", since = "1.0.0")] -impl Deref for RefMut<'_, T> { +#[rustc_const_unstable(feature = "const_deref", issue = "88955")] +impl const Deref for RefMut<'_, T> { type Target = T; #[inline] @@ -1866,7 +1901,8 @@ impl Deref for RefMut<'_, T> { } #[stable(feature = "rust1", since = "1.0.0")] -impl DerefMut for RefMut<'_, T> { +#[rustc_const_unstable(feature = "const_deref", issue = "88955")] +impl 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/core/src/lib.rs b/library/core/src/lib.rs index 64a7ec8906b6b..66ed13c4dbe4d 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -141,6 +141,7 @@ #![feature(cfg_target_has_atomic)] #![feature(cfg_target_has_atomic_equal_alignment)] #![feature(cfg_ub_checks)] +#![feature(const_destruct)] #![feature(const_precise_live_drops)] #![feature(const_trait_impl)] #![feature(decl_macro)] diff --git a/library/coretests/tests/cell.rs b/library/coretests/tests/cell.rs index 781a46c3744f5..2e7418fd665de 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(); */ } + +#[allow(dead_code)] +fn const_refcell() { + struct Dummy; + impl Drop for Dummy { + fn drop(&mut self) { + panic!("should never be called"); + } + } + + // Check that `borrow` is usable at compile-time + const BORROW_TEST: RefCell = { + 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()); + } + a + }; + // Check that `borrow_mut` is usable at compile-time + const BORROW_MUT_TEST: RefCell = { + 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); + a + }; + + // Check that `replace` is usable at compile-time + const REPLACE_TEST: u32 = { + let a = RefCell::new(0); + assert!(a.replace(10) == 0); + let a = a.into_inner(); + assert!(a == 10); + a + }; + // Check that `replace` is usable at compile-time + const REPLACE_DUMMY_TEST: RefCell = { + let a = RefCell::new(Dummy); + forget(a.replace(Dummy)); + a + }; + + // Check that `swap` is usable at compile-time + const SWAP_TEST: (u32, u32) = { + 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); + (a, b) + }; + const SWAP_DUMMY_TEST: [RefCell; 2] = { + let a = RefCell::new(Dummy); + let b = RefCell::new(Dummy); + a.swap(&b); + [a, b] + }; +} diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index a71c4139308aa..ed3291c3d668d 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -14,7 +14,10 @@ #![feature(bstr)] #![feature(char_max_len)] #![feature(clone_to_uninit)] +#![feature(const_deref)] +#![feature(const_destruct)] #![feature(const_eval_select)] +#![feature(const_ref_cell)] #![feature(const_trait_impl)] #![feature(core_intrinsics)] #![feature(core_intrinsics_fallbacks)]