-
Notifications
You must be signed in to change notification settings - Fork 14.1k
Description
Given this enum that has two inner enums for a total of 3 + 2 = 5 variants, rustc fails to notice that the match output is always within arr's bounds and still generates bounds-checking code. Godbolt
pub enum Foo {
A(A),
B(B),
}
pub enum A {
A0,
A1,
A2,
}
pub enum B {
B0,
B1,
}
pub fn bar(foo: Foo, arr: &[u8; 5]) -> &u8 {
let offset: usize = match foo {
Foo::A(A::A0) => 0,
Foo::A(A::A1) => 1,
Foo::A(A::A2) => 2,
Foo::B(B::B0) => 3,
Foo::B(B::B1) => 4,
};
// assert!(offset < 5);
// unsafe { std::hint::assert_unchecked(offset < 5); }
&arr[offset]
}Commenting out any of the five variants removes the bounds check. This is the case even if commenting out, say, A2, so that the min and max values generated by the match are still 0..=4. In other words it seems the number of variants matters, not the values they map to.
Keeping all five variants and increasing the array size to 256 also eliminates the bounds check. In this case, changing the B1 arm to map to 255 still does not generate bounds checks. So it seems rustc does track the range of all five arms, but somehow the array length being 256 or less than 256 matters to it.
Flattening all five variants into one enum also eliminates the bounds check.
Uncommenting the assert! replaces the bounds check code with assert panic code, so there's no net benefit. Uncommenting the assert_unchecked eliminates the bounds check as expected.
Based on the compilers Godbolt provides, this regressed between 1.81.0 and 1.82.0. That is, 1.81.0 does not generate bounds checks but 1.82.0 does. These are old enough that I figured this doesn't deserve to be tagged as "regression". In any case it is not noticeable in benchmarks of my real code (more top-level variants and a lot more sub-variants) since the branch is predicted correctly.
I noticed #147831 , but given that that one regressed from 1.89 to 1.90, I assume it is not the same as this one.
Meta
rustc --version --verbose:
rustc 1.93.0-nightly (c871d09d1 2025-11-24)
binary: rustc
commit-hash: c871d09d1cc32a649f4c5177bb819646260ed120
commit-date: 2025-11-24
host: x86_64-unknown-linux-gnu
release: 1.93.0-nightly
LLVM version: 21.1.5
rustc 1.81.0 (eeb90cda1 2024-09-04)
binary: rustc
commit-hash: eeb90cda1969383f56a2637cbd3037bdf598841c
commit-date: 2024-09-04
host: x86_64-unknown-linux-gnu
release: 1.81.0
LLVM version: 18.1.7
rustc 1.82.0 (f6e511eec 2024-10-15)
binary: rustc
commit-hash: f6e511eec7342f59a25f7c0534f1dbea00d01b14
commit-date: 2024-10-15
host: x86_64-unknown-linux-gnu
release: 1.82.0
LLVM version: 19.1.1