/** This module contains compiler support for constructing dynamic arrays Copyright: Copyright Digital Mars 2000 - 2019. License: Distributed under the $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). (See accompanying file LICENSE) Source: $(DRUNTIMESRC core/internal/_array/_construction.d) */ module core.internal.array.construction; import core.internal.traits : Unqual; /** * Does array initialization (not assignment) from another array of the same element type. * Params: * to = what array to initialize * from = what data the array should be initialized with * makeWeaklyPure = unused; its purpose is to prevent the function from becoming * strongly pure and risk being optimised out * Returns: * The created and initialized array `to` * Bugs: * This function template was ported from a much older runtime hook that bypassed safety, * purity, and throwabilty checks. To prevent breaking existing code, this function template * is temporarily declared `@trusted` until the implementation can be brought up to modern D expectations. * * The third parameter is never used, but is necessary in order for the * function be treated as weakly pure, instead of strongly pure. * This is needed because constructions such as the one below can be ignored by * the compiler if `_d_arrayctor` is believed to be pure, because purity would * mean the call to `_d_arrayctor` has no effects (no side effects and the * return value is ignored), despite it actually modifying the contents of `a`. * const S[2] b; * const S[2] a = b; // this would get lowered to _d_arrayctor(a, b) */ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from, char* makeWeaklyPure = null) @trusted { pragma(inline, false); import core.internal.traits : hasElaborateCopyConstructor; import core.lifetime : copyEmplace; import core.stdc.string : memcpy; import core.stdc.stdint : uintptr_t; debug(PRINTF) import core.stdc.stdio : printf; debug(PRINTF) printf("_d_arrayctor(from = %p,%d) size = %d\n", from.ptr, from.length, T.sizeof); void[] vFrom = (cast(void*) from.ptr)[0..from.length]; void[] vTo = (cast(void*) to.ptr)[0..to.length]; // Force `enforceRawArraysConformable` to remain weakly `pure` void enforceRawArraysConformable(const char[] action, const size_t elementSize, const void[] a1, const void[] a2) @trusted { import core.internal.util.array : enforceRawArraysConformableNogc; alias Type = void function(const char[] action, const size_t elementSize, const void[] a1, const void[] a2, in bool allowOverlap = false) @nogc pure nothrow; (cast(Type)&enforceRawArraysConformableNogc)(action, elementSize, a1, a2, false); } enforceRawArraysConformable("initialization", T.sizeof, vFrom, vTo); static if (hasElaborateCopyConstructor!T) { size_t i; try { for (i = 0; i < to.length; i++) copyEmplace(from[i], to[i]); } catch (Exception o) { /* Destroy, in reverse order, what we've constructed so far */ while (i--) { auto elem = cast(Unqual!T*) &to[i]; destroy(*elem); } throw o; } } else { // blit all elements at once memcpy(cast(void*) to.ptr, from.ptr, to.length * T.sizeof); } return to; } // postblit @safe unittest { int counter; struct S { int val; this(this) { counter++; } } S[4] arr1; S[4] arr2 = [S(0), S(1), S(2), S(3)]; _d_arrayctor(arr1[], arr2[]); assert(counter == 4); assert(arr1 == arr2); } // copy constructor @safe unittest { int counter; struct S { int val; this(int val) { this.val = val; } this(const scope ref S rhs) { val = rhs.val; counter++; } } S[4] arr1; S[4] arr2 = [S(0), S(1), S(2), S(3)]; _d_arrayctor(arr1[], arr2[]); assert(counter == 4); assert(arr1 == arr2); } @safe nothrow unittest { // Test that throwing works int counter; bool didThrow; struct Throw { int val; this(this) { counter++; if (counter == 2) throw new Exception(""); } } try { Throw[4] a; Throw[4] b = [Throw(1), Throw(2), Throw(3), Throw(4)]; _d_arrayctor(a[], b[]); } catch (Exception) { didThrow = true; } assert(didThrow); assert(counter == 2); // Test that `nothrow` works didThrow = false; counter = 0; struct NoThrow { int val; this(this) { counter++; } } try { NoThrow[4] a; NoThrow[4] b = [NoThrow(1), NoThrow(2), NoThrow(3), NoThrow(4)]; _d_arrayctor(a[], b[]); } catch (Exception) { didThrow = false; } assert(!didThrow); assert(counter == 4); } /** * Do construction of an array. * ti[count] p = value; * Params: * p = what array to initialize * value = what data to construct the array with * Bugs: * This function template was ported from a much older runtime hook that bypassed safety, * purity, and throwabilty checks. To prevent breaking existing code, this function template * is temporarily declared `@trusted` until the implementation can be brought up to modern D expectations. */ void _d_arraysetctor(Tarr : T[], T)(scope Tarr p, scope ref T value) @trusted { pragma(inline, false); import core.lifetime : copyEmplace; size_t i; try { for (i = 0; i < p.length; i++) copyEmplace(value, p[i]); } catch (Exception o) { // Destroy, in reverse order, what we've constructed so far while (i--) { auto elem = cast(Unqual!T*)&p[i]; destroy(*elem); } throw o; } } // postblit @safe unittest { int counter; struct S { int val; this(this) { counter++; } } S[4] arr; S s = S(1234); _d_arraysetctor(arr[], s); assert(counter == arr.length); assert(arr == [S(1234), S(1234), S(1234), S(1234)]); } // copy constructor @safe unittest { int counter; struct S { int val; this(int val) { this.val = val; } this(const scope ref S rhs) { val = rhs.val; counter++; } } S[4] arr; S s = S(1234); _d_arraysetctor(arr[], s); assert(counter == arr.length); assert(arr == [S(1234), S(1234), S(1234), S(1234)]); } @safe nothrow unittest { // Test that throwing works int counter; bool didThrow; struct Throw { int val; this(this) { counter++; if (counter == 2) throw new Exception("Oh no."); } } try { Throw[4] a; Throw[4] b = [Throw(1), Throw(2), Throw(3), Throw(4)]; _d_arrayctor(a[], b[]); } catch (Exception) { didThrow = true; } assert(didThrow); assert(counter == 2); // Test that `nothrow` works didThrow = false; counter = 0; struct NoThrow { int val; this(this) { counter++; } } try { NoThrow[4] a; NoThrow b = NoThrow(1); _d_arraysetctor(a[], b); foreach (ref e; a) assert(e == NoThrow(1)); } catch (Exception) { didThrow = false; } assert(!didThrow); assert(counter == 4); }