Skip to content

Commit ee04f9a

Browse files
committed
Auto merge of rust-lang#74437 - ssomers:btree_no_root_in_noderef, r=Mark-Simulacrum
BTreeMap: move up reference to map's root from NodeRef Since the introduction of `NodeRef` years ago, it also contained a mutable reference to the owner of the root node of the tree (somewhat disguised as *const). Its intent is to be used only when the rest of the `NodeRef` is no longer needed. Moving this to where it's actually used, thought me 2 things: - Some sort of "postponed mutable reference" is required in most places that it is/was used, and that's exactly where we also need to store a reference to the length (number of elements) of the tree, for the same reason. The length reference can be a normal reference, because the tree code does not care about tree length (just length per node). - It's downright obfuscation in `from_sorted_iter` (transplanted to rust-lang#75329) - It's one of the reasons for the scary notice on `reborrow_mut`, the other one being addressed in rust-lang#73971. This does repeat the raw pointer code in a few places, but it could be bundled up with the length reference. r? `@Mark-Simulacrum`
2 parents a1947b3 + 2b54ab8 commit ee04f9a

File tree

5 files changed

+163
-117
lines changed

5 files changed

+163
-117
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use core::marker::PhantomData;
2+
use core::ptr::NonNull;
3+
4+
/// Models a reborrow of some unique reference, when you know that the reborrow
5+
/// and all its descendants (i.e., all pointers and references derived from it)
6+
/// will not be used any more at some point, after which you want to use the
7+
/// original unique reference again.
8+
///
9+
/// The borrow checker usually handles this stacking of borrows for you, but
10+
/// some control flows that accomplish this stacking are too complicated for
11+
/// the compiler to follow. A `DormantMutRef` allows you to check borrowing
12+
/// yourself, while still expressing its stacked nature, and encapsulating
13+
/// the raw pointer code needed to do this without undefined behavior.
14+
pub struct DormantMutRef<'a, T> {
15+
ptr: NonNull<T>,
16+
_marker: PhantomData<&'a mut T>,
17+
}
18+
19+
impl<'a, T> DormantMutRef<'a, T> {
20+
/// Capture a unique borrow, and immediately reborrow it. For the compiler,
21+
/// the lifetime of the new reference is the same as the lifetime of the
22+
/// original reference, but you promise to use it for a shorter period.
23+
pub fn new(t: &'a mut T) -> (&'a mut T, Self) {
24+
let ptr = NonNull::from(t);
25+
// SAFETY: we hold the borrow throughout 'a via `_marker`, and we expose
26+
// only this reference, so it is unique.
27+
let new_ref = unsafe { &mut *ptr.as_ptr() };
28+
(new_ref, Self { ptr, _marker: PhantomData })
29+
}
30+
31+
/// Revert to the unique borrow initially captured.
32+
///
33+
/// # Safety
34+
///
35+
/// The reborrow must have ended, i.e., the reference returned by `new` and
36+
/// all pointers and references derived from it, must not be used anymore.
37+
pub unsafe fn awaken(self) -> &'a mut T {
38+
// SAFETY: our own safety conditions imply this reference is again unique.
39+
unsafe { &mut *self.ptr.as_ptr() }
40+
}
41+
}
42+
43+
#[cfg(test)]
44+
mod tests;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use super::DormantMutRef;
2+
3+
#[test]
4+
fn test_borrow() {
5+
let mut data = 1;
6+
let mut stack = vec![];
7+
let mut rr = &mut data;
8+
for factor in [2, 3, 7].iter() {
9+
let (r, dormant_r) = DormantMutRef::new(rr);
10+
rr = r;
11+
assert_eq!(*rr, 1);
12+
stack.push((factor, dormant_r));
13+
}
14+
while let Some((factor, dormant_r)) = stack.pop() {
15+
let r = unsafe { dormant_r.awaken() };
16+
*r *= factor;
17+
}
18+
assert_eq!(data, 42);
19+
}

library/alloc/src/collections/btree/map.rs

+72-49
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use core::mem::{self, ManuallyDrop};
88
use core::ops::{Index, RangeBounds};
99
use core::ptr;
1010

