www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Error and Exception chaining

reply Yuxuan Shui <yshuiv7 gmail.com> writes:
I read https://dlang.org/spec/statement.html, which told me that 
Error is different in the way it's chained. But that is pretty 
vague, and I'm still confused.

Can someone explain that using examples?

Thanks.
Dec 12 2016
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
(Note: Looks like there is a bug regarding Error.bypassedException 
member. Would others please confirm.)

On 12/12/2016 01:15 PM, Yuxuan Shui wrote:
 I read https://dlang.org/spec/statement.html, which told me that Error
 is different in the way it's chained. But that is pretty vague, and I'm
 still confused.

 Can someone explain that using examples?

 Thanks.
You're referring to "[Errors] bypass the normal chaining mechanism, such that the chain can only be caught by catching the first Error." What it means is that an Error cannot be a collateral of an Exception, hiding in its chain. So, when there is an Error that would otherwise be a collateral of an Exception, you cannot catch the original Exception. What is more importantly in-flight at that time is the Error. The following program causes a chain of exceptions: import std.stdio; import std.string; import std.range; class TestException : Exception { this(string msg) { const fullMmsg = format("%s: %s",typeof(this).stringof, msg); writefln("throwing '%s'", fullMmsg); super(fullMmsg); } } class TestError : Error { this(string msg) { const fullMmsg = format("%s: %s",typeof(this).stringof, msg); writefln("throwing '%s'", fullMmsg); super(fullMmsg); } } // Causes an exception chain where the node at index errorIndex is an // Error (others are all Exceptions). void causeExceptionChain(size_t chainLength, size_t errorIndex) { void throws(size_t n) { scope (exit) { const msg = format("%s", n); if (n == errorIndex) { throw new TestError(msg); } else { throw new TestException(msg); } } if (n != 0) { // Redundant 'return' keyword due to // https://issues.dlang.org/show_bug.cgi?id=16960 return throws(n - 1); } } throws(chainLength - 1); } void main() { try { // -1 would mean "no Error in the chain". Change this to a // number between 0 and 4 (inclusive) then you will realize // that the Exception below will not be caught. size_t errorIndex = -1; causeExceptionChain(5, errorIndex); } catch (Exception original) { writefln("Caught"); // Unrelated: If you're not familiar with the curly braces in // the for loop, see the Note under "The sections of the for // loop" section at http://ddili.org/ders/d.en/for.html for ({ size_t i; Throwable ex = original; } ex; ex = ex.next, ++i) { writeln(" ".replicate(i), ex.msg); } } } throwing 'TestException: 0' throwing 'TestException: 1' throwing 'TestException: 2' throwing 'TestException: 3' throwing 'TestException: 4' Caught TestException: 0 TestException: 1 TestException: 2 TestException: 3 TestException: 4 Make errorIndex something other than -1 e.g. 3 and you will see that the Exception cannot be caught. size_t errorIndex = 3; You get the usual stack trace of an uncaught exception. throwing 'TestException: 0' throwing 'TestException: 1' throwing 'TestException: 2' throwing 'TestError: 3' throwing 'TestException: 4' deneme.TestError (0): TestError: 3 ---------------- [...] Then, replace Exception with Error in the catch clause, and you will see that Error is again caught. (Side note: Errors are not supposed to be caught by programs because the whole state of the program is in question.) catch (Error original) { // <-- Now Error // ... } You will see that Error is in its own chain. It will contain just Error 3 and Error 4: throwing 'TestException: 0' throwing 'TestException: 1' throwing 'TestException: 2' throwing 'TestError: 3' throwing 'TestException: 4' Caught TestError: 3 TestException: 4 Now you would hope to get the original bypassed Exception chain with the following code writefln("The bypassed exception was '%s'", original.bypassedException); But bypassedException member of Error is always null. Bug? Thank you, Ali
Dec 12 2016
parent reply Yuxuan Shui <yshuiv7 gmail.com> writes:
Thanks a lot for the explanation!

On Monday, 12 December 2016 at 22:01:54 UTC, Ali Çehreli wrote:
 (Note: Looks like there is a bug regarding 
 Error.bypassedException member. Would others please confirm.)

 On 12/12/2016 01:15 PM, Yuxuan Shui wrote:
 [...]
that Error
 [...]
vague, and I'm
 [...]
You're referring to "[Errors] bypass the normal chaining mechanism, such that the chain can only be caught by catching the first Error." What it means is that an Error cannot be a collateral of an Exception, hiding in its chain. So, when there is an Error that would otherwise be a collateral of an Exception, you cannot catch the original Exception. What is more importantly in-flight at that time is the Error.
But chaining Error to Error works just like chaining Exception to Exception?
 But bypassedException member of Error is always null. Bug?
Did I just randomly found a bug?
 Thank you,
 Ali
