Change:
{ body.basic_blocks.reverse_postorder().iter().map(|&bb| (bb, &body.basic_blocks[bb])) } <ins> /// Traversal of a [`Body`] that tries to avoid unreachable blocks in a monomorphized [`Instance`]. /// /// This is allowed to have false positives; blocks may be visited even if they are not actually /// reachable. /// /// Such a traversal is mostly useful because it lets us skip lowering the `false` side /// of `if <T as Trait>::CONST`, as well as [`NullOp::UbChecks`]. /// /// [`NullOp::UbChecks`]: rustc_middle::mir::NullOp::UbChecks pub fn mono_reachable<'a, 'tcx>( body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, ) -> MonoReachable<'a, 'tcx> { MonoReachable::new(body, tcx, instance) } /// [`MonoReachable`] internally accumulates a [`BitSet`] of visited blocks. This is just a /// convenience function to run that traversal then extract its set of reached blocks. pub fn mono_reachable_as_bitset<'a, 'tcx>( body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, ) -> BitSet<BasicBlock> { let mut iter = mono_reachable(body, tcx, instance); while let Some(_) = iter.next() {} iter.visited } pub struct MonoReachable<'a, 'tcx> { body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, visited: BitSet<BasicBlock>, // Other traversers track their worklist in a Vec. But we don't care about order, so we can // store ours in a BitSet and thus save allocations because BitSet has a small size // optimization. worklist: BitSet<BasicBlock>, } impl<'a, 'tcx> MonoReachable<'a, 'tcx> { pub fn new( body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, ) -> MonoReachable<'a, 'tcx> { let mut worklist = BitSet::new_empty(body.basic_blocks.len()); worklist.insert(START_BLOCK); MonoReachable { body, tcx, instance, visited: BitSet::new_empty(body.basic_blocks.len()), worklist, } } fn add_work(&mut self, blocks: impl IntoIterator<Item = BasicBlock>) { for block in blocks.into_iter() { if !self.visited.contains(block) { self.worklist.insert(block); } } } } impl<'a, 'tcx> Iterator for MonoReachable<'a, 'tcx> { type Item = (BasicBlock, &'a BasicBlockData<'tcx>); fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> { while let Some(idx) = self.worklist.iter().next() { self.worklist.remove(idx); if !self.visited.insert(idx) { continue; } let data = &self.body[idx]; if let Some((bits, targets)) = Body::try_const_mono_switchint(self.tcx, self.instance, data) { let target = targets.target_for_value(bits); self.add_work([target]); } else { self.add_work(data.terminator().successors()); } return Some((idx, data)); } None } } </ins>

Discussion:
tracking issue: Inspired by comment in For things like it'd be nice to not have to even give LLVM the code for the else branch to have to emit. Ought to save time and binary size. Tentatively marked mir-opt, since we presumably still need to make the MIR for it, in order to borrow-check and such the unreachable code. But might need to be codegen-time instead to pick up on monomorphized inline constants. Hopefully should be doable relatively easily when the const is an inline const, since that forces const folding already, and thus there should just be a in the MIR.
Well, turns out this already works for simple constants in MIR! However, the place I actually needed it was more like , which doesn't work with today. So maybe come back to this once that's fixed.
This should be done by which AFAIK is run unconditionally. I wonder if its match needs to be expanded? Is there mcve?
The problem is that this can't be done on generic constants, as we don't mir-optimize post-monomorphization. We could start doing some benchmarks to see how bad running all mir opts (or at least a select few) would be if we duplicated, substituted and optimized MIR bodies for concrete generic parameters.
I would say qualifies as an MCVE as the monomorphized gives the following llvm ir
Maybe it should also be evaluated together with polymorphization or constevaluatablechecked since those can cut down on the duplication before applying further optimizations.
Bonus points if this can avoid monomorphizing functions called in the dead code too. Demo extracted from : (cc ) Would be great to not codegen all the downstream stuff that's only called in the else block of a .
FYI this should work with
Totally wild (and probably bad) idea: could we avoid borrow-checking unreachable code? After all, the purpose of borrow-checking is to prevent memory unsafety at runtime, but unreachable code cannot be memory unsafe, because it never executes!
We already do! As a , (But this isn't really the right issue to discuss possible extensions to that.)
This seems closely related to and
recent PR seems related here? If the condition becomes a constant during codegen, we should be entirely skipping it now and not even generate LLVM IR.

However, if that dead code calls functions, those still get codegen'd as usual -- the collector that determines which function to codegen doesn't know about dead branches. We'd have to embed a (simple) monomorphization-time reachability analysis in the collector to fix that.