<p>This question is about the expected semantics when combining resource management with branching control flow. From my perspective, it applies equally to all <code>finally</code>-like constructs, i.e. last wishes, irrespective of the actual language. The situation can be illustrated in Java:</p>
<pre><code>public class T implements AutoCloseable {
   static int f() {
      try (var m1 = new T()) {
         try (var m2 = new T()) {
            System.out.println(&quot;inner&quot;);
         } finally {
            if (null != m1)
               return 1;
         }
         System.out.println(&quot;outer&quot;);
      } finally {
         return 2;
      }
   }
   @Override
   public void close() throws Exception {
      System.out.println(&quot;did close&quot;);
   }
   public static void main(String[] args) throws Exception {
      System.out.println(f());
   }
}
</code></pre>
<p>The obvious options here are:</p>
<ol>
<li>rule usage of <code>return</code>/<code>break</code>/<code>continue</code>/... illegal in that context</li>
<li>rule usage of <code>return</code>/<code>break</code>/<code>continue</code>/... illegal in that context and ensure that it cannot get there by other means (same as above for Java)</li>
<li>make finally blocks implicit and let return jump to its end (my understanding of RAII in C++ and defer in Go)</li>
<li>place final actions such as implicit exception continuation and resource release at all exit blocks</li>
<li>rule the situation undefined behaviour</li>
<li>ignore the situation as it has little practical relevance</li>
</ol>
<p>The situation in Java is that ECJ crashes for this example resulting in some code cleaning being unavailable. Javac compiles without any messages and creates a program printing:</p>
<pre><code>inner
did close
did close
2
</code></pre>
<p>I have not checked if the JIT would crash or other exception-related corner cases.</p>
<p>The question here is what language semantics should be chosen to result in a behaviour that allows programmers to write correct code and likewise feels consistent to them.</p>

