From 9202fbdbdbd60adb62839c3230738274e30f15fc Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 20 Oct 2020 17:18:08 -0700 Subject: [PATCH] Check for exhaustion in SliceIndex for RangeInclusive --- library/alloc/tests/str.rs | 29 +++++++++++++++++++++++++++++ library/core/src/ops/range.rs | 14 ++++++++++++++ library/core/src/slice/index.rs | 16 ++++++---------- library/core/src/str/traits.rs | 16 ++++++---------- library/core/tests/slice.rs | 30 ++++++++++++++++++++++++++++++ 5 files changed, 85 insertions(+), 20 deletions(-) diff --git a/library/alloc/tests/str.rs b/library/alloc/tests/str.rs index ed8ee2d8823c0..834dd4656ff76 100644 --- a/library/alloc/tests/str.rs +++ b/library/alloc/tests/str.rs @@ -529,6 +529,13 @@ mod slice_index { message: "out of bounds"; } + in mod rangeinclusive_len { + data: "abcdef"; + good: data[0..=5] == "abcdef"; + bad: data[0..=6]; + message: "out of bounds"; + } + in mod range_len_len { data: "abcdef"; good: data[6..6] == ""; @@ -544,6 +551,28 @@ mod slice_index { } } + panic_cases! { + in mod rangeinclusive_exhausted { + data: "abcdef"; + + good: data[0..=5] == "abcdef"; + good: data[{ + let mut iter = 0..=5; + iter.by_ref().count(); // exhaust it + iter + }] == ""; + + // 0..=6 is out of bounds before exhaustion, so it + // stands to reason that it still would be after. + bad: data[{ + let mut iter = 0..=6; + iter.by_ref().count(); // exhaust it + iter + }]; + message: "out of bounds"; + } + } + panic_cases! { in mod range_neg_width { data: "abcdef"; diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index 084ddffab0b7a..1d67e65e51f5f 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -446,6 +446,20 @@ impl RangeInclusive { } } +impl RangeInclusive { + /// Converts to an exclusive `Range` for `SliceIndex` implementations. + /// The caller is responsible for dealing with `end == usize::MAX`. + #[inline] + pub(crate) fn into_slice_range(self) -> Range { + // If we're not exhausted, we want to simply slice `start..end + 1`. + // If we are exhausted, then slicing with `end + 1..end + 1` gives us an + // empty range that is still subject to bounds-checks for that endpoint. + let exclusive_end = self.end + 1; + let start = if self.exhausted { exclusive_end } else { self.start }; + start..exclusive_end + } +} + #[stable(feature = "inclusive_range", since = "1.26.0")] impl fmt::Debug for RangeInclusive { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index f1f21c1d24b0b..660c8a2da5da0 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -376,28 +376,24 @@ unsafe impl SliceIndex<[T]> for ops::RangeInclusive { #[inline] fn get(self, slice: &[T]) -> Option<&[T]> { - if *self.end() == usize::MAX { None } else { (*self.start()..self.end() + 1).get(slice) } + if *self.end() == usize::MAX { None } else { self.into_slice_range().get(slice) } } #[inline] fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { - if *self.end() == usize::MAX { - None - } else { - (*self.start()..self.end() + 1).get_mut(slice) - } + if *self.end() == usize::MAX { None } else { self.into_slice_range().get_mut(slice) } } #[inline] unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { // SAFETY: the caller has to uphold the safety contract for `get_unchecked`. - unsafe { (*self.start()..self.end() + 1).get_unchecked(slice) } + unsafe { self.into_slice_range().get_unchecked(slice) } } #[inline] unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`. - unsafe { (*self.start()..self.end() + 1).get_unchecked_mut(slice) } + unsafe { self.into_slice_range().get_unchecked_mut(slice) } } #[inline] @@ -405,7 +401,7 @@ unsafe impl SliceIndex<[T]> for ops::RangeInclusive { if *self.end() == usize::MAX { slice_end_index_overflow_fail(); } - (*self.start()..self.end() + 1).index(slice) + self.into_slice_range().index(slice) } #[inline] @@ -413,7 +409,7 @@ unsafe impl SliceIndex<[T]> for ops::RangeInclusive { if *self.end() == usize::MAX { slice_end_index_overflow_fail(); } - (*self.start()..self.end() + 1).index_mut(slice) + self.into_slice_range().index_mut(slice) } } diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index 4f8aa246e5232..f363541431100 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -398,39 +398,35 @@ unsafe impl SliceIndex for ops::RangeInclusive { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { - if *self.end() == usize::MAX { None } else { (*self.start()..self.end() + 1).get(slice) } + if *self.end() == usize::MAX { None } else { self.into_slice_range().get(slice) } } #[inline] fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { - if *self.end() == usize::MAX { - None - } else { - (*self.start()..self.end() + 1).get_mut(slice) - } + if *self.end() == usize::MAX { None } else { self.into_slice_range().get_mut(slice) } } #[inline] unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { // SAFETY: the caller must uphold the safety contract for `get_unchecked`. - unsafe { (*self.start()..self.end() + 1).get_unchecked(slice) } + unsafe { self.into_slice_range().get_unchecked(slice) } } #[inline] unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { // SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`. - unsafe { (*self.start()..self.end() + 1).get_unchecked_mut(slice) } + unsafe { self.into_slice_range().get_unchecked_mut(slice) } } #[inline] fn index(self, slice: &str) -> &Self::Output { if *self.end() == usize::MAX { str_index_overflow_fail(); } - (*self.start()..self.end() + 1).index(slice) + self.into_slice_range().index(slice) } #[inline] fn index_mut(self, slice: &mut str) -> &mut Self::Output { if *self.end() == usize::MAX { str_index_overflow_fail(); } - (*self.start()..self.end() + 1).index_mut(slice) + self.into_slice_range().index_mut(slice) } } diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs index ac5c9353ccb46..9ccc5a08dcbea 100644 --- a/library/core/tests/slice.rs +++ b/library/core/tests/slice.rs @@ -1341,6 +1341,14 @@ mod slice_index { message: "out of range"; } + in mod rangeinclusive_len { + data: [0, 1, 2, 3, 4, 5]; + + good: data[0..=5] == [0, 1, 2, 3, 4, 5]; + bad: data[0..=6]; + message: "out of range"; + } + in mod range_len_len { data: [0, 1, 2, 3, 4, 5]; @@ -1358,6 +1366,28 @@ mod slice_index { } } + panic_cases! { + in mod rangeinclusive_exhausted { + data: [0, 1, 2, 3, 4, 5]; + + good: data[0..=5] == [0, 1, 2, 3, 4, 5]; + good: data[{ + let mut iter = 0..=5; + iter.by_ref().count(); // exhaust it + iter + }] == []; + + // 0..=6 is out of range before exhaustion, so it + // stands to reason that it still would be after. + bad: data[{ + let mut iter = 0..=6; + iter.by_ref().count(); // exhaust it + iter + }]; + message: "out of range"; + } + } + panic_cases! { in mod range_neg_width { data: [0, 1, 2, 3, 4, 5];