11+
use super::borrow::DormantMutRef;
1112
use super::node::{self, marker, ForceResult::*, Handle, InsertResult::*, NodeRef};
1213
use super::search::{self, SearchResult::*};
1314
use super::unwrap_unchecked;
@@ -228,24 +229,23 @@ where
228229
}
229230

230231
fn take(&mut self, key: &Q) -> Option<K> {
231-
let root_node = self.root.as_mut()?.node_as_mut();
232+
let (map, dormant_map) = DormantMutRef::new(self);
233+
let root_node = map.root.as_mut()?.node_as_mut();
232234
match search::search_tree(root_node, key) {
233-
Found(handle) => Some(
234-
OccupiedEntry { handle, length: &mut self.length, _marker: PhantomData }
235-
.remove_kv()
236-
.0,
237-
),
235+
Found(handle) => {
236+
Some(OccupiedEntry { handle, dormant_map, _marker: PhantomData }.remove_kv().0)
237+
}
238238
GoDown(_) => None,
239239
}
240240
}
241241

242242
fn replace(&mut self, key: K) -> Option<K> {
243-
let root = Self::ensure_is_owned(&mut self.root);
244-
match search::search_tree::<marker::Mut<'_>, K, (), K>(root.node_as_mut(), &key) {
243+
let (map, dormant_map) = DormantMutRef::new(self);
244+
let root_node = Self::ensure_is_owned(&mut map.root).node_as_mut();
245+
match search::search_tree::<marker::Mut<'_>, K, (), K>(root_node, &key) {
245246
Found(handle) => Some(mem::replace(handle.into_key_mut(), key)),
246247
GoDown(handle) => {
247-
VacantEntry { key, handle, length: &mut self.length, _marker: PhantomData }
248-
.insert(());
248+
VacantEntry { key, handle, dormant_map, _marker: PhantomData }.insert(());
249249
None
250250
}
251251
}
@@ -459,7 +459,7 @@ impl<K: Debug + Ord, V: Debug> Debug for Entry<'_, K, V> {
459459
pub struct VacantEntry<'a, K: 'a, V: 'a> {
460460
key: K,
461461
handle: Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>,
462-
length: &'a mut usize,
462+
dormant_map: DormantMutRef<'a, BTreeMap<K, V>>,
463463

464464
// Be invariant in `K` and `V`
465465
_marker: PhantomData<&'a mut (K, V)>,
@@ -479,8 +479,7 @@ impl<K: Debug + Ord, V> Debug for VacantEntry<'_, K, V> {
479479
#[stable(feature = "rust1", since = "1.0.0")]
480480
pub struct OccupiedEntry<'a, K: 'a, V: 'a> {
481481
handle: Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, marker::KV>,
482-
483-
length: &'a mut usize,
482+
dormant_map: DormantMutRef<'a, BTreeMap<K, V>>,
484483

485484
// Be invariant in `K` and `V`
486485
_marker: PhantomData<&'a mut (K, V)>,
@@ -644,13 +643,10 @@ impl<K: Ord, V> BTreeMap<K, V> {
644643
/// ```
645644
#[unstable(feature = "map_first_last", issue = "62924")]
646645
pub fn first_entry(&mut self) -> Option<OccupiedEntry<'_, K, V>> {
647-
let root_node = self.root.as_mut()?.node_as_mut();
646+
let (map, dormant_map) = DormantMutRef::new(self);
647+
let root_node = map.root.as_mut()?.node_as_mut();
648648
let kv = root_node.first_leaf_edge().right_kv().ok()?;
649-
Some(OccupiedEntry {
650-
handle: kv.forget_node_type(),
651-
length: &mut self.length,
652-
_marker: PhantomData,
653-
})
649+
Some(OccupiedEntry { handle: kv.forget_node_type(), dormant_map, _marker: PhantomData })
654650
}
655651

