Unimplemented! macro optimization bug


#1

I saw a bug with optimization for unimplemented! macro :confused:

I have a piece of code which never reach an arm with unimplemented! call, and it’s absolutely true for debug build, but… when I try to build the release it reaches (!) unreachable arm and do unimplemented! panic.

I believe there is no bug in my code, because if I change unreachable arm to return expression bug disappears. Release build works well and the second arm never reached again.

match model.find_resource(entry.key()) {
    Some(res) => {
        let record = ResourceRecord::new(
            res.initial_value,
            res.lower_limit,
            res.upper_limit);
        entry.insert(record)
    },
    None => {
        // unimplemented!()
        return Err(E::from(Error::ResourceNotFound(format!("resource '{}' lost", entry.key()))));
    },
}

I didn’t create an issue, because I haven’t an good example to reproduce it. The real code where I saw it is large and proprietary. But I believe there is inconstant optimizer’s behavior.

Did anyone see something similar?

I use: nightly-x86_64-pc-windows-gnu (default) rustc 1.16.0-nightly (9761b17d5 2017-01-21)


#2

While compiler bugs are always a possibility, most of the time, issues that look like compiler bugs are the result of undefined behavior in your code. There are two ways this can result in differing behavior between unoptimized and optimized builds: sometimes the compiler makes an optimization that is valid according to the language rules but not what you expected; sometimes it’s just random differences in code generation (e.g. stack layout) that cause things like use-after-free to have different observed behavior.

In Rust, it should only be possible to invoke undefined behavior using unsafe, though there are a fair number of known bugs. Of course, the code triggering undefined behavior doesn’t have to be anywhere near the code where visible symptoms occur.

Sorry for the very general advice, but without more details on your code there’s no way to investigate further. One thing you can try is running your code under valgrind to identify certain classes of errors.


#3

You can see that unimplemented just expands to a panic, here: https://github.com/rust-lang/rust/blob/1.14.0/src/libcore/macros.rs#L513

Could it be that your change to return an Err does actually get triggered, but the Err is never inspected or is ignored, and so it seems like it doesn’t happen? For example, if you change it to an Err, and then all the places that call your method could your_method(key).unwrap().


#4

Interesting idea! I’ve changed it to .unwrap(). If I build it with cargo build and run the program works without panic (it completes long simulation process), but it panics and fails if I build the program with cargo build --release. I don’t change the code, but behavior is different. Strange thing :grin:


#5

To be honest I use a lot of context switching in the program. But optimizer breaks something if panics included.


#6

I think we’re going to need a minimized, self-contained test case to help you any further, and probably this conversation should be happening on the users board.


#7

What exactly do you mean by context switching?


#8

I use https://github.com/rustcc/coroutine-rs library and lua coroutines both.