Skip to content

Commit 774e814

Browse files
committed
Fix BTreeSet's range API panic message, document
1 parent 4334739 commit 774e814

File tree

7 files changed

+117
-13
lines changed

7 files changed

+117
-13
lines changed

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use super::dedup_sorted_iter::DedupSortedIter;
1616
use super::navigate::{LazyLeafRange, LeafRange};
1717
use super::node::{self, marker, ForceResult::*, Handle, NodeRef, Root};
1818
use super::search::SearchResult::*;
19+
use super::set_val::SetValZST;
1920

2021
mod entry;
2122

@@ -271,7 +272,7 @@ impl<K: Clone, V: Clone, A: Allocator + Clone> Clone for BTreeMap<K, V, A> {
271272
}
272273
}
273274

274-
impl<K, Q: ?Sized, A: Allocator + Clone> super::Recover<Q> for BTreeMap<K, (), A>
275+
impl<K, Q: ?Sized, A: Allocator + Clone> super::Recover<Q> for BTreeMap<K, SetValZST, A>
275276
where
276277
K: Borrow<Q> + Ord,
277278
Q: Ord,
@@ -318,7 +319,7 @@ where
318319
alloc: (*map.alloc).clone(),
319320
_marker: PhantomData,
320321
}
321-
.insert(());
322+
.insert(SetValZST::default());
322323
None
323324
}
324325
}

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

+33
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,39 @@ fn test_range_mut() {
897897
map.check();
898898
}
899899

