Why does this piece of code works on Debug but fails on Release?

I'm on windows using Rust 1.20 on MSVC and a piece of code generates an illegal instruction in release mode, but work fine in debug mode. I'm wondering why (if it's a bug or not).

I reproduced the error here (the error is caused by the value assigned to dummy):

Note that I know that this is very ugly and I've replaced that line in my code with something more appropriate...

It is undefined behavior either way, so it is not guaranteed to work in Debug mode just as much as it is not guaranteed to work in Release mode. Function pointers like fn() are not allowed to be null in Rust.

The equivalent of C's void (*)(void) type is Option<unsafe extern fn()> which is guaranteed to be ABI-compatible with function pointers in a C API.

3 Likes

Interesting! On the playground, the asm in release mode shows that it generates nothing for your fn main, not even a ret instruction. So it will just fall through to the C-style main() entry point, which calls Rust's std::rt::lang_start again, which leads to:

thread 'main' panicked at 'assertion failed: c.borrow().is_none()', /checkout/src/libstd/sys_common/thread_info.rs:46:25

because this code doesn't expect to be called twice.

The LLVM IR looks like this:

; playground::main
; Function Attrs: norecurse noreturn nounwind readnone uwtable
define internal void @_ZN10playground4main17h641c332fe05dc699E() unnamed_addr #0 {
start:
  unreachable
}

My guess is that the DummyFunction type is getting some kind of non-null annotation to LLVM, and since you set it to null anyway, LLVM calls this UB -> unreachable.

2 Likes

thanks to both of you for the quick answer!

I didn't knwo about the equivalent of Option<unsafe extern fn()>.