December 14, 2016
Test case originally by Ali Çehreli, modified by me to expose a more serious bug:

import core.stdc.stdio;

class TestException : Exception {
    this(string msg) {
        super(typeof(this).stringof~": "~msg);
    }
}

class UnrelatedException: Exception {
    this() {
        super("You should've caught me!!");
    }
}

class TestError : Error {
    this(string msg) {
        super(typeof(this).stringof~": "~msg);
    }
}

// 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) {
            string msg = [ cast(char)('a'+n) ].idup;
            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() {
    // Step 1: Setup a corrupted thread exception chain
    version(bug) {
        try {
            size_t errorIndex = 1;
            causeExceptionChain(2, errorIndex);
        }
        catch (Error original) {
            printf("Caught\n");
            string prefix = "";
            for ({ size_t i; Throwable ex = original; } ex; ex = ex.next, ++i) {
                printf("%.*s%.*s\n", prefix.length, prefix.ptr, ex.msg.length, ex.msg.ptr);
                prefix = prefix~" ";
            }
            printf("Bypassed chain was:\n");
            prefix = "";
            // This loop should print something, but nope.
            for ({ size_t i; Throwable ex = original.bypassedException; } ex; ex = ex.next, ++i) {
                printf("%.*s%.*s\n", prefix.length, prefix.ptr, ex.msg.length, ex.msg.ptr);
                prefix = prefix~" ";
            }
        }
    }

    // Now there's leftover exceptions unhandled

    try {
        // Step 2
        throw new UnrelatedException();
    } catch (UnrelatedException x) {
        // Should be caught here
    }
    // But it escaped!!
    // It even has some ghost exceptions chained to it.
}

Bug is fixed in https://github.com/dlang/druntime/pull/1712, please review.
December 13, 2016
On 12/13/2016 4:14 PM, Yuxuan Shui wrote:
> Bug is fixed in https://github.com/dlang/druntime/pull/1712, please review.

My favorite kind of bug report is one that comes with a fix! Thanks!

But please also file the bug report in bugzilla, that makes it much easier for us to track and document the changelog.