Dec 12 2016
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 12/12/2016 02:08 PM, Yuxuan Shui wrote:
 Thanks a lot for the explanation!

 On Monday, 12 December 2016 at 22:01:54 UTC, Ali Çehreli wrote:
 (Note: Looks like there is a bug regarding Error.bypassedException
 member. Would others please confirm.)

 On 12/12/2016 01:15 PM, Yuxuan Shui wrote:
 [...]
that Error
 [...]
vague, and I'm
 [...]
You're referring to "[Errors] bypass the normal chaining mechanism, such that the chain can only be caught by catching the first Error." What it means is that an Error cannot be a collateral of an Exception, hiding in its chain. So, when there is an Error that would otherwise be a collateral of an Exception, you cannot catch the original Exception. What is more importantly in-flight at that time is the Error.
But chaining Error to Error works just like chaining Exception to Exception?
Currently yes. Now this line in my versatile :o) program: if (n >= errorIndex) { Two Errors are chained: Caught TestError: 3 TestError: 4 However, there is the following ongoing thread claiming that it was a wrong decision: http://forum.dlang.org/post/o2n347$2i1g$1 digitalmars.com
 But bypassedException member of Error is always null. Bug?
Did I just randomly found a bug?
From what I could graps from that much of documentation, yes, it seems to be a bug. Ali
Dec 12 2016
parent reply Yuxuan Shui <yshuiv7 gmail.com> writes:
On Monday, 12 December 2016 at 22:13:59 UTC, Ali Çehreli wrote:
 On 12/12/2016 02:08 PM, Yuxuan Shui wrote:
 [...]
wrote:
 [...]
Error.bypassedException
 [...]
mechanism,
 [...]
Error."
 [...]
Exception,
 [...]
otherwise
 [...]
original
 [...]
is the Error.
 [...]
Exception to
 [...]
Currently yes. Now this line in my versatile :o) program: if (n >= errorIndex) { Two Errors are chained: Caught TestError: 3 TestError: 4 However, there is the following ongoing thread claiming that it was a wrong decision: http://forum.dlang.org/post/o2n347$2i1g$1 digitalmars.com
 [...]
From what I could graps from that much of documentation, yes, it seems to be a bug.
I did some testing and bypassedException is null in 2.072.1, but is not null in 2.070.2
 Ali
Dec 12 2016
parent reply Yuxuan Shui <yshuiv7 gmail.com> writes:
On Monday, 12 December 2016 at 22:35:22 UTC, Yuxuan Shui wrote:
 On Monday, 12 December 2016 at 22:13:59 UTC, Ali Çehreli wrote:
 On 12/12/2016 02:08 PM, Yuxuan Shui wrote:
 [...]
wrote:
 [...]
Error.bypassedException
 [...]
mechanism,
 [...]
Error."
 [...]
Exception,
 [...]
otherwise
 [...]
original
 [...]
is the Error.
 [...]
Exception to
 [...]
Currently yes. Now this line in my versatile :o) program: if (n >= errorIndex) { Two Errors are chained: Caught TestError: 3 TestError: 4 However, there is the following ongoing thread claiming that it was a wrong decision: http://forum.dlang.org/post/o2n347$2i1g$1 digitalmars.com
 [...]
From what I could graps from that much of documentation, yes, it seems to be a bug.
I did some testing and bypassedException is null in 2.072.1, but is not null in 2.070.2
 Ali
The unwind process seem to stop one level too early, causing language_specific_data to be different, causing __dmd_personality_v0 to not chain exception into .bypassException.
Dec 12 2016
parent Yuxuan Shui <yshuiv7 gmail.com> writes:
On Tuesday, 13 December 2016 at 00:33:58 UTC, Yuxuan Shui wrote:
 On Monday, 12 December 2016 at 22:35:22 UTC, Yuxuan Shui wrote:
 On Monday, 12 December 2016 at 22:13:59 UTC, Ali Çehreli wrote:
 On 12/12/2016 02:08 PM, Yuxuan Shui wrote:
 [...]
wrote:
 [...]
Error.bypassedException
 [...]
mechanism,
 [...]
Error."
 [...]
Exception,
 [...]
otherwise
 [...]
original
 [...]
is the Error.
 [...]
Exception to
 [...]
Currently yes. Now this line in my versatile :o) program: if (n >= errorIndex) { Two Errors are chained: Caught TestError: 3 TestError: 4 However, there is the following ongoing thread claiming that it was a wrong decision: http://forum.dlang.org/post/o2n347$2i1g$1 digitalmars.com
 [...]
From what I could graps from that much of documentation, yes, it seems to be a bug.
I did some testing and bypassedException is null in 2.072.1, but is not null in 2.070.2
 Ali
The unwind process seem to stop one level too early, causing language_specific_data to be different, causing __dmd_personality_v0 to not chain exception into .bypassException.
OK. I think I figured it out. catch(Error x) won't catch TestException, so TestException 0,1,2 is not handled at main(). However TestError 3 (and TestExcepotion 4 which is chained to it) is handled at main(). And dmd won't put Throwables that are handled at different places together. This behavior seems reasonable. So maybe this is actually a bug is LDC?
Dec 12 2016