656652
/// Removes and returns the first element in the map.
@@ -721,13 +717,10 @@ impl<K: Ord, V> BTreeMap<K, V> {
721717
/// ```
722718
#[unstable(feature = "map_first_last", issue = "62924")]
723719
pub fn last_entry(&mut self) -> Option<OccupiedEntry<'_, K, V>> {
724-
let root_node = self.root.as_mut()?.node_as_mut();
720+
let (map, dormant_map) = DormantMutRef::new(self);
721+
let root_node = map.root.as_mut()?.node_as_mut();
725722
let kv = root_node.last_leaf_edge().left_kv().ok()?;
726-
Some(OccupiedEntry {
727-
handle: kv.forget_node_type(),
728-
length: &mut self.length,
729-
_marker: PhantomData,
730-
})
723+
Some(OccupiedEntry { handle: kv.forget_node_type(), dormant_map, _marker: PhantomData })
731724
}
732725

733726
/// Removes and returns the last element in the map.
@@ -901,12 +894,12 @@ impl<K: Ord, V> BTreeMap<K, V> {
901894
K: Borrow<Q>,
902895
Q: Ord,
903896
{
904-
let root_node = self.root.as_mut()?.node_as_mut();
897+
let (map, dormant_map) = DormantMutRef::new(self);
898+
let root_node = map.root.as_mut()?.node_as_mut();
905899
match search::search_tree(root_node, key) {
906-
Found(handle) => Some(
907-
OccupiedEntry { handle, length: &mut self.length, _marker: PhantomData }
908-
.remove_entry(),
909-
),
900+
Found(handle) => {
901+
Some(OccupiedEntry { handle, dormant_map, _marker: PhantomData }.remove_entry())
902+
}
910903
GoDown(_) => None,
911904
}
912905
}
@@ -1073,13 +1066,12 @@ impl<K: Ord, V> BTreeMap<K, V> {
10731066
#[stable(feature = "rust1", since = "1.0.0")]
10741067
pub fn entry(&mut self, key: K) -> Entry<'_, K, V> {
10751068
// FIXME(@porglezomp) Avoid allocating if we don't insert
1076-
let root = Self::ensure_is_owned(&mut self.root);
1077-
match search::search_tree(root.node_as_mut(), &key) {
1078-
Found(handle) => {
1079-
Occupied(OccupiedEntry { handle, length: &mut self.length, _marker: PhantomData })
1080-
}
1069+
let (map, dormant_map) = DormantMutRef::new(self);
1070+
let root_node = Self::ensure_is_owned(&mut map.root).node_as_mut();
1071+
match search::search_tree(root_node, &key) {
1072+
Found(handle) => Occupied(OccupiedEntry { handle, dormant_map, _marker: PhantomData }),
10811073
GoDown(handle) => {
1082-
Vacant(VacantEntry { key, handle, length: &mut self.length, _marker: PhantomData })
1074+
Vacant(VacantEntry { key, handle, dormant_map, _marker: PhantomData })
10831075
}
10841076
}
10851077
}
@@ -1284,9 +1276,17 @@ impl<K: Ord, V> BTreeMap<K, V> {
12841276
}
12851277

12861278
pub(super) fn drain_filter_inner(&mut self) -> DrainFilterInner<'_, K, V> {
1287-
let root_node = self.root.as_mut().map(|r| r.node_as_mut());
1288-
let front = root_node.map(|rn| rn.first_leaf_edge());
1289-
DrainFilterInner { length: &mut self.length, cur_leaf_edge: front }
1279+
if let Some(root) = self.root.as_mut() {
1280+
let (root, dormant_root) = DormantMutRef::new(root);
1281+
let front = root.node_as_mut().first_leaf_edge();
1282+
DrainFilterInner {
1283+
length: &mut self.length,
1284+
dormant_root: Some(dormant_root),
1285+
cur_leaf_edge: Some(front),
1286+
}
1287+
} else {
1288+
DrainFilterInner { length: &mut self.length, dormant_root: None, cur_leaf_edge: None }
1289+
}
12901290
}
12911291

12921292
/// Creates a consuming iterator visiting all the keys, in sorted order.
@@ -1671,6 +1671,9 @@ where
16711671
/// of the predicate, thus also serving for BTreeSet::DrainFilter.
16721672
pub(super) struct DrainFilterInner<'a, K: 'a, V: 'a> {
16731673
length: &'a mut usize,
1674+
// dormant_root is wrapped in an Option to be able to `take` it.
1675+
dormant_root: Option<DormantMutRef<'a, node::Root<K, V>>>,
1676+
// cur_leaf_edge is wrapped in an Option because maps without root lack a leaf edge.
16741677
cur_leaf_edge: Option<Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>>,
16751678
}
16761679

