Bug 16960 – implicit function return breaks chaining of exceptions

Status
NEW
Severity
normal
Priority
P3
Component
dmd
Product
D
Version
D2
Platform
x86_64
OS
Linux
Creation time
2016-12-09T22:24:14Z
Last change time
2024-12-13T18:50:56Z
Assigned to
No Owner
Creator
Ali Cehreli
Moved to GitHub: dmd#19213 →

Comments

Comment #0 by acehreli — 2016-12-09T22:24:14Z
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
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.
Comment #2 by acehreli — 2016-12-14T23:51:54Z
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
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/19213 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB