Switch branches/tags
Nothing to show
Find file Copy path
70768ad Jun 18, 2017
1 contributor

Users who have contributed to this file

108 lines (67 sloc) 4.95 KB

Exceptions and @nogc

Field Value
DIP: 1008
Review Count: 1 Most Recent
Author: Walter Bright -
Implementation: PR 6681
Status: Post-Preliminary Review Round 1


A proposal for allowing exceptions to be allocated and thrown in @nogc code just as they are, otherwise via automated refcounting.


Previous discussions can be found in the following threads in the General forum:

Exceptions in @nogc code

Proposal 2: Exceptions and @nogc


Make Throwable optionally refcounted. Add a field _refcount which is !=0 when it is a refcounted instance. The number of parents of a refcounted Throwable is _refcount+1. This member is private and only accessible via the @system member function refcount(), to prevent its use in @safe code.

The only place a refcounted Throwable is ever created is when the following statement is in the user code:

throw new E(string);

where E is Throwable or derived from Throwable. Instead of calling the usual _d_newclass() to allocate E on the GC heap, it will call the new function _d_newThrowable() which will allocate E and intialize it for refcounting.

When the exception is thrown, either _d_throwc() or _d_throwdwarf() gets called, which is where druntime takes over. The refcount is incremented in these functions, usually from 1 to 2.

The thrown object will then wind up either in a catch statement, or in the next linked list of thrown exceptions in flight.

A catch variable e as in:

catch (E e)

becomes a RAII object, meaning that it is destroyed at the closing }. Such destruction is done by calling:


which will test e._refcount to see if it is a refcounted object. If not, it does nothing. If it is, the refcount is decremented, and if it hits one then e's destructor is called, followed by freeing the memory used by e.

In catch blocks, e is regarded as scope so that it cannot escape the catch block. As a special case, if e is thrown in the catch block as:

throw e;

then e can escape. This works because, as mentioned before, _d_throwc() or _d_throwdwarf() will increment the reference count.

The destructor for Throwable will, if the refcount is 1, call _d_delThrowable(, i.e. on the head of the chained list of exceptions. This means that the chained list can be a mixed collection of refcounted and non-refcounted Throwables, although the refcounted ones can only be freed when their antecedent gets reaped by whatever means.

The chained list must be protected so it cannot be altered or escaped by user code.


Exceptions are assumed to be GC-collected by the EH design, in that no attempt is made to control copies or lifetimes. This dependency is not a performance issue, as exceptions are presumed to be slow.

The issue is it impairs use of @nogc on any code that throws exceptions, and prevents building D programs that do not link in the GC runtime.

To fix this, the allocation and destruction of the exception objects must be completely controlled. The result of this should be no leaking memory, no need to link in the GC, and memory safety. GC, stack, and refcounted exception objects can coexist in the same program.

Breaking changes

This will break an unknown amount of existing code.

Breakage will come in the form of:

  1. Leaking Exception objects from catch clauses (caught by the compiler)

  2. Disallowing Exception objects with postblit fields.

  3. Catch objects being scope will cause problems in that everything done with those objects will also have to be scope. The most likely problem will be printing the objects, which relies on Object.toString(), which is not scope. One possible solution is to force Throwable.toString() to be scope, which will likely cause minimal disruption. Of course, compiling with -dip1000 will disable such checking and can work in the interim. Code that needs to leak the thrown exception object can clone the object.

Copyright & License

Copyright (c) 2016 by the D Language Foundation

Licensed under Creative Commons Zero 1.0


Preliminary Review Round 1