diff --git a/src/core/internal/array/construction.d b/src/core/internal/array/construction.d index b58ed51557..b244686ed0 100644 --- a/src/core/internal/array/construction.d +++ b/src/core/internal/array/construction.d @@ -9,6 +9,35 @@ */ module core.internal.array.construction; +package void cpCtorRecurse(S1, S2)(ref S1 to, ref S2 from) + if (is(S1 == struct) && is (immutable S1 == immutable S2)) +{ + static if (__traits(hasCopyConstructor, S1)) + { + to.__ctor(from); + } +} + +package void cpCtorRecurse(T, size_t n, U)(ref T[n] to, ref U[n] from) +{ + import core.internal.destruction: destructRecurse; + + static if (__traits(hasCopyConstructor, T)) + { + size_t i; + scope(failure) + { + for (; i != 0; --i) + { + destructRecurse(to[i - 1]); // Don't care if it throws, as throwing in dtor can lead to UB + } + } + + for (i = 0; i < to.length; ++i) + cpCtorRecurse(to[i], from[i]); + } +} + /** * Does array initialization (not assignment) from another array of the same element type. * Params: @@ -21,7 +50,8 @@ module core.internal.array.construction; * 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. */ -Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted +Tarr _d_arrayctor(Tarr : T[], T, Uarr : U[], U)(return scope Tarr to, scope Uarr from) @trusted +if (is (immutable T == immutable U)) { pragma(inline, false); import core.internal.traits : hasElaborateCopyConstructor, Unqual; @@ -51,8 +81,17 @@ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted size_t i; try { - for (i = 0; i < to.length; i++) - copyEmplace(from[i], to[i]); + static if (__traits(hasCopyConstructor, T)) + { + cpCtorRecurse(to[i], from[i]); + } + else + { + auto elem = cast(Unqual!T*)&to[i]; + // Copy construction is defined as bit copy followed by postblit. + memcpy(elem, &from[i], element_size); + postblitRecurse(*elem); + } } catch (Exception o) { @@ -94,19 +133,26 @@ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted assert(arr1 == arr2); } -// copy constructor @safe unittest { - int counter; - struct S + // Test that copy constructor works + static int counter; + static struct S { int val; - this(int val) { this.val = val; } - this(const scope ref S rhs) + this(int v) + { + val = v; + } + this(ref typeof(this) rhs) { val = rhs.val; counter++; } + this(ref typeof(this) rhs) immutable + { + val = rhs.val + 1; // just to check that we call the correct cpctor + } } S[4] arr1; @@ -115,6 +161,22 @@ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted assert(counter == 4); assert(arr1 == arr2); + + immutable S[4] arr3; + _d_arrayctor(arr3[], arr2[]); + + assert(arr3 == [S(1), S(2), S(3), S(4)]); + + S[2][2] arr4; + S[2][2] arr5 = [[S(0), S(1)], [S(2), S(3)]]; + _d_arrayctor(arr4[], arr5[]); + assert(counter == 8); + assert(arr4 == arr5); + + immutable S[2][2] arr6; + _d_arrayctor(arr6[], arr5[]); + + assert(arr6 == [[S(1), S(2)], [S(3), S(4)]]); } @safe nothrow unittest @@ -172,6 +234,85 @@ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted assert(counter == 4); } +@safe nothrow unittest +{ + // Test that throwing copy constructor works + static int counter; + bool didThrow; + + static struct Throw + { + int val; + this(int v) + { + val = v; + } + this(ref typeof(this) rhs) + { + val = rhs.val; + 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); + + didThrow = false; + counter = 0; + try + { + Throw[2][2] a; + Throw[2][2] 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; + static struct NoThrow + { + int val; + this(int v) + { + val = v; + } + this(ref typeof(this) rhs) + { + val = rhs.val; + 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; @@ -183,7 +324,8 @@ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted * 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 +void _d_arraysetctor(Tarr : T[], T, U)(scope Tarr p, scope ref U value) @trusted +if (is (immutable T == immutable U)) { pragma(inline, false); import core.internal.traits : Unqual; @@ -192,8 +334,21 @@ void _d_arraysetctor(Tarr : T[], T)(scope Tarr p, scope ref T value) @trusted size_t i; try { - for (i = 0; i < p.length; i++) - copyEmplace(value, p[i]); + foreach (i; 0 .. p.length) + { + static if (__traits(hasCopyConstructor, T)) + { + cpCtorRecurse(p[walker], value); + } + else + { + auto elem = cast(Unqual!T*)&p[walker]; + // Copy construction is defined as bit copy followed by postblit. + memcpy(elem, &value, element_size); + postblitRecurse(*elem); + } + walker++; + } } catch (Exception o) { @@ -228,19 +383,26 @@ void _d_arraysetctor(Tarr : T[], T)(scope Tarr p, scope ref T value) @trusted assert(arr == [S(1234), S(1234), S(1234), S(1234)]); } -// copy constructor @safe unittest { - int counter; - struct S + // Test that copy constructor works + static int counter; + static struct S { int val; - this(int val) { this.val = val; } - this(const scope ref S rhs) + this(int v) + { + val = v; + } + this(ref typeof(this) rhs) { val = rhs.val; counter++; } + this(ref typeof(this) rhs) immutable + { + val = rhs.val + 1; // just to check that we call the correct cpctor + } } S[4] arr; @@ -248,6 +410,21 @@ void _d_arraysetctor(Tarr : T[], T)(scope Tarr p, scope ref T value) @trusted _d_arraysetctor(arr[], s); assert(counter == arr.length); assert(arr == [S(1234), S(1234), S(1234), S(1234)]); + immutable S[4] arr2; + _d_arraysetctor(arr2[], s); + assert(arr2 == [S(1235), S(1235), S(1235), S(1235)]); + + counter = 0; + S[2] s2 = [S(1234), S(1234)]; + S[2][2] arr3; + + _d_arraysetctor(arr3[], s2); + assert(counter == arr.length); + assert(arr3 == [[S(1234), S(1234)], [S(1234), S(1234)]]); + + immutable S[2][2] arr4; + _d_arraysetctor(arr4[], s2); + assert(arr4 == [[S(1235), S(1235)], [S(1235), S(1235)]]); } @safe nothrow unittest @@ -268,8 +445,8 @@ void _d_arraysetctor(Tarr : T[], T)(scope Tarr p, scope ref T value) @trusted try { Throw[4] a; - Throw[4] b = [Throw(1), Throw(2), Throw(3), Throw(4)]; - _d_arrayctor(a[], b[]); + Throw b = Throw(1); + _d_arraysetctor(a[], b); } catch (Exception) { @@ -305,3 +482,69 @@ void _d_arraysetctor(Tarr : T[], T)(scope Tarr p, scope ref T value) @trusted assert(!didThrow); assert(counter == 4); } + +@safe nothrow unittest +{ + // Test that throwing copy constructor works + static int counter; + bool didThrow; + static struct Throw + { + int val; + this(int v) + { + val = v; + } + this(ref typeof(this) rhs) + { + val = rhs.val; + counter++; + if (counter == 2) + throw new Exception("Oh no."); + } + } + try + { + Throw[4] a; + Throw b = Throw(1); + _d_arraysetctor(a[], b); + } + catch (Exception) + { + didThrow = true; + } + assert(didThrow); + assert(counter == 2); + + + // Test that `nothrow` works + didThrow = false; + counter = 0; + static struct NoThrow + { + int val; + this(int v) + { + val = v; + } + this(ref typeof(this) rhs) + { + val = rhs.val; + 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); +}