Return vs. Finally (2)
The Return statement and finally have competition. Both can are the "last" thing to execute when a function exits, so which of them is really last? I blogged about this before regarding C#'s semantics setting data. Now I'll compare some interesting differences between C# and Python.
Who wins if you set them up in direct competition like this (non-exceptional path):
void f1()
{
try
{
return 10;
}
finally
{
return 5;
}
}
or this (the exceptional path):
void f2()
{
try
{
throw new Exception();
}
finally
{
return 5;
}
}
There are multiple intelligent ways a language design could answer this.
C#'s answer: Illegal
In C#, this is just illegal. Returns are forbidden in finally clauses. This nicely just avoids the whole problem by refusing to let you write such potentially ambiguous and confusing code. (CS0157: "Control cannot leave the body of a finally clause"). And if a consensus emerges down the road that there really is a clear "right" answer, C# can always make it legal with the "right" semantics.
IL's answer: Illegal
The underlying CLR's IL instruction provides try/catch/finally and branching opcodes. The only branching opcode out of a finally is 'endfinally', which jumps to the end of the finally block. You can't return / branch out of a finally. (See VER_E_RET_FROM_HND=0x80131845 in CorError.h).
Since .NET languages compile to IL, it may be natural for a language to have the same semantics as the IL instructions it compiles to. It's not surprising that C# has the same restrictions as the underlying IL instruction set here. However, a .NET language can have more clever opcode usage to provide their own semantics.
Python's answer: Legal
In Python, return inside of finally is actually allowed.
|
In this case, you can see that even on the exceptional path, the return statement will swallow the exception and return a value.
Note that IronPython compiles to IL, and still faithfully maintains the python exception semantics here.
Comments
Anonymous
December 16, 2007
I'm very curious. How does the IL look like that IronPython generates?Anonymous
December 16, 2007
Python: return self.i++ class Sample: def meth(self): try: return self.i finally: self.i += 1Anonymous
December 16, 2007
If you treat the IL finally block as the moral equivalent of C++'s automatic destructor calls, then C++ has a hybrid of the two, where it is not possible to return from a "finally" block, since there is no where to put the return statement, however it is possible to throw. In C++ the consensus seems to be that throwing from destructors is a "bad thing"(TM), and I have only ever had cause to do it when writing classes expressly designed to do it as part of dynamic exception generation (don't ask :)). It is particularly dodgy in C++ of course due to the amusing C++ double fault behaviour, where if you throw from a destructor during an exception unwind, std::terminate is called and the process is to all intents and purposes dead, however you cannot know at the time when you throw that this is going to happen. I'm not saying its right, but I thought it was another perspective i'd point out.Anonymous
December 17, 2007
The comment has been removed