| /** | |
| 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/_postblit.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); | |
| } |