www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - The Proper Use of Exception Chaining: Caught between two conflicting

reply FeepingCreature <feepingcreature gmail.com> writes:
There's a feature where, when creating an exception, you can pass 
another exception as a second parameter. I always thought that 
the point of this feature was that you could get a backtrace like:

void foo() {
   throw new FooException("foo failure");
}

void bar() {
   try foo;
   catch (FooException fooException) {
     throw new BarException("bar failure", fooException);
   }
}

-----------
   FooException: foo failure
main.d: foo()
   BarException: bar failure
main.d: bar()
main.d: main()

But it turns out that according to the runtime it's related to 
throwing exceptions as a side effect of unwinding? Ie. if you

scope(exit) throw new BarException("bar");
throw new FooException("foo");

then you get a BarException chained to the FooException? Or the 
other way around?

That seems a **very** different usecase from the original one.

In the second case, while processing an error we coincidentally 
encountered a second error.

In the first case, it's still the same error, just being 
represented now by a different class.

Furthermore, the runtime seems to think case 2 is the correct use 
for chained exceptions, whereas the documentation ( 
https://dlang.org/library/object/throwable.next.html ), 
admittedly a bit hidden away, claims that case 1 is the proper 
usecase.

In any case, Throwable.toString doesn't care and just omits the 
chained exception outright.

This seems a very unsatisfying state of affairs! If the use of 
exception chaining on throwing an exception during unwind cannot 
be changed, then there is still a case for creating a whole 
separate implementation of chaining ("exception wrapping"?) for 
case 2, and reinterpreting the rarely used two-parameter syntax 
(new Exception("bla", wrapped)) to use this idiom, as well as 
prominently displaying the wrapped exception in toString output.

I'm very willing to work out a PR for this, but I want to avoid 
the effort if it's unlikely to be accepted.

What do you think?
Apr 09
parent reply FeepingCreature <feepingcreature gmail.com> writes:
On Tuesday, 9 April 2019 at 14:27:37 UTC, FeepingCreature wrote:
 What do you think?
Ping! This is a severe issue making debugging pointlessly hard.
Apr 26
parent reply Meta <jared771 gmail.com> writes:
On Friday, 26 April 2019 at 08:32:58 UTC, FeepingCreature wrote:
 On Tuesday, 9 April 2019 at 14:27:37 UTC, FeepingCreature wrote:
 What do you think?
Ping! This is a severe issue making debugging pointlessly hard.
Case 2 is the intended use for chained exceptions. It's an answer to the question of "what happens if you throw an exception while another one is already in flight?" In the case of C++, the program is immediately aborted. In D's case, the second exception will be chained onto the first one.
Apr 27
parent reply FeepingCreature <feepingcreature gmail.com> writes:
On Saturday, 27 April 2019 at 07:08:50 UTC, Meta wrote:
 On Friday, 26 April 2019 at 08:32:58 UTC, FeepingCreature wrote:
 Ping! This is a severe issue making debugging pointlessly hard.
Case 2 is the intended use for chained exceptions. It's an answer to the question of "what happens if you throw an exception while another one is already in flight?" In the case of C++, the program is immediately aborted. In D's case, the second exception will be chained onto the first one.
Okay, but then that first of all contradicts the documentation at https://dlang.org/library/object/throwable.next.html which indicates that this feature is certainly confusing enough to be misunderstandable, and second of all there's still very much a call for being able to react to an exception by throwing an exception of another type, without losing the first exception's stack trace. Currently if we catch three types of exceptions that happen somewhere in Phobos and rethrow them as some generic or domain exception, the error will appear to come from the exception handler. Does the error come from the exception handler? No! The actual source of the error has been completely obfuscated. This is bad!
May 02
parent reply Meta <jared771 gmail.com> writes:
On Thursday, 2 May 2019 at 07:10:45 UTC, FeepingCreature wrote:
 Case 2 is the intended use for chained exceptions. It's an 
 answer to the question of "what happens if you throw an 
 exception while another one is already in flight?" In the case 
 of C++, the program is immediately aborted. In D's case, the 
 second exception will be chained onto the first one.
Okay, but then that first of all contradicts the documentation at https://dlang.org/library/object/throwable.next.html
I don't understand how what I said contradicts what's on that page. It seems to agree with what I said, from what I can tell: A reference to the next error in the list. This is used when a new Throwable is thrown from inside a catch block. The originally caught Exception will be chained to the new Throwable via this field
 ...and second of all there's still very much a call for being 
 able to react to an exception by throwing an exception of 
 another type, without losing the first exception's stack trace.
I completely agree; this feature is invaluable in figuring out what went wrong when looking at a stack trace.
 Currently if we catch three types of exceptions that happen 
 somewhere in Phobos and rethrow them as some generic or domain 
 exception, the error will appear to come from the exception 
 handler. Does the error come from the exception handler? No! 
 The actual source of the error has been completely obfuscated. 
 This is bad!
Again, agreed. My only disagreement is over what Throwable.next is intended to be used for.
May 02
next sibling parent FeepingCreature <feepingcreature gmail.com> writes:
On Thursday, 2 May 2019 at 18:34:39 UTC, Meta wrote:
 I don't understand how what I said contradicts what's on that 
 page. It seems to agree with what I said, from what I can tell:

 A reference to the next error in the list. This is used when a 
 new Throwable is thrown from inside a catch block. The 
 originally caught Exception will be chained to the new 
 Throwable via this field
Subtle difference: It says "thrown from inside a *catch* block", but current exception chaining is for exceptions thrown from inside a *finally* block. Btw, I want to take this opportunity to highlight this wonderful bug I found a few days ago, coincidentally also with exception chaining: https://issues.dlang.org/show_bug.cgi?id=19831 , when you throw *and catch* an exception inside a finally block the runtime gets very confused, and calls the handler for the *chained* exception - but with the *thrown* exception.
May 03
prev sibling parent reply FeepingCreature <feepingcreature gmail.com> writes:
On Thursday, 2 May 2019 at 18:34:39 UTC, Meta wrote:
 I don't understand how what I said contradicts what's on that 
 page. It seems to agree with what I said, from what I can tell:

 A reference to the next error in the list. This is used when a 
 new Throwable is thrown from inside a catch block. The 
 originally caught Exception will be chained to the new 
 Throwable via this field
Subtle difference: It says "thrown from inside a *catch* block", but current exception chaining is for exceptions thrown from inside a *finally* block. Btw, I want to take this opportunity to highlight this wonderful bug I found a few days ago, coincidentally also with exception chaining: https://issues.dlang.org/show_bug.cgi?id=19831 , when you throw *and catch* an exception inside a finally block the runtime gets very confused, and calls the handler for the *chained* exception - but with the *thrown* exception. Fun!
May 03
parent Meta <jared771 gmail.com> writes:
On Friday, 3 May 2019 at 07:12:15 UTC, FeepingCreature wrote:
 On Thursday, 2 May 2019 at 18:34:39 UTC, Meta wrote:
 I don't understand how what I said contradicts what's on that 
 page. It seems to agree with what I said, from what I can tell:

 A reference to the next error in the list. This is used when a 
 new Throwable is thrown from inside a catch block. The 
 originally caught Exception will be chained to the new 
 Throwable via this field
Subtle difference: It says "thrown from inside a *catch* block", but current exception chaining is for exceptions thrown from inside a *finally* block.
Ah, I see.
 Btw, I want to take this opportunity to highlight this 
 wonderful bug I found a few days ago, coincidentally also with 
 exception chaining: 
 https://issues.dlang.org/show_bug.cgi?id=19831 , when you throw 
 *and catch* an exception inside a finally block the runtime 
 gets very confused, and calls the handler for the *chained* 
 exception - but with the *thrown* exception. Fun!
That's a nasty one.
May 03