make RefCell unstably const

This commit is contained in:
Daniel Bloom
2025-05-07 16:29:04 -07:00
parent 2801f9aaf9
commit 1f1000f4b8
3 changed files with 138 additions and 35 deletions
+63 -35
View File
@@ -255,6 +255,7 @@
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 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[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 @@ fn panic_already_mutably_borrowed(err: BorrowError) -> ! {
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 @@ pub const fn into_inner(self) -> 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 @@ pub fn replace_with<F: FnOnce(&mut T) -> T>(&self, f: F) -> 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 @@ pub fn borrow(&self) -> Ref<'_, 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 @@ pub fn try_borrow(&self) -> Result<Ref<'_, T>, BorrowError> {
#[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 @@ pub fn borrow_mut(&self) -> RefMut<'_, 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 @@ pub fn try_borrow_mut(&self) -> Result<RefMut<'_, T>, BorrowMutError> {
#[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 @@ pub fn as_ptr(&self) -> *mut 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 @@ pub fn get_mut(&mut self) -> &mut 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 @@ pub fn undo_leak(&mut self) -> &mut 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 @@ fn new(borrow: &'b Cell<BorrowCounter>) -> Option<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 @@ fn clone(&self) -> Self {
// 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 @@ pub fn map_split<U: ?Sized, V: ?Sized, F>(orig: Ref<'b, T>, f: F) -> (Ref<'b, U>
/// 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 @@ pub fn map_split<U: ?Sized, V: ?Sized, F>(
/// 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 @@ fn deref(&self) -> &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.
+72
View File
@@ -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));
};
}
+3
View File
@@ -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)]