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);
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.