View RSS Feed

Development Team Blog

Why does if/else statements behave so strangely when debugging?

Rate this Entry
In most cases you don't even realize it, but if you look closely enough when stepping through if/else statements in the debugger, you notice it seems to behave in an unintuitive manner, even though it actually works correctly.

Code:
If (1=1) Begin
    Showln "yup"
End
Else If (1=1) Begin
    Showln "huh?"
End
Else If (1=1) Begin
    Showln "what?"
End
When stepping through this in the debugger, you'll notice that it executes the first block, and then it seems to visit each and every Else statement as well, but it doesn't execute the associated code blocks. Is it evaluating all the other Else If and then throwing away the results? At first you probably just dismiss it and assume it's alright, but if you look closer you begin to have doubts. Don't worry, it's an illusion, your first glance interpretation was right.

This reaction often occurs when there's something else that's confusing about the if/else statement, where the logic most likely really is wrong even though it appears correct, and then the if/else illusion which appears wrong even though it's correct gets blamed.

It's recommended to use Begin/End blocks together with If/Else statements even when you only need one line of code, like this:
Code:
If (blah blah) Begin
    ...
End
Else Begin
    ...
End
The reason for that recommendation is that it reduces the chance of confusion with code like this:
Code:
If (blah blah) Move x to y
When stepping through that line of code when debugging, it can be very difficult to tell whether it executed the branch of code associated with the If statement, since it's a one step operation in the debugger. Imagine if the code example at the beginning wasn't using Begin/End blocks, since it appears to visit each Else, you wouldn't be able to tell which one of the many If/Else branches it's executing, since they all look the same when stepping through in the debugger. Using Begin/End blocks also lets you place breakpoints in the conditional block, which isn't possible if it's all on one line. Actually, that's not entirely true, since it is possible to do with conditional breakpoints, which was introduced in VDF 15.0.

OK, now that we know about this If/Else illusion, and we know that using Begin/End blocks can help reduce the confusion, why does it appear to briefly stop at each Else? This all has to do with the details of the code generated by the Else statement, and the answers are basically in fmac. The internal details for the If command goes something like this, if the condition evaluates to false, then jump to end of the If statement, effectively skipping the next statement/block. The internal details of Else then first adds an unconditional jump to the end of the statement/block, then it goes back and modifies the previous jump destination, effectively incrementing it to also skip the newly added unconditional jump.

This sound complicated, but it means that when it evaluates to true, and executes the If block, it will then find an unconditional jump to skip over the Else block. If there's then another Else block, it will find another unconditional jump to skip over that block as well. Like a happy kid, it's merrily skipping and bouncing on each Else until it reaches the end of the last one.

This If/Else logic was of course written very long ago, before there was a VDF debugger, and nobody would really notice any of that skipping and bouncing. Even now when you can clearly see the effects in the debugger, most people don't really pay attention to it.

Note that the If/Else illusion described here is unrelated to the more common generic confusion of which If does the Else belong to. As Anders comments in that thread, it's a generic and common programming logic trap, which is best solved by using Begin/End blocks to reduce confusion.

Comments