@@ -1728,7 +1731,13 @@ impl<'a, K: 'a, V: 'a> DrainFilterInner<'a, K, V> {
17281731
let (k, v) = kv.kv_mut();
17291732
if pred(k, v) {
17301733
*self.length -= 1;
1731-
let (kv, pos) = kv.remove_kv_tracking();
1734+
let (kv, pos) = kv.remove_kv_tracking(|| {
1735+
// SAFETY: we will touch the root in a way that will not
1736+
// invalidate the position returned.
1737+
let root = unsafe { self.dormant_root.take().unwrap().awaken() };
1738+
root.pop_internal_level();
1739+
self.dormant_root = Some(DormantMutRef::new(root).1);
1740+
});
17321741
self.cur_leaf_edge = Some(pos);
17331742
return Some(kv);
17341743
}
@@ -2456,13 +2465,20 @@ impl<'a, K: Ord, V> VacantEntry<'a, K, V> {
24562465
/// ```
24572466
#[stable(feature = "rust1", since = "1.0.0")]
24582467
pub fn insert(self, value: V) -> &'a mut V {
2459-
*self.length += 1;
2460-
24612468
let out_ptr = match self.handle.insert_recursing(self.key, value) {
2462-
(Fit(_), val_ptr) => val_ptr,
2469+
(Fit(_), val_ptr) => {
2470+
// Safety: We have consumed self.handle and the handle returned.
2471+
let map = unsafe { self.dormant_map.awaken() };
2472+
map.length += 1;
2473+
val_ptr
2474+
}
24632475
(Split(ins), val_ptr) => {
2464-
let root = ins.left.into_root_mut();
2476+
drop(ins.left);
2477+
// Safety: We have consumed self.handle and the reference returned.
2478+
let map = unsafe { self.dormant_map.awaken() };
2479+
let root = map.root.as_mut().unwrap();
24652480
root.push_internal_level().push(ins.k, ins.v, ins.right);
2481+
map.length += 1;
24662482
val_ptr
24672483
}
24682484
};
@@ -2636,18 +2652,25 @@ impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> {
26362652

26372653
// Body of `remove_entry`, separate to keep the above implementations short.
26382654
fn remove_kv(self) -> (K, V) {
2639-
*self.length -= 1;
2640-
2641-
let (old_kv, _) = self.handle.remove_kv_tracking();
2655+
let mut emptied_internal_root = false;
2656+
let (old_kv, _) = self.handle.remove_kv_tracking(|| emptied_internal_root = true);
2657+
// SAFETY: we consumed the intermediate root borrow, `self.handle`.
2658+
let map = unsafe { self.dormant_map.awaken() };
2659+
map.length -= 1;
2660+
if emptied_internal_root {
2661+
let root = map.root.as_mut().unwrap();
2662+
root.pop_internal_level();
2663+
}
26422664
old_kv
26432665
}
26442666
}
26452667

26462668
impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, marker::KV> {
26472669
/// Removes a key/value-pair from the map, and returns that pair, as well as
26482670
/// the leaf edge corresponding to that former pair.
2649-
fn remove_kv_tracking(
2671+
fn remove_kv_tracking<F: FnOnce()>(
26502672
self,
2673+
handle_emptied_internal_root: F,
26512674
) -> ((K, V), Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>) {
26522675
let (old_kv, mut pos, was_internal) = match self.force() {
26532676
Leaf(leaf) => {
@@ -2700,7 +2723,7 @@ impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInter
27002723
// The parent that was just emptied must be the root,
27012724
// because nodes on a lower level would not have been
27022725
// left with a single child.
2703-
parent.into_root_mut().pop_internal_level();
2726+
handle_emptied_internal_root();
27042727
break;
27052728
} else {
27062729
cur_node = parent.forget_type();

library/alloc/src/collections/btree/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod borrow;
12
pub mod map;
23
mod navigate;
34
mod node;

0 commit comments

Comments
 (0)