Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

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.


> It is part of similar conventional thinking that "a method call must return back to the caller on termination".

No, it's a part of a conventional thinking that "if a return statement executes without any exceptions thrown in the process, then the control will return from the current invocation". It's an extension of a conventional thinking "if an e.g. assignment statement executes without any exceptions thrown in the process, the control will go to the next statement". Really helps with reasoning about the program behaviour without spending an undue amount of brainpower.

> But what is the alternative otherwise?

Disallow "return", "break", "continue", and "goto" statements inside "finally" blocks, like C# does. Nobody sane misses this functionality anyway.


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


No, it's not executed before the return, it's executed in the middle of it, so to speak:

    class Main {
        static int f() { System.out.println("f()"); return 1; }
        static int g() { System.out.println("g()"); return 2; }

        static int h() {
            try {
                return f();
            } finally {
                return g();
            }
        }

        public static void main(String[] args) {
            System.out.println(h());
        }
    }
will print

    f()
    g()
    2
If "finally" block was executed before the "return" in the "try", the call to f() would not have been made.


Yes, very much is... return is not a single statement or a single opcode. the call to f() is returned on the stack, that's assigned to some local variable via istore, there is no 'ireturn', yet. Then the g() method is called along with whatever is printed, and the next instruction can be ireturn. which would return '2' indeed.


> return is not a single statement.

It is a single statement. It's just that executing it contains of two parts: evaluating the expression, and the non-local transfer of control ― and the weird part is that this control transfer can be hijacked. And it is weird, you can't do this with e.g. "if": if the test condition is evaluated successfully, then the control will go into one of the branches, and you can't mess with that... but with "return", you can abort this transfer of control. Which is, again, weird: all other expression-evaluating statements can go haywire only if the evaluation of the expression inside them throws an exception — but if that doesn't happen, the control transfer is completely trivial, with no special rules about try-catch blocks.


> control transfer can be hijacked.

But useful. Similar to Go's "defer".


Go's "defer" can't prevent return from actually returning from the function. Most of the time, it can't change the computed return value either, unless you go out of your way and use named return values. Go's "defer" also can't do crimes with "break" and "continue".

So Go's "defer" has more "and also do some things on the way out" semantics than "you suddenly forget why you were going to leave the kitchen, so you don't".


you cut the statement in the middle, it's very obviously in terms of bytecode as we talk about bytecode generation.


Mo, we were talking about what would be reasonable semantics for the interaction of two features in a high-level programming language Java; the semantics of bytecode doesn't really matter in this case. The precedent for that would be that "finally"m in fact, really doesn't mesh all that well with what is available for bytecode, so javac has to resort to code duplication.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: