Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.
/ druntime Public archive
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 30 additions & 45 deletions src/core/internal/array/appending.d
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ module core.internal.array.appending;
/// See $(REF _d_arrayappendcTX, rt,lifetime,_d_arrayappendcTX)
private extern (C) byte[] _d_arrayappendcTX(const TypeInfo ti, ref byte[] px, size_t n) @trusted pure nothrow;

private enum isCopyingNothrow(T) = __traits(compiles, (ref T rhs) nothrow { T lhs = rhs; });

/// Implementation of `_d_arrayappendcTX` and `_d_arrayappendcTXTrace`
template _d_arrayappendcTXImpl(Tarr : T[], T)
{
import core.internal.array.utils : _d_HookTraceImpl, isPostblitNoThrow;
import core.internal.array.utils : _d_HookTraceImpl;

private enum errorMessage = "Cannot append to array if compiling without support for runtime type information!";

Expand All @@ -32,7 +34,7 @@ template _d_arrayappendcTXImpl(Tarr : T[], T)
* purity, and throwabilty checks. To prevent breaking existing code, this function template
* is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations.
*/
static if (isPostblitNoThrow!T) // `nothrow` deduction doesn't work, so this is needed
static if (isCopyingNothrow!T) // `nothrow` deduction doesn't work, so this is needed
ref Tarr _d_arrayappendcTX(return scope ref Tarr px, size_t n) @trusted pure nothrow
{
pragma(inline, false);
Expand Down Expand Up @@ -77,7 +79,7 @@ template _d_arrayappendcTXImpl(Tarr : T[], T)
/// Implementation of `_d_arrayappendT` and `_d_arrayappendTTrace`
template _d_arrayappendTImpl(Tarr : T[], T)
{
import core.internal.array.utils : _d_HookTraceImpl, isPostblitNoThrow;
import core.internal.array.utils : _d_HookTraceImpl;

private enum errorMessage = "Cannot append to array if compiling without support for runtime type information!";

Expand All @@ -93,7 +95,7 @@ template _d_arrayappendTImpl(Tarr : T[], T)
* purity, and throwabilty checks. To prevent breaking existing code, this function template
* is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations.
*/
static if (isPostblitNoThrow!T)
static if (isCopyingNothrow!T)
ref Tarr _d_arrayappendT(return scope ref Tarr x, scope Tarr y) @trusted pure nothrow
{
pragma(inline, false);
Expand All @@ -110,18 +112,25 @@ template _d_arrayappendTImpl(Tarr : T[], T)

private enum _d_arrayappendTBody = q{
import core.stdc.string : memcpy;
import core.internal.traits : Unqual;
import core.internal.traits : hasElaborateCopyConstructor, Unqual;
import core.lifetime : copyEmplace;

auto length = x.length;
auto sizeelem = T.sizeof;

_d_arrayappendcTXImpl!Tarr._d_arrayappendcTX(x, y.length);

if (y.length)
memcpy(cast(Unqual!T *)&x[length], cast(Unqual!T *)&y[0], y.length * sizeelem);
static if (hasElaborateCopyConstructor!T)
{
foreach (i; 0 .. y.length)
copyEmplace(y[i], x[length + i]);
}
else
{
// blit all elements at once
if (y.length)
memcpy(cast(Unqual!T *)&x[length], cast(Unqual!T *)&y[0], y.length * T.sizeof);
}

// do postblit
__doPostblit(cast(Unqual!Tarr)x[length .. length + y.length]);
return x;
};

Expand All @@ -135,30 +144,6 @@ template _d_arrayappendTImpl(Tarr : T[], T)
alias _d_arrayappendTTrace = _d_HookTraceImpl!(Tarr, _d_arrayappendT, errorMessage);
}

/**
* Run postblit on `t` if it is a struct and needs it.
* Or if `t` is a array, run it on the children if they have a postblit.
*/
private void __doPostblit(T)(auto ref T t) @trusted pure
{
import core.internal.traits : hasElaborateCopyConstructor;

static if (is(T == struct))
{
// run the postblit function incase the struct has one
static if (__traits(hasMember, T, "__xpostblit") &&
// Bugzilla 14746: Check that it's the exact member of S.
__traits(isSame, T, __traits(parent, t.__xpostblit)))
t.__xpostblit();
}
else static if (is(T U : U[]) && hasElaborateCopyConstructor!U)
{
// only do a postblit if the `U` requires it.
foreach (ref el; t)
__doPostblit(el);
}
}

@safe unittest
{
double[] arr1;
Expand All @@ -184,16 +169,16 @@ private void __doPostblit(T)(auto ref T t) @trusted pure
arr1_org ~= arr2;
_d_arrayappendTImpl!(typeof(arr1))._d_arrayappendT(arr1, arr2);

// postblit should have triggered on atleast the items in arr2
// postblit should have triggered on at least the items in arr2
assert(blitted >= arr2.length);
}

@safe unittest
@safe nothrow unittest
{
int blitted;
struct Item
{
this(this)
this(this) nothrow
{
blitted++;
}
Expand All @@ -204,30 +189,30 @@ private void __doPostblit(T)(auto ref T t) @trusted pure

_d_arrayappendTImpl!(typeof(arr1))._d_arrayappendT(arr1, arr2);

// no postblit should have happend because arr{1,2} contains dynamic arrays
// no postblit should have happened because arr{1,2} contain dynamic arrays
assert(blitted == 0);
}

@safe unittest
@safe nothrow unittest
{
int blitted;
int copied;
struct Item
{
this(this)
this(const scope ref Item) nothrow
{
blitted++;
copied++;
}
}

Item[1][] arr1 = [[Item()]];
Item[1][] arr2 = [[Item()]];

_d_arrayappendTImpl!(typeof(arr1))._d_arrayappendT(arr1, arr2);
// postblit should have happend because arr{1,2} contains static arrays
assert(blitted >= arr2.length);
// copy constructor should have been invoked because arr{1,2} contain static arrays
assert(copied >= arr2.length);
}

@safe unittest
@safe nothrow unittest
{
string str;
_d_arrayappendTImpl!(typeof(str))._d_arrayappendT(str, "a");
Expand Down
109 changes: 75 additions & 34 deletions src/core/internal/array/construction.d
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ module core.internal.array.construction;
Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted
{
pragma(inline, false);
import core.internal.postblit : postblitRecurse;
import core.internal.traits : Unqual;
import core.internal.traits : hasElaborateCopyConstructor, Unqual;
import core.lifetime : copyEmplace;
import core.stdc.string : memcpy;
debug(PRINTF) import core.stdc.stdio;

Expand All @@ -46,33 +46,37 @@ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted
void[] vTo = (cast(void*)to.ptr)[0..to.length];
enforceRawArraysConformable("initialization", element_size, vFrom, vTo, false);

size_t i;
try
static if (hasElaborateCopyConstructor!T)
{
for (i = 0; i < to.length; i++)
size_t i;
try
{
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);
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--)
catch (Exception o)
{
auto elem = cast(Unqual!T*)&to[i];
destroy(*elem);
}
/* Destroy, in reverse order, what we've constructed so far
*/
while (i--)
{
auto elem = cast(Unqual!T*)&to[i];
destroy(*elem);
}

throw o;
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;
Expand All @@ -90,6 +94,29 @@ 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
{
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
Expand Down Expand Up @@ -159,37 +186,29 @@ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted
void _d_arraysetctor(Tarr : T[], T)(scope Tarr p, scope ref T value) @trusted
{
pragma(inline, false);
import core.internal.postblit : postblitRecurse;
import core.stdc.string : memcpy;
import core.internal.traits : Unqual;
size_t walker;
auto element_size = T.sizeof;
import core.lifetime : copyEmplace;

size_t i;
try
{
foreach (i; 0 .. p.length)
{
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++;
}
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 (walker > 0)
while (i--)
{
walker--;
auto elem = cast(Unqual!T*)&p[walker];
auto elem = cast(Unqual!T*)&p[i];
destroy(*elem);
}

throw o;
}
}

// postblit
@safe unittest
{
int counter;
Expand All @@ -209,6 +228,28 @@ 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
{
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
Expand Down
Loading