<semantics><control-flow><undefined-behavior><control-flow-graph>
What should the semantics from branching from finally be?
CC BY-SA 4.0
2024-10-06T13:20:54.533Z
Javac's behavior is correct according to [section 14.20.2 in the Java Language Specification](https://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.20.2): "If execution of the try block completes abruptly for any other reason R, then the finally block is executed, and then there is a choice: If the finally block completes normally, then the try statement completes abruptly for reason R. If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and reason R is discarded)."
tarzh
CC BY-SA 4.0
2024-10-06T14:15:04.990Z
So, the answer would be "do whatever Java does" because return, ultimately, is not any different from throwing an exception which, fundamentally, cannot be prevented here? I think I would accept that as an answer even if I cannot tell if I'm going to implement it that way.
feldentm
CC BY-SA 4.0
2024-10-06T17:57:44.610Z
"*ignore the situation as it has little practical relevance*" - that's essentially the same as option 5 (undefined behaviour). I would recommend to never apply that, and instead choose option 1 (illegal) if you consider the use case to be irrelevant.
Bergi
CC BY-SA 4.0
2024-10-06T18:06:32.840Z
@Bergi 1, 2, 5 and 6 widely differ in effort and results. Option 6 would imply that crashing compiler is an explicitly accepted behavior until someone complains (with a valid use case). Also 1 and 2 inflict distributed cost on every usage of the `finally` or `return` construct as the check must be performed somehow. My perspective of 5 would be that the compiler must at least not crash.
feldentm
CC BY-SA 4.0
2024-10-06T18:07:03.103Z
The fact that you're asking about what semantics "feel right" is waving a flag that you should pay attention to. Just because lots of languages have "finally" doesn't mean that it's the right choice for your language in the first place! "Finally" semantics are *weird* so it's a good idea to take a step back and say "what problems does finally attempt to solve, and are there solutions that feel more natural in my language?"
Eric Lippert
CC BY-SA 4.0
2024-10-06T18:09:25.597Z
You are specifically looking for a solution for *resource management*, so rather than phrasing the solution in the jargon of exception handling control flow, can you rephrase the problem so that it uses the jargon of resource management? That might help ensure that you're coming up with a solution that solves the right problem -- even if you end up inventing something that is a sugar for try-finally behind the scenes.
Eric Lippert
CC BY-SA 4.0
2024-10-06T18:13:04.627Z
@feldentm Ah, I thought undefined behaviour can include crashing the compiler.
Bergi
CC BY-SA 4.0
2024-10-06T18:17:39.660Z
@EricLippert I would have to add a lot more context to make it about my language and that wasn't really the intention. The need of properly terminating network connections or child processes is universal and, to me, looks like the primary motivation behind such constructs. What is odd here is that, even without exception handling, the solutions run into issues where choices must be made, such as returning 1 or 2. It's not only about what feels natural (to me) but also about what results in mortal programmers producing somewhat correct code.
feldentm
CC BY-SA 4.0
2024-10-06T19:48:02.223Z
Mortal programmers are more likely to produce correct code when the language features afford a clear representation of the semantics of the business domain, is what I'm saying. If "finally" doesn't clearly represent a concept in the business domain of the developers then asking what "finally" semantics best ensures success of those developers is a non-starter. I think of "finally" as a borderline misfeature; we could have done better.
Eric Lippert
CC BY-SA 4.0
2024-10-07T03:20:12.667Z
In the language Tcl, `return` is implemented using throw (called `error`), and could even be caught, and used outside functions.
user23013
CC BY-SA 4.0
2024-10-07T16:10:11.940Z
@user23013 Scala had a similar pattern and long story short it does not work.
feldentm
CC BY-SA 4.0
2024-10-08T04:01:52.073Z
@feldentm How? I tried to search about it. The only non-stylistic reason to not use return in Scala I found is they are not bound to anonymous functions, which I don't think there is any relevance.
user23013
CC BY-SA 4.0
2024-10-08T17:58:14.523Z
@user23013 you cannot return accross physical function boundaries in Scala, e.g. break out of their for loops. Exceptions are considered a workaround for this shortcoming, but there is hidden code in all sorts of places in Scala that could react to such exceptions arbitrarily including the good old try{...}catch(Throwable e){}
feldentm
CC BY-SA 4.0
2024-10-22T09:41:25.327Z
C++ RAII is 1, not 3. Destructors don't return a value, don't interact with control flow, and are forbidden (on pain of terminating the process) from throwing
Caleth
<h3>Schedule and overwrite</h3>
<p>Branching around <code>finally</code> or in the presence of <code>defer</code> semantics is to <em>schedule</em> the effect, i.e., <code>goto</code>s and <code>return</code>s first calculate their targets or values, and the run any static (<code>finally</code>) or dynamic (<code>defer</code>) scheduled code.</p>
<p>Branching inside <code>finally</code>/<code>defer</code> code blocks has normal semantics in principle, but can also <em>overwrite</em> the effects of previous code, i.e., this code can replace generated and scheduled results or exceptions.</p>
<hr />
<p>Compare this with pure imperative code, where <code>goto</code> immediately jumps, or the first <code>return</code> immediately returns. In the absence of these inline code scheduling constructs, all of &quot;resource management&quot; takes the form of a lot of complicated direct imperative branching and jumping around, obfuscating &quot;normal&quot; code with a lot of distracting or exceptional code that rarely runs.</p>
<p>By <em>not</em> making exceptional code to have even more exceptional semantics, it is possible to use code composition to write very succinct <em>or</em> very complicated patterns, without further complicating the compiler itself, with extra excepcional semantics.</p>

CC BY-SA 4.0
2024-10-06T15:27:39.950Z
2024-10-07T13:50:44.193Z
2024-10-07T13:50:44.193Z
André L F S Bacci
CC BY-SA 4.0
2024-10-06T18:00:38.933Z
The answer captures the essence of the question. I do not share the perception of "without complicating the compiler itself". It really adds complexity, even distributed complexity. Also, I need to think about it, if it really answers the question. The `defer` part is particularly surprising as it is the most counter-intuitive construct I've ever encountered in productive code. Still, your perspective could be correct.
feldentm
<p>Consider how you would lower this to something like continuation-passing form. I’ll illustrate this in pseudocode using <code>[]</code> for scope/closure parameters and <code>()</code> for ordinary parameters.</p>
<pre><code>f[return, throw]() {
  try1[return, throw]();
}

