Translate _d_arrayassign{,_l,_r} to templates (#14310)

- Implement template `_d_arrayassign{_l,_r}`
- Lower array asignment expressions to the above templates
- Remove old lowering from e2ir.d
- Remove the old `_d_arrayassign{,_l,_r}` hooks
- Merge the usage of `_d_arrayassign` with that of `_d_arrayassign_l`

Signed-off-by: Teodor Dutu <teodor.dutu@gmail.com>
This commit is contained in:
Teodor Dutu 2022-08-02 08:11:40 +00:00 committed by GitHub
parent 06fffd6925
commit c83dff2e1b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 440 additions and 227 deletions

View file

@ -114,8 +114,10 @@ extern (C++) /* CT */ BE canThrow(Expression e, FuncDeclaration func, bool mustN
import dmd.id : Id;
auto sd = ts.sym;
const id = ce.f.ident;
if (sd.postblit &&
(ce.f.ident == Id._d_arrayctor || ce.f.ident == Id._d_arraysetctor))
(id == Id._d_arrayctor || id == Id._d_arraysetctor ||
id == Id._d_arrayassign_l || id == Id._d_arrayassign_r))
{
checkFuncThrows(ce, sd.postblit);
return;

View file

@ -4778,6 +4778,12 @@ public:
// If `_d_HookTraceImpl` is found, resolve the underlying hook and replace `e` and `fd` with it.
removeHookTraceImpl(e, fd);
bool isArrayConstructionOrAssign(FuncDeclaration fd)
{
return fd.ident == Id._d_arrayctor || fd.ident == Id._d_arraysetctor ||
fd.ident == Id._d_arrayassign_l || fd.ident == Id._d_arrayassign_r;
}
if (fd.ident == Id.__ArrayPostblit || fd.ident == Id.__ArrayDtor)
{
assert(e.arguments.dim == 1);
@ -4831,27 +4837,36 @@ public:
result = interpretRegion(ae, istate);
return;
}
else if (fd.ident == Id._d_arrayctor || fd.ident == Id._d_arraysetctor)
else if (isArrayConstructionOrAssign(fd))
{
// In expressionsem.d `T[x] ea = eb;` was lowered to `_d_array{,set}ctor(ea[], eb[]);`.
// The following code will rewrite it back to `ea = eb` and then interpret that expression.
if (fd.ident == Id._d_arraysetctor)
assert(e.arguments.dim == 2);
else
// In expressionsem.d, the following lowerings were performed:
// * `T[x] ea = eb;` to `_d_array{,set}ctor(ea[], eb[]);`.
// * `ea = eb` (ea and eb are arrays) to `_d_arrayassign_{l,r}(ea[], eb[])`.
// The following code will rewrite them back to `ea = eb` and
// then interpret that expression.
if (fd.ident == Id._d_arrayctor)
assert(e.arguments.dim == 3);
else
assert(e.arguments.dim == 2);
Expression ea = (*e.arguments)[0];
if (ea.isCastExp)
ea = ea.isCastExp.e1;
Expression eb = (*e.arguments)[1];
if (eb.isCastExp && fd.ident == Id._d_arrayctor)
if (eb.isCastExp() && fd.ident != Id._d_arraysetctor)
eb = eb.isCastExp.e1;
ConstructExp ce = new ConstructExp(e.loc, ea, eb);
ce.type = ea.type;
Expression rewrittenExp;
if (fd.ident == Id._d_arrayctor || fd.ident == Id._d_arraysetctor)
rewrittenExp = new ConstructExp(e.loc, ea, eb);
else
rewrittenExp = new AssignExp(e.loc, ea, eb);
rewrittenExp.type = ea.type;
result = interpret(rewrittenExp, istate);
result = interpret(ce, istate);
return;
}
else if (fd.ident == Id._d_arrayappendT || fd.ident == Id._d_arrayappendTTrace)

View file

@ -2312,22 +2312,7 @@ elem* toElem(Expression e, IRState *irs)
else if ((postblit || destructor) &&
ae.op != EXP.blit &&
ae.op != EXP.construct)
{
/* Generate:
* _d_arrayassign(ti, efrom, eto)
*/
el_free(esize);
elem *eti = getTypeInfo(ae.e1, t1.nextOf().toBasetype(), irs);
if (irs.target.os == Target.OS.Windows && irs.target.is64bit)
{
eto = addressElem(eto, Type.tvoid.arrayOf());
efrom = addressElem(efrom, Type.tvoid.arrayOf());
}
elem *ep = el_params(eto, efrom, eti, null);
auto rtl = RTLSYM.ARRAYASSIGN;
elem* e = el_bin(OPcall, totym(ae.type), el_var(getRtlsym(rtl)), ep);
return setResult(e);
}
assert(0, "Trying to reference `_d_arrayassign`, this should not happen!");
else
{
// Generate:
@ -2636,27 +2621,10 @@ elem* toElem(Expression e, IRState *irs)
}
else
{
e1 = sarray_toDarray(ae.e1.loc, ae.e1.type, null, e1);
e2 = sarray_toDarray(ae.e2.loc, ae.e2.type, null, e2);
Symbol *stmp = symbol_genauto(Type_toCtype(t1b.nextOf()));
elem *etmp = el_una(OPaddr, TYnptr, el_var(stmp));
/* Generate:
* _d_arrayassign_l(ti, e2, e1, etmp)
* or:
* _d_arrayassign_r(ti, e2, e1, etmp)
*/
elem *eti = getTypeInfo(ae.e1, t1b.nextOf().toBasetype(), irs);
if (irs.target.os == Target.OS.Windows && irs.target.is64bit)
{
e1 = addressElem(e1, Type.tvoid.arrayOf());
e2 = addressElem(e2, Type.tvoid.arrayOf());
}
elem *ep = el_params(etmp, e1, e2, eti, null);
const rtl = lvalueElem ? RTLSYM.ARRAYASSIGN_L : RTLSYM.ARRAYASSIGN_R;
elem* e = el_bin(OPcall, TYdarray, el_var(getRtlsym(rtl)), ep);
return setResult2(e);
if (ae.e2.isLvalue)
assert(0, "Trying to reference `_d_arrayassign_l`, this should not happen!");
else
assert(0, "Trying to reference `_d_arrayassign_r`, this should not happen!");
}
}
else

View file

@ -9976,10 +9976,90 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
message("lowered %s =>\n %s", exp.toChars(), res.toChars());
}
}
else if (auto ae = res.isAssignExp())
res = lowerArrayAssign(ae);
else if (auto ce = res.isCommaExp())
{
if (auto ae1 = ce.e1.isAssignExp())
ce.e1 = lowerArrayAssign(ae1, true);
if (auto ae2 = ce.e2.isAssignExp())
ce.e2 = lowerArrayAssign(ae2, true);
}
return setResult(res);
}
/***************************************
* Lower AssignExp to `_d_arrayassign_{l,r}` if needed.
*
* Params:
* ae = the AssignExp to be lowered
* fromCommaExp = indicates whether `ae` is part of a CommaExp or not,
* so no unnecessary temporay variable is created.
* Returns:
* a CommaExp contiaining call a to `_d_arrayassign_{l,r}` if needed or
* `ae` otherwise
*/
private Expression lowerArrayAssign(AssignExp ae, bool fromCommaExp = false)
{
Type t1b = ae.e1.type.toBasetype();
if (t1b.ty != Tsarray && t1b.ty != Tarray)
return ae;
const isArrayAssign =
(ae.e1.isSliceExp || ae.e1.type.ty == Tsarray) &&
(ae.e2.type.ty == Tsarray || ae.e2.type.ty == Tarray) &&
(ae.e1.type.nextOf && ae.e2.type.nextOf && ae.e1.type.nextOf.mutableOf.equals(ae.e2.type.nextOf.mutableOf));
if (!isArrayAssign)
return ae;
const ts = t1b.nextOf().baseElemOf().isTypeStruct();
if (!ts || (!ts.sym.postblit && !ts.sym.dtor))
return ae;
Expression res;
auto func = ae.e2.isLvalue || ae.e2.isSliceExp ? Id._d_arrayassign_l : Id._d_arrayassign_r;
// Lower to `.object._d_arrayassign_l{r}(e1, e2)``
Expression id = new IdentifierExp(ae.loc, Id.empty);
id = new DotIdExp(ae.loc, id, Id.object);
id = new DotIdExp(ae.loc, id, func);
auto arguments = new Expressions();
arguments.push(new CastExp(ae.loc, ae.e1, ae.e1.type.nextOf.arrayOf)
.expressionSemantic(sc));
Expression eValue2, value2 = ae.e2;
if (ae.e2.isLvalue)
value2 = new CastExp(ae.loc, ae.e2, ae.e2.type.nextOf.arrayOf)
.expressionSemantic(sc);
else if (!fromCommaExp)
{
// Rvalues from CommaExps were introduced in `visit(AssignExp)`
// and are temporary variables themselves. Rvalues from trivial
// SliceExps are simply passed by reference without any copying.
// `__assigntmp` will be destroyed together with the array `ae.e1`.
// When `ae.e2` is a variadic arg array, it is also `scope`, so
// `__assigntmp` may also be scope.
auto vd = copyToTemp(STC.rvalue | STC.nodtor | STC.maybescope,
"__assigntmp", ae.e2);
eValue2 = new DeclarationExp(vd.loc, vd).expressionSemantic(sc);
value2 = new VarExp(vd.loc, vd).expressionSemantic(sc);
}
arguments.push(value2);
Expression ce = new CallExp(ae.loc, id, arguments);
res = Expression.combine(eValue2, ce).expressionSemantic(sc);
res = Expression.combine(res, ae.e1).expressionSemantic(sc);
if (global.params.verbose)
message("lowered %s =>\n %s", ae.toChars(), res.toChars());
return res;
}
override void visit(PowAssignExp exp)
{
if (exp.type)

View file

@ -8366,6 +8366,8 @@ struct Id final
static Identifier* _aaApply2;
static Identifier* _d_arrayctor;
static Identifier* _d_arraysetctor;
static Identifier* _d_arrayassign_l;
static Identifier* _d_arrayassign_r;
static Identifier* Pinline;
static Identifier* lib;
static Identifier* linkerDirective;

View file

@ -318,6 +318,8 @@ immutable Msgtable[] msgtable =
{ "_aaApply2" },
{ "_d_arrayctor" },
{ "_d_arraysetctor" },
{ "_d_arrayassign_l" },
{ "_d_arrayassign_r" },
// For pragma's
{ "Pinline", "inline" },

View file

@ -1,26 +1,27 @@
/*
TEST_OUTPUT:
---
fail_compilation/fail10968.d(41): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit`
fail_compilation/fail10968.d(41): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
fail_compilation/fail10968.d(29): `fail10968.SA.__postblit` is declared here
fail_compilation/fail10968.d(42): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit`
fail_compilation/fail10968.d(42): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
fail_compilation/fail10968.d(29): `fail10968.SA.__postblit` is declared here
fail_compilation/fail10968.d(30): `fail10968.SA.__postblit` is declared here
fail_compilation/fail10968.d(43): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit`
fail_compilation/fail10968.d(43): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
fail_compilation/fail10968.d(29): `fail10968.SA.__postblit` is declared here
fail_compilation/fail10968.d(46): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit`
fail_compilation/fail10968.d(46): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
fail_compilation/fail10968.d(29): `fail10968.SA.__postblit` is declared here
fail_compilation/fail10968.d(30): `fail10968.SA.__postblit` is declared here
fail_compilation/fail10968.d(44): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit`
fail_compilation/fail10968.d(44): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
fail_compilation/fail10968.d(30): `fail10968.SA.__postblit` is declared here
fail_compilation/fail10968.d(44): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.arrayassign._d_arrayassign_l!(SA[], SA)._d_arrayassign_l`
fail_compilation/fail10968.d(47): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit`
fail_compilation/fail10968.d(47): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
fail_compilation/fail10968.d(29): `fail10968.SA.__postblit` is declared here
fail_compilation/fail10968.d(47): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.construction._d_arraysetctor!(SA[], SA)._d_arraysetctor`
fail_compilation/fail10968.d(30): `fail10968.SA.__postblit` is declared here
fail_compilation/fail10968.d(48): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit`
fail_compilation/fail10968.d(48): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
fail_compilation/fail10968.d(29): `fail10968.SA.__postblit` is declared here
fail_compilation/fail10968.d(48): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.construction._d_arrayctor!(SA[], SA)._d_arrayctor`
fail_compilation/fail10968.d(30): `fail10968.SA.__postblit` is declared here
fail_compilation/fail10968.d(48): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.construction._d_arraysetctor!(SA[], SA)._d_arraysetctor`
fail_compilation/fail10968.d(49): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit`
fail_compilation/fail10968.d(49): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
fail_compilation/fail10968.d(30): `fail10968.SA.__postblit` is declared here
fail_compilation/fail10968.d(49): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.construction._d_arrayctor!(SA[], SA)._d_arrayctor`
---
*/
@ -51,12 +52,12 @@ void bar() pure @safe
/*
TEST_OUTPUT:
---
fail_compilation/fail10968.d(74): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit
fail_compilation/fail10968.d(75): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit
fail_compilation/fail10968.d(76): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit
fail_compilation/fail10968.d(79): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit
fail_compilation/fail10968.d(77): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit
fail_compilation/fail10968.d(80): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit
fail_compilation/fail10968.d(81): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit
fail_compilation/fail10968.d(82): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit
---
*/

View file

@ -46,6 +46,7 @@ COPY=\
$(IMPDIR)\core\internal\lifetime.d \
\
$(IMPDIR)\core\internal\array\appending.d \
$(IMPDIR)\core\internal\array\arrayassign.d \
$(IMPDIR)\core\internal\array\comparison.d \
$(IMPDIR)\core\internal\array\construction.d \
$(IMPDIR)\core\internal\array\equality.d \

View file

@ -43,6 +43,7 @@ SRCS=\
src\core\internal\lifetime.d \
\
src\core\internal\array\appending.d \
src\core\internal\array\arrayassign.d \
src\core\internal\array\comparison.d \
src\core\internal\array\construction.d \
src\core\internal\array\equality.d \

View file

@ -0,0 +1,304 @@
module core.internal.array.arrayassign;
// Force `enforceRawArraysConformable` to remain `pure` `@nogc`
private void enforceRawArraysConformable(const char[] action, const size_t elementSize,
const void[] a1, const void[] a2, const bool allowOverlap) @trusted @nogc pure nothrow
{
import core.internal.util.array : enforceRawArraysConformable;
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)&enforceRawArraysConformable)(action, elementSize, a1, a2, allowOverlap);
}
private template CopyElem(string CopyAction)
{
const char[] CopyElem = "{\n" ~ q{
memcpy(&tmp, cast(void*) &dst, elemSize);
} ~ CopyAction ~ q{
auto elem = cast(Unqual!T*) &tmp;
destroy(*elem);
} ~ "}\n";
}
private template CopyArray(bool CanOverlap, string CopyAction)
{
const char[] CopyArray = CanOverlap ? q{
if (vFrom.ptr < vTo.ptr && vTo.ptr < vFrom.ptr + elemSize * vFrom.length)
foreach_reverse (i, ref dst; to)
} ~ CopyElem!(CopyAction) ~ q{
else
foreach (i, ref dst; to)
} ~ CopyElem!(CopyAction)
: q{
foreach (i, ref dst; to)
} ~ CopyElem!(CopyAction);
}
private template ArrayAssign(string CopyLogic, string AllowOverLap)
{
const char[] ArrayAssign = q{
import core.internal.traits : hasElaborateCopyConstructor, Unqual;
import core.lifetime : copyEmplace;
import core.stdc.string : memcpy;
void[] vFrom = (cast(void*) from.ptr)[0 .. from.length];
void[] vTo = (cast(void*) to.ptr)[0 .. to.length];
enum elemSize = T.sizeof;
enforceRawArraysConformable("copy", elemSize, vFrom, vTo, } ~ AllowOverLap ~ q{);
void[elemSize] tmp = void;
} ~ CopyLogic ~ q{
return to;
};
}
/**
* Does array assignment (not construction) from another array of the same
* element type. Handles overlapping copies. Assumes the right hand side is an
* lvalue,
*
* Used for static array assignment with non-POD element types:
* ---
* struct S
* {
* ~this() {} // destructor, so not Plain Old Data
* }
*
* void main()
* {
* S[3] arr;
* S[3] lvalue;
*
* arr = lvalue;
* // Generates:
* // _d_arrayassign_l(arr[], lvalue[]), arr;
* }
* ---
*
* Params:
* to = destination array
* from = source array
* Returns:
* `to`
*/
Tarr _d_arrayassign_l(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted
{
mixin(ArrayAssign!(q{
static if (hasElaborateCopyConstructor!T)
} ~ CopyArray!(true, "copyEmplace(from[i], dst);") ~ q{
else
} ~ CopyArray!(true, "memcpy(cast(void*) &dst, cast(void*) &from[i], elemSize);"),
"true"));
}
@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_arrayassign_l(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_arrayassign_l(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_arrayassign_l(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_arrayassign_l(a[], b[]);
}
catch (Exception)
{
didThrow = false;
}
assert(!didThrow);
assert(counter == 4);
}
/**
* Does array assignment (not construction) from another array of the same
* element type. Does not support overlapping copies. Assumes the right hand
* side is an rvalue,
*
* Used for static array assignment with non-POD element types:
* ---
* struct S
* {
* ~this() {} // destructor, so not Plain Old Data
* }
*
* void main()
* {
* S[3] arr;
* S[3] getRvalue() {return lvalue;}
*
* arr = getRvalue();
* // Generates:
* // (__appendtmp = getRvalue), _d_arrayassign_l(arr[], __appendtmp), arr;
* }
* ---
*
* Params:
* to = destination array
* from = source array
* Returns:
* `to`
*/
Tarr _d_arrayassign_r(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted
{
mixin(ArrayAssign!(
CopyArray!(false, "memcpy(cast(void*) &dst, cast(void*) &from[i], elemSize);"),
"false"));
}
@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_arrayassign_r(arr1[], arr2[]);
assert(counter == 0);
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_arrayassign_r(arr1[], arr2[]);
assert(counter == 0);
assert(arr1 == arr2);
}
@safe nothrow unittest
{
// Test that `nothrow` works
bool didThrow = false;
int 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_arrayassign_r(a[], b[]);
}
catch (Exception)
{
didThrow = false;
}
assert(!didThrow);
assert(counter == 0);
}

View file

@ -4603,6 +4603,8 @@ public import core.internal.array.casting: __ArrayCast;
public import core.internal.array.concatenation : _d_arraycatnTXImpl;
public import core.internal.array.construction : _d_arrayctor;
public import core.internal.array.construction : _d_arraysetctor;
public import core.internal.array.arrayassign : _d_arrayassign_l;
public import core.internal.array.arrayassign : _d_arrayassign_r;
public import core.internal.array.capacity: _d_arraysetlengthTImpl;
public import core.internal.dassert: _d_assert_fail;

View file

@ -19,171 +19,6 @@ private
debug(PRINTF) import core.stdc.stdio;
}
/*
* Superseded array assignment hook. Does not take into account destructors:
* https://issues.dlang.org/show_bug.cgi?id=13661
* Kept for backward binary compatibility. This function can be removed in the future.
*/
extern (C) void[] _d_arrayassign(TypeInfo ti, void[] from, void[] to)
{
debug(PRINTF) printf("_d_arrayassign(from = %p,%d, to = %p,%d) size = %d\n", from.ptr, from.length, to.ptr, to.length, ti.tsize);
immutable elementSize = ti.tsize;
// Need a temporary buffer tmp[] big enough to hold one element
void[16] buf = void;
void* ptmp = (elementSize > buf.sizeof) ? malloc(elementSize) : buf.ptr;
scope (exit)
{
if (ptmp != buf.ptr)
free(ptmp);
}
return _d_arrayassign_l(ti, from, to, ptmp);
}
/**
Does array assignment (not construction) from another array of the same
element type.
Handles overlapping copies.
The `_d_arrayassign_l` variant assumes the right hand side is an lvalue,
while `_d_arrayassign_r` assumes it's an rvalue, which means it doesn't have to call copy constructors.
Used for static array assignment with non-POD element types:
---
struct S
{
~this() {} // destructor, so not Plain Old Data
}
void main()
{
S[3] arr;
S[3] lvalue;
arr = lvalue;
// Generates:
// S _tmp;
// _d_arrayassign_l(typeid(S), (cast(void*) lvalue.ptr)[0..lvalue.length], (cast(void*) arr.ptr)[0..arr.length], &_tmp);
S[3] getRvalue() {return lvalue;}
arr = getRvalue();
// Similar, but `_d_arrayassign_r`
}
---
Params:
ti = `TypeInfo` of the array element type.
dst = target memory. Its `.length` is equal to the element count, not byte length.
src = source memory. Its `.length` is equal to the element count, not byte length.
ptmp = Temporary memory for element swapping, must have capacity of `ti.tsize` bytes.
Returns: `dst`
*/
extern (C) void[] _d_arrayassign_l(TypeInfo ti, void[] src, void[] dst, void* ptmp)
{
debug(PRINTF) printf("_d_arrayassign_l(src = %p,%d, dst = %p,%d) size = %d\n", src.ptr, src.length, dst.ptr, dst.length, ti.tsize);
immutable elementSize = ti.tsize;
enforceRawArraysConformable("copy", elementSize, src, dst, true);
if (src.ptr < dst.ptr && dst.ptr < src.ptr + elementSize * src.length)
{
// If dst is in the middle of src memory, use reverse order.
for (auto i = dst.length; i--; )
{
void* pdst = dst.ptr + i * elementSize;
void* psrc = src.ptr + i * elementSize;
memcpy(ptmp, pdst, elementSize);
memcpy(pdst, psrc, elementSize);
ti.postblit(pdst);
ti.destroy(ptmp);
}
}
else
{
// Otherwise, use normal order.
foreach (i; 0 .. dst.length)
{
void* pdst = dst.ptr + i * elementSize;
void* psrc = src.ptr + i * elementSize;
memcpy(ptmp, pdst, elementSize);
memcpy(pdst, psrc, elementSize);
ti.postblit(pdst);
ti.destroy(ptmp);
}
}
return dst;
}
unittest // Bugzilla 14024
{
string op;
struct S
{
char x = 'x';
this(this) { op ~= x-0x20; } // upper case
~this() { op ~= x; } // lower case
}
S[4] mem;
ref S[2] slice(int a, int b) { return mem[a .. b][0 .. 2]; }
op = null;
mem[0].x = 'a';
mem[1].x = 'b';
mem[2].x = 'x';
mem[3].x = 'y';
slice(0, 2) = slice(2, 4); // [ab] = [xy]
assert(op == "XaYb", op);
op = null;
mem[0].x = 'x';
mem[1].x = 'y';
mem[2].x = 'a';
mem[3].x = 'b';
slice(2, 4) = slice(0, 2); // [ab] = [xy]
assert(op == "XaYb", op);
op = null;
mem[0].x = 'a';
mem[1].x = 'b';
mem[2].x = 'c';
slice(0, 2) = slice(1, 3); // [ab] = [bc]
assert(op == "BaCb", op);
op = null;
mem[0].x = 'x';
mem[1].x = 'y';
mem[2].x = 'z';
slice(1, 3) = slice(0, 2); // [yz] = [xy]
assert(op == "YzXy", op);
}
/// ditto
extern (C) void[] _d_arrayassign_r(TypeInfo ti, void[] src, void[] dst, void* ptmp)
{
debug(PRINTF) printf("_d_arrayassign_r(src = %p,%d, dst = %p,%d) size = %d\n", src.ptr, src.length, dst.ptr, dst.length, ti.tsize);
immutable elementSize = ti.tsize;
enforceRawArraysConformable("copy", elementSize, src, dst, false);
// Always use normal order, because we can assume that
// the rvalue src has no overlapping with dst.
foreach (i; 0 .. dst.length)
{
void* pdst = dst.ptr + i * elementSize;
void* psrc = src.ptr + i * elementSize;
memcpy(ptmp, pdst, elementSize);
memcpy(pdst, psrc, elementSize);
ti.destroy(ptmp);
}
return dst;
}
/**
Set all elements of an array to a single value.