Exception chaining and collectException

2017-08-17 Thread H. S. Teoh via Digitalmars-d
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

2017-08-17 Thread H. S. Teoh via Digitalmars-d
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

2017-08-17 Thread H. S. Teoh via Digitalmars-d
Filed a bug:

https://issues.dlang.org/show_bug.cgi?id=17760


--T


Re: Exception chaining and collectException

2017-08-17 Thread Walter Bright via Digitalmars-d

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

2017-08-18 Thread Don Clugston via Digitalmars-d

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

2017-08-18 Thread jmh530 via Digitalmars-d

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

2017-08-18 Thread Steven Schveighoffer via Digitalmars-d

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

2017-08-18 Thread Walter Bright via Digitalmars-d

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

2017-08-18 Thread Walter Bright via Digitalmars-d

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

2017-08-19 Thread Nemanja Boric via Digitalmars-d

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

2017-08-21 Thread H. S. Teoh via Digitalmars-d
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

2017-08-21 Thread Ali Çehreli via Digitalmars-d

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

2017-08-21 Thread Ali Çehreli via Digitalmars-d

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

2017-08-22 Thread Nemanja Boric via Digitalmars-d

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

2017-08-22 Thread Nemanja Boric via Digitalmars-d

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