mirror of
https://github.com/dlang/dmd.git
synced 2025-04-26 13:10:12 +03:00
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:
parent
06fffd6925
commit
c83dff2e1b
12 changed files with 440 additions and 227 deletions
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -318,6 +318,8 @@ immutable Msgtable[] msgtable =
|
|||
{ "_aaApply2" },
|
||||
{ "_d_arrayctor" },
|
||||
{ "_d_arraysetctor" },
|
||||
{ "_d_arrayassign_l" },
|
||||
{ "_d_arrayassign_r" },
|
||||
|
||||
// For pragma's
|
||||
{ "Pinline", "inline" },
|
||||
|
|
|
@ -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
|
||||
---
|
||||
*/
|
||||
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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 \
|
||||
|
|
304
druntime/src/core/internal/array/arrayassign.d
Normal file
304
druntime/src/core/internal/array/arrayassign.d
Normal 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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue