Skip to content

Commit b519b27

Browse files
author
Ulrik Sverdrup
committed
core: Fix size_hint for signed integer Range<T> iterators
There was an overflow bug in .size_hint() for signed iterators, which produced an hilariously incorrect size or an overflow panic. Incorrect size is a serious bug since the iterators are marked ExactSizeIterator. (And leads to abort() on (-1i8..127).collect() when the collection tries to preallocate too much). > (-1i8..127).size_hint() (18446744073709551488, Some(18446744073709551488)) Bug found using quickcheck. Fixes #24851
1 parent f191f92 commit b519b27

File tree

2 files changed

+13
-1
lines changed

2 files changed

+13
-1
lines changed

src/libcore/iter.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2407,6 +2407,8 @@ pub trait Step: PartialOrd {
24072407
/// `start` should always be less than `end`, so the result should never
24082408
/// be negative.
24092409
///
2410+
/// `by` must be > 0.
2411+
///
24102412
/// Returns `None` if it is not possible to calculate steps_between
24112413
/// without overflow.
24122414
fn steps_between(start: &Self, end: &Self, by: &Self) -> Option<usize>;
@@ -2423,7 +2425,11 @@ macro_rules! step_impl {
24232425
#[allow(trivial_numeric_casts)]
24242426
fn steps_between(start: &$t, end: &$t, by: &$t) -> Option<usize> {
24252427
if *start <= *end {
2426-
Some(((*end - *start) / *by) as usize)
2428+
// Note: We assume $t <= usize/isize here
2429+
Some(
2430+
((*end as isize).wrapping_sub(*start as isize) as usize
2431+
/ (*by as usize))
2432+
)
24272433
} else {
24282434
Some(0)
24292435
}

src/libcoretest/iter.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use core::iter::*;
1212
use core::iter::order::*;
1313
use core::iter::MinMaxResult::*;
14+
use core::i16;
1415
use core::usize;
1516
use core::cmp;
1617

@@ -758,6 +759,11 @@ fn test_range() {
758759
assert_eq!((usize::MAX - 1..usize::MAX).size_hint(), (1, Some(1)));
759760
assert_eq!((-10..-1).size_hint(), (9, Some(9)));
760761
assert_eq!((-1..-10).size_hint(), (0, Some(0)));
762+
763+
assert_eq!((-70..58i8).size_hint(), (128, Some(128)));
764+
assert_eq!((-128..127i8).size_hint(), (255, Some(255)));
765+
let m16 = i16::MAX;
766+
assert_eq!((-2..m16).size_hint(), (m16 as usize + 2, Some(m16 as usize + 2)));
761767
}
762768

763769
#[test]

0 commit comments

Comments
 (0)