The following function tries to throw an exception that has n+1 chains. Uncomment any one of the 3 return statements inside foo() and you will get the expected output:
foo called with 3
foo called with 2
foo called with 1
foo called with 0
thrown for 0
thrown for 1
thrown for 2
thrown for 3
However, when foo() returns implicitly without any return statement, there is only one exception in the chain. main() detects this case by following the .next links.
import std.stdio;
import std.string;
void foo(int n) {
writeln("foo called with ", n);
scope (exit) throw new Exception(format("thrown for %s", n));
if (n > 0) {
/* return */ foo(n - 1);
// return;
}
// return;
}
void main() {
// Never mind the unconventional range limits:
// Throws one exception for each value in the range 0..n, including n.
enum chainLength = 3;
enum expectedLength = chainLength + 1;
try {
foo(chainLength);
}
catch (Exception original) {
size_t count = 0;
for (Throwable ex = original; ex; ex = ex.next) {
writeln(ex.msg);
++count;
}
if (count != expectedLength) {
writefln("Expected %s but walked %s links", expectedLength, count);
writefln("\nTHE ORIGINAL EXCEPTION:\n\n%s", original);
}
}
}
Ali
Comment #1 by pro.mathias.lang — 2016-12-14T21:50:28Z
Thanks opened issue 16972 as an enhancement. Perhaps the compiler can warn about the obvious throw there.
Comment #3 by acehreli — 2016-12-14T23:56:27Z
The issue remains even if I don't throw from within a scope(exit) block. Uncomment any 'return' inside foo() and the code works as expected:
import std.stdio;
import std.string;
struct ThrowsInDestructor {
int n;
~this() {
throw new Exception(format("thrown for %s", n));
}
}
void foo(int n) {
writeln("foo called with ", n);
auto throws = ThrowsInDestructor(n);
if (n > 0) {
/* return */ foo(n - 1);
// return;
}
// return;
}
void main() {
// Never mind the unconventional range limits:
// Throws one exception for each value in the range 0..n, including n.
enum chainLength = 3;
enum expectedLength = chainLength + 1;
try {
foo(chainLength);
}
catch (Exception original) {
size_t count = 0;
for (Throwable ex = original; ex; ex = ex.next) {
writeln(ex.msg);
++count;
}
if (count != expectedLength) {
writefln("Expected %s but walked %s links", expectedLength, count);
writefln("\nTHE ORIGINAL EXCEPTION:\n\n%s", original);
}
}
}
Comment #4 by yshuiv7 — 2016-12-15T18:28:43Z
(In reply to Mathias LANG from comment #1)
> This is UB, according to
> http://dlang.org/spec/statement.html#ScopeGuardStatement
>
> > A scope(exit) or scope(success) statement may not exit with a throw, goto, break, continue, or return; nor may it be entered with a goto.
I don't think it make sense to forbid exiting scope block with a throw. Plus it totally works barring a few bugs not specific to it.
Comment #5 by robert.schadek — 2024-12-13T18:50:56Z