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