From 91119115ece08ed2b649f5bbd4c29427028fac8f Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Sat, 1 Aug 2020 20:24:56 +0300 Subject: [PATCH 1/3] Make _d_arrayctor and _d_arraysetctor aware of copy constructor --- src/core/internal/array/construction.d | 179 +++++++++++++++++++++++-- 1 file changed, 169 insertions(+), 10 deletions(-) diff --git a/src/core/internal/array/construction.d b/src/core/internal/array/construction.d index b58ed51557..aec87895ee 100644 --- a/src/core/internal/array/construction.d +++ b/src/core/internal/array/construction.d @@ -51,8 +51,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)) + { + to[i].__ctor(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,15 +103,18 @@ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted assert(arr1 == arr2); } -// copy constructor @safe unittest { + // Test that copy constructor works int counter; 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++; @@ -172,6 +184,71 @@ 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 + int counter; + bool didThrow; + + 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); + + + // Test that `nothrow` works + didThrow = false; + counter = 0; + 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; @@ -192,8 +269,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)) + { + p[walker].__ctor(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,15 +318,18 @@ 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 { + // Test that copy constructor works int counter; 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++; @@ -305,3 +398,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 + int counter; + bool didThrow; + 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[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(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); +} From 2145c0bfc78ef773ac765f64d97a1da1a0283da2 Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Mon, 3 Aug 2020 22:37:32 +0300 Subject: [PATCH 2/3] Copy ctor can be built from a differently qualified type --- src/core/internal/array/construction.d | 42 ++++++++++++++++++-------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/core/internal/array/construction.d b/src/core/internal/array/construction.d index aec87895ee..f9a10a1e4c 100644 --- a/src/core/internal/array/construction.d +++ b/src/core/internal/array/construction.d @@ -21,7 +21,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; @@ -106,8 +107,8 @@ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted @safe unittest { // Test that copy constructor works - int counter; - struct S + static int counter; + static struct S { int val; this(int v) @@ -119,6 +120,10 @@ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted 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; @@ -127,6 +132,11 @@ 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)]); } @safe nothrow unittest @@ -187,10 +197,10 @@ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted @safe nothrow unittest { // Test that throwing copy constructor works - int counter; + static int counter; bool didThrow; - struct Throw + static struct Throw { int val; this(int v) @@ -222,7 +232,7 @@ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted // Test that `nothrow` works didThrow = false; counter = 0; - struct NoThrow + static struct NoThrow { int val; this(int v) @@ -260,7 +270,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; @@ -321,8 +332,8 @@ void _d_arraysetctor(Tarr : T[], T)(scope Tarr p, scope ref T value) @trusted @safe unittest { // Test that copy constructor works - int counter; - struct S + static int counter; + static struct S { int val; this(int v) @@ -334,6 +345,10 @@ void _d_arraysetctor(Tarr : T[], T)(scope Tarr p, scope ref T value) @trusted 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; @@ -341,6 +356,9 @@ 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)]); } @safe nothrow unittest @@ -402,9 +420,9 @@ void _d_arraysetctor(Tarr : T[], T)(scope Tarr p, scope ref T value) @trusted @safe nothrow unittest { // Test that throwing copy constructor works - int counter; + static int counter; bool didThrow; - struct Throw + static struct Throw { int val; this(int v) @@ -436,7 +454,7 @@ void _d_arraysetctor(Tarr : T[], T)(scope Tarr p, scope ref T value) @trusted // Test that `nothrow` works didThrow = false; counter = 0; - struct NoThrow + static struct NoThrow { int val; this(int v) From e8e4be68550b130a5c158b323ff835ab705855d2 Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Thu, 20 Aug 2020 18:34:04 +0300 Subject: [PATCH 3/3] Add support for static array of structs --- src/core/internal/array/construction.d | 78 ++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 6 deletions(-) diff --git a/src/core/internal/array/construction.d b/src/core/internal/array/construction.d index f9a10a1e4c..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: @@ -54,7 +83,7 @@ if (is (immutable T == immutable U)) { static if (__traits(hasCopyConstructor, T)) { - to[i].__ctor(from[i]); + cpCtorRecurse(to[i], from[i]); } else { @@ -137,6 +166,17 @@ if (is (immutable T == immutable U)) _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 @@ -228,6 +268,20 @@ if (is (immutable T == immutable U)) 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; @@ -284,7 +338,7 @@ if (is (immutable T == immutable U)) { static if (__traits(hasCopyConstructor, T)) { - p[walker].__ctor(value); + cpCtorRecurse(p[walker], value); } else { @@ -359,6 +413,18 @@ if (is (immutable T == immutable U)) 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 @@ -379,8 +445,8 @@ if (is (immutable T == immutable U)) 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) { @@ -440,8 +506,8 @@ if (is (immutable T == immutable U)) 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) {