Skip to content

Commit 4d9b6c9

Browse files
committed
Change RangeInclusive to a three-field struct.
Fix #45222.
1 parent 87ecf54 commit 4d9b6c9

File tree

5 files changed

+155
-97
lines changed

5 files changed

+155
-97
lines changed

src/libcore/iter/range.rs

+30-70
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

+31-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,37 @@ 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+
#[stable(feature = "inclusive_range", since = "1.26.0")]
345+
impl<Idx: PartialEq> PartialEq for RangeInclusive<Idx> {
346+
#[inline]
347+
fn eq(&self, other: &Self) -> bool {
348+
self.start == other.start && self.end == other.end
349+
}
350+
}
351+
352+
#[stable(feature = "inclusive_range", since = "1.26.0")]
353+
impl<Idx: Eq> Eq for RangeInclusive<Idx> {}
354+
355+
#[stable(feature = "inclusive_range", since = "1.26.0")]
356+
impl<Idx: Hash> Hash for RangeInclusive<Idx> {
357+
fn hash<H: Hasher>(&self, state: &mut H) {
358+
self.start.hash(state);
359+
self.end.hash(state);
360+
}
338361
}
339362

340363
impl<Idx> RangeInclusive<Idx> {
@@ -350,7 +373,7 @@ impl<Idx> RangeInclusive<Idx> {
350373
#[stable(feature = "inclusive_range_methods", since = "1.27.0")]
351374
#[inline]
352375
pub const fn new(start: Idx, end: Idx) -> Self {
353-
Self { start, end }
376+
Self { start, end, is_iterating: None }
354377
}
355378

356379
/// Returns the lower bound of the range (inclusive).
@@ -492,8 +515,9 @@ impl<Idx: PartialOrd<Idx>> RangeInclusive<Idx> {
492515
/// assert!(r.is_empty());
493516
/// ```
494517
#[unstable(feature = "range_is_empty", reason = "recently added", issue = "48111")]
518+
#[inline]
495519
pub fn is_empty(&self) -> bool {
496-
!(self.start <= self.end)
520+
!self.is_iterating.unwrap_or_else(|| self.start <= self.end)
497521
}
498522
}
499523

src/libcore/slice/mod.rs

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

22602260
#[inline]
22612261
fn get(self, slice: &[T]) -> Option<&[T]> {
2262-
if self.end == usize::max_value() { None }
2263-
else { (self.start..self.end + 1).get(slice) }
2262+
if *self.end() == usize::max_value() { None }
2263+
else { (*self.start()..self.end() + 1).get(slice) }
22642264
}
22652265

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

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

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

22822282
#[inline]
22832283
fn index(self, slice: &[T]) -> &[T] {
2284-
if self.end == usize::max_value() { slice_index_overflow_fail(); }
2285-
(self.start..self.end + 1).index(slice)
2284+
if *self.end() == usize::max_value() { slice_index_overflow_fail(); }
2285+
(*self.start()..self.end() + 1).index(slice)
22862286
}
22872287

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

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

src/test/codegen/issue-45222.rs

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags: -O
12+
// min-llvm-version 6.0
13+
14+
#![crate_type = "lib"]
15+
16+
// verify that LLVM recognizes a loop involving 0..=n and will const-fold it.
17+
18+
//------------------------------------------------------------------------------
19+
// Example from original issue #45222
20+
21+
fn foo2(n: u64) -> u64 {
22+
let mut count = 0;
23+
for _ in 0..n {
24+
for j in (0..=n).rev() {
25+
count += j;
26+
}
27+
}
28+
count
29+
}
30+
31+
// CHECK-LABEL: @check_foo2
32+
#[no_mangle]
33+
pub fn check_foo2() -> u64 {
34+
// CHECK: ret i64 500005000000000
35+
foo2(100000)
36+
}
37+
38+
//------------------------------------------------------------------------------
39+
// Simplified example of #45222
40+
41+
fn triangle_inc(n: u64) -> u64 {
42+
let mut count = 0;
43+
for j in 0 ..= n {
44+
count += j;
45+
}
46+
count
47+
}
48+
49+
// CHECK-LABEL: @check_triangle_inc
50+
#[no_mangle]
51+
pub fn check_triangle_inc() -> u64 {
52+
// CHECK: ret i64 5000050000
53+
triangle_inc(100000)
54+
}
55+
56+
//------------------------------------------------------------------------------
57+
// Demo in #48012
58+
59+
fn foo3r(n: u64) -> u64 {
60+
let mut count = 0;
61+
(0..n).for_each(|_| {
62+
(0 ..= n).rev().for_each(|j| {
63+
count += j;
64+
})
65+
});
66+
count
67+
}
68+
69+
// CHECK-LABEL: @check_foo3r
70+
#[no_mangle]
71+
pub fn check_foo3r() -> u64 {
72+
// CHECK: ret i64 500005000000000
73+
foo3r(100000)
74+
}

0 commit comments

Comments
 (0)