try1[return, throw]() {
  newT[
    return=try2[return, throw],
    throw=finally1[return, throw]
  ]();
}

try2[return, throw](m1) {
  newT[
    return=inner[return, throw, m1],
    throw=finally2[return, throw, m1]
  ]();
}

inner[return, throw, m1](m2) {
  System.out.println(&quot;inner&quot;);
  finally2[return, throw, m1](null);
}

finally2[return, throw, m1](e) {
  // drops ‘throw’:
  if (m1 != null) return(1);
  if (e != null) throw(e);
  outer[return, throw]();
}

outer[return, throw]() {
  System.out.println(&quot;outer&quot;);
  finally1[return, throw](null);
}

finally1[return, throw](e) {
  // drops ‘throw’:
  return(2);
  // unreachable:
  // if (e != null) throw(e);
  // undefined;
}
</code></pre>
<p><code>try</code> makes a new <code>throw</code> continuation that captures the previous one, and passes it down the call stack. Adding a <code>finally</code> block just creates a merge point for the normal-exit and <code>throw</code> continuations of the <code>try</code> block, letting them share some prefix.</p>
<p>Calling the <code>return</code> continuation from a <code>finally</code> block to return early means that you’re <em>dropping</em> the <code>throw</code> continuation and any exception <code>e</code> that would’ve been rethrown at the end of the <code>finally</code> block. This is just the ordinary semantics of <code>return</code>, <code>throw</code>, and an unused variable, so I think it’s really the only reasonable interpretation if you allow this at all.</p>
<ul>
<li><p>I <em>would</em> expect this to be allowed, but an argument for banning it is that, outside of this situation, an exception can prevent a return, but a return can never cancel an in-flight exception. An exceptional condition is something you probably want to know about, and not implicitly “swallow”.</p>
</li>
<li><p>I <em>wouldn’t</em> expect <code>return</code> to jump to the end of <code>finally</code>, because it’s unlike the semantics of <code>return</code> in every other context. That is, it’s not the return continuation that you’d be jumping to, but rather the default one. (If anything that more closely resembles the meaning of <code>break</code>, and in that case <code>continue</code> might mean “retry”.)</p>
</li>
<li><p>Similarly I <em>wouldn’t</em> add final actions to all exit blocks, because it’s also likely to accidentally lose information: <code>return</code> would exit the block early, but if there’s an exception, now the returned value would be dropped instead.</p>
</li>
</ul>
<p>Edge cases like this are worth considering even if they don’t have any apparent practical use. If the user writes <code>return</code>, they probably want the normal semantics of <code>return</code>, and it’s a separate issue that this is a situation where you might want to <em>additionally</em> warn them about the potential loss of exception information. If there are multiple reasonable ways of resolving a conflict, you can always ask the user to spell out more explicitly which one they want.</p>

CC BY-SA 4.0
2024-10-06T23:34:07.470Z
2024-10-07T05:08:02.210Z
2024-10-07T05:08:02.210Z
Jon Purdy
# What should the semantics from branching from finally be?
Posted by: username_0 on 2024-10-06
This question is about the expected semantics when combining resource management with branching control flow. From my perspective, it applies equally to all finally-like constructs, i.e. last wishes, irrespective of the actual language. The situation can be illustrated in Java:
public class T implements AutoCloseable {
   static int f() {
      try (var m1 = new T()) {
         try (var m2 = new T()) {
            System.out.println("inner");
         } finally {
            if (null != m1)
               return 1;
         }
         System.out.println("outer");
      } finally {
         return 2;
      }
   }
   @Override
   public void close() throws Exception {
      System.out.println("did close");
   }
   public static void main(String[] args) throws Exception {
      System.out.println(f());
   }
}

The obvious options here are:

