Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 14 additions & 12 deletions library/alloctests/tests/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -612,14 +612,14 @@ mod slice_index {
data: "abcdef";
good: data[4..4] == "";
bad: data[4..3];
message: "begin <= end (4 <= 3)";
message: "begin > end (4 > 3)";
}

in mod rangeinclusive_neg_width {
data: "abcdef";
good: data[4..=3] == "";
bad: data[4..=2];
message: "begin <= end (4 <= 3)";
message: "begin > end (4 > 3)";
}
}

Expand All @@ -630,13 +630,13 @@ mod slice_index {
// note: using 0 specifically ensures that the result of overflowing is 0..0,
// so that `get` doesn't simply return None for the wrong reason.
bad: data[0..=usize::MAX];
message: "maximum usize";
message: "out of bounds";
}

in mod rangetoinclusive {
data: "hello";
bad: data[..=usize::MAX];
message: "maximum usize";
message: "out of bounds";
}
}
}
Expand All @@ -659,49 +659,49 @@ mod slice_index {
data: super::DATA;
bad: data[super::BAD_START..super::GOOD_END];
message:
"byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of";
"start byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of";
}

in mod range_2 {
data: super::DATA;
bad: data[super::GOOD_START..super::BAD_END];
message:
"byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
"end byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
}

in mod rangefrom {
data: super::DATA;
bad: data[super::BAD_START..];
message:
"byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of";
"start byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of";
}

in mod rangeto {
data: super::DATA;
bad: data[..super::BAD_END];
message:
"byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
"end byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
}

in mod rangeinclusive_1 {
data: super::DATA;
bad: data[super::BAD_START..=super::GOOD_END_INCL];
message:
"byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of";
"start byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of";
}

in mod rangeinclusive_2 {
data: super::DATA;
bad: data[super::GOOD_START..=super::BAD_END_INCL];
message:
"byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
"end byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
}

in mod rangetoinclusive {
data: super::DATA;
bad: data[..=super::BAD_END_INCL];
message:
"byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
"end byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
}
}
}
Expand All @@ -716,7 +716,9 @@ mod slice_index {

// check the panic includes the prefix of the sliced string
#[test]
#[should_panic(expected = "byte index 1024 is out of bounds of `Lorem ipsum dolor sit amet")]
#[should_panic(
expected = "end byte index 1024 is out of bounds of `Lorem ipsum dolor sit amet"
)]
fn test_slice_fail_truncated_1() {
let _ = &LOREM_PARAGRAPH[..1024];
}
Expand Down
9 changes: 0 additions & 9 deletions library/core/src/range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,15 +352,6 @@ impl<Idx: Step> RangeInclusive<Idx> {
}
}

impl RangeInclusive<usize> {
/// Converts to an exclusive `Range` for `SliceIndex` implementations.
/// The caller is responsible for dealing with `last == usize::MAX`.
#[inline]
pub(crate) const fn into_slice_range(self) -> Range<usize> {
Range { start: self.start, end: self.last + 1 }
}
}

