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