Skip to content

Commit 7f2cb4f

Browse files
manual borrow counting in CmRefCell so we don't need to match in CmRef::deref
1 parent 5475d00 commit 7f2cb4f

1 file changed

Lines changed: 125 additions & 32 deletions

File tree

  • compiler/rustc_resolve/src

compiler/rustc_resolve/src/lib.rs

Lines changed: 125 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@
1818
#![feature(option_into_flat_iter)]
1919
#![feature(rustc_attrs)]
2020
#![feature(trim_prefix_suffix)]
21+
#![feature(unsafe_cell_access)]
2122
#![recursion_limit = "256"]
2223
// tidy-alphabetical-end
2324

24-
use std::cell::RefMut;
2525
use std::collections::BTreeSet;
2626
use std::ops::ControlFlow;
2727
use std::sync::{Arc, OnceLock};
@@ -81,7 +81,7 @@ use crate::diagnostics::impls::{
8181
ImportSuggestion, LabelSuggestion, OnUnknownData, StructCtor, Suggestion,
8282
};
8383
use crate::imports::{ImportResolution, NameResolutionRef};
84-
use crate::ref_mut::{CmCell, CmRef, CmRefCell};
84+
use crate::ref_mut::{CmCell, CmRef, CmRefCell, RefMut};
8585

