Functions with uninhabited return values codegen trap instead of unreachable · Issue #59793 · rust-lang/rust (original) (raw)
With #59639, I would expect the following two functions to generate the same LLVM IR:
#[derive(Clone, Copy)] pub enum EmptyEnum {}
#[no_mangle] pub fn empty(x: &EmptyEnum) -> EmptyEnum { *x }
#[no_mangle] pub fn unreach(x: &EmptyEnum) -> EmptyEnum { unsafe { std::hint::unreachable_unchecked() } }
However, they do not:
; playground::empty
; Function Attrs: noreturn nounwind nonlazybind uwtable
define void @_ZN10playground5empty17he3f416ff39576b93E(%EmptyEnum* noalias nocapture nonnull readonly align 1 %x) unnamed_addr #0 {
start:
tail call void @llvm.trap()
unreachable
}
; playground::unreach
; Function Attrs: norecurse noreturn nounwind nonlazybind readnone uwtable
define void @_ZN10playground7unreach17h416ea08edc51ffc9E(%EmptyEnum* noalias nocapture nonnull readonly align 1 %x) unnamed_addr #1 {
start:
unreachable
}
Namely, empty
triggers a well-defined trap while unreach
causes UB.
That this makes a difference can be seen when adding:
pub fn test_unreach(x: bool) -> i32 { if x { 42 } else { unsafe { unreach(&*(8 as *const EmptyEnum)) }; 13 } }
pub fn test_empty(x: bool) -> i32 { if x { 42 } else { unsafe { empty(&*(8 as *const EmptyEnum)) }; 13 } }
The first becomes
; playground::test_unreach
; Function Attrs: norecurse nounwind nonlazybind readnone uwtable
define i32 @_ZN10playground12test_unreach17he4cbbc8d69597b0eE(i1 zeroext %x) unnamed_addr #2 {
start:
ret i32 42
}
but the second becomes
; playground::test_empty
; Function Attrs: nounwind nonlazybind uwtable
define i32 @_ZN10playground10test_empty17hcb53970308f4f532E(i1 zeroext %x) unnamed_addr #3 {
start:
br i1 %x, label %bb1, label %bb2
bb1: ; preds = %start
ret i32 42
bb2: ; preds = %start
tail call void @llvm.trap() #5
unreachable
}
because the second branch does not actually cause UB.