Home | History | Annotate | Line # | Download | only in internal
      1 /**
      2  This module contains support for D's postblit feature
      3 
      4   Copyright: Copyright Digital Mars 2000 - 2019.
      5   License: Distributed under the
      6        $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
      7      (See accompanying file LICENSE)
      8   Source: $(DRUNTIMESRC core/_internal/_destruction.d)
      9 */
     10 module core.internal.postblit;
     11 
     12 // compiler frontend lowers struct array postblitting to this
     13 void __ArrayPostblit(T)(T[] a)
     14 {
     15     foreach (ref T e; a)
     16         e.__xpostblit();
     17 }
     18 
     19 package void postblitRecurse(S)(ref S s)
     20     if (is(S == struct))
     21 {
     22     static if (__traits(hasMember, S, "__xpostblit") &&
     23                // Bugzilla 14746: Check that it's the exact member of S.
     24                __traits(isSame, S, __traits(parent, s.__xpostblit)))
     25         s.__xpostblit();
     26 }
     27 
     28 package void postblitRecurse(E, size_t n)(ref E[n] arr)
     29 {
     30     import core.internal.destruction: destructRecurse;
     31     import core.internal.traits : hasElaborateCopyConstructor;
     32 
     33     static if (hasElaborateCopyConstructor!E)
     34     {
     35         size_t i;
     36         scope(failure)
     37         {
     38             for (; i != 0; --i)
     39             {
     40                 destructRecurse(arr[i - 1]); // What to do if this throws?
     41             }
     42         }
     43 
     44         for (i = 0; i < arr.length; ++i)
     45             postblitRecurse(arr[i]);
     46     }
     47 }
     48 
     49 // Test destruction/postblit order
     50 @safe nothrow pure unittest
     51 {
     52     string[] order;
     53 
     54     struct InnerTop
     55     {
     56         ~this() @safe nothrow pure
     57         {
     58             order ~= "destroy inner top";
     59         }
     60 
     61         this(this) @safe nothrow pure
     62         {
     63             order ~= "copy inner top";
     64         }
     65     }
     66 
     67     struct InnerMiddle {}
     68 
     69     version (none) // https://issues.dlang.org/show_bug.cgi?id=14242
     70     struct InnerElement
     71     {
     72         static char counter = '1';
     73 
     74         ~this() @safe nothrow pure
     75         {
     76             order ~= "destroy inner element #" ~ counter++;
     77         }
     78 
     79         this(this) @safe nothrow pure
     80         {
     81             order ~= "copy inner element #" ~ counter++;
     82         }
     83     }
     84 
     85     struct InnerBottom
     86     {
     87         ~this() @safe nothrow pure
     88         {
     89             order ~= "destroy inner bottom";
     90         }
     91 
     92         this(this) @safe nothrow pure
     93         {
     94             order ~= "copy inner bottom";
     95         }
     96     }
     97 
     98     struct S
     99     {
    100         char[] s;
    101         InnerTop top;
    102         InnerMiddle middle;
    103         version (none) InnerElement[3] array; // https://issues.dlang.org/show_bug.cgi?id=14242
    104         int a;
    105         InnerBottom bottom;
    106         ~this() @safe nothrow pure { order ~= "destroy outer"; }
    107         this(this) @safe nothrow pure { order ~= "copy outer"; }
    108     }
    109 
    110     string[] destructRecurseOrder;
    111     {
    112         import core.internal.destruction: destructRecurse;
    113 
    114         S s;
    115         destructRecurse(s);
    116         destructRecurseOrder = order;
    117         order = null;
    118     }
    119 
    120     assert(order.length);
    121     assert(destructRecurseOrder == order);
    122     order = null;
    123 
    124     S s;
    125     postblitRecurse(s);
    126     assert(order.length);
    127     auto postblitRecurseOrder = order;
    128     order = null;
    129     S s2 = s;
    130     assert(order.length);
    131     assert(postblitRecurseOrder == order);
    132 }
    133 
    134 @safe unittest
    135 {
    136     // Bugzilla 14746
    137     static struct HasPostblit
    138     {
    139         this(this) { assert(0); }
    140     }
    141     static struct Owner
    142     {
    143         HasPostblit* ptr;
    144         alias ptr this;
    145     }
    146 
    147     Owner o;
    148     assert(o.ptr is null);
    149     postblitRecurse(o);     // must not reach in HasPostblit.__postblit()
    150 }
    151 
    152 // Test handling of fixed-length arrays
    153 // Separate from first test because of https://issues.dlang.org/show_bug.cgi?id=14242
    154 @safe unittest
    155 {
    156     string[] order;
    157 
    158     struct S
    159     {
    160         char id;
    161 
    162         this(this)
    163         {
    164             order ~= "copy #" ~ id;
    165         }
    166 
    167         ~this()
    168         {
    169             order ~= "destroy #" ~ id;
    170         }
    171     }
    172 
    173     string[] destructRecurseOrder;
    174     {
    175         import core.internal.destruction: destructRecurse;
    176 
    177         S[3] arr = [S('1'), S('2'), S('3')];
    178         destructRecurse(arr);
    179         destructRecurseOrder = order;
    180         order = null;
    181     }
    182     assert(order.length);
    183     assert(destructRecurseOrder == order);
    184     order = null;
    185 
    186     S[3] arr = [S('1'), S('2'), S('3')];
    187     postblitRecurse(arr);
    188     assert(order.length);
    189     auto postblitRecurseOrder = order;
    190     order = null;
    191 
    192     auto arrCopy = arr;
    193     assert(order.length);
    194     assert(postblitRecurseOrder == order);
    195 }
    196 
    197 // Test handling of failed postblit
    198 // Not nothrow or @safe because of https://issues.dlang.org/show_bug.cgi?id=14242
    199 /+ nothrow @safe +/ unittest
    200 {
    201     static class FailedPostblitException : Exception { this() nothrow @safe { super(null); } }
    202     static string[] order;
    203     static struct Inner
    204     {
    205         char id;
    206 
    207         @safe:
    208         this(this)
    209         {
    210             order ~= "copy inner #" ~ id;
    211             if (id == '2')
    212                 throw new FailedPostblitException();
    213         }
    214 
    215         ~this() nothrow
    216         {
    217             order ~= "destroy inner #" ~ id;
    218         }
    219     }
    220 
    221     static struct Outer
    222     {
    223         Inner inner1, inner2, inner3;
    224 
    225         nothrow @safe:
    226         this(char first, char second, char third)
    227         {
    228             inner1 = Inner(first);
    229             inner2 = Inner(second);
    230             inner3 = Inner(third);
    231         }
    232 
    233         this(this)
    234         {
    235             order ~= "copy outer";
    236         }
    237 
    238         ~this()
    239         {
    240             order ~= "destroy outer";
    241         }
    242     }
    243 
    244     auto outer = Outer('1', '2', '3');
    245 
    246     try postblitRecurse(outer);
    247     catch (FailedPostblitException) {}
    248     catch (Exception) assert(false);
    249 
    250     auto postblitRecurseOrder = order;
    251     order = null;
    252 
    253     try auto copy = outer;
    254     catch (FailedPostblitException) {}
    255     catch (Exception) assert(false);
    256 
    257     assert(postblitRecurseOrder == order);
    258     order = null;
    259 
    260     Outer[3] arr = [Outer('1', '1', '1'), Outer('1', '2', '3'), Outer('3', '3', '3')];
    261 
    262     try postblitRecurse(arr);
    263     catch (FailedPostblitException) {}
    264     catch (Exception) assert(false);
    265 
    266     postblitRecurseOrder = order;
    267     order = null;
    268 
    269     try auto arrCopy = arr;
    270     catch (FailedPostblitException) {}
    271     catch (Exception) assert(false);
    272 
    273     assert(postblitRecurseOrder == order);
    274 }
    275