Skip to content

Niched option check not optimized out #144329

@DaniPopes

Description

@DaniPopes

Code

I tried this code (godbolt):

type T = [u64; 4];
const N: usize = 0;

#[no_mangle]
pub fn f(stack: &mut Stack, f: fn(&T)) -> bool {
    let Some((a, b)) = stack.popn_top::<N>() else {
        return false;
    };
    a.iter().for_each(f);
    f(b);
    true
}

pub struct Stack {
    data: Vec<T>,
}

impl Stack {
    #[inline(always)]
    fn popn_top<const N: usize>(&mut self) -> Option<([T; N], &mut T)> {
        if self.data.len() < N + 1 {
            return None;
        }
        unsafe {
            Some((self.popn_unchecked(), self.top_unchecked()))
        }
    }

    unsafe fn popn_unchecked<const N: usize>(&mut self) -> [T; N] {
        core::array::from_fn(|_| unsafe{self.pop_unchecked()})
    }

    unsafe fn pop_unchecked(&mut self) -> T {
        self.data.pop().unwrap_unchecked()
    }

    unsafe fn top_unchecked(&mut self) -> &mut T {
        self.data.last_mut().unwrap_unchecked()
    }
}

I expected to see this happen: only 1 check (len < 1)

  %_2.i = icmp ne i64 %_3.i, 0
  br i1 %_2.i, label %bb2, label %bb6

Instead, this happened: length check and null check (from let-else on Option<&mut T>), the latter of which is always false

; ... above plus:
  %.not.not = icmp eq ptr %last.i, null
  br i1 %.not.not, label %bb6, label %bb2

The #[inline(always)] is not necessary, but without it a weirder construct is generated:

  %_0.sroa.0.0.i = select i1 %_2.i, ptr null, ptr %last.i
  %.not = icmp ne ptr %_0.sroa.0.0.i, null
  br i1 %.not, label %bb2, label %bb6

Version it worked on

It most recently worked on: 1.84

Version with regression

rustc --version --verbose:

rustc 1.88.0 (6b00bc388 2025-06-23)
binary: rustc
commit-hash: 6b00bc3880198600130e1cf62b8f8a93494488cc
commit-date: 2025-06-23
host: x86_64-unknown-linux-gnu
release: 1.88.0
LLVM version: 20.1.5
rustc 1.90.0-nightly (5795086bd 2025-07-16)
binary: rustc
commit-hash: 5795086bdfe7ed988aa53a110bd0692c33d8755b
commit-date: 2025-07-16
host: x86_64-unknown-linux-gnu
release: 1.90.0-nightly
LLVM version: 20.1.8

Regression in nightly-2024-12-16
get_commits_between returning commits, len: 9
commit[0] 2024-12-14: Auto merge of #134305 - matthiaskrgr:rollup-bja3lsz, r=matthiaskrgr
commit[1] 2024-12-14: Auto merge of #133734 - scottmcm:lower-indexing-to-ptrmetadata, r=davidtwco,RalfJung
commit[2] 2024-12-15: Auto merge of #134318 - matthiaskrgr:rollup-jda0jkx, r=matthiaskrgr
commit[3] 2024-12-15: Auto merge of #134258 - bjorn3:no_public_specialization, r=petrochenkov
commit[4] 2024-12-15: Auto merge of #133223 - zachs18:uniquerc-impls, r=Noratrieb
commit[5] 2024-12-15: Auto merge of #134332 - Zalathar:rollup-oe23hkw, r=Zalathar
commit[6] 2024-12-15: Auto merge of #134117 - DianQK:gep-i8, r=oli-obk
commit[7] 2024-12-15: Auto merge of #133417 - RalfJung:aarch64-float-abi, r=workingjubilee
commit[8] 2024-12-15: Auto merge of #134349 - jieyouxu:rollup-zqn0jox, r=jieyouxu

@rustbot modify labels: +regression-from-stable-to-stable -regression-untriaged

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.I-prioritizeIssue: Indicates that prioritization has been requested for this issue.needs-triageThis issue may need triage. Remove it if it has been sufficiently triaged.regression-from-stable-to-stablePerformance or correctness regression from one stable version to another.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions