xref: /netbsd-src/external/gpl3/gcc/dist/libphobos/libdruntime/core/internal/postblit.d (revision 0a3071956a3a9fdebdbf7f338cf2d439b45fc728)
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
__ArrayPostblit(T)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 
postblitRecurse(E,size_t n)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     {
~thisInnerTop56         ~this() @safe nothrow pure
57         {
58             order ~= "destroy inner top";
59         }
60 
thisInnerTop61         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 
~thisInnerElement74         ~this() @safe nothrow pure
75         {
76             order ~= "destroy inner element #" ~ counter++;
77         }
78 
thisInnerElement79         this(this) @safe nothrow pure
80         {
81             order ~= "copy inner element #" ~ counter++;
82         }
83     }
84 
85     struct InnerBottom
86     {
~this()87         ~this() @safe nothrow pure
88         {
89             order ~= "destroy inner bottom";
90         }
91 
this(this)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;
~thisS106         ~this() @safe nothrow pure { order ~= "destroy outer"; }
thisS107         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     {
thisHasPostblit139         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 
thisS162         this(this)
163         {
164             order ~= "copy #" ~ id;
165         }
166 
~thisS167         ~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 {
this()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:
thisInner208         this(this)
209         {
210             order ~= "copy inner #" ~ id;
211             if (id == '2')
212                 throw new FailedPostblitException();
213         }
214 
~thisInner215         ~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:
this(char first,char second,char third)226         this(char first, char second, char third)
227         {
228             inner1 = Inner(first);
229             inner2 = Inner(second);
230             inner3 = Inner(third);
231         }
232 
this(this)233         this(this)
234         {
235             order ~= "copy outer";
236         }
237 
~this()238         ~this()
239         {
240             order ~= "destroy outer";
241         }
242     }
243 
244     auto outer = Outer('1', '2', '3');
245 
246     try postblitRecurse(outer);
catch(FailedPostblitException)247     catch (FailedPostblitException) {}
248     catch (Exception) assert(false);
249 
250     auto postblitRecurseOrder = order;
251     order = null;
252 
253     try auto copy = outer;
catch(FailedPostblitException)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);
catch(FailedPostblitException)263     catch (FailedPostblitException) {}
264     catch (Exception) assert(false);
265 
266     postblitRecurseOrder = order;
267     order = null;
268 
269     try auto arrCopy = arr;
catch(FailedPostblitException)270     catch (FailedPostblitException) {}
271     catch (Exception) assert(false);
272 
273     assert(postblitRecurseOrder == order);
274 }
275