Currently, when a constructor throws, it will not call destructors of already initialized member fields. The user is responsible for catching exceptions or using `scope(failure)` and cleaning up manually. Of course, this is error prone and can easily be forgotten.
I suggest to apply the same heuristic to mutable fields that is also use for tracking the initialization of immutable fields: The first "assignment" to a field must not appear in a loop or after a label, and calls the constructor instead of opAssign(). For immutable and const fields, no further writes are allowed; mutable fields can be changed as desired.
At each point where an exception can occur, the compiler is then aware which fields have already been initialized, and can call their destructor.
Shachar Shemesh wrote this example:
import std.stdio;
struct A {
int a = 3;
this( int var ) { a += var; }
~this() { writeln("A down ", a); }
}
struct B {
A a;
this( int var ) {
a = A(var+1);
throw new Exception("An exception");
}
}
void main() {
try {
auto b = B(2);
} catch( Exception ex ) {
}
}
I'd expect A's destructor to run, which does not seem to be the case.
The fix for this has been reverted in [1] and testing Shachar Shemesh's example with 2.075.0 shows that the destructor for A is not run.
That this issue has been fixed is also erroneously shown in the changelog for 2.075.0.
[1] https://github.com/dlang/dmd/pull/6913
Comment #10 by github-bugzilla — 2017-08-07T13:16:40Z