#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "const_range", issue = "none")]
impl<T> const RangeBounds<T> for RangeInclusive<T> {
Expand Down
44 changes: 24 additions & 20 deletions library/core/src/slice/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,6 @@ unsafe impl<T> const SliceIndex<[T]> for ops::RangeFull {
}

/// The methods `index` and `index_mut` panic if:
/// - the end of the range is `usize::MAX` or
/// - the start of the range is greater than the end of the range or
/// - the end of the range is out of bounds.
#[stable(feature = "inclusive_range", since = "1.26.0")]
Expand All @@ -673,12 +672,12 @@ unsafe impl<T> const SliceIndex<[T]> for ops::RangeInclusive<usize> {

#[inline]
fn get(self, slice: &[T]) -> Option<&[T]> {
if *self.end() == usize::MAX { None } else { self.into_slice_range().get(slice) }
if *self.end() >= slice.len() { 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.into_slice_range().get_mut(slice) }
if *self.end() >= slice.len() { None } else { self.into_slice_range().get_mut(slice) }
}

#[inline]
Expand Down Expand Up @@ -950,8 +949,7 @@ where
R: ops::RangeBounds<usize>,
{
let len = bounds.end;
let r = into_range(len, (range.start_bound().copied(), range.end_bound().copied()))?;
if r.start > r.end || r.end > len { None } else { Some(r) }
try_into_slice_range(len, (range.start_bound().copied(), range.end_bound().copied()))
}

/// Converts a pair of `ops::Bound`s into `ops::Range` without performing any
Expand All @@ -978,25 +976,31 @@ pub(crate) const fn into_range_unchecked(
/// Returns `None` on overflowing indices.
#[rustc_const_unstable(feature = "const_range", issue = "none")]
#[inline]
pub(crate) const fn into_range(
pub(crate) const fn try_into_slice_range(
len: usize,
(start, end): (ops::Bound<usize>, ops::Bound<usize>),
) -> Option<ops::Range<usize>> {
use ops::Bound;
let start = match start {
Bound::Included(start) => start,
Bound::Excluded(start) => start.checked_add(1)?,
Bound::Unbounded => 0,
};

let end = match end {
Bound::Included(end) => end.checked_add(1)?,
Bound::Excluded(end) => end,
Bound::Unbounded => len,
ops::Bound::Included(end) if end >= len => return None,
// Cannot overflow because `end < len` implies `end < usize::MAX`.
ops::Bound::Included(end) => end + 1,

ops::Bound::Excluded(end) if end > len => return None,
ops::Bound::Excluded(end) => end,

ops::Bound::Unbounded => len,
};

// Don't bother with checking `start < end` and `end <= len`
// since these checks are handled by `Range` impls
let start = match start {
ops::Bound::Excluded(start) if start >= end => return None,
// Cannot overflow because `start < end` implies `start < usize::MAX`.
ops::Bound::Excluded(start) => start + 1,

ops::Bound::Included(start) if start > end => return None,
ops::Bound::Included(start) => start,

ops::Bound::Unbounded => 0,
};

Some(start..end)
}
Expand Down Expand Up @@ -1039,12 +1043,12 @@ unsafe impl<T> SliceIndex<[T]> for (ops::Bound<usize>, ops::Bound<usize>) {

#[inline]
fn get(self, slice: &[T]) -> Option<&Self::Output> {
into_range(slice.len(), self)?.get(slice)
try_into_slice_range(slice.len(), self)?.get(slice)
}

#[inline]
fn get_mut(self, slice: &mut [T]) -> Option<&mut Self::Output> {
into_range(slice.len(), self)?.get_mut(slice)
try_into_slice_range(slice.len(), self)?.get_mut(slice)
}

#[inline]
Expand Down
70 changes: 43 additions & 27 deletions library/core/src/str/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,34 +85,50 @@ fn slice_error_fail_rt(s: &str, begin: usize, end: usize) -> ! {
let trunc_len = s.floor_char_boundary(MAX_DISPLAY_LENGTH);
let s_trunc = &s[..trunc_len];
let ellipsis = if trunc_len < s.len() { "[...]" } else { "" };
let len = s.len();

// 1. out of bounds
if begin > s.len() || end > s.len() {
let oob_index = if begin > s.len() { begin } else { end };
panic!("byte index {oob_index} is out of bounds of `{s_trunc}`{ellipsis}");
}

// 2. begin <= end
assert!(
begin <= end,
"begin <= end ({} <= {}) when slicing `{}`{}",
begin,
end,
s_trunc,
ellipsis
);

// 3. character boundary
let index = if !s.is_char_boundary(begin) { begin } else { end };
// find the character
let char_start = s.floor_char_boundary(index);
// `char_start` must be less than len and a char boundary
let ch = s[char_start..].chars().next().unwrap();
let char_range = char_start..char_start + ch.len_utf8();
panic!(
"byte index {} is not a char boundary; it is inside {:?} (bytes {:?}) of `{}`{}",
index, ch, char_range, s_trunc, ellipsis
);
// 1. begin is OOB.
if begin > len {
panic!("start byte index {begin} is out of bounds of `{s_trunc}`{ellipsis}");
}

// 2. end is OOB.
if end > len {
panic!("end byte index {end} is out of bounds of `{s_trunc}`{ellipsis}");
}

// 3. range is backwards.
if begin > end {
panic!("begin > end ({begin} > {end}) when slicing `{s_trunc}`{ellipsis}")
}

// 4. begin is inside a character.
if !s.is_char_boundary(begin) {
let floor = s.floor_char_boundary(begin);
let ceil = s.ceil_char_boundary(begin);
let range = floor..ceil;
let ch = s[floor..ceil].chars().next().unwrap();
panic!(
"start byte index {begin} is not a char boundary; it is inside {ch:?} (bytes {range:?}) of `{s_trunc}`{ellipsis}"
)
}

// 5. end is inside a character.
if !s.is_char_boundary(end) {
let floor = s.floor_char_boundary(end);
let ceil = s.ceil_char_boundary(end);
let range = floor..ceil;
let ch = s[floor..ceil].chars().next().unwrap();
panic!(
"end byte index {end} is not a char boundary; it is inside {ch:?} (bytes {range:?}) of `{s_trunc}`{ellipsis}"
)
}

// 6. end is OOB and range is inclusive (end == len).
// This test cannot be combined with 2. above because for cases like
// `"abcαβγ"[4..9]` the error is that 4 is inside 'α', not that 9 is OOB.
debug_assert_eq!(end, len);
panic!("end byte index {end} is out of bounds of `{s_trunc}`{ellipsis}");
}

impl str {
Expand Down
Loading
Loading