Exception chaining and collectException
Code: import std.exception : collectException; import std.stdio; class MyException : Exception { this() { super("MYMY"); } } void f() { throw new MyException(); } struct S { ~this() { auto e = collectException!MyException(f()); writefln("Collected: %s (%s)", typeid(e).toString, e.msg); } void method() { throw new Exception("Dumb error"); } } void main() { try { S s; s.method(); } catch (Exception e) { writefln("Caught: %s (%s) (next=%s)", typeid(e).toString, e.msg, e.next ? e.next.toString : "null"); } } Expected output: collectException should collect MyException while "Dumb error" is in transit, and the output should contain a "Collected:" line. Actual behaviour: collectException doesn't work as advertised; MyException gets chained to the Exception in transit, and the "Collected:" line is never printed. If we change the collectException call to: auto e = collectException!Exception(f()); then it *does* work as advertised: the MyException instance is not chained, but is correctly collected by collectException. Why??? The code in question is reduced from a larger project where the dtor needs to do some non-trivial cleanup, but needs to ignore certain errors that may occur during cleanup. So it needs collectException to catch only certain subclasses of Exception, rather than all Exceptions. But the above inconsistent behaviour hampers this. Investigating the implementation of collectException, it seems that all it does is to use a try-catch block to catch an exception of the requested type. So the question becomes, why does the catch block *not* catch the instance of MyException when another exception is in transit?! T -- Gone Chopin. Bach in a minuet.
Re: Exception chaining and collectException
Here's a reduced example that does not depend on std.exception: --- import std.stdio; class MyException : Exception { this() { super("MYMY"); } } struct S { ~this() { try { throw new MyException; } catch(MyException e) { writefln("Collected MyException: %s", e.msg); } catch(Exception e) { writefln("Collected Exception: %s", e.msg); } } void method() { throw new Exception("Dumb error"); } } void main() { try { S s; s.method(); } catch(Exception) {} } --- Expected output: Collected MyException: MYMY Actual output: Collected Exception: MYMY Looks like a bug in druntime, or a codegen bug? T -- Help a man when he is in trouble and he will remember you when he is in trouble again.
Re: Exception chaining and collectException
Filed a bug: https://issues.dlang.org/show_bug.cgi?id=17760 --T
Re: Exception chaining and collectException
Chained exceptions are a good idea, but are more or less a disaster: 1. No other language does chained exceptions 2. Attempting to hammer D chained exceptions into other language schemes (such as C++) makes for lots of unfun hours attempting to decode undocumented behavior in those other schemes 3. Makes D exceptions incompatible with other language exceptions and their infrastructure 4. Are incomprehensibly implemented (I defy anyone to explain how the test cases in the test suite are actually supposed to work) 5. Are more or less incompatible with non-GC memory allocation I'd like to remove them from D. I recommend *not* designing any program that requires them.
Re: Exception chaining and collectException
On Friday, 18 August 2017 at 03:31:38 UTC, Walter Bright wrote: Chained exceptions are a good idea, but are more or less a disaster: 1. No other language does chained exceptions 2. Attempting to hammer D chained exceptions into other language schemes (such as C++) makes for lots of unfun hours attempting to decode undocumented behavior in those other schemes 3. Makes D exceptions incompatible with other language exceptions and their infrastructure 4. Are incomprehensibly implemented (I defy anyone to explain how the test cases in the test suite are actually supposed to work) Well, I wrote them, so I can explain that. The problem is that the idea that you can form a "chain" of exceptions turns out to be naive. What if a chained exception needs to get chained to another chained exception? And that then needs to be chained to another exception? It forms a tree! That's why the test cases are so complicated. So to a large extent, this extremely obscure corner case destroys the elegance of the concept. Secondly, exception handling in windows is practically undocumented. Certainly it's not documented in a single place. When I began to implement it, I feared it might be impossible. There isn't any guarantee that exception chaining can actually be implemented on all platforms. 5. Are more or less incompatible with non-GC memory allocation I'd like to remove them from D. I recommend *not* designing any program that requires them. I invested quite a lot personally in implementing chained exceptions. But I agree with you. I was actually quite proud that I worked out the nasty corner cases during the initial implementation. As far as I can tell, problems with chained exceptions are not because of bugs and implementation issues, but because of problems with the concept itself. I think it's just a bit too clever.
Re: Exception chaining and collectException
On Friday, 18 August 2017 at 09:09:47 UTC, Don Clugston wrote: Secondly, exception handling in windows is practically undocumented. Certainly it's not documented in a single place. When I began to implement it, I feared it might be impossible. There isn't any guarantee that exception chaining can actually be implemented on all platforms. I had actually tested the above on Windows with DMD 2.075.1 and got the expected behavior, not the buggy behavior. I invested quite a lot personally in implementing chained exceptions. But I agree with you. I was actually quite proud that I worked out the nasty corner cases during the initial implementation. As far as I can tell, problems with chained exceptions are not because of bugs and implementation issues, but because of problems with the concept itself. I think it's just a bit too clever. Do you mind pointing me in the direction of where chained exceptions are explained?
Re: Exception chaining and collectException
On Friday, 18 August 2017 at 03:31:38 UTC, Walter Bright wrote: Chained exceptions are a good idea, but are more or less a disaster: 1. No other language does chained exceptions 2. Attempting to hammer D chained exceptions into other language schemes (such as C++) makes for lots of unfun hours attempting to decode undocumented behavior in those other schemes 3. Makes D exceptions incompatible with other language exceptions and their infrastructure 4. Are incomprehensibly implemented (I defy anyone to explain how the test cases in the test suite are actually supposed to work) 5. Are more or less incompatible with non-GC memory allocation I'd like to remove them from D. I recommend *not* designing any program that requires them. If we are to remove them, what happens when exceptions would normally chain? -Steve
Re: Exception chaining and collectException
On 8/18/2017 5:07 AM, Steven Schveighoffer wrote: If we are to remove them, what happens when exceptions would normally chain? In C++, throwing an exception while unwinding is a fatal error. More information: https://stackoverflow.com/questions/130117/throwing-exceptions-out-of-a-destructor
Re: Exception chaining and collectException
On 8/18/2017 2:09 AM, Don Clugston wrote: I invested quite a lot personally in implementing chained exceptions. But I agree with you. I was actually quite proud that I worked out the nasty corner cases during the initial implementation. As far as I can tell, problems with chained exceptions are not because of bugs and implementation issues, but because of problems with the concept itself. I think it's just a bit too clever. Thanks for the explanation. When I decided to support Dwarf exceptions, I spent a lot of time trying to match that behavior. We've all invested lots of time in things that didn't pay off. We mustn't get trapped by the sunk cost fallacy: https://en.wikipedia.org/wiki/Sunk_cost and I'm glad you're ok with that!
Re: Exception chaining and collectException
On Friday, 18 August 2017 at 22:51:35 UTC, Walter Bright wrote: On 8/18/2017 5:07 AM, Steven Schveighoffer wrote: If we are to remove them, what happens when exceptions would normally chain? In C++, throwing an exception while unwinding is a fatal error. Well, you still can throw it, but you're not allowed to let it escape the destructor (you need to catch them before they would chain). C++ also provides a way to inspect if you're in the middle of the stack unwinding caused by an exception, to make this a bit more controllable, and I would think we should provide the similar primitive: http://en.cppreference.com/w/cpp/error/uncaught_exception.
Re: Exception chaining and collectException
On Sat, Aug 19, 2017 at 08:58:51PM +, Nemanja Boric via Digitalmars-d wrote: > On Friday, 18 August 2017 at 22:51:35 UTC, Walter Bright wrote: > > On 8/18/2017 5:07 AM, Steven Schveighoffer wrote: > > > If we are to remove them, what happens when exceptions would > > > normally chain? > > > > In C++, throwing an exception while unwinding is a fatal error. > > > > Well, you still can throw it, but you're not allowed to let it escape > the destructor (you need to catch them before they would chain). This was what I was trying to do: wrap some code in a try-catch in the dtor so that any exceptions thrown won't escape the dtor. However, the current inconsistent behaviour is making this difficult. The same dtor behaves differently depending on whether it was called from a normal end of scope, or while an Exception is in transit. I.e., when called normally, the catch clause catches MyException, but when another Exception is in transit, the catch clause fails to catch MyException (yet it does catch the base class Exception, a rather strange behaviour). T -- The most powerful one-line C program: #include "/dev/tty" -- IOCCC
Re: Exception chaining and collectException
On 08/17/2017 02:48 PM, H. S. Teoh via Digitalmars-d wrote: > So the question becomes, why does the catch block *not* > catch the instance of MyException when another exception is in transit?! I caught (!) the same or similar behavior last year: https://issues.dlang.org/show_bug.cgi?id=16177 Ali
Re: Exception chaining and collectException
On 08/19/2017 01:58 PM, Nemanja Boric wrote: C++ also provides a way to inspect if you're in the middle of the stack unwinding caused by an exception, to make this a bit more controllable, and I would think we should provide the similar primitive: http://en.cppreference.com/w/cpp/error/uncaught_exception. I don't know whether C++11 changed matters but according to popular C++98 wisdom, we were told "Don't use [std::uncaught_exception]": www.gotw.ca/gotw/047.htm Ali
Re: Exception chaining and collectException
On Monday, 21 August 2017 at 20:15:53 UTC, Ali Çehreli wrote: On 08/19/2017 01:58 PM, Nemanja Boric wrote: C++ also provides a way to inspect if you're in the middle of the stack unwinding caused by an exception, to make this a bit more controllable, and I would think we should provide the similar primitive: . I don't know whether C++11 changed matters but according to popular C++98 wisdom, we were told "Don't use [std::uncaught_exception]": www.gotw.ca/gotw/047.htm Ali Rationale for changing this in C++17 is actually comming from Herb Sutter and it is referring to gotw47: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3614.pdf
Re: Exception chaining and collectException
On Tuesday, 22 August 2017 at 07:03:03 UTC, Nemanja Boric wrote: On Monday, 21 August 2017 at 20:15:53 UTC, Ali Çehreli wrote: On 08/19/2017 01:58 PM, Nemanja Boric wrote: C++ also provides a way to inspect if you're in the middle of the stack unwinding caused by an exception, to make this a bit more controllable, and I would think we should provide the similar primitive: . I don't know whether C++11 changed matters but according to popular C++98 wisdom, we were told "Don't use [std::uncaught_exception]": www.gotw.ca/gotw/047.htm Ali Rationale for changing this in C++17 is actually comming from Herb Sutter and it is referring to gotw47: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3614.pdf Sorry, on the phone, so I've pasted wrong link: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4152.pdf