rule usage of return/break/continue/... illegal in that context
rule usage of return/break/continue/... illegal in that context and ensure that it cannot get there by other means (same as above for Java)
make finally blocks implicit and let return jump to its end (my understanding of RAII in C++ and defer in Go)
place final actions such as implicit exception continuation and resource release at all exit blocks
rule the situation undefined behaviour
ignore the situation as it has little practical relevance

The situation in Java is that ECJ crashes for this example resulting in some code cleaning being unavailable. Javac compiles without any messages and creates a program printing:
inner
did close
did close
2

I have not checked if the JIT would crash or other exception-related corner cases.
The question here is what language semantics should be chosen to result in a behaviour that allows programmers to write correct code and likewise feels consistent to them.
**Tags:** semantics control-flow undefined-behavior control-flow-graph
> **Comments:**
> **username_1**: Javac's behavior is correct according to [section 14.20.2 in the Java Language Specification](https://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.20.2): "If execution of the try block completes abruptly for any other reason R, then the finally block is executed, and then there is a choice: If the finally block completes normally, then the try statement completes abruptly for reason R. If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and reason R is discarded)."  
> **username_0**: So, the answer would be "do whatever Java does" because return, ultimately, is not any different from throwing an exception which, fundamentally, cannot be prevented here? I think I would accept that as an answer even if I cannot tell if I'm going to implement it that way.  
> **username_2**: "*ignore the situation as it has little practical relevance*" - that's essentially the same as option 5 (undefined behaviour). I would recommend to never apply that, and instead choose option 1 (illegal) if you consider the use case to be irrelevant.  
> **username_0**: @Bergi 1, 2, 5 and 6 widely differ in effort and results. Option 6 would imply that crashing compiler is an explicitly accepted behavior until someone complains (with a valid use case). Also 1 and 2 inflict distributed cost on every usage of the `finally` or `return` construct as the check must be performed somehow. My perspective of 5 would be that the compiler must at least not crash.  
> **username_3**: The fact that you're asking about what semantics "feel right" is waving a flag that you should pay attention to. Just because lots of languages have "finally" doesn't mean that it's the right choice for your language in the first place! "Finally" semantics are *weird* so it's a good idea to take a step back and say "what problems does finally attempt to solve, and are there solutions that feel more natural in my language?"  
> **username_3**: You are specifically looking for a solution for *resource management*, so rather than phrasing the solution in the jargon of exception handling control flow, can you rephrase the problem so that it uses the jargon of resource management? That might help ensure that you're coming up with a solution that solves the right problem -- even if you end up inventing something that is a sugar for try-finally behind the scenes.  
> **username_2**: @feldentm Ah, I thought undefined behaviour can include crashing the compiler.  
> **username_0**: @EricLippert I would have to add a lot more context to make it about my language and that wasn't really the intention. The need of properly terminating network connections or child processes is universal and, to me, looks like the primary motivation behind such constructs. What is odd here is that, even without exception handling, the solutions run into issues where choices must be made, such as returning 1 or 2. It's not only about what feels natural (to me) but also about what results in mortal programmers producing somewhat correct code.  
> **username_3**: Mortal programmers are more likely to produce correct code when the language features afford a clear representation of the semantics of the business domain, is what I'm saying. If "finally" doesn't clearly represent a concept in the business domain of the developers then asking what "finally" semantics best ensures success of those developers is a non-starter. I think of "finally" as a borderline misfeature; we could have done better.  
> **username_4**: In the language Tcl, `return` is implemented using throw (called `error`), and could even be caught, and used outside functions.  
> **username_0**: @user23013 Scala had a similar pattern and long story short it does not work.  
> **username_4**: @feldentm How? I tried to search about it. The only non-stylistic reason to not use return in Scala I found is they are not bound to anonymous functions, which I don't think there is any relevance.  
> **username_0**: @user23013 you cannot return accross physical function boundaries in Scala, e.g. break out of their for loops. Exceptions are considered a workaround for this shortcoming, but there is hidden code in all sorts of places in Scala that could react to such exceptions arbitrarily including the good old try{...}catch(Throwable e){}  
> **username_5**: C++ RAII is 1, not 3. Destructors don't return a value, don't interact with control flow, and are forbidden (on pain of terminating the process) from throwing  
---
## 2 answers
### Answer by username_7 on 2024-10-06. Score: 3 | Accepted answer
Consider how you would lower this to something like continuation-passing form. I’ll illustrate this in pseudocode using [] for scope/closure parameters and () for ordinary parameters.
f[return, throw]() {
  try1[return, throw]();
}

