Manual bounds check does not eliminate slice bounds checks?

#[inline(never)]
pub fn decode_32_le(buffer: &[u8], offset: usize) -> u32 {
    if offset + 3 >= buffer.len() {
        panic!();
    }
    let b1 = buffer[offset];
    let b2 = buffer[offset + 1];
    let b3 = buffer[offset + 2];
    let b4 = buffer[offset + 3];
    u32::from_le_bytes([b1, b2, b3, b4])
}

In this code I was expecting that knowing that offset + 3 < buffer.len() after the conditional would make the bounds checks when accessing buffer disappear, but that doesn't seem to happen: Compiler Explorer.

Why is the compiler not able to eliminate the bounds checks here?

Please do not suggest alternative implementations. I'm interested in why the compiler is not able to eliminate the bounds check in this particular code.

Given your intent here, you might have better luck asking on the internals forum. This one is more focused on the usage of the compiler and toolchain rather than its inner workings.

the bounds check is needed when offset + 3 overflows. As far as i can tell the compiler has actually eliminated the one bounds check that it can eliminate

Aha! You are right. Here's the correct version:

#[inline(never)]
pub fn decode_32_le(buffer: &[u8], offset: usize) -> u32 {
    if buffer.len() < 4 || buffer.len() - 4 < offset {
        panic!();
    }
    let b1 = buffer[offset];
    let b2 = buffer[offset + 1];
    let b3 = buffer[offset + 2];
    let b4 = buffer[offset + 3];
    u32::from_le_bytes([b1, b2, b3, b4])
}

Thanks!