Skip to content

Commit ad44811

Browse files
authored
Unrolled build for #145137
Rollup merge of #145137 - Kmeakin:km/optimize-slice-index-panicking, r=jhpratt Consolidate panicking functions in `slice/index.rs` Consolidate all the panicking functions in `slice/index.rs` to use a single `slice_index_fail` function, similar to how it is done in `str/traits.rs`. Split off from #145024
2 parents 6ba0ce4 + 377a0c8 commit ad44811

13 files changed

+269
-137
lines changed

library/core/src/slice/index.rs

Lines changed: 103 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -34,53 +34,44 @@ where
3434
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
3535
#[cfg_attr(feature = "panic_immediate_abort", inline)]
3636
#[track_caller]
37-
const fn slice_start_index_len_fail(index: usize, len: usize) -> ! {
38-
const_panic!(
39-
"slice start index is out of range for slice",
40-
"range start index {index} out of range for slice of length {len}",
41-
index: usize,
42-
len: usize,
43-
)
44-
}
37+
const fn slice_index_fail(start: usize, end: usize, len: usize) -> ! {
38+
if start > len {
39+
const_panic!(
40+
"slice start index is out of range for slice",
41+
"range start index {start} out of range for slice of length {len}",
42+
start: usize,
43+
len: usize,
44+
)
45+
}
4546

46-
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
47-
#[cfg_attr(feature = "panic_immediate_abort", inline)]
48-
#[track_caller]
49-
const fn slice_end_index_len_fail(index: usize, len: usize) -> ! {
50-
const_panic!(
51-
"slice end index is out of range for slice",
52-
"range end index {index} out of range for slice of length {len}",
53-
index: usize,
54-
len: usize,
55-
)
56-
}
47+
if end > len {
48+
const_panic!(
49+
"slice end index is out of range for slice",
50+
"range end index {end} out of range for slice of length {len}",
51+
end: usize,
52+
len: usize,
53+
)
54+
}
5755

58-
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
59-
#[cfg_attr(feature = "panic_immediate_abort", inline)]
60-
#[track_caller]
61-
const fn slice_index_order_fail(index: usize, end: usize) -> ! {
56+
if start > end {
57+
const_panic!(
58+
"slice index start is larger than end",
59+
"slice index starts at {start} but ends at {end}",
60+
start: usize,
61+
end: usize,
62+
)
63+
}
64+
65+
// Only reachable if the range was a `RangeInclusive` or a
66+
// `RangeToInclusive`, with `end == len`.
6267
const_panic!(
63-
"slice index start is larger than end",
64-
"slice index starts at {index} but ends at {end}",
65-
index: usize,
68+
"slice end index is out of range for slice",
69+
"range end index {end} out of range for slice of length {len}",
6670
end: usize,
71+
len: usize,
6772
)
6873
}
6974

70-
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
71-
#[cfg_attr(feature = "panic_immediate_abort", inline)]
72-
#[track_caller]
73-
const fn slice_start_index_overflow_fail() -> ! {
74-
panic!("attempted to index slice from after maximum usize");
75-
}
76-
77-
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
78-
#[cfg_attr(feature = "panic_immediate_abort", inline)]
79-
#[track_caller]
80-
const fn slice_end_index_overflow_fail() -> ! {
81-
panic!("attempted to index slice up to maximum usize");
82-
}
83-
8475
// The UbChecks are great for catching bugs in the unsafe methods, but including
8576
// them in safe indexing is unnecessary and hurts inlining and debug runtime perf.
8677
// Both the safe and unsafe public methods share these helpers,
@@ -341,7 +332,7 @@ unsafe impl<T> const SliceIndex<[T]> for ops::IndexRange {
341332
// SAFETY: `self` is checked to be valid and in bounds above.
342333
unsafe { &*get_offset_len_noubcheck(slice, self.start(), self.len()) }
343334
} else {
344-
slice_end_index_len_fail(self.end(), slice.len())
335+
slice_index_fail(self.start(), self.end(), slice.len())
345336
}
346337
}
347338

