1 /** 2 This module contains compiler support for constructing dynamic arrays 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/_array/_construction.d) 9 */ 10 module core.internal.array.construction; 11 12 import core.internal.traits : Unqual; 13 14 /** 15 * Does array initialization (not assignment) from another array of the same element type. 16 * Params: 17 * to = what array to initialize 18 * from = what data the array should be initialized with 19 * makeWeaklyPure = unused; its purpose is to prevent the function from becoming 20 * strongly pure and risk being optimised out 21 * Returns: 22 * The created and initialized array `to` 23 * Bugs: 24 * This function template was ported from a much older runtime hook that bypassed safety, 25 * purity, and throwabilty checks. To prevent breaking existing code, this function template 26 * is temporarily declared `@trusted` until the implementation can be brought up to modern D expectations. 27 * 28 * The third parameter is never used, but is necessary in order for the 29 * function be treated as weakly pure, instead of strongly pure. 30 * This is needed because constructions such as the one below can be ignored by 31 * the compiler if `_d_arrayctor` is believed to be pure, because purity would 32 * mean the call to `_d_arrayctor` has no effects (no side effects and the 33 * return value is ignored), despite it actually modifying the contents of `a`. 34 * const S[2] b; 35 * const S[2] a = b; // this would get lowered to _d_arrayctor(a, b) 36 */ 37 Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from, char* makeWeaklyPure = null) @trusted 38 { 39 pragma(inline, false); 40 import core.internal.traits : hasElaborateCopyConstructor; 41 import core.lifetime : copyEmplace; 42 import core.stdc.string : memcpy; 43 import core.stdc.stdint : uintptr_t; 44 debug(PRINTF) import core.stdc.stdio : printf; 45 46 debug(PRINTF) printf("_d_arrayctor(from = %p,%d) size = %d\n", from.ptr, from.length, T.sizeof); 47 48 void[] vFrom = (cast(void*) from.ptr)[0..from.length]; 49 void[] vTo = (cast(void*) to.ptr)[0..to.length]; 50 51 // Force `enforceRawArraysConformable` to remain weakly `pure` enforceRawArraysConformable(const char[]action,const size_t elementSize,const void[]a1,const void[]a2)52 void enforceRawArraysConformable(const char[] action, const size_t elementSize, 53 const void[] a1, const void[] a2) @trusted 54 { 55 import core.internal.util.array : enforceRawArraysConformableNogc; 56 57 alias Type = void function(const char[] action, const size_t elementSize, 58 const void[] a1, const void[] a2, in bool allowOverlap = false) @nogc pure nothrow; 59 (cast(Type)&enforceRawArraysConformableNogc)(action, elementSize, a1, a2, false); 60 } 61 62 enforceRawArraysConformable("initialization", T.sizeof, vFrom, vTo); 63 64 static if (hasElaborateCopyConstructor!T) 65 { 66 size_t i; 67 try 68 { 69 for (i = 0; i < to.length; i++) 70 copyEmplace(from[i], to[i]); 71 } catch(Exception o)72 catch (Exception o) 73 { 74 /* Destroy, in reverse order, what we've constructed so far 75 */ 76 while (i--) 77 { 78 auto elem = cast(Unqual!T*) &to[i]; 79 destroy(*elem); 80 } 81 82 throw o; 83 } 84 } 85 else 86 { 87 // blit all elements at once 88 memcpy(cast(void*) to.ptr, from.ptr, to.length * T.sizeof); 89 } 90 91 return to; 92 } 93 94 // postblit 95 @safe unittest 96 { 97 int counter; 98 struct S 99 { 100 int val; thisS101 this(this) { counter++; } 102 } 103 104 S[4] arr1; 105 S[4] arr2 = [S(0), S(1), S(2), S(3)]; 106 _d_arrayctor(arr1[], arr2[]); 107 108 assert(counter == 4); 109 assert(arr1 == arr2); 110 } 111 112 // copy constructor 113 @safe unittest 114 { 115 int counter; 116 struct S 117 { 118 int val; thisS119 this(int val) { this.val = val; } thisS120 this(const scope ref S rhs) 121 { 122 val = rhs.val; 123 counter++; 124 } 125 } 126 127 S[4] arr1; 128 S[4] arr2 = [S(0), S(1), S(2), S(3)]; 129 _d_arrayctor(arr1[], arr2[]); 130 131 assert(counter == 4); 132 assert(arr1 == arr2); 133 } 134 135 @safe nothrow unittest 136 { 137 // Test that throwing works 138 int counter; 139 bool didThrow; 140 141 struct Throw 142 { 143 int val; thisThrow144 this(this) 145 { 146 counter++; 147 if (counter == 2) 148 throw new Exception(""); 149 } 150 } 151 try 152 { 153 Throw[4] a; 154 Throw[4] b = [Throw(1), Throw(2), Throw(3), Throw(4)]; 155 _d_arrayctor(a[], b[]); 156 } catch(Exception)157 catch (Exception) 158 { 159 didThrow = true; 160 } 161 assert(didThrow); 162 assert(counter == 2); 163 164 165 // Test that `nothrow` works 166 didThrow = false; 167 counter = 0; 168 struct NoThrow 169 { 170 int val; thisNoThrow171 this(this) 172 { 173 counter++; 174 } 175 } 176 try 177 { 178 NoThrow[4] a; 179 NoThrow[4] b = [NoThrow(1), NoThrow(2), NoThrow(3), NoThrow(4)]; 180 _d_arrayctor(a[], b[]); 181 } catch(Exception)182 catch (Exception) 183 { 184 didThrow = false; 185 } 186 assert(!didThrow); 187 assert(counter == 4); 188 } 189 190 /** 191 * Do construction of an array. 192 * ti[count] p = value; 193 * Params: 194 * p = what array to initialize 195 * value = what data to construct the array with 196 * Bugs: 197 * This function template was ported from a much older runtime hook that bypassed safety, 198 * purity, and throwabilty checks. To prevent breaking existing code, this function template 199 * is temporarily declared `@trusted` until the implementation can be brought up to modern D expectations. 200 */ 201 void _d_arraysetctor(Tarr : T[], T)(scope Tarr p, scope ref T value) @trusted 202 { 203 pragma(inline, false); 204 import core.lifetime : copyEmplace; 205 206 size_t i; 207 try 208 { 209 for (i = 0; i < p.length; i++) 210 copyEmplace(value, p[i]); 211 } catch(Exception o)212 catch (Exception o) 213 { 214 // Destroy, in reverse order, what we've constructed so far 215 while (i--) 216 { 217 auto elem = cast(Unqual!T*)&p[i]; 218 destroy(*elem); 219 } 220 221 throw o; 222 } 223 } 224 225 // postblit 226 @safe unittest 227 { 228 int counter; 229 struct S 230 { 231 int val; thisS232 this(this) 233 { 234 counter++; 235 } 236 } 237 238 S[4] arr; 239 S s = S(1234); 240 _d_arraysetctor(arr[], s); 241 assert(counter == arr.length); 242 assert(arr == [S(1234), S(1234), S(1234), S(1234)]); 243 } 244 245 // copy constructor 246 @safe unittest 247 { 248 int counter; 249 struct S 250 { 251 int val; thisS252 this(int val) { this.val = val; } thisS253 this(const scope ref S rhs) 254 { 255 val = rhs.val; 256 counter++; 257 } 258 } 259 260 S[4] arr; 261 S s = S(1234); 262 _d_arraysetctor(arr[], s); 263 assert(counter == arr.length); 264 assert(arr == [S(1234), S(1234), S(1234), S(1234)]); 265 } 266 267 @safe nothrow unittest 268 { 269 // Test that throwing works 270 int counter; 271 bool didThrow; 272 struct Throw 273 { 274 int val; thisThrow275 this(this) 276 { 277 counter++; 278 if (counter == 2) 279 throw new Exception("Oh no."); 280 } 281 } 282 try 283 { 284 Throw[4] a; 285 Throw[4] b = [Throw(1), Throw(2), Throw(3), Throw(4)]; 286 _d_arrayctor(a[], b[]); 287 } catch(Exception)288 catch (Exception) 289 { 290 didThrow = true; 291 } 292 assert(didThrow); 293 assert(counter == 2); 294 295 296 // Test that `nothrow` works 297 didThrow = false; 298 counter = 0; 299 struct NoThrow 300 { 301 int val; thisNoThrow302 this(this) 303 { 304 counter++; 305 } 306 } 307 try 308 { 309 NoThrow[4] a; 310 NoThrow b = NoThrow(1); 311 _d_arraysetctor(a[], b); 312 foreach (ref e; a) 313 assert(e == NoThrow(1)); 314 } catch(Exception)315 catch (Exception) 316 { 317 didThrow = false; 318 } 319 assert(!didThrow); 320 assert(counter == 4); 321 } 322