8686
mod build_reduced_graph;
8787
mod check_unused;
@@ -2182,9 +2182,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
21822182
match &module.0.0.lazy_resolutions {
21832183
Resolutions::Local(local_res) => local_res.borrow(self),
21842184
Resolutions::Extern(extern_res) => {
2185-
// It is fine to return a `CmRef::Untracked`, we never give out a `&mut`
2185+
// It is fine to return a `CmRef::untracked`, we never give out a `&mut`
21862186
// to an external table.
2187-
CmRef::Untracked(
2187+
CmRef::untracked(
21882188
// As long as 1 thread is building this external table, all other threads will wait.
21892189
extern_res
21902190
.get_or_init(|| self.build_reduced_graph_external(module.expect_extern())),
@@ -2831,9 +2831,11 @@ type CmResolver<'r, 'ra, 'tcx> = ref_mut::RefOrMut<'r, Resolver<'ra, 'tcx>>;
28312831
use std::cell::{Cell as CacheCell, RefCell as CacheRefCell};
28322832

28332833
mod ref_mut {
2834-
use std::cell::{BorrowMutError, Cell, Ref, RefCell, RefMut};
2834+
use std::cell::{Cell, UnsafeCell};
28352835
use std::fmt;
2836-
use std::ops::Deref;
2836+
use std::marker::PhantomData;
2837+
use std::ops::{Deref, DerefMut};
2838+
use std::ptr::NonNull;
28372839

28382840
use crate::Resolver;
28392841

@@ -2946,31 +2948,82 @@ mod ref_mut {
29462948
}
29472949
}
29482950

2949-
pub(crate) enum CmRef<'b, T> {
2950-
/// A tracked borrow of a [`CmRefCell`]
2951-
Tracked(Ref<'b, T>),
2952-
/// An untracked or normal reference (not dynamically borrow-checked by `RefCell`)
2953-
Untracked(&'b T),
2951+
const UNUSED: isize = 0;
2952+
2953+
pub(crate) struct CmRef<'b, T> {
2954+
value: NonNull<T>,
2955+
borrow: Option<&'b Cell<isize>>,
2956+
marker: PhantomData<&'b T>,
2957+
}
2958+
2959+
impl<'b, T> CmRef<'b, T> {
2960+
pub(crate) fn tracked(value: &'b T, borrow: &'b Cell<isize>) -> Self {
2961+
Self { value: NonNull::from_ref(value), borrow: Some(borrow), marker: PhantomData }
2962+
}
2963+
2964+
pub(crate) fn untracked(value: &'b T) -> Self {
2965+
Self { value: NonNull::from_ref(value), borrow: None, marker: PhantomData }
2966+
}
2967+
}
2968+
2969+
impl<'b, T> Drop for CmRef<'b, T> {
2970+
fn drop(&mut self) {
2971+
if let Some(borrow) = self.borrow {
2972+
borrow.update(|b| b - 1);
2973+
}
2974+
}
29542975
}
29552976

29562977
impl<'b, T> Deref for CmRef<'b, T> {
29572978
type Target = T;
29582979

29592980
fn deref(&self) -> &Self::Target {
2960-
match self {
2961-
CmRef::Tracked(r) => r,
2962-
CmRef::Untracked(r) => r,
2963-
}
2981+
// SAFETY: This type is proof we have shared access
2982+
unsafe { self.value.as_ref() }
2983+
}
2984+
}
2985+
2986+
pub(crate) struct RefMut<'b, T> {
2987+
value: NonNull<T>,
2988+
borrow: &'b Cell<isize>,
2989+
marker: PhantomData<&'b mut T>,
2990+
}
2991+
2992+
impl<'b, T> Drop for RefMut<'b, T> {
2993+
fn drop(&mut self) {
2994+
self.borrow.update(|b| {
2995+
debug_assert!(b < UNUSED, "`RefMut` exists but no borrow is counted");
2996+
UNUSED // `RefMut` does not impl clone, so UNUSED is enough here
2997+
});
2998+
}
2999+
}
3000+
3001+
impl<'b, T> Deref for RefMut<'b, T> {
3002+
type Target = T;
3003+
3004+
fn deref(&self) -> &Self::Target {
3005+
// SAFETY: This type is proof we have exclusive access
3006+
unsafe { self.value.as_ref() }
3007+
}
3008+
}
3009+
3010+
impl<'b, T> DerefMut for RefMut<'b, T> {
3011+
fn deref_mut(&mut self) -> &mut Self::Target {
3012+
// SAFETY: This type is proof we have exclusive access
3013+
unsafe { self.value.as_mut() }
29643014
}
29653015
}
29663016

29673017
/// A wrapper around a [`RefCell`] that only allows writes (mutable borrows) based on a condition in the resolver.
29683018
#[derive(Default)]
2969-
pub(crate) struct CmRefCell<T>(RefCell<T>);
3019+
pub(crate) struct CmRefCell<T> {
3020+
value: UnsafeCell<T>,
3021+
borrow: Cell<isize>,
3022+
}
29703023

29713024
impl<T> CmRefCell<T> {
29723025
pub(crate) fn new(value: T) -> CmRefCell<T> {
2973-
CmRefCell(RefCell::new(value))
3026+
CmRefCell { value: UnsafeCell::new(value), borrow: Cell::new(UNUSED) }
29743027
}
29753028

29763029
#[track_caller]
@@ -2982,37 +3035,76 @@ mod ref_mut {
29823035
pub(crate) fn try_borrow_mut<'ra, 'tcx>(
29833036
&self,
29843037
r: &Resolver<'ra, 'tcx>,
2985-
) -> Result<RefMut<'_, T>, BorrowMutError> {
3038+
) -> Result<RefMut<'_, T>, ()> {
29863039
if r.assert_speculative {
29873040
panic!("not allowed to mutably borrow a `CmRefCell` during speculative resolution");
29883041
}
2989-
self.0.try_borrow_mut()
3042+
// SAFETY: we just checked that we are not in speculative resolution
3043+
unsafe { self.try_borrow_mut_inner() }
3044+
}
3045+
3046+
/// # SAFETY
3047+
///
3048+
/// Caller must guarentee that we are not in speculative resolution
3049+
unsafe fn try_borrow_mut_inner(&self) -> Result<RefMut<'_, T>, ()> {
3050+
let borrow = self.borrow.get();
3051+
if borrow != UNUSED {
3052+
return Err(());
3053+
}
3054+
self.borrow.update(|_| UNUSED - 1);
3055+
// SAFETY: We can take exclusive access since this is unused.
3056+
let value = unsafe { self.value.as_mut_unchecked() };
3057+
Ok(RefMut {
3058+
value: NonNull::from_mut(value),
3059+
borrow: &self.borrow,
3060+
marker: PhantomData,
3061+
})
29903062
}
29913063

29923064
#[track_caller]
2993-
pub(crate) fn borrow_mut_with_token(&self, _: token::CmToken<'_>) -> RefMut<'_, T> {
2994-
self.0.borrow_mut()
3065+
pub(crate) fn borrow_mut_with_token(&self, t: token::CmToken<'_>) -> RefMut<'_, T> {
3066+
self.try_borrow_mut_with_token(t).unwrap()
29953067
}
29963068

29973069
#[track_caller]
29983070
pub(crate) fn try_borrow_mut_with_token(
29993071
&self,
30003072
_: token::CmToken<'_>,
3001-
) -> Result<RefMut<'_, T>, BorrowMutError> {
3002-
self.0.try_borrow_mut()
3073+
) -> Result<RefMut<'_, T>, ()> {
3074+
// SAFETY: the given token guarentees us we are not in speculative resolution.
3075+
unsafe { self.try_borrow_mut_inner() }
3076+
}
3077+
3078+
/// # SAFETY
3079+
///
3080+
/// Caller must guarentee that we are not in speculative resolution
3081+
pub(crate) unsafe fn borrow_inner(&self) -> CmRef<'_, T> {
3082+
let borrow = self.borrow.get();
3083+
if borrow < UNUSED {
3084+
panic!("`CmRefCell` already mutably borrowed")
3085+
}
3086+
self.borrow.update(|b| b + 1);
3087+
// SAFETY: We can take shared access since this is not mutably borrowed.
3088+
let value = unsafe { self.value.as_ref_unchecked() };
3089+
3090+
CmRef::tracked(value, &self.borrow)
30033091
}
30043092

30053093
#[track_caller]
3006-
pub(crate) fn borrow_with_token(&self, _: token::CmToken<'_>) -> Ref<'_, T> {
3007-
self.0.borrow()
3094+
pub(crate) fn borrow_with_token(&self, _: token::CmToken<'_>) -> CmRef<'_, T> {
3095+
// SAFETY: token guarentees us that we are not in speculative resolution.
3096+
unsafe { self.borrow_inner() }
30083097
}
30093098

30103099
#[track_caller]
30113100
pub(crate) fn borrow<'ra, 'tcx>(&self, r: &Resolver<'ra, 'tcx>) -> CmRef<'_, T> {
30123101
if r.assert_speculative {
3013-
// `try_borrow_unguarded` is unsafe because it returns a `&T` instead
3014-
// of `Ref<'_, T>`. It does provides an extra check to make sure no live
3015-
// `RefMut`s are still alive, but the other way can not be checked, so:
3102+
if self.borrow.get() < UNUSED {
3103+
panic!("`CmRefCell` already mutably borrowed");
3104+
}
3105+
// mimics `RefCell::try_borrow_unguarded`, which is unsafe because it returns a
3106+
// `&T` instead of `Ref<'_, T>`. It does provides an extra check to make sure no
3107+
// live `RefMut`s are still alive, but the other way can not be checked, so:
30163108
//
30173109
// SAFETY: This is only safe because we know that every `Untracked` borrow
30183110
// is only created during the import resolutions phase:
@@ -3027,10 +3119,11 @@ mod ref_mut {
30273119
//
30283120
// We know that none of these `Untracked` borrows are alive after the import
30293121
// resolution phase is done (`assert_speculative = false`); so we deem this "safe".
3030-
let unguarded_borrow = unsafe { self.0.try_borrow_unguarded().unwrap() };
3031-
CmRef::Untracked(unguarded_borrow)
3122+
let unguarded_borrow = unsafe { self.value.as_ref_unchecked() };
3123+
CmRef::untracked(unguarded_borrow)
30323124
} else {
3033-
CmRef::Tracked(self.0.borrow())
3125+
// SAFETY: we just checked that we are not in speculative resolution.
3126+
unsafe { self.borrow_inner() }
30343127
}
30353128
}
30363129
}
@@ -3040,7 +3133,7 @@ mod ref_mut {
30403133
if r.assert_speculative {
30413134
panic!("not allowed to mutate a CmRefCell during speculative resolution");
30423135
}
3043-
self.0.take()
3136+
std::mem::take(&mut *self.borrow_mut(r))
30443137
}
30453138
}
30463139
}

0 commit comments

Comments
 (0)