I was fixing a bug in the compiler, and when I fixed it I broke this regression test:
proc main: int =
try:
try:
return 1
finally:
echo("came")
return 2
finally:
echo("here")
return 3
echo main() #OUT came here 3
Looking over the related code now, I'm not entirely sure that "return in finally" was implemented right to begin with, so it's a bit of work getting it right. I don't mind that at all, but..
Should Nimrod support it? The way I see it, the code gets harder to reason about, which makes it less safe, and I can't imagine a real good use-case for it.
If you put an unconditional return in the finally stmt, why bother with the return within the try stmt?
If you have a return inside a condition, it might make more sense.. but still, if it's in the finally block it's because you want to make sure it gets called when there's an unhandled exception, right? And in that case, if the exception jumps out of the function, the return value doesn't matter?
Looking at other languages I found that C# gives you a compiler error, while Java and Javascript supports it.
zahary: I believe that it's the final (or outermost) return statement that counts in Java. It should return 3 regardless of what happens.
This could get quite hairy. Does return statements in finally reraise exceptions in the current implementation? This could also be a bug (I don't have a compiler at this computer, so I can't check).
proc main: int =
var x = 0
try:
try:
raise newException(EOS, "Universe seized to exist")
return 1
finally:
echo("came")
return 2
finally:
echo("here")
if x == 2:
return 3
try:
echo main() #OUT came here 3
except EOS:
echo "Restarting universe"
I will try to implement it, but I'm still not entirely convinced it's a good idea to use it. Seems like a recipe for disaster, especially when templates are involved. But it's of course always better to have the option if it's possible. Some people have legitimate reasons for shooting themselves in the foot.
Maybe generating a warning would be a good idea?
For reference, here's a test case in Javascript:
function test() {
try {
try {
throw "Universe seized to exist";
}
finally {
console.log("2A");
return 2;
console.log("2B");
}
}
finally {
console.log("1A");
return 1;
console.log("1B");
}
}
try {
console.log(test());
}
catch(e) {
console.log("Gotcha");
}
Result:
2A
1A
1
Discussing with Araq, we've found that it seems the only (relatively easy) way of getting this right is to implement return within try-except-finally statements by raising a dummy exception which is only handled by finally-statements. But this would impact performance quite badly, and would go against the spirit of Nimrod.
It seems for now it might be best to throw an error, and move on to other things (but I will probably keep thinking about it for a couple of days)
Okay, never mind all that fuzz, it seems that I got it working. All in all it wasn't that hard to implement, but a bit tough to reason about. So I'm still not 100% sure it's correct. I'm putting in a tough test-case, but there are so many corners here, someone should create a really diabolical test for this (not me :P)
Here's an interesting little corner case:
proc test() =
try:
try:
raise newException(EOS, "")
finally:
return
except EOS:
echo "catch"
finally:
echo "finally"
test()
With the return you only get "finally", without it you get "catch finally". Confirmed it works the same in Javascript.