Repository: rust-lang/rust
File: library/core/src/slice/index.rs (Rust)

Code before:
{
    type Output = I::Output;

    #[inline(always)]
    fn index(&self, index: I) -> &I::Output {
        index.index(self)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
impl<T, I> const ops::IndexMut<I> for [T]
where
    I: ~const SliceIndex<[T]>,
{
    #[inline(always)]
    fn index_mut(&mut self, index: I) -> &mut I::Output {
        index.index_mut(self)
    }
}

#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
#[cfg_attr(feature = "panic_immediate_abort", inline)]
#[track_caller]
const fn slice_index_fail(start: usize, end: usize, len: usize) -> ! {
    if start > len {
        const_panic!(
            "slice start index is out of range for slice",
            "range start index {start} out of range for slice of length {len}",
            start: usize,
            len: usize,
        )
    }

    if end > len {
        const_panic!(
            "slice end index is out of range for slice",
            "range end index {end} out of range for slice of length {len}",
            end: usize,
            len: usize,
        )
    }

    if start > end {
        const_panic!(
            "slice index start is larger than end",
            "slice index starts at {start} but ends at {end}",
            start: usize,
            end: usize,
        )
    }

Review comment (bug):
There's a reason why there's a zoo of different functions for different ways that slicing can panic. It's marginally cheaper for the code that can panic to pass precisely the values that are needed by the specific panic (e.g., start and len in the `start > len` case) and no more. If nothing else, it results in slightly smaller binary size because one less parameters needs to be put in the right register before the call.

It's possible that the above reason is outdated/wrong. But merging these functions seems unrelated to the main point of the PR (more efficient check for whether slicing panics). So I'd suggest doing only the optimization and evaluate the function merging separately.

Code after:
    I: [const] SliceIndex<[T]>,
{
    type Output = I::Output;

    #[inline(always)]
    fn index(&self, index: I) -> &I::Output {
        index.index(self)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
impl<T, I> const ops::IndexMut<I> for [T]
where
    I: [const] SliceIndex<[T]>,
{
    #[inline(always)]
    fn index_mut(&mut self, index: I) -> &mut I::Output {
        index.index_mut(self)
    }
}

#[cfg_attr(not(panic = "immediate-abort"), inline(never), cold)]
#[cfg_attr(panic = "immediate-abort", inline)]
#[track_caller]
const fn slice_index_fail(start: usize, end: usize, len: usize) -> ! {
    if start > len {
        const_panic!(
            "slice start index is out of range for slice",
            "range start index {start} out of range for slice of length {len}",
            start: usize,
            len: usize,
        )
    }

    if end > len {
        const_panic!(
            "slice end index is out of range for slice",
            "range end index {end} out of range for slice of length {len}",
            end: usize,
            len: usize,
        )
    }

    if start > end {
        const_panic!(
            "slice index start is larger than end",
            "slice index starts at {start} but ends at {end}",
            start: usize,
            end: usize,
        )