← Back to context

Comment by taneq

6 days ago

So the function returns, and then during its tidyup, the 'continue' basically comefrom()s the VM back into the loop? That is, indeed, cursed.

I would not call this snippet particularly "cursed". There is no "aktshchually this happens, therefore this is activated" hidden magic going on. The try-catch-finally construct is doing exactly what it is designed and documented to do: finally block is executed regardless of the outcome within try. The purpose of finally block is to fire regardless of exceptionality in control flow.

Surprising at first? Maybe. Cursed? Wouldn't say so. It is merely unconventional use of the construct.

  • It messes with the semantics of "return" statement. The conventional intuition is that right after a "return" statement completes, the current method's invocation ends and the control returns to the caller. Unfortunately, the actual semantics has to be that is "attempts to return control":

        The preceding descriptions say "attempts to transfer control" rather than just "transfers control" because if there are any try statements (§14.20) within the method or constructor whose try blocks or catch clauses contain the return statement, then any finally clauses of those try statements will be executed, in order, innermost to outermost, before control is transferred to the invoker of the method or constructor. Abrupt completion of a finally clause can disrupt the transfer of control initiated by a return statement.
    

    This, I believe, is the only way for "return E", after the evaluation of E completes normally, to not return the E's value to the caller. Thanks for a needless corner-case complication, I guess.

    • > It messes with the semantics of "return" statement.

      Yes, that's what exceptions and their handling handling does - it messes with control flow.

      > The conventional intuition is that right after a "return" statement completes, the current method's invocation ends and the control returns to the caller.

      It is part of similar conventional thinking that "a method call must return back to the caller on termination", which is not even applicable to Java and other languages with conventional exceptions.

      > Thanks for a needless corner-case complication, I guess.

      But what is the alternative otherwise? Not execute finally block if try block exits normally? Execute finally block, but ignore control flow statements? What if code in finally block throws? I maintain that execution of finally block, including control flow statements within, normally is the only sane behavior.

      1 reply →

    • finally stuff is executed prior to the 'return'; effectively it's placed before the 'return' statement. So it's quite obvious.

      6 replies →

  • Sufficiently unexpected (surprising) is cursed. If anything, that’s the way most people use that term for programming language constructs (weird coercions in JavaScript, conflicting naming conventions in PHP, overflow checks that get erased by the compiler due to UB in c/c++, etc.)

    • > Sufficiently unexpected (surprising) is cursed.

      I think there three distinct classes of surprising that fall under this umbrella: 1. a behavior overrides another behavior due to some precedence rules 2. behavior of a construct in language x is different than behavior of similar construct in most mainstream languages 3. who in their right mind would design the thing to behave like this.

      Examples could be: string "arithmetic" in most dynamically typed languages, PHP's ternary operator order of precedence and Python's handling of default function arguments. In all of these cases when you sit down, think about it and ask yourself "what's the alternative?", there's always a very reasonable answer to it, therefore, I think it is reasonable to call these behaviors cursed.

      In this case the surprise factor comes in, because we are used to equating finally block with cleanup and I concur that many would trip on this the first time. But if you gave this exercise some time and asked "what should happen if finally block contains control flow statements?" the reasonable answer should be "take precedence", because the behavior would be surprising/cursed otherwise.

      That's my reasoning.

      5 replies →

  • It's been many, many moons since I touched Java but I would have expected this to run the finally { } clause and then return from the function (similar to how in C++, objects on the stack will run their destructors after a return call before the function ends. I certainly wouldn't expect it to cancel the return.