900+
#[should_panic(expected = "range start is greater than range end in BTreeMap")]
901+
#[test]
902+
fn test_range_panic_1() {
903+
let mut map = BTreeMap::new();
904+
map.insert(3, "a");
905+
map.insert(5, "b");
906+
map.insert(8, "c");
907+
908+
let _invalid_range = map.range((Included(&8), Included(&3)));
909+
}
910+
911+
#[should_panic(expected = "range start and end are equal and excluded in BTreeMap")]
912+
#[test]
913+
fn test_range_panic_2() {
914+
let mut map = BTreeMap::new();
915+
map.insert(3, "a");
916+
map.insert(5, "b");
917+
map.insert(8, "c");
918+
919+
let _invalid_range = map.range((Excluded(&5), Excluded(&5)));
920+
}
921+
922+
#[should_panic(expected = "range start and end are equal and excluded in BTreeMap")]
923+
#[test]
924+
fn test_range_panic_3() {
925+
let mut map: BTreeMap<i32, ()> = BTreeMap::new();
926+
map.insert(3, ());
927+
map.insert(5, ());
928+
map.insert(8, ());
929+
930+
let _invalid_range = map.range((Excluded(&5), Excluded(&5)));
931+
}
932+
900933
#[test]
901934
fn test_retain() {
902935
let mut map = BTreeMap::from_iter((0..100).map(|x| (x, x * 10)));

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

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ mod node;
1010
mod remove;
1111
mod search;
1212
pub mod set;
13+
mod set_val;
1314
mod split;
1415

1516
#[doc(hidden)]

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

+13-2
Original file line numberDiff line numberDiff line change
@@ -97,17 +97,28 @@ impl<BorrowType: marker::BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Lea
9797
K: Borrow<Q>,
9898
R: RangeBounds<Q>,
9999
{
100+
// Determine if map or set is being searched
101+
let is_set = <V as super::set_val::IsSetVal>::is_set_val();
102+
100103
// Inlining these variables should be avoided. We assume the bounds reported by `range`
101104
// remain the same, but an adversarial implementation could change between calls (#81138).
102105
let (start, end) = (range.start_bound(), range.end_bound());
103106
match (start, end) {
104107
(Bound::Excluded(s), Bound::Excluded(e)) if s == e => {
105-
panic!("range start and end are equal and excluded in BTreeMap")
108+
if is_set {
109+
panic!("range start and end are equal and excluded in BTreeSet")
110+
} else {
111+
panic!("range start and end are equal and excluded in BTreeMap")
112+
}
106113
}
107114
(Bound::Included(s) | Bound::Excluded(s), Bound::Included(e) | Bound::Excluded(e))
108115
if s > e =>
109116
{
110-
panic!("range start is greater than range end in BTreeMap")
117+
if is_set {
118+
panic!("range start is greater than range end in BTreeSet")
119+
} else {
120+
panic!("range start is greater than range end in BTreeMap")
121+
}
111122
}
112123
_ => {}
113124
}

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

+15-9
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use core::ops::{BitAnd, BitOr, BitXor, RangeBounds, Sub};
1313

1414
use super::map::{BTreeMap, Keys};
1515
use super::merge_iter::MergeIterInner;
16+
use super::set_val::SetValZST;
1617
use super::Recover;
1718

1819
use crate::alloc::{Allocator, Global};
@@ -81,7 +82,7 @@ pub struct BTreeSet<
8182
T,
8283
#[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + Clone = Global,
8384
> {
84-
map: BTreeMap<T, (), A>,
85+
map: BTreeMap<T, SetValZST, A>,
8586
}
8687

8788
#[stable(feature = "rust1", since = "1.0.0")]
@@ -135,7 +136,7 @@ impl<T: Clone, A: Allocator + Clone> Clone for BTreeSet<T, A> {
135136
#[must_use = "iterators are lazy and do nothing unless consumed"]
136137
#[stable(feature = "rust1", since = "1.0.0")]
137138
pub struct Iter<'a, T: 'a> {
138-
iter: Keys<'a, T, ()>,
139+
iter: Keys<'a, T, SetValZST>,
139140
}
140141

141142
#[stable(feature = "collection_debug", since = "1.17.0")]
@@ -158,7 +159,7 @@ pub struct IntoIter<
158159
T,
159160
#[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + Clone = Global,
160161
> {
161-
iter: super::map::IntoIter<T, (), A>,
162+
iter: super::map::IntoIter<T, SetValZST, A>,
162163
}
163164

164165
/// An iterator over a sub-range of items in a `BTreeSet`.
@@ -171,7 +172,7 @@ pub struct IntoIter<
171172
#[derive(Debug)]
172173
#[stable(feature = "btree_range", since = "1.17.0")]
173174
pub struct Range<'a, T: 'a> {
174-
iter: super::map::Range<'a, T, ()>,
175+
iter: super::map::Range<'a, T, SetValZST>,
175176
}
176177

177178
/// A lazy iterator producing elements in the difference of `BTreeSet`s.
@@ -375,6 +376,11 @@ impl<T, A: Allocator + Clone> BTreeSet<T, A> {
375376
/// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive
376377
/// range from 4 to 10.
377378
///
379+
/// # Panics
380+
///
381+
/// Panics if range `start > end`.
382+
/// Panics if range `start == end` and both bounds are `Excluded`.
383+
///
378384
/// # Examples
379385
///
380386
/// ```
@@ -905,7 +911,7 @@ impl<T, A: Allocator + Clone> BTreeSet<T, A> {
905911
where
906912
T: Ord,
907913
{
908-
self.map.insert(value, ()).is_none()
914+
self.map.insert(value, SetValZST::default()).is_none()
909915
}
910916

911917
/// Adds a value to the set, replacing the existing element, if any, that is
@@ -1210,7 +1216,7 @@ impl<T: Ord> FromIterator<T> for BTreeSet<T> {
12101216

12111217
impl<T: Ord, A: Allocator + Clone> BTreeSet<T, A> {
12121218
fn from_sorted_iter<I: Iterator<Item = T>>(iter: I, alloc: A) -> BTreeSet<T, A> {
1213-
let iter = iter.map(|k| (k, ()));
1219+
let iter = iter.map(|k| (k, SetValZST::default()));
12141220
let map = BTreeMap::bulk_build_from_sorted_iter(iter, alloc);
12151221
BTreeSet { map }
12161222
}
@@ -1234,7 +1240,7 @@ impl<T: Ord, const N: usize> From<[T; N]> for BTreeSet<T> {
12341240

12351241
// use stable sort to preserve the insertion order.
12361242
arr.sort();
1237-
let iter = IntoIterator::into_iter(arr).map(|k| (k, ()));
1243+
let iter = IntoIterator::into_iter(arr).map(|k| (k, SetValZST::default()));
12381244
let map = BTreeMap::bulk_build_from_sorted_iter(iter, Global);
12391245
BTreeSet { map }
12401246
}
@@ -1284,7 +1290,7 @@ pub struct DrainFilter<
12841290
F: 'a + FnMut(&T) -> bool,
12851291
{
12861292
pred: F,
1287-
inner: super::map::DrainFilterInner<'a, T, ()>,
1293+
inner: super::map::DrainFilterInner<'a, T, SetValZST>,
12881294
/// The BTreeMap will outlive this IntoIter so we don't care about drop order for `alloc`.
12891295
alloc: A,
12901296
}
@@ -1319,7 +1325,7 @@ where
13191325

13201326
fn next(&mut self) -> Option<T> {
13211327
let pred = &mut self.pred;
1322-
let mut mapped_pred = |k: &T, _v: &mut ()| pred(k);
1328+
let mut mapped_pred = |k: &T, _v: &mut SetValZST| pred(k);
13231329
self.inner.next(&mut mapped_pred, self.alloc.clone()).map(|(k, _)| k)
13241330
}
13251331

library/alloc/src/collections/btree/set/tests.rs

+23
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::vec::Vec;
55
use std::cmp::Ordering;
66
use std::hash::{Hash, Hasher};
77
use std::iter::FromIterator;
8+
use std::ops::Bound::{Excluded, Included};
89
use std::panic::{catch_unwind, AssertUnwindSafe};
910

1011
#[test]
@@ -831,3 +832,25 @@ fn from_array() {
831832
let unordered_duplicates = BTreeSet::from([4, 1, 4, 3, 2]);
832833
assert_eq!(set, unordered_duplicates);
833834
}
835+
836+
#[should_panic(expected = "range start is greater than range end in BTreeSet")]
837+
#[test]
838+
fn test_range_panic_1() {
839+
let mut set = BTreeSet::new();
840+
set.insert(3);
841+
set.insert(5);
842+
set.insert(8);
843+
844+
let _invalid_range = set.range((Included(&8), Included(&3)));
845+
}
846+
847+
#[should_panic(expected = "range start and end are equal and excluded in BTreeSet")]
848+
#[test]
849+
fn test_range_panic_2() {
850+
let mut set = BTreeSet::new();
851+
set.insert(3);
852+
set.insert(5);
853+
set.insert(8);
854+
855+
let _invalid_range = set.range((Excluded(&5), Excluded(&5)));
856+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/// Zero-Sized Type (ZST) for internal `BTreeSet` values.
2+
/// Used instead of `()` to differentiate between:
3+
/// * `BTreeMap<T, ()>` (possible user-defined map)
4+
/// * `BTreeMap<T, SetValZST>` (internal set representation)
5+
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Default)]
6+
pub struct SetValZST;
7+
8+
/// A trait to differentiate between `BTreeMap` and `BTreeSet` values.
9+
/// Returns `true` only for type `SetValZST`, `false` for all other types (blanket implementation).
10+
/// `TypeId` requires a `'static` lifetime, use of this trait avoids that restriction.
11+
///
12+
/// [`TypeId`]: std::any::TypeId
13+
pub trait IsSetVal {
14+
fn is_set_val() -> bool;
15+
}
16+
17+
// Blanket implementation
18+
impl<V> IsSetVal for V {
19+
default fn is_set_val() -> bool {
20+
false
21+
}
22+
}
23+
24+
// Specialization
25+
impl IsSetVal for SetValZST {
26+
fn is_set_val() -> bool {
27+
true
28+
}
29+
}

0 commit comments

Comments
 (0)