Error and Exception chaining

Ali Çehreli via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Mon Dec 12 14:01:54 PST 2016


(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



More information about the Digitalmars-d-learn mailing list