Skip to content

Commit c0955a3

Browse files
committed
Auto merge of #51622 - kennytm:three-field-range-inclusive, r=SimonSapin
Change RangeInclusive to a three-field struct. Fix #45222. This PR also reverts #48012 (i.e. removed the `try_fold`/`try_rfold` specialization for `RangeInclusive`) because LLVM no longer has trouble recognizing a RangeInclusive loop.
2 parents 8b48b24 + 6093128 commit c0955a3

File tree

7 files changed

+208
-112
lines changed

7 files changed

+208
-112
lines changed

src/libcore/iter/mod.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -787,17 +787,19 @@ where
787787
#[inline]
788788
fn spec_next(&mut self) -> Option<Self::Item> {
789789
self.first_take = false;
790-
if !(self.iter.start <= self.iter.end) {
790+
self.iter.compute_is_empty();
791+
if self.iter.is_empty.unwrap_or_default() {
791792
return None;
792793
}
793794
// add 1 to self.step to get original step size back
794795
// it was decremented for the general case on construction
795796
if let Some(n) = self.iter.start.add_usize(self.step+1) {
797+
self.iter.is_empty = Some(!(n <= self.iter.end));
796798
let next = mem::replace(&mut self.iter.start, n);
797799
Some(next)
798800
} else {
799-
let last = self.iter.start.replace_one();
800-
self.iter.end.replace_zero();
801+
let last = self.iter.start.clone();
802+
self.iter.is_empty = Some(true);
801803
Some(last)
802804
}
803805
}

src/libcore/iter/range.rs

+32-72
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
use convert::TryFrom;
1212
use mem;
13-
use ops::{self, Add, Sub, Try};
13+
use ops::{self, Add, Sub};
1414
use usize;
1515

1616
use super::{FusedIterator, TrustedLen};
@@ -330,23 +330,23 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> {
330330

331331
#[inline]
332332
fn next(&mut self) -> Option<A> {
333-
if self.start <= self.end {
334-
if self.start < self.end {
335-
let n = self.start.add_one();
336-
Some(mem::replace(&mut self.start, n))
337-
} else {
338-
let last = self.start.replace_one();
339-
self.end.replace_zero();
340-
Some(last)
341-
}
342-
} else {
343-
None
333+
self.compute_is_empty();
334+
if self.is_empty.unwrap_or_default() {
335+
return None;
344336
}
337+
let is_iterating = self.start < self.end;
338+
self.is_empty = Some(!is_iterating);
339+
Some(if is_iterating {
340+
let n = self.start.add_one();
341+
mem::replace(&mut self.start, n)
342+
} else {
343+
self.start.clone()
344+
})
345345
}
346346

347347
#[inline]
348348
fn size_hint(&self) -> (usize, Option<usize>) {
349-
if !(self.start <= self.end) {
349+
if self.is_empty() {
350350
return (0, Some(0));
351351
}
352352

@@ -358,25 +358,29 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> {
358358

359359
#[inline]
360360
fn nth(&mut self, n: usize) -> Option<A> {
361+
self.compute_is_empty();
362+
if self.is_empty.unwrap_or_default() {
363+
return None;
364+
}
365+
361366
if let Some(plus_n) = self.start.add_usize(n) {
362367
use cmp::Ordering::*;
363368

364369
match plus_n.partial_cmp(&self.end) {
365370
Some(Less) => {
371+
self.is_empty = Some(false);
366372
self.start = plus_n.add_one();
367373
return Some(plus_n)
368374
}
369375
Some(Equal) => {
370-
self.start.replace_one();
371-
self.end.replace_zero();
376+
self.is_empty = Some(true);
372377
return Some(plus_n)
373378
}
374379
_ => {}
375380
}
376381
}
377382

378-
self.start.replace_one();
379-
self.end.replace_zero();
383+
self.is_empty = Some(true);
380384
None
381385
}
382386

@@ -394,68 +398,24 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> {
394398
fn max(mut self) -> Option<A> {
395399
self.next_back()
396400
}
397-
398-
#[inline]
399-
fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R where
400-
Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try<Ok=B>
401-
{
402-
let mut accum = init;
403-
if self.start <= self.end {
404-
loop {
405-
let (x, done) =
406-
if self.start < self.end {
407-
let n = self.start.add_one();
408-
(mem::replace(&mut self.start, n), false)
409-
} else {
410-
self.end.replace_zero();
411-
(self.start.replace_one(), true)
412-
};
413-
accum = f(accum, x)?;
414-
if done { break }
415-
}
416-
}
417-
Try::from_ok(accum)
418-
}
419401
}
420402

421403
#[stable(feature = "inclusive_range", since = "1.26.0")]
422404
impl<A: Step> DoubleEndedIterator for ops::RangeInclusive<A> {
423405
#[inline]
424406
fn next_back(&mut self) -> Option<A> {
425-
if self.start <= self.end {
426-
if self.start < self.end {
427-
let n = self.end.sub_one();
428-
Some(mem::replace(&mut self.end, n))
429-
} else {
430-
let last = self.end.replace_zero();
431-
self.start.replace_one();
432-
Some(last)
433-
}
434-
} else {
435-
None
407+
self.compute_is_empty();
408+
if self.is_empty.unwrap_or_default() {
409+
return None;
436410
}
437-
}
438-
439-
#[inline]
440-
fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R where
441-
Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try<Ok=B>
442-
{
443-
let mut accum = init;
444-
if self.start <= self.end {
445-
loop {
446-
let (x, done) =
447-
if self.start < self.end {
448-
let n = self.end.sub_one();
449-
(mem::replace(&mut self.end, n), false)
450-
} else {
451-
self.start.replace_one();
452-
(self.end.replace_zero(), true)
453-
};
454-
accum = f(accum, x)?;
455-
if done { break }
456-
}
457-
}
458-
Try::from_ok(accum)
411+
let is_iterating = self.start < self.end;
412+
self.is_empty = Some(!is_iterating);
413+
Some(if is_iterating {
414+
let n = self.end.sub_one();
415+
mem::replace(&mut self.end, n)
416+
} else {
417+
self.end.clone()
418+
})
459419
}
460420
}
461421

src/libcore/ops/range.rs

+58-7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// except according to those terms.
1010

1111
use fmt;
12+
use hash::{Hash, Hasher};
1213

1314
/// An unbounded range (`..`).
1415
///
@@ -326,15 +327,56 @@ impl<Idx: PartialOrd<Idx>> RangeTo<Idx> {
326327
/// assert_eq!(arr[1..=2], [ 1,2 ]); // RangeInclusive
327328
/// ```
328329
#[doc(alias = "..=")]
329-
#[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186
330+
#[derive(Clone)] // not Copy -- see #27186
330331
#[stable(feature = "inclusive_range", since = "1.26.0")]
331332
pub struct RangeInclusive<Idx> {
332-
// FIXME: The current representation follows RFC 1980,
333-
// but it is known that LLVM is not able to optimize loops following that RFC.
334-
// Consider adding an extra `bool` field to indicate emptiness of the range.
335-
// See #45222 for performance test cases.
336333
pub(crate) start: Idx,
337334
pub(crate) end: Idx,
335+
pub(crate) is_empty: Option<bool>,
336+
// This field is:
337+
// - `None` when next() or next_back() was never called
338+
// - `Some(false)` when `start <= end` assuming no overflow
339+
// - `Some(true)` otherwise
340+
// The field cannot be a simple `bool` because the `..=` constructor can
341+
// accept non-PartialOrd types, also we want the constructor to be const.
342+
}
343+
344+
trait RangeInclusiveEquality: Sized {
345+
fn canonicalized_is_empty(range: &RangeInclusive<Self>) -> bool;
346+
}
347+
impl<T> RangeInclusiveEquality for T {
348+
#[inline]
349+
default fn canonicalized_is_empty(range: &RangeInclusive<Self>) -> bool {
350+
range.is_empty.unwrap_or_default()
351+
}
352+
}
353+
impl<T: PartialOrd> RangeInclusiveEquality for T {
354+
#[inline]
355+
fn canonicalized_is_empty(range: &RangeInclusive<Self>) -> bool {
356+
range.is_empty()
357+
}
358+
}
359+
360+
#[stable(feature = "inclusive_range", since = "1.26.0")]
361+
impl<Idx: PartialEq> PartialEq for RangeInclusive<Idx> {
362+
#[inline]
363+
fn eq(&self, other: &Self) -> bool {
364+
self.start == other.start && self.end == other.end
365+
&& RangeInclusiveEquality::canonicalized_is_empty(self)
366+
== RangeInclusiveEquality::canonicalized_is_empty(other)
367+
}
368+
}
369+
370+
#[stable(feature = "inclusive_range", since = "1.26.0")]
371+
impl<Idx: Eq> Eq for RangeInclusive<Idx> {}
372+
373+
#[stable(feature = "inclusive_range", since = "1.26.0")]
374+
impl<Idx: Hash> Hash for RangeInclusive<Idx> {
375+
fn hash<H: Hasher>(&self, state: &mut H) {
376+
self.start.hash(state);
377+
self.end.hash(state);
378+
RangeInclusiveEquality::canonicalized_is_empty(self).hash(state);
379+
}
338380
}
339381

340382
impl<Idx> RangeInclusive<Idx> {
@@ -350,7 +392,7 @@ impl<Idx> RangeInclusive<Idx> {
350392
#[stable(feature = "inclusive_range_methods", since = "1.27.0")]
351393
#[inline]
352394
pub const fn new(start: Idx, end: Idx) -> Self {
353-
Self { start, end }
395+
Self { start, end, is_empty: None }
354396
}
355397

356398
/// Returns the lower bound of the range (inclusive).
@@ -492,8 +534,17 @@ impl<Idx: PartialOrd<Idx>> RangeInclusive<Idx> {
492534
/// assert!(r.is_empty());
493535
/// ```
494536
#[unstable(feature = "range_is_empty", reason = "recently added", issue = "48111")]
537+
#[inline]
495538
pub fn is_empty(&self) -> bool {
496-
!(self.start <= self.end)
539+
self.is_empty.unwrap_or_else(|| !(self.start <= self.end))
540+
}
541+
542+
// If this range's `is_empty` is field is unknown (`None`), update it to be a concrete value.
543+
#[inline]
544+
pub(crate) fn compute_is_empty(&mut self) {
545+
if self.is_empty.is_none() {
546+
self.is_empty = Some(!(self.start <= self.end));
547+
}
497548
}
498549
}
499550

src/libcore/slice/mod.rs

+10-10
Original file line numberDiff line numberDiff line change
@@ -2265,36 +2265,36 @@ impl<T> SliceIndex<[T]> for ops::RangeInclusive<usize> {
22652265

22662266
#[inline]
22672267
fn get(self, slice: &[T]) -> Option<&[T]> {
2268-
if self.end == usize::max_value() { None }
2269-
else { (self.start..self.end + 1).get(slice) }
2268+
if *self.end() == usize::max_value() { None }
2269+
else { (*self.start()..self.end() + 1).get(slice) }
22702270
}
22712271

22722272
#[inline]
22732273
fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> {
2274-
if self.end == usize::max_value() { None }
2275-
else { (self.start..self.end + 1).get_mut(slice) }
2274+
if *self.end() == usize::max_value() { None }
2275+
else { (*self.start()..self.end() + 1).get_mut(slice) }
22762276
}
22772277

22782278
#[inline]
22792279
unsafe fn get_unchecked(self, slice: &[T]) -> &[T] {
2280-
(self.start..self.end + 1).get_unchecked(slice)
2280+
(*self.start()..self.end() + 1).get_unchecked(slice)
22812281
}
22822282

22832283
#[inline]
22842284
unsafe fn get_unchecked_mut(self, slice: &mut [T]) -> &mut [T] {
2285-
(self.start..self.end + 1).get_unchecked_mut(slice)
2285+
(*self.start()..self.end() + 1).get_unchecked_mut(slice)
22862286
}
22872287

22882288
#[inline]
22892289
fn index(self, slice: &[T]) -> &[T] {
2290-
if self.end == usize::max_value() { slice_index_overflow_fail(); }
2291-
(self.start..self.end + 1).index(slice)
2290+
if *self.end() == usize::max_value() { slice_index_overflow_fail(); }
2291+
(*self.start()..self.end() + 1).index(slice)
22922292
}
22932293

22942294
#[inline]
22952295
fn index_mut(self, slice: &mut [T]) -> &mut [T] {
2296-
if self.end == usize::max_value() { slice_index_overflow_fail(); }
2297-
(self.start..self.end + 1).index_mut(slice)
2296+
if *self.end() == usize::max_value() { slice_index_overflow_fail(); }
2297+
(*self.start()..self.end() + 1).index_mut(slice)
22982298
}
22992299
}
23002300

src/libcore/str/mod.rs

+10-10
Original file line numberDiff line numberDiff line change
@@ -2004,31 +2004,31 @@ mod traits {
20042004
type Output = str;
20052005
#[inline]
20062006
fn get(self, slice: &str) -> Option<&Self::Output> {
2007-
if self.end == usize::max_value() { None }
2008-
else { (self.start..self.end+1).get(slice) }
2007+
if *self.end() == usize::max_value() { None }
2008+
else { (*self.start()..self.end()+1).get(slice) }
20092009
}
20102010
#[inline]
20112011
fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> {
2012-
if self.end == usize::max_value() { None }
2013-
else { (self.start..self.end+1).get_mut(slice) }
2012+
if *self.end() == usize::max_value() { None }
2013+
else { (*self.start()..self.end()+1).get_mut(slice) }
20142014
}
20152015
#[inline]
20162016
unsafe fn get_unchecked(self, slice: &str) -> &Self::Output {
2017-
(self.start..self.end+1).get_unchecked(slice)
2017+
(*self.start()..self.end()+1).get_unchecked(slice)
20182018
}
20192019
#[inline]
20202020
unsafe fn get_unchecked_mut(self, slice: &mut str) -> &mut Self::Output {
2021-
(self.start..self.end+1).get_unchecked_mut(slice)
2021+
(*self.start()..self.end()+1).get_unchecked_mut(slice)
20222022
}
20232023
#[inline]
20242024
fn index(self, slice: &str) -> &Self::Output {
2025-
if self.end == usize::max_value() { str_index_overflow_fail(); }
2026-
(self.start..self.end+1).index(slice)
2025+
if *self.end() == usize::max_value() { str_index_overflow_fail(); }
2026+
(*self.start()..self.end()+1).index(slice)
20272027
}
20282028
#[inline]
20292029
fn index_mut(self, slice: &mut str) -> &mut Self::Output {
2030-
if self.end == usize::max_value() { str_index_overflow_fail(); }
2031-
(self.start..self.end+1).index_mut(slice)
2030+
if *self.end() == usize::max_value() { str_index_overflow_fail(); }
2031+
(*self.start()..self.end()+1).index_mut(slice)
20322032
}
20332033
}
20342034

0 commit comments

Comments
 (0)