How does this optimization of branches work?

I have this function

fn flag_if(condition: bool, f: FieldFlags) -> FieldFlags {
    condition.then(|| f).unwrap_or(FieldFlags::empty())
}

And Godbolt tells me that this pattern compiles down to a single branch, as I'd expect. The part I don't understand is how the compiler knows that. Looking into the source of then and unwrap_or tells me that this will inline into something like,

fn flag_if(condition: bool, f: FieldFlags) -> FieldFlags {
    match (if condition { Some(f) } else { None }) {
        Some(x) => x,
        None => FieldFlags::empty(),
    }
}

But at this point I lose the compiler's logic and can't see what rule or transform it uses to realize the result of the match is logically equivalent to the condition and only needs to be checked once. Am I missing something or looking at this at too high a level?

I believe LLVM's jump-threading pass is responsible for this optimization.

For more details, see this blog post about jump threading by Aldy Hernandez.

4 Likes

Thanks for the links, I hadn't heard that term before and that was exactly what I was looking for

I wonder if there's a MIR optimization that handles this or if it would make sense to write one. Even the simplified version (match on an option you just produced from a match) seems pretty common (think for example of for loops, they match on an option that the Iterator::next just produced, usually by checking some other condition)

Yup, very common -- it's what ? does, for example. See the work happening in