Skip to content

Commit 66602ac

Browse files
authored
Rollup merge of rust-lang#51622 - kennytm:three-field-range-inclusive, r=SimonSapin
Change RangeInclusive to a three-field struct. Fix rust-lang#45222. This PR also reverts rust-lang#48012 (i.e. removed the `try_fold`/`try_rfold` specialization for `RangeInclusive`) because LLVM no longer has trouble recognizing a RangeInclusive loop.
2 parents 6cc42a4 + 5e3bd09 commit 66602ac

File tree

7 files changed

+198
-110
lines changed

7 files changed

+198
-110
lines changed

src/libcore/iter/mod.rs

Lines changed: 5 additions & 3 deletions
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+
if self.iter.is_empty() {
791+
self.iter.is_iterating = Some(false);
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_iterating = 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_iterating = Some(false);
801803
Some(last)
802804
}
803805
}

src/libcore/iter/range.rs

Lines changed: 30 additions & 70 deletions
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-
}
333+
if self.is_empty() {
334+
self.is_iterating = Some(false);
335+
return None;
336+
}
337+
if self.start < self.end {
338+
let n = self.start.add_one();
339+
self.is_iterating = Some(true);
340+
Some(mem::replace(&mut self.start, n))
342341
} else {
343-
None
342+
self.is_iterating = Some(false);
343+
Some(self.start.clone())
344344
}
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+
if self.is_empty() {
362+
self.is_iterating = Some(false);
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_iterating = Some(true);
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_iterating = Some(false);
372377
return Some(plus_n)
373378
}
374379
_ => {}
375380
}
376381
}
377382

378-
self.start.replace_one();
379-
self.end.replace_zero();
383+
self.is_iterating = Some(false);
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+
if self.is_empty() {
408+
self.is_iterating = Some(false);
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-
}
411+
if self.start < self.end {
412+
let n = self.end.sub_one();
413+
self.is_iterating = Some(true);
414+
Some(mem::replace(&mut self.end, n))
415+
} else {
416+
self.is_iterating = Some(false);
417+
Some(self.end.clone())
457418
}
458-
Try::from_ok(accum)
459419
}
460420
}
461421

src/libcore/ops/range.rs

Lines changed: 50 additions & 7 deletions
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_iterating: Option<bool>,
336+
// This field is:
337+
// - `None` when next() or next_back() was never called
338+
// - `Some(true)` when `start <= end` assuming no overflow
339+
// - `Some(false)` 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_iterating.unwrap_or(false)
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_iterating: None }
354396
}
355397

356398
/// Returns the lower bound of the range (inclusive).
@@ -492,8 +534,9 @@ 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_iterating.unwrap_or_else(|| self.start <= self.end)
497540
}
498541
}
499542

src/libcore/slice/mod.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2262,36 +2262,36 @@ impl<T> SliceIndex<[T]> for ops::RangeInclusive<usize> {
22622262

22632263
#[inline]
22642264
fn get(self, slice: &[T]) -> Option<&[T]> {
2265-
if self.end == usize::max_value() { None }
2266-
else { (self.start..self.end + 1).get(slice) }
2265+
if *self.end() == usize::max_value() { None }
2266+
else { (*self.start()..self.end() + 1).get(slice) }
22672267
}
22682268

22692269
#[inline]
22702270
fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> {
2271-
if self.end == usize::max_value() { None }
2272-
else { (self.start..self.end + 1).get_mut(slice) }
2271+
if *self.end() == usize::max_value() { None }
2272+
else { (*self.start()..self.end() + 1).get_mut(slice) }
22732273
}
22742274

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

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

22852285
#[inline]
22862286
fn index(self, slice: &[T]) -> &[T] {
2287-
if self.end == usize::max_value() { slice_index_overflow_fail(); }
2288-
(self.start..self.end + 1).index(slice)
2287+
if *self.end() == usize::max_value() { slice_index_overflow_fail(); }
2288+
(*self.start()..self.end() + 1).index(slice)
22892289
}
22902290

22912291
#[inline]
22922292
fn index_mut(self, slice: &mut [T]) -> &mut [T] {
2293-
if self.end == usize::max_value() { slice_index_overflow_fail(); }
2294-
(self.start..self.end + 1).index_mut(slice)
2293+
if *self.end() == usize::max_value() { slice_index_overflow_fail(); }
2294+
(*self.start()..self.end() + 1).index_mut(slice)
22952295
}
22962296
}
22972297

src/libcore/str/mod.rs

Lines changed: 10 additions & 10 deletions
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)