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*/ 10module core.internal.postblit; 11 12// compiler frontend lowers struct array postblitting to this 13void __ArrayPostblit(T)(T[] a) 14{ 15 foreach (ref T e; a) 16 e.__xpostblit(); 17} 18 19package 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 28package 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