Skip to content

Commit 0446a0d

Browse files
Rollup merge of #137843 - Daniel-Aaron-Bloom:const_cell, r=oli-obk
make RefCell unstably const Now that we can do interior mutability in `const`, most of the `RefCell` API can be `const fn`. The main exceptions are APIs which use `FnOnce` (`RefCell::replace_with` and `Ref[Mut]::[filter_]map[_split]`) and `RefCell::take` which calls `Default::default`. Tracking issue: #137844
2 parents b03b3a7 + 1f1000f commit 0446a0d

File tree

3 files changed

+138
-35
lines changed

3 files changed

+138
-35
lines changed

library/core/src/cell.rs

Lines changed: 63 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ use crate::fmt::{self, Debug, Display};
255255
use crate::marker::{PhantomData, PointerLike, Unsize};
256256
use crate::mem;
257257
use crate::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn};
258+
use crate::panic::const_panic;
258259
use crate::pin::PinCoerceUnsized;
259260
use crate::ptr::{self, NonNull};
260261

@@ -781,16 +782,24 @@ impl Display for BorrowMutError {
781782
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
782783
#[track_caller]
783784
#[cold]
784-
fn panic_already_borrowed(err: BorrowMutError) -> ! {
785-
panic!("{err}")
785+
const fn panic_already_borrowed(err: BorrowMutError) -> ! {
786+
const_panic!(
787+
"RefCell already borrowed",
788+
"{err}",
789+
err: BorrowMutError = err,
790+
)
786791
}
787792

788793
// This ensures the panicking code is outlined from `borrow` for `RefCell`.
789794
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
790795
#[track_caller]
791796
#[cold]
792-
fn panic_already_mutably_borrowed(err: BorrowError) -> ! {
793-
panic!("{err}")
797+
const fn panic_already_mutably_borrowed(err: BorrowError) -> ! {
798+
const_panic!(
799+
"RefCell already mutably borrowed",
800+
"{err}",
801+
err: BorrowError = err,
802+
)
794803
}
795804

796805
// Positive values represent the number of `Ref` active. Negative values
@@ -810,12 +819,12 @@ type BorrowCounter = isize;
810819
const UNUSED: BorrowCounter = 0;
811820

812821
#[inline(always)]
813-
fn is_writing(x: BorrowCounter) -> bool {
822+
const fn is_writing(x: BorrowCounter) -> bool {
814823
x < UNUSED
815824
}
816825

817826
#[inline(always)]
818-
fn is_reading(x: BorrowCounter) -> bool {
827+
const fn is_reading(x: BorrowCounter) -> bool {
819828
x > UNUSED
820829
}
821830

@@ -884,8 +893,9 @@ impl<T> RefCell<T> {
884893
#[stable(feature = "refcell_replace", since = "1.24.0")]
885894
#[track_caller]
886895
#[rustc_confusables("swap")]
887-
pub fn replace(&self, t: T) -> T {
888-
mem::replace(&mut *self.borrow_mut(), t)
896+
#[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
897+
pub const fn replace(&self, t: T) -> T {
898+
mem::replace(&mut self.borrow_mut(), t)
889899
}
890900

891901
/// Replaces the wrapped value with a new one computed from `f`, returning
@@ -935,7 +945,8 @@ impl<T> RefCell<T> {
935945
/// ```
936946
#[inline]
937947
#[stable(feature = "refcell_swap", since = "1.24.0")]
938-
pub fn swap(&self, other: &Self) {
948+
#[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
949+
pub const fn swap(&self, other: &Self) {
939950
mem::swap(&mut *self.borrow_mut(), &mut *other.borrow_mut())
940951
}
941952
}
@@ -975,7 +986,8 @@ impl<T: ?Sized> RefCell<T> {
975986
#[stable(feature = "rust1", since = "1.0.0")]
976987
#[inline]
977988
#[track_caller]
978-
pub fn borrow(&self) -> Ref<'_, T> {
989+
#[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
990+
pub const fn borrow(&self) -> Ref<'_, T> {
979991
match self.try_borrow() {
980992
Ok(b) => b,
981993
Err(err) => panic_already_mutably_borrowed(err),
@@ -1010,14 +1022,15 @@ impl<T: ?Sized> RefCell<T> {
10101022
#[stable(feature = "try_borrow", since = "1.13.0")]
10111023
#[inline]
10121024
#[cfg_attr(feature = "debug_refcell", track_caller)]
1013-
pub fn try_borrow(&self) -> Result<Ref<'_, T>, BorrowError> {
1025+
#[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
1026+
pub const fn try_borrow(&self) -> Result<Ref<'_, T>, BorrowError> {
10141027
match BorrowRef::new(&self.borrow) {
10151028
Some(b) => {
10161029
#[cfg(feature = "debug_refcell")]
10171030
{
10181031
// `borrowed_at` is always the *first* active borrow
10191032
if b.borrow.get() == 1 {
1020-
self.borrowed_at.set(Some(crate::panic::Location::caller()));
1033+
self.borrowed_at.replace(Some(crate::panic::Location::caller()));
10211034
}
10221035
}
10231036

@@ -1071,7 +1084,8 @@ impl<T: ?Sized> RefCell<T> {
10711084
#[stable(feature = "rust1", since = "1.0.0")]
10721085
#[inline]
10731086
#[track_caller]
1074-
pub fn borrow_mut(&self) -> RefMut<'_, T> {
1087+
#[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
1088+
pub const fn borrow_mut(&self) -> RefMut<'_, T> {
10751089
match self.try_borrow_mut() {
10761090
Ok(b) => b,
10771091
Err(err) => panic_already_borrowed(err),
@@ -1103,12 +1117,13 @@ impl<T: ?Sized> RefCell<T> {
11031117
#[stable(feature = "try_borrow", since = "1.13.0")]
11041118
#[inline]
11051119
#[cfg_attr(feature = "debug_refcell", track_caller)]
1106-
pub fn try_borrow_mut(&self) -> Result<RefMut<'_, T>, BorrowMutError> {
1120+
#[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
1121+
pub const fn try_borrow_mut(&self) -> Result<RefMut<'_, T>, BorrowMutError> {
11071122
match BorrowRefMut::new(&self.borrow) {
11081123
Some(b) => {
11091124
#[cfg(feature = "debug_refcell")]
11101125
{
1111-
self.borrowed_at.set(Some(crate::panic::Location::caller()));
1126+
self.borrowed_at.replace(Some(crate::panic::Location::caller()));
11121127
}
11131128

11141129
// SAFETY: `BorrowRefMut` guarantees unique access.
@@ -1139,7 +1154,8 @@ impl<T: ?Sized> RefCell<T> {
11391154
#[stable(feature = "cell_as_ptr", since = "1.12.0")]
11401155
#[rustc_as_ptr]
11411156
#[rustc_never_returns_null_ptr]
1142-
pub fn as_ptr(&self) -> *mut T {
1157+
#[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
1158+
pub const fn as_ptr(&self) -> *mut T {
11431159
self.value.get()
11441160
}
11451161

@@ -1176,7 +1192,8 @@ impl<T: ?Sized> RefCell<T> {
11761192
/// ```
11771193
#[inline]
11781194
#[stable(feature = "cell_get_mut", since = "1.11.0")]
1179-
pub fn get_mut(&mut self) -> &mut T {
1195+
#[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
1196+
pub const fn get_mut(&mut self) -> &mut T {
11801197
self.value.get_mut()
11811198
}
11821199

@@ -1202,7 +1219,8 @@ impl<T: ?Sized> RefCell<T> {
12021219
/// assert!(c.try_borrow().is_ok());
12031220
/// ```
12041221
#[unstable(feature = "cell_leak", issue = "69099")]
1205-
pub fn undo_leak(&mut self) -> &mut T {
1222+
#[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
1223+
pub const fn undo_leak(&mut self) -> &mut T {
12061224
*self.borrow.get_mut() = UNUSED;
12071225
self.get_mut()
12081226
}
@@ -1236,7 +1254,8 @@ impl<T: ?Sized> RefCell<T> {
12361254
/// ```
12371255
#[stable(feature = "borrow_state", since = "1.37.0")]
12381256
#[inline]
1239-
pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, BorrowError> {
1257+
#[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
1258+
pub const unsafe fn try_borrow_unguarded(&self) -> Result<&T, BorrowError> {
12401259
if !is_writing(self.borrow.get()) {
12411260
// SAFETY: We check that nobody is actively writing now, but it is
12421261
// the caller's responsibility to ensure that nobody writes until
@@ -1400,7 +1419,7 @@ struct BorrowRef<'b> {
14001419

14011420
impl<'b> BorrowRef<'b> {
14021421
#[inline]
1403-
fn new(borrow: &'b Cell<BorrowCounter>) -> Option<BorrowRef<'b>> {
1422+
const fn new(borrow: &'b Cell<BorrowCounter>) -> Option<BorrowRef<'b>> {
14041423
let b = borrow.get().wrapping_add(1);
14051424
if !is_reading(b) {
14061425
// Incrementing borrow can result in a non-reading value (<= 0) in these cases:
@@ -1417,22 +1436,24 @@ impl<'b> BorrowRef<'b> {
14171436
// 1. It was = 0, i.e. it wasn't borrowed, and we are taking the first read borrow
14181437
// 2. It was > 0 and < isize::MAX, i.e. there were read borrows, and isize
14191438
// is large enough to represent having one more read borrow
1420-
borrow.set(b);
1439+
borrow.replace(b);
14211440
Some(BorrowRef { borrow })
14221441
}
14231442
}
14241443
}
14251444

1426-
impl Drop for BorrowRef<'_> {
1445+
#[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
1446+
impl const Drop for BorrowRef<'_> {
14271447
#[inline]
14281448
fn drop(&mut self) {
14291449
let borrow = self.borrow.get();
14301450
debug_assert!(is_reading(borrow));
1431-
self.borrow.set(borrow - 1);
1451+
self.borrow.replace(borrow - 1);
14321452
}
14331453
}
14341454

1435-
impl Clone for BorrowRef<'_> {
1455+
#[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
1456+
impl const Clone for BorrowRef<'_> {
14361457
#[inline]
14371458
fn clone(&self) -> Self {
14381459
// Since this Ref exists, we know the borrow flag
@@ -1442,7 +1463,7 @@ impl Clone for BorrowRef<'_> {
14421463
// Prevent the borrow counter from overflowing into
14431464
// a writing borrow.
14441465
assert!(borrow != BorrowCounter::MAX);
1445-
self.borrow.set(borrow + 1);
1466+
self.borrow.replace(borrow + 1);
14461467
BorrowRef { borrow: self.borrow }
14471468
}
14481469
}
@@ -1463,7 +1484,8 @@ pub struct Ref<'b, T: ?Sized + 'b> {
14631484
}
14641485

14651486
#[stable(feature = "rust1", since = "1.0.0")]
1466-
impl<T: ?Sized> Deref for Ref<'_, T> {
1487+
#[rustc_const_unstable(feature = "const_deref", issue = "88955")]
1488+
impl<T: ?Sized> const Deref for Ref<'_, T> {
14671489
type Target = T;
14681490

14691491
#[inline]
@@ -1488,7 +1510,8 @@ impl<'b, T: ?Sized> Ref<'b, T> {
14881510
#[stable(feature = "cell_extras", since = "1.15.0")]
14891511
#[must_use]
14901512
#[inline]
1491-
pub fn clone(orig: &Ref<'b, T>) -> Ref<'b, T> {
1513+
#[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
1514+
pub const fn clone(orig: &Ref<'b, T>) -> Ref<'b, T> {
14921515
Ref { value: orig.value, borrow: orig.borrow.clone() }
14931516
}
14941517

@@ -1610,7 +1633,8 @@ impl<'b, T: ?Sized> Ref<'b, T> {
16101633
/// assert!(cell.try_borrow_mut().is_err());
16111634
/// ```
16121635
#[unstable(feature = "cell_leak", issue = "69099")]
1613-
pub fn leak(orig: Ref<'b, T>) -> &'b T {
1636+
#[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
1637+
pub const fn leak(orig: Ref<'b, T>) -> &'b T {
16141638
// By forgetting this Ref we ensure that the borrow counter in the RefCell can't go back to
16151639
// UNUSED within the lifetime `'b`. Resetting the reference tracking state would require a
16161640
// unique reference to the borrowed RefCell. No further mutable references can be created
@@ -1776,7 +1800,8 @@ impl<'b, T: ?Sized> RefMut<'b, T> {
17761800
/// assert!(cell.try_borrow_mut().is_err());
17771801
/// ```
17781802
#[unstable(feature = "cell_leak", issue = "69099")]
1779-
pub fn leak(mut orig: RefMut<'b, T>) -> &'b mut T {
1803+
#[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
1804+
pub const fn leak(mut orig: RefMut<'b, T>) -> &'b mut T {
17801805
// By forgetting this BorrowRefMut we ensure that the borrow counter in the RefCell can't
17811806
// go back to UNUSED within the lifetime `'b`. Resetting the reference tracking state would
17821807
// require a unique reference to the borrowed RefCell. No further references can be created
@@ -1792,25 +1817,26 @@ struct BorrowRefMut<'b> {
17921817
borrow: &'b Cell<BorrowCounter>,
17931818
}
17941819

1795-
impl Drop for BorrowRefMut<'_> {
1820+
#[rustc_const_unstable(feature = "const_ref_cell", issue = "137844")]
1821+
impl const Drop for BorrowRefMut<'_> {
17961822
#[inline]
17971823
fn drop(&mut self) {
17981824
let borrow = self.borrow.get();
17991825
debug_assert!(is_writing(borrow));
1800-
self.borrow.set(borrow + 1);
1826+
self.borrow.replace(borrow + 1);
18011827
}
18021828
}
18031829

18041830
impl<'b> BorrowRefMut<'b> {
18051831
#[inline]
1806-
fn new(borrow: &'b Cell<BorrowCounter>) -> Option<BorrowRefMut<'b>> {
1832+
const fn new(borrow: &'b Cell<BorrowCounter>) -> Option<BorrowRefMut<'b>> {
18071833
// NOTE: Unlike BorrowRefMut::clone, new is called to create the initial
18081834
// mutable reference, and so there must currently be no existing
18091835
// references. Thus, while clone increments the mutable refcount, here
18101836
// we explicitly only allow going from UNUSED to UNUSED - 1.
18111837
match borrow.get() {
18121838
UNUSED => {
1813-
borrow.set(UNUSED - 1);
1839+
borrow.replace(UNUSED - 1);
18141840
Some(BorrowRefMut { borrow })
18151841
}
18161842
_ => None,
@@ -1849,7 +1875,8 @@ pub struct RefMut<'b, T: ?Sized + 'b> {
18491875
}
18501876

18511877
#[stable(feature = "rust1", since = "1.0.0")]
1852-
impl<T: ?Sized> Deref for RefMut<'_, T> {
1878+
#[rustc_const_unstable(feature = "const_deref", issue = "88955")]
1879+
impl<T: ?Sized> const Deref for RefMut<'_, T> {
18531880
type Target = T;
18541881

18551882
#[inline]
@@ -1860,7 +1887,8 @@ impl<T: ?Sized> Deref for RefMut<'_, T> {
18601887
}
18611888

18621889
#[stable(feature = "rust1", since = "1.0.0")]
1863-
impl<T: ?Sized> DerefMut for RefMut<'_, T> {
1890+
#[rustc_const_unstable(feature = "const_deref", issue = "88955")]
1891+
impl<T: ?Sized> const DerefMut for RefMut<'_, T> {
18641892
#[inline]
18651893
fn deref_mut(&mut self) -> &mut T {
18661894
// SAFETY: the value is accessible as long as we hold our borrow.

library/coretests/tests/cell.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use core::cell::*;
2+
use core::mem::forget;
23

34
#[test]
45
fn smoketest_unsafe_cell() {
@@ -477,3 +478,74 @@ fn const_cells() {
477478
const _: i32 = CELL.into_inner();
478479
*/
479480
}
481+
482+
#[test]
483+
fn refcell_borrow() {
484+
// Check that `borrow` is usable at compile-time
485+
const {
486+
let a = RefCell::new(0);
487+
assert!(a.try_borrow().is_ok());
488+
assert!(a.try_borrow_mut().is_ok());
489+
let a_ref = a.borrow();
490+
assert!(*a_ref == 0);
491+
assert!(a.try_borrow().is_ok());
492+
assert!(a.try_borrow_mut().is_err());
493+
}
494+
}
495+
496+
#[test]
497+
fn refcell_borrow_mut() {
498+
// Check that `borrow_mut` is usable at compile-time
499+
const {
500+
let mut a = RefCell::new(0);
501+
{
502+
assert!(a.try_borrow().is_ok());
503+
assert!(a.try_borrow_mut().is_ok());
504+
let mut a_ref = a.borrow_mut();
505+
assert!(*a_ref == 0);
506+
*a_ref = 10;
507+
assert!(*a_ref == 10);
508+
assert!(a.try_borrow().is_err());
509+
assert!(a.try_borrow_mut().is_err());
510+
}
511+
assert!(*a.get_mut() == 10);
512+
};
513+
}
514+
struct NeverDrop;
515+
impl Drop for NeverDrop {
516+
fn drop(&mut self) {
517+
panic!("should never be called");
518+
}
519+
}
520+
521+
#[test]
522+
fn refcell_replace() {
523+
// Check that `replace` is usable at compile-time
524+
const {
525+
let a = RefCell::new(0);
526+
assert!(a.replace(10) == 0);
527+
let a = a.into_inner();
528+
assert!(a == 10);
529+
530+
let b = RefCell::new(NeverDrop);
531+
forget(b.replace(NeverDrop));
532+
forget(b)
533+
};
534+
}
535+
536+
#[test]
537+
fn refcell_swap() {
538+
// Check that `swap` is usable at compile-time
539+
const {
540+
let (a, b) = (RefCell::new(31), RefCell::new(41));
541+
a.swap(&b);
542+
let (a, b) = (a.into_inner(), b.into_inner());
543+
assert!(a == 41);
544+
assert!(b == 31);
545+
546+
let c = RefCell::new(NeverDrop);
547+
let d = RefCell::new(NeverDrop);
548+
c.swap(&d);
549+
forget((c, d));
550+
};
551+
}

library/coretests/tests/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515
#![feature(cfg_target_has_reliable_f16_f128)]
1616
#![feature(char_max_len)]
1717
#![feature(clone_to_uninit)]
18+
#![feature(const_deref)]
19+
#![feature(const_destruct)]
1820
#![feature(const_eval_select)]
1921
#![feature(const_float_round_methods)]
22+
#![feature(const_ref_cell)]
2023
#![feature(const_trait_impl)]
2124
#![feature(core_float_math)]
2225
#![feature(core_intrinsics)]

0 commit comments

Comments
 (0)