@@ -351,7 +342,7 @@ unsafe impl<T> const SliceIndex<[T]> for ops::IndexRange {
351342
// SAFETY: `self` is checked to be valid and in bounds above.
352343
unsafe { &mut *get_offset_len_mut_noubcheck(slice, self.start(), self.len()) }
353344
} else {
354-
slice_end_index_len_fail(self.end(), slice.len())
345+
slice_index_fail(self.start(), self.end(), slice.len())
355346
}
356347
}
357348
}
@@ -436,26 +427,27 @@ unsafe impl<T> const SliceIndex<[T]> for ops::Range<usize> {
436427
#[inline(always)]
437428
fn index(self, slice: &[T]) -> &[T] {
438429
// Using checked_sub is a safe way to get `SubUnchecked` in MIR
439-
let Some(new_len) = usize::checked_sub(self.end, self.start) else {
440-
slice_index_order_fail(self.start, self.end)
441-
};
442-
if self.end > slice.len() {
443-
slice_end_index_len_fail(self.end, slice.len());
430+
if let Some(new_len) = usize::checked_sub(self.end, self.start)
431+
&& self.end <= slice.len()
432+
{
433+
// SAFETY: `self` is checked to be valid and in bounds above.
434+
unsafe { &*get_offset_len_noubcheck(slice, self.start, new_len) }
435+
} else {
436+
slice_index_fail(self.start, self.end, slice.len())
444437
}
445-
// SAFETY: `self` is checked to be valid and in bounds above.
446-
unsafe { &*get_offset_len_noubcheck(slice, self.start, new_len) }
447438
}
448439

449440
#[inline]
450441
fn index_mut(self, slice: &mut [T]) -> &mut [T] {
451-
let Some(new_len) = usize::checked_sub(self.end, self.start) else {
452-
slice_index_order_fail(self.start, self.end)
453-
};
454-
if self.end > slice.len() {
455-
slice_end_index_len_fail(self.end, slice.len());
442+
// Using checked_sub is a safe way to get `SubUnchecked` in MIR
443+
if let Some(new_len) = usize::checked_sub(self.end, self.start)
444+
&& self.end <= slice.len()
445+
{
446+
// SAFETY: `self` is checked to be valid and in bounds above.
447+
unsafe { &mut *get_offset_len_mut_noubcheck(slice, self.start, new_len) }
448+
} else {
449+
slice_index_fail(self.start, self.end, slice.len())
456450
}
457-
// SAFETY: `self` is checked to be valid and in bounds above.
458-
unsafe { &mut *get_offset_len_mut_noubcheck(slice, self.start, new_len) }
459451
}
460452
}
461453

@@ -567,7 +559,7 @@ unsafe impl<T> const SliceIndex<[T]> for ops::RangeFrom<usize> {
567559
#[inline]
568560
fn index(self, slice: &[T]) -> &[T] {
569561
if self.start > slice.len() {
570-
slice_start_index_len_fail(self.start, slice.len());
562+
slice_index_fail(self.start, slice.len(), slice.len())
571563
}
572564
// SAFETY: `self` is checked to be valid and in bounds above.
573565
unsafe { &*self.get_unchecked(slice) }
@@ -576,7 +568,7 @@ unsafe impl<T> const SliceIndex<[T]> for ops::RangeFrom<usize> {
576568
#[inline]
577569
fn index_mut(self, slice: &mut [T]) -> &mut [T] {
578570
if self.start > slice.len() {
579-
slice_start_index_len_fail(self.start, slice.len());
571+
slice_index_fail(self.start, slice.len(), slice.len())
580572
}
581573
// SAFETY: `self` is checked to be valid and in bounds above.
582574
unsafe { &mut *self.get_unchecked_mut(slice) }
@@ -690,18 +682,32 @@ unsafe impl<T> const SliceIndex<[T]> for ops::RangeInclusive<usize> {
690682

691683
#[inline]
692684
fn index(self, slice: &[T]) -> &[T] {
693-
if *self.end() == usize::MAX {
694-
slice_end_index_overflow_fail();
685+
let Self { mut start, mut end, exhausted } = self;
686+
let len = slice.len();
687+
if end < len {
688+
end = end + 1;
689+
start = if exhausted { end } else { start };
690+
if let Some(new_len) = usize::checked_sub(end, start) {
691+
// SAFETY: `self` is checked to be valid and in bounds above.
692+
unsafe { return &*get_offset_len_noubcheck(slice, start, new_len) }
693+
}
695694
}
696-
self.into_slice_range().index(slice)
695+
slice_index_fail(start, end, slice.len())
697696
}
698697

699698
#[inline]
700699
fn index_mut(self, slice: &mut [T]) -> &mut [T] {
701-
if *self.end() == usize::MAX {
702-
slice_end_index_overflow_fail();
700+
let Self { mut start, mut end, exhausted } = self;
701+
let len = slice.len();
702+
if end < len {
703+
end = end + 1;
704+
start = if exhausted { end } else { start };
705+
if let Some(new_len) = usize::checked_sub(end, start) {
706+
// SAFETY: `self` is checked to be valid and in bounds above.
707+
unsafe { return &mut *get_offset_len_mut_noubcheck(slice, start, new_len) }
708+
}
703709
}
704-
self.into_slice_range().index_mut(slice)
710+
slice_index_fail(start, end, slice.len())
705711
}
706712
}
707713

@@ -852,28 +858,26 @@ where
852858
{
853859
let len = bounds.end;
854860

855-
let start = match range.start_bound() {
856-
ops::Bound::Included(&start) => start,
857-
ops::Bound::Excluded(start) => {
858-
start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail())
859-
}
860-
ops::Bound::Unbounded => 0,
861-
};
862-
863861
let end = match range.end_bound() {
864-
ops::Bound::Included(end) => {
865-
end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail())
866-
}
862+
ops::Bound::Included(&end) if end >= len => slice_index_fail(0, end, len),
863+
// Cannot overflow because `end < len` implies `end < usize::MAX`.
864+
ops::Bound::Included(&end) => end + 1,
865+
866+
ops::Bound::Excluded(&end) if end > len => slice_index_fail(0, end, len),
867867
ops::Bound::Excluded(&end) => end,
868868
ops::Bound::Unbounded => len,
869869
};
870870

871-
if start > end {
872-
slice_index_order_fail(start, end);
873-
}
874-
if end > len {
875-
slice_end_index_len_fail(end, len);
876-
}
871+
let start = match range.start_bound() {
872+
ops::Bound::Excluded(&start) if start >= end => slice_index_fail(start, end, len),
873+
// Cannot overflow because `start < end` implies `start < usize::MAX`.
874+
ops::Bound::Excluded(&start) => start + 1,
875+
876+
ops::Bound::Included(&start) if start > end => slice_index_fail(start, end, len),
877+
ops::Bound::Included(&start) => start,
878+
879+
ops::Bound::Unbounded => 0,
880+
};
877881

878882
ops::Range { start, end }
879883
}
@@ -982,25 +986,27 @@ pub(crate) fn into_slice_range(
982986
len: usize,
983987
(start, end): (ops::Bound<usize>, ops::Bound<usize>),
984988
) -> ops::Range<usize> {
985-
use ops::Bound;
986-
let start = match start {
987-
Bound::Included(start) => start,
988-
Bound::Excluded(start) => {
989-
start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail())
990-
}
991-
Bound::Unbounded => 0,
992-
};
993-
994989
let end = match end {
995-
Bound::Included(end) => {
996-
end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail())
997-
}
998-
Bound::Excluded(end) => end,
999-
Bound::Unbounded => len,
990+
ops::Bound::Included(end) if end >= len => slice_index_fail(0, end, len),
991+
// Cannot overflow because `end < len` implies `end < usize::MAX`.
992+
ops::Bound::Included(end) => end + 1,
993+
994+
ops::Bound::Excluded(end) if end > len => slice_index_fail(0, end, len),
995+
ops::Bound::Excluded(end) => end,
996+
997+
ops::Bound::Unbounded => len,
1000998
};
1001999

1002-
// Don't bother with checking `start < end` and `end <= len`
1003-
// since these checks are handled by `Range` impls
1000+
let start = match start {
1001+
ops::Bound::Excluded(start) if start >= end => slice_index_fail(start, end, len),
1002+
// Cannot overflow because `start < end` implies `start < usize::MAX`.
1003+
ops::Bound::Excluded(start) => start + 1,
1004+
1005+
ops::Bound::Included(start) if start > end => slice_index_fail(start, end, len),
1006+
ops::Bound::Included(start) => start,
1007+
1008+
ops::Bound::Unbounded => 0,
1009+
};
10041010

10051011
start..end
10061012
}

library/coretests/tests/slice.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1492,28 +1492,28 @@ mod slice_index {
14921492
// note: using 0 specifically ensures that the result of overflowing is 0..0,
14931493
// so that `get` doesn't simply return None for the wrong reason.
14941494
bad: data[0 ..= usize::MAX];
1495-
message: "maximum usize";
1495+
message: "out of range";
14961496
}
14971497

14981498
in mod rangetoinclusive_overflow {
14991499
data: [0, 1];
15001500

15011501
bad: data[..= usize::MAX];
1502-
message: "maximum usize";
1502+
message: "out of range";
15031503
}
15041504

15051505
in mod boundpair_overflow_end {
15061506
data: [0; 1];
15071507

15081508
bad: data[(Bound::Unbounded, Bound::Included(usize::MAX))];
1509-
message: "maximum usize";
1509+
message: "out of range";
15101510
}
15111511

15121512
in mod boundpair_overflow_start {
15131513
data: [0; 1];
15141514

15151515
bad: data[(Bound::Excluded(usize::MAX), Bound::Unbounded)];
1516-
message: "maximum usize";
1516+
message: "out of range";
15171517
}
15181518
} // panic_cases!
15191519
}
@@ -2008,7 +2008,7 @@ fn test_copy_within_panics_src_inverted() {
20082008
bytes.copy_within(2..1, 0);
20092009
}
20102010
#[test]
2011-
#[should_panic(expected = "attempted to index slice up to maximum usize")]
2011+
#[should_panic(expected = "out of range")]
20122012
fn test_copy_within_panics_src_out_of_bounds() {
20132013
let mut bytes = *b"Hello, World!";
20142014
// an inclusive range ending at usize::MAX would make src_end overflow
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

22
thread 'main' ($TID) panicked at tests/panic/oob_subslice.rs:LL:CC:
3-
range end index 5 out of range for slice of length 4
3+
range end index 4 out of range for slice of length 4
44
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
55
note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect

tests/codegen-llvm/binary-search-index-no-bound-check.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
#[no_mangle]
99
pub fn binary_search_index_no_bounds_check(s: &[u8]) -> u8 {
1010
// CHECK-NOT: panic
11-
// CHECK-NOT: slice_start_index_len_fail
12-
// CHECK-NOT: slice_end_index_len_fail
11+
// CHECK-NOT: slice_index_fail
1312
// CHECK-NOT: panic_bounds_check
1413
if let Ok(idx) = s.binary_search(&b'\\') { s[idx] } else { 42 }
1514
}

tests/codegen-llvm/integer-overflow.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub struct S1<'a> {
1010
// CHECK-LABEL: @slice_no_index_order
1111
#[no_mangle]
1212
pub fn slice_no_index_order<'a>(s: &'a mut S1, n: usize) -> &'a [u8] {
13-
// CHECK-NOT: slice_index_order_fail
13+
// CHECK-COUNT-1: slice_index_fail
1414
let d = &s.data[s.position..s.position + n];
1515
s.position += n;
1616
return d;
@@ -19,6 +19,6 @@ pub fn slice_no_index_order<'a>(s: &'a mut S1, n: usize) -> &'a [u8] {
1919
// CHECK-LABEL: @test_check
2020
#[no_mangle]
2121
pub fn test_check<'a>(s: &'a mut S1, x: usize, y: usize) -> &'a [u8] {
22-
// CHECK: slice_index_order_fail
22+
// CHECK-COUNT-1: slice_index_fail
2323
&s.data[x..y]
2424
}

tests/codegen-llvm/issues/issue-113757-bounds-check-after-cmp-max.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use std::cmp::max;
66

77
// CHECK-LABEL: @foo
8-
// CHECK-NOT: slice_start_index_len_fail
8+
// CHECK-NOT: slice_index_fail
99
// CHECK-NOT: unreachable
1010
#[no_mangle]
1111
pub fn foo(v: &mut Vec<u8>, size: usize) -> Option<&mut [u8]> {

tests/codegen-llvm/issues/issue-27130.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#[no_mangle]
77
pub fn trim_in_place(a: &mut &[u8]) {
88
while a.first() == Some(&42) {
9-
// CHECK-NOT: slice_index_order_fail
9+
// CHECK-NOT: slice_index_fail
1010
*a = &a[1..];
1111
}
1212
}
@@ -15,7 +15,7 @@ pub fn trim_in_place(a: &mut &[u8]) {
1515
#[no_mangle]
1616
pub fn trim_in_place2(a: &mut &[u8]) {
1717
while let Some(&42) = a.first() {
18-
// CHECK-NOT: slice_index_order_fail
18+
// CHECK-COUNT-1: slice_index_fail
1919
*a = &a[2..];
2020
}
2121
}

0 commit comments

Comments
 (0)