try1[return, throw]() {
  newT[
    return=try2[return, throw],
    throw=finally1[return, throw]
  ]();
}

try2[return, throw](m1) {
  newT[
    return=inner[return, throw, m1],
    throw=finally2[return, throw, m1]
  ]();
}

inner[return, throw, m1](m2) {
  System.out.println("inner");
  finally2[return, throw, m1](null);
}

finally2[return, throw, m1](e) {
  // drops ‘throw’:
  if (m1 != null) return(1);
  if (e != null) throw(e);
  outer[return, throw]();
}

outer[return, throw]() {
  System.out.println("outer");
  finally1[return, throw](null);
}

finally1[return, throw](e) {
  // drops ‘throw’:
  return(2);
  // unreachable:
  // if (e != null) throw(e);
  // undefined;
}

try makes a new throw continuation that captures the previous one, and passes it down the call stack. Adding a finally block just creates a merge point for the normal-exit and throw continuations of the try block, letting them share some prefix.
Calling the return continuation from a finally block to return early means that you’re dropping the throw continuation and any exception e that would’ve been rethrown at the end of the finally block. This is just the ordinary semantics of return, throw, and an unused variable, so I think it’s really the only reasonable interpretation if you allow this at all.

I would expect this to be allowed, but an argument for banning it is that, outside of this situation, an exception can prevent a return, but a return can never cancel an in-flight exception. An exceptional condition is something you probably want to know about, and not implicitly “swallow”.

I wouldn’t expect return to jump to the end of finally, because it’s unlike the semantics of return in every other context. That is, it’s not the return continuation that you’d be jumping to, but rather the default one. (If anything that more closely resembles the meaning of break, and in that case continue might mean “retry”.)

Similarly I wouldn’t add final actions to all exit blocks, because it’s also likely to accidentally lose information: return would exit the block early, but if there’s an exception, now the returned value would be dropped instead.


Edge cases like this are worth considering even if they don’t have any apparent practical use. If the user writes return, they probably want the normal semantics of return, and it’s a separate issue that this is a situation where you might want to additionally warn them about the potential loss of exception information. If there are multiple reasonable ways of resolving a conflict, you can always ask the user to spell out more explicitly which one they want.
---
### Answer by username_6 on 2024-10-06. Score: 5
### Schedule and overwrite

Branching around `finally` or in the presence of `defer` semantics is to *schedule* the effect, i.e., `goto`s and `return`s first calculate their targets or values, and the run any static (`finally`) or dynamic (`defer`) scheduled code.

Branching inside `finally`/`defer` code blocks has normal semantics in principle, but can also *overwrite* the effects of previous code, i.e., this code can replace generated and scheduled results or exceptions.

---

Compare this with pure imperative code, where `goto` immediately jumps, or the first `return` immediately returns. In the absence of these inline code scheduling constructs, all of "resource management" takes the form of a lot of complicated direct imperative branching and jumping around, obfuscating "normal" code with a lot of distracting or exceptional code that rarely runs.

By *not* making exceptional code to have even more exceptional semantics, it is possible to use code composition to write very succinct *or* very complicated patterns, without further complicating the compiler itself, with extra excepcional semantics.
> **Comments:**
> **username_0** (2024-10-06): The answer captures the essence of the question. I do not share the perception of "without complicating the compiler itself". It really adds complexity, even distributed complexity. Also, I need to think about it, if it really answers the question. The `defer` part is particularly surprising as it is the most counter-intuitive construct I've ever encountered in productive code. Still, your perspective could be correct.  
---
