/** This module contains support for D's postblit feature Copyright: Copyright Digital Mars 2000 - 2019. License: Distributed under the $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). (See accompanying file LICENSE) Source: $(DRUNTIMESRC core/_internal/_destruction.d) */ module core.internal.postblit; // compiler frontend lowers struct array postblitting to this void __ArrayPostblit(T)(T[] a) { foreach (ref T e; a) e.__xpostblit(); } package void postblitRecurse(S)(ref S s) if (is(S == struct)) { static if (__traits(hasMember, S, "__xpostblit") && // Bugzilla 14746: Check that it's the exact member of S. __traits(isSame, S, __traits(parent, s.__xpostblit))) s.__xpostblit(); } package void postblitRecurse(E, size_t n)(ref E[n] arr) { import core.internal.destruction: destructRecurse; import core.internal.traits : hasElaborateCopyConstructor; static if (hasElaborateCopyConstructor!E) { size_t i; scope(failure) { for (; i != 0; --i) { destructRecurse(arr[i - 1]); // What to do if this throws? } } for (i = 0; i < arr.length; ++i) postblitRecurse(arr[i]); } } // Test destruction/postblit order @safe nothrow pure unittest { string[] order; struct InnerTop { ~this() @safe nothrow pure { order ~= "destroy inner top"; } this(this) @safe nothrow pure { order ~= "copy inner top"; } } struct InnerMiddle {} version (none) // https://issues.dlang.org/show_bug.cgi?id=14242 struct InnerElement { static char counter = '1'; ~this() @safe nothrow pure { order ~= "destroy inner element #" ~ counter++; } this(this) @safe nothrow pure { order ~= "copy inner element #" ~ counter++; } } struct InnerBottom { ~this() @safe nothrow pure { order ~= "destroy inner bottom"; } this(this) @safe nothrow pure { order ~= "copy inner bottom"; } } struct S { char[] s; InnerTop top; InnerMiddle middle; version (none) InnerElement[3] array; // https://issues.dlang.org/show_bug.cgi?id=14242 int a; InnerBottom bottom; ~this() @safe nothrow pure { order ~= "destroy outer"; } this(this) @safe nothrow pure { order ~= "copy outer"; } } string[] destructRecurseOrder; { import core.internal.destruction: destructRecurse; S s; destructRecurse(s); destructRecurseOrder = order; order = null; } assert(order.length); assert(destructRecurseOrder == order); order = null; S s; postblitRecurse(s); assert(order.length); auto postblitRecurseOrder = order; order = null; S s2 = s; assert(order.length); assert(postblitRecurseOrder == order); } @safe unittest { // Bugzilla 14746 static struct HasPostblit { this(this) { assert(0); } } static struct Owner { HasPostblit* ptr; alias ptr this; } Owner o; assert(o.ptr is null); postblitRecurse(o); // must not reach in HasPostblit.__postblit() } // Test handling of fixed-length arrays // Separate from first test because of https://issues.dlang.org/show_bug.cgi?id=14242 @safe unittest { string[] order; struct S { char id; this(this) { order ~= "copy #" ~ id; } ~this() { order ~= "destroy #" ~ id; } } string[] destructRecurseOrder; { import core.internal.destruction: destructRecurse; S[3] arr = [S('1'), S('2'), S('3')]; destructRecurse(arr); destructRecurseOrder = order; order = null; } assert(order.length); assert(destructRecurseOrder == order); order = null; S[3] arr = [S('1'), S('2'), S('3')]; postblitRecurse(arr); assert(order.length); auto postblitRecurseOrder = order; order = null; auto arrCopy = arr; assert(order.length); assert(postblitRecurseOrder == order); } // Test handling of failed postblit // Not nothrow or @safe because of https://issues.dlang.org/show_bug.cgi?id=14242 /+ nothrow @safe +/ unittest { static class FailedPostblitException : Exception { this() nothrow @safe { super(null); } } static string[] order; static struct Inner { char id; @safe: this(this) { order ~= "copy inner #" ~ id; if (id == '2') throw new FailedPostblitException(); } ~this() nothrow { order ~= "destroy inner #" ~ id; } } static struct Outer { Inner inner1, inner2, inner3; nothrow @safe: this(char first, char second, char third) { inner1 = Inner(first); inner2 = Inner(second); inner3 = Inner(third); } this(this) { order ~= "copy outer"; } ~this() { order ~= "destroy outer"; } } auto outer = Outer('1', '2', '3'); try postblitRecurse(outer); catch (FailedPostblitException) {} catch (Exception) assert(false); auto postblitRecurseOrder = order; order = null; try auto copy = outer; catch (FailedPostblitException) {} catch (Exception) assert(false); assert(postblitRecurseOrder == order); order = null; Outer[3] arr = [Outer('1', '1', '1'), Outer('1', '2', '3'), Outer('3', '3', '3')]; try postblitRecurse(arr); catch (FailedPostblitException) {} catch (Exception) assert(false); postblitRecurseOrder = order; order = null; try auto arrCopy = arr; catch (FailedPostblitException) {} catch (Exception) assert(false); assert(postblitRecurseOrder == order); }