Daily bump.
[gcc.git] / libphobos / libdruntime / core / internal / array / construction.d
blobc5d8c79bfbc7d823243d524687ccce50bd74f77a
1 /**
2 This module contains compiler support for constructing dynamic arrays
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;
12 import core.internal.traits : Unqual;
14 debug(PRINTF)
16 import core.stdc.stdio;
19 /**
20 * Does array initialization (not assignment) from another array of the same element type.
21 * Params:
22 * to = what array to initialize
23 * from = what data the array should be initialized with
24 * makeWeaklyPure = unused; its purpose is to prevent the function from becoming
25 * strongly pure and risk being optimised out
26 * Returns:
27 * The created and initialized array `to`
28 * Bugs:
29 * This function template was ported from a much older runtime hook that bypassed safety,
30 * purity, and throwabilty checks. To prevent breaking existing code, this function template
31 * is temporarily declared `@trusted` until the implementation can be brought up to modern D expectations.
33 * The third parameter is never used, but is necessary in order for the
34 * function be treated as weakly pure, instead of strongly pure.
35 * This is needed because constructions such as the one below can be ignored by
36 * the compiler if `_d_arrayctor` is believed to be pure, because purity would
37 * mean the call to `_d_arrayctor` has no effects (no side effects and the
38 * return value is ignored), despite it actually modifying the contents of `a`.
39 * const S[2] b;
40 * const S[2] a = b; // this would get lowered to _d_arrayctor(a, b)
42 Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from, char* makeWeaklyPure = null) @trusted
44 version (DigitalMars) pragma(inline, false);
45 import core.internal.traits : hasElaborateCopyConstructor;
46 import core.lifetime : copyEmplace;
47 import core.stdc.string : memcpy;
48 import core.stdc.stdint : uintptr_t;
49 debug(PRINTF) import core.stdc.stdio : printf;
51 debug(PRINTF) printf("_d_arrayctor(from = %p,%zd) size = %zd\n", from.ptr, from.length, T.sizeof);
53 void[] vFrom = (cast(void*) from.ptr)[0..from.length];
54 void[] vTo = (cast(void*) to.ptr)[0..to.length];
56 // Force `enforceRawArraysConformable` to remain weakly `pure`
57 void enforceRawArraysConformable(const char[] action, const size_t elementSize,
58 const void[] a1, const void[] a2) @trusted
60 import core.internal.util.array : enforceRawArraysConformableNogc;
62 alias Type = void function(const char[] action, const size_t elementSize,
63 const void[] a1, const void[] a2, in bool allowOverlap = false) @nogc pure nothrow;
64 (cast(Type)&enforceRawArraysConformableNogc)(action, elementSize, a1, a2, false);
67 enforceRawArraysConformable("initialization", T.sizeof, vFrom, vTo);
69 static if (hasElaborateCopyConstructor!T)
71 size_t i;
72 try
74 for (i = 0; i < to.length; i++)
75 copyEmplace(from[i], to[i]);
77 catch (Exception o)
79 /* Destroy, in reverse order, what we've constructed so far
81 while (i--)
83 auto elem = cast(Unqual!T*) &to[i];
84 destroy(*elem);
87 throw o;
90 else
92 // blit all elements at once
93 memcpy(cast(void*) to.ptr, from.ptr, to.length * T.sizeof);
96 return to;
99 // postblit
100 @safe unittest
102 int counter;
103 struct S
105 int val;
106 this(this) { counter++; }
109 S[4] arr1;
110 S[4] arr2 = [S(0), S(1), S(2), S(3)];
111 _d_arrayctor(arr1[], arr2[]);
113 assert(counter == 4);
114 assert(arr1 == arr2);
117 // copy constructor
118 @safe unittest
120 int counter;
121 struct S
123 int val;
124 this(int val) { this.val = val; }
125 this(const scope ref S rhs)
127 val = rhs.val;
128 counter++;
132 S[4] arr1;
133 S[4] arr2 = [S(0), S(1), S(2), S(3)];
134 _d_arrayctor(arr1[], arr2[]);
136 assert(counter == 4);
137 assert(arr1 == arr2);
140 @safe nothrow unittest
142 // Test that throwing works
143 int counter;
144 bool didThrow;
146 struct Throw
148 int val;
149 this(this)
151 counter++;
152 if (counter == 2)
153 throw new Exception("");
158 Throw[4] a;
159 Throw[4] b = [Throw(1), Throw(2), Throw(3), Throw(4)];
160 _d_arrayctor(a[], b[]);
162 catch (Exception)
164 didThrow = true;
166 assert(didThrow);
167 assert(counter == 2);
170 // Test that `nothrow` works
171 didThrow = false;
172 counter = 0;
173 struct NoThrow
175 int val;
176 this(this)
178 counter++;
183 NoThrow[4] a;
184 NoThrow[4] b = [NoThrow(1), NoThrow(2), NoThrow(3), NoThrow(4)];
185 _d_arrayctor(a[], b[]);
187 catch (Exception)
189 didThrow = false;
191 assert(!didThrow);
192 assert(counter == 4);
196 * Do construction of an array.
197 * ti[count] p = value;
198 * Params:
199 * p = what array to initialize
200 * value = what data to construct the array with
201 * Bugs:
202 * This function template was ported from a much older runtime hook that bypassed safety,
203 * purity, and throwabilty checks. To prevent breaking existing code, this function template
204 * is temporarily declared `@trusted` until the implementation can be brought up to modern D expectations.
206 void _d_arraysetctor(Tarr : T[], T)(scope Tarr p, scope ref T value) @trusted
208 version (DigitalMars) pragma(inline, false);
209 import core.lifetime : copyEmplace;
211 size_t i;
214 for (i = 0; i < p.length; i++)
215 copyEmplace(value, p[i]);
217 catch (Exception o)
219 // Destroy, in reverse order, what we've constructed so far
220 while (i--)
222 auto elem = cast(Unqual!T*)&p[i];
223 destroy(*elem);
226 throw o;
230 // postblit
231 @safe unittest
233 int counter;
234 struct S
236 int val;
237 this(this)
239 counter++;
243 S[4] arr;
244 S s = S(1234);
245 _d_arraysetctor(arr[], s);
246 assert(counter == arr.length);
247 assert(arr == [S(1234), S(1234), S(1234), S(1234)]);
250 // copy constructor
251 @safe unittest
253 int counter;
254 struct S
256 int val;
257 this(int val) { this.val = val; }
258 this(const scope ref S rhs)
260 val = rhs.val;
261 counter++;
265 S[4] arr;
266 S s = S(1234);
267 _d_arraysetctor(arr[], s);
268 assert(counter == arr.length);
269 assert(arr == [S(1234), S(1234), S(1234), S(1234)]);
272 @safe nothrow unittest
274 // Test that throwing works
275 int counter;
276 bool didThrow;
277 struct Throw
279 int val;
280 this(this)
282 counter++;
283 if (counter == 2)
284 throw new Exception("Oh no.");
289 Throw[4] a;
290 Throw[4] b = [Throw(1), Throw(2), Throw(3), Throw(4)];
291 _d_arrayctor(a[], b[]);
293 catch (Exception)
295 didThrow = true;
297 assert(didThrow);
298 assert(counter == 2);
301 // Test that `nothrow` works
302 didThrow = false;
303 counter = 0;
304 struct NoThrow
306 int val;
307 this(this)
309 counter++;
314 NoThrow[4] a;
315 NoThrow b = NoThrow(1);
316 _d_arraysetctor(a[], b);
317 foreach (ref e; a)
318 assert(e == NoThrow(1));
320 catch (Exception)
322 didThrow = false;
324 assert(!didThrow);
325 assert(counter == 4);
329 * Allocate an array with the garbage collector. Also initalize elements if
330 * their type has an initializer. Otherwise, not zero-initialize the array.
332 * Has three variants:
333 * `_d_newarrayU` leaves elements uninitialized
334 * `_d_newarrayT` initializes to 0 or based on initializer
336 * Params:
337 * length = `.length` of resulting array
339 * Returns:
340 * newly allocated array
342 T[] _d_newarrayUPureNothrow(T)(size_t length, bool isShared=false) pure nothrow @trusted
344 alias PureType = T[] function(size_t length, bool isShared) pure nothrow @trusted;
345 return (cast(PureType) &_d_newarrayU!T)(length, isShared);
348 T[] _d_newarrayU(T)(size_t length, bool isShared=false) @trusted
350 import core.exception : onOutOfMemoryError;
351 import core.internal.traits : Unqual;
352 import core.internal.array.utils : __arrayAlloc;
354 alias UnqT = Unqual!T;
356 size_t elemSize = T.sizeof;
357 size_t arraySize;
359 debug(PRINTF) printf("_d_newarrayU(length = x%zu, size = %zu)\n", length, elemSize);
360 if (length == 0 || elemSize == 0)
361 return null;
363 version (D_InlineAsm_X86)
365 asm pure nothrow @nogc
367 mov EAX, elemSize ;
368 mul EAX, length ;
369 mov arraySize, EAX ;
370 jnc Lcontinue ;
373 else version (D_InlineAsm_X86_64)
375 asm pure nothrow @nogc
377 mov RAX, elemSize ;
378 mul RAX, length ;
379 mov arraySize, RAX ;
380 jnc Lcontinue ;
383 else
385 import core.checkedint : mulu;
387 bool overflow = false;
388 arraySize = mulu(elemSize, length, overflow);
389 if (!overflow)
390 goto Lcontinue;
393 Loverflow:
394 onOutOfMemoryError();
395 assert(0);
397 Lcontinue:
398 auto arr = __arrayAlloc!UnqT(arraySize);
399 if (!arr.ptr)
400 goto Loverflow;
401 debug(PRINTF) printf("p = %p\n", arr.ptr);
402 return (cast(T*) arr.ptr)[0 .. length];
405 /// ditto
406 T[] _d_newarrayT(T)(size_t length, bool isShared=false) @trusted
408 T[] result = _d_newarrayU!T(length, isShared);
410 static if (__traits(isZeroInit, T))
412 import core.stdc.string : memset;
413 memset(result.ptr, 0, length * T.sizeof);
415 else
417 import core.internal.lifetime : emplaceInitializer;
418 foreach (ref elem; result)
419 emplaceInitializer(elem);
422 return result;
425 unittest
428 // zero-initialization
429 struct S { int x, y; }
430 S[] s = _d_newarrayT!S(10);
432 assert(s !is null);
433 assert(s.length == 10);
434 foreach (ref elem; s)
436 assert(elem.x == 0);
437 assert(elem.y == 0);
441 // S.init
442 struct S { int x = 2, y = 3; }
443 S[] s = _d_newarrayT!S(10);
445 assert(s.length == 10);
446 foreach (ref elem; s)
448 assert(elem.x == 2);
449 assert(elem.y == 3);
454 unittest
456 int pblits;
458 struct S
460 this(this) { pblits++; }
463 S[] s = _d_newarrayT!S(2);
465 assert(s.length == 2);
466 assert(pblits == 0);
469 version (D_ProfileGC)
472 * TraceGC wrapper around $(REF _d_newitemT, core,lifetime).
474 T[] _d_newarrayTTrace(T)(string file, int line, string funcname, size_t length, bool isShared) @trusted
476 version (D_TypeInfo)
478 import core.internal.array.utils : TraceHook, gcStatsPure, accumulatePure;
479 mixin(TraceHook!(T.stringof, "_d_newarrayT"));
481 return _d_newarrayT!T(length, isShared);
483 else
484 assert(0, "Cannot create new array if compiling without support for runtime type information!");
489 * Create a new multi-dimensional array. Also initalize elements if their type has an initializer.
490 * Otherwise, not zero-initialize the array.
492 * ---
493 * void main()
495 * S[][] s = new S[][](2, 3)
497 * // lowering:
498 * S[] s = _d_newarraymTX!(S[][], S)([2, 3]);
500 * ---
502 * Params:
503 * dims = array length values for each dimension
504 * isShared = whether the array should be shared
506 * Returns:
507 * newly allocated array
509 Tarr _d_newarraymTX(Tarr : U[], T, U)(size_t[] dims, bool isShared=false) @trusted
511 debug(PRINTF) printf("_d_newarraymTX(dims.length = %zd)\n", dims.length);
513 if (dims.length == 0)
514 return null;
516 alias UnqT = Unqual!(T);
518 void[] __allocateInnerArray(size_t[] dims)
520 import core.internal.array.utils : __arrayAlloc;
522 auto dim = dims[0];
524 debug(PRINTF) printf("__allocateInnerArray(UnqT = %s, dim = %lu, ndims = %lu\n", UnqT.stringof.ptr, dim, dims.length);
525 if (dims.length == 1)
527 auto r = _d_newarrayT!UnqT(dim, isShared);
528 return *cast(void[]*)&r;
531 auto allocSize = (void[]).sizeof * dim;
532 // the array-of-arrays holds pointers! Don't use UnqT here!
533 auto arr = __arrayAlloc!(void[])(allocSize);
535 foreach (i; 0..dim)
537 (cast(void[]*)arr.ptr)[i] = __allocateInnerArray(dims[1..$]);
539 return arr.ptr[0 .. dim];
542 auto result = __allocateInnerArray(dims);
543 debug(PRINTF) printf("result = %p\n", result.ptr);
545 return (cast(U*) result.ptr)[0 .. dims[0]];
548 unittest
550 int[][] a = _d_newarraymTX!(int[][], int)([2, 3]);
552 assert(a.length == 2);
553 for (size_t i = 0; i < a.length; i++)
555 assert(a[i].length == 3);
556 for (size_t j = 0; j < a[i].length; j++)
557 assert(a[i][j] == 0);
561 unittest
563 struct S { int x = 1; }
565 S[][] a = _d_newarraymTX!(S[][], S)([2, 3]);
567 assert(a.length == 2);
568 for (size_t i = 0; i < a.length; i++)
570 assert(a[i].length == 3);
571 for (size_t j = 0; j < a[i].length; j++)
572 assert(a[i][j].x == 1);
576 // Test 3-level array allocation (this uses different code paths).
577 unittest
579 int[][][] a = _d_newarraymTX!(int[][][], int)([3, 4, 5]);
580 int[5] zeros = 0;
582 assert(a.length == 3);
583 foreach(l2; a)
585 assert(l2.length == 4);
586 foreach(l3; l2)
587 assert(l3 == zeros[]);
591 // https://issues.dlang.org/show_bug.cgi?id=24436
592 @system unittest
594 import core.memory : GC;
596 int[][] a = _d_newarraymTX!(int[][], int)([2, 2]);
598 assert(!(GC.getAttr(a.ptr) & GC.BlkAttr.NO_SCAN));
601 version (D_ProfileGC)
604 * TraceGC wrapper around $(REF _d_newarraymT, core,internal,array,construction).
606 Tarr _d_newarraymTXTrace(Tarr : U[], T, U)(string file, int line, string funcname, size_t[] dims, bool isShared=false) @trusted
608 version (D_TypeInfo)
610 import core.internal.array.utils : TraceHook, gcStatsPure, accumulatePure;
611 mixin(TraceHook!(T.stringof, "_d_newarraymTX"));
613 return _d_newarraymTX!(Tarr, T)(dims, isShared);
615 else
616 assert(0, "Cannot create new multi-dimensional array if compiling without support for runtime type information!");