hasComplexDestruction

Whether the given type has either a user-defined destructor or a compiler-generated destructor.

The compiler will generate a destructor for a struct when a member variable of that struct defines a destructor.

Note that hasComplexDestruction is also true for static arrays whose element type has a destructor, since while the static array itself does not have a destructor, the compiler must use the destructor of the elements when destroying the static array.

Due to https://issues.dlang.org/show_bug.cgi?id=24833, enums never have complex destruction even if their base type does. Their destructor is never called, resulting in incorrect behavior for such enums. So, because the compiler does not treat them as having complex destruction, hasComplexDestruction is false for them.

Note that while the language spec currently refers to ~this() on classes as destructors (whereas the runtime refers to them as finalizers, and they're arguably finalizers rather than destructors given how they work), classes are not considered to have complex destruction. Under normal circumstances, it's just the GC or $(REF1 destroy, object) which calls the destructor / finalizer on a class (and it's not guaranteed that a class destructor / finalizer will even ever be called), which is in stark contrast to structs, which normally live on the stack and need to be destroyed when they leave scope. So, hasComplexDestruction is concerned with whether that type will have a destructor that's run when it leaves scope and not with what happens when the GC destroys an object prior to freeing its memory.

No other types (including pointers and unions) ever have a destructor and thus hasComplexDestruction is never true for them. It is particularly important to note that unions never have a destructor, so if a struct contains a union which contains one or more members which have a destructor, that struct will have to have a user-defined destructor which explicitly calls $(REF1 destroy, object) on the correct member of the union if you want the object in question to be destroyed properly.

template hasComplexDestruction (
T
) {}

Examples

1 static assert(!hasComplexDestruction!int);
2 static assert(!hasComplexDestruction!real);
3 static assert(!hasComplexDestruction!string);
4 static assert(!hasComplexDestruction!(int[]));
5 static assert(!hasComplexDestruction!(int[42]));
6 static assert(!hasComplexDestruction!(int[string]));
7 static assert(!hasComplexDestruction!Object);
8 
9 static struct NoDtor
10 {
11     int i;
12 }
13 static assert(!hasComplexDestruction!NoDtor);
14 
15 struct HasDtor
16 {
17     ~this() {}
18 }
19 static assert( hasComplexDestruction!HasDtor);
20 
21 // The compiler will generate a destructor if a member variable has one.
22 static struct HasMemberWithDtor
23 {
24     HasDtor s;
25 }
26 static assert( hasComplexDestruction!HasMemberWithDtor);
27 
28 // If a struct has @disabled destruction, hasComplexDestruction is still
29 // true. Code that wants to check whether destruction works can either
30 // test for whether the __xdtor member is disabled, or it can test whether
31 // code that will destroy the object compiles. That being said, a disabled
32 // destructor probably isn't very common in practice, because about all that
33 // such a type is good for is being allocated on the heap.
34 static struct DisabledDtor
35 {
36     @disable ~this() {}
37 }
38 static assert( hasComplexDestruction!DisabledDtor);
39 static assert( __traits(isDisabled,
40                         __traits(getMember, DisabledDtor, "__xdtor")));
41 
42 // A type with a disabled destructor cannot be created on the stack or used
43 // in any way that would ever trigger a destructor, making it pretty much
44 // useless outside of providing a way to force a struct to be allocated on
45 // the heap - though that could be useful in some situations, since it
46 // it makes it possible to have a type that has to be a reference type but
47 // which doesn't have the overhead of a class.
48 static assert(!__traits(compiles, { DisabledDtor d; }));
49 static assert( __traits(compiles, { auto d = new DisabledDtor; }));
50 
51 // Static arrays have complex destruction if their elements do.
52 static assert( hasComplexDestruction!(HasDtor[1]));
53 
54 // Static arrays with no elements do not have complex destruction, because
55 // there's nothing to destroy.
56 static assert(!hasComplexDestruction!(HasDtor[0]));
57 
58 // Dynamic arrays do not have complex destruction, because their elements
59 // are contained in the memory that the dynamic array is a slice of and not
60 // in the dynamic array itself, so there's nothing to destroy when a
61 // dynamic array leaves scope.
62 static assert(!hasComplexDestruction!(HasDtor[]));
63 
64 // Classes and unions do not have complex copying even if they have
65 // members which do.
66 class C
67 {
68     HasDtor s;
69 }
70 static assert(!hasComplexDestruction!C);
71 
72 union U
73 {
74     HasDtor s;
75 }
76 static assert(!hasComplexDestruction!U);
77 
78 // https://issues.dlang.org/show_bug.cgi?id=24833
79 // This static assertion fails, because the compiler
80 // currently ignores assignment operators for enum types.
81 enum E : HasDtor { a = HasDtor.init }
82 //static assert( hasComplexDestruction!E);

See Also

Meta