mirror of
https://gcc.gnu.org/git/gcc.git
synced 2026-02-23 04:09:42 -05:00
D front-end changes: - Import latest fixes from dmd v2.110.0-beta.1. - Added traits `getBitfieldOffset' and `getBitfieldWidth'. - Added trait `isCOMClass' to detect if a type is a COM class. - Added `-fpreview=safer` which enables safety checking on unattributed functions. D runtime changes: - Import latest fixes from druntime v2.110.0-beta.1. Phobos changes: - Import latest fixes from phobos v2.110.0-beta.1. - Added `fromHexString' and `fromHexStringAsRange' functions to `std.digest'. gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd 82a5d2a7c4. * d-lang.cc (d_handle_option): Handle new option `-fpreview=safer'. * expr.cc (ExprVisitor::NewExp): Remove gcc_unreachable for the generation of `_d_newThrowable'. * lang.opt: Add -fpreview=safer. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime 82a5d2a7c4. * libdruntime/Makefile.am (DRUNTIME_DSOURCES): Add core/internal/gc/blkcache.d, core/internal/gc/blockmeta.d. * libdruntime/Makefile.in: Regenerate. * src/MERGE: Merge upstream phobos dbc09d823.
467 lines
9.7 KiB
D
467 lines
9.7 KiB
D
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);
|
|
}
|
|
|
|
/**
|
|
* Sets all elements of an array to a single value. Takes into account postblits,
|
|
* copy constructors and destructors. For Plain Old Data elements,`rt/memset.d`
|
|
* is used.
|
|
*
|
|
* ---
|
|
* struct S
|
|
* {
|
|
* ~this() {} // destructor, so not Plain Old Data
|
|
* }
|
|
*
|
|
* void main()
|
|
* {
|
|
* S[3] arr;
|
|
* S value;
|
|
*
|
|
* arr = value;
|
|
* // Generates:
|
|
* // _d_arraysetassign(arr[], value), arr;
|
|
* }
|
|
* ---
|
|
*
|
|
* Params:
|
|
* to = destination array
|
|
* value = the element to set
|
|
* Returns:
|
|
* `to`
|
|
*/
|
|
Tarr _d_arraysetassign(Tarr : T[], T)(return scope Tarr to, scope ref T value) @trusted
|
|
{
|
|
import core.internal.traits : Unqual;
|
|
import core.lifetime : copyEmplace;
|
|
import core.stdc.string : memcpy;
|
|
|
|
enum elemSize = T.sizeof;
|
|
void[elemSize] tmp = void;
|
|
|
|
foreach (ref dst; to)
|
|
{
|
|
memcpy(&tmp, cast(void*) &dst, elemSize);
|
|
// Use `memcpy` if `T` has a `@disable`d postblit.
|
|
static if (__traits(isCopyable, T))
|
|
copyEmplace(value, dst);
|
|
else
|
|
memcpy(cast(void*) &dst, cast(void*) &value, elemSize);
|
|
auto elem = cast(Unqual!T*) &tmp;
|
|
destroy(*elem);
|
|
}
|
|
|
|
return to;
|
|
}
|
|
|
|
// postblit and destructor
|
|
@safe unittest
|
|
{
|
|
string ops;
|
|
struct S
|
|
{
|
|
int val;
|
|
this(this) { ops ~= "="; }
|
|
~this() { ops ~= "~"; }
|
|
}
|
|
|
|
S[4] arr;
|
|
S s = S(1234);
|
|
_d_arraysetassign(arr[], s);
|
|
assert(ops == "=~=~=~=~");
|
|
assert(arr == [S(1234), S(1234), S(1234), S(1234)]);
|
|
}
|
|
|
|
// copy constructor
|
|
@safe unittest
|
|
{
|
|
string ops;
|
|
struct S
|
|
{
|
|
int val;
|
|
this(const scope ref S rhs)
|
|
{
|
|
val = rhs.val;
|
|
ops ~= "=";
|
|
}
|
|
~this() { ops ~= "~"; }
|
|
}
|
|
|
|
S[4] arr;
|
|
S s = S(1234);
|
|
_d_arraysetassign(arr[], s);
|
|
assert(ops == "=~=~=~=~");
|
|
assert(arr == [S(1234), S(1234), S(1234), S(1234)]);
|
|
}
|
|
|
|
// disabled copy constructor
|
|
@safe unittest
|
|
{
|
|
static struct S
|
|
{
|
|
int val;
|
|
@disable this(ref S);
|
|
}
|
|
S[1] arr;
|
|
S s = S(1234);
|
|
_d_arraysetassign(arr[], s);
|
|
assert(arr[0].val == 1234);
|
|
}
|
|
|
|
// throwing and `nothrow`
|
|
@safe nothrow unittest
|
|
{
|
|
// Test that throwing works
|
|
bool didThrow;
|
|
int counter;
|
|
struct Throw
|
|
{
|
|
int val;
|
|
this(this)
|
|
{
|
|
counter++;
|
|
if (counter == 2)
|
|
throw new Exception("Oh no.");
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
Throw[4] a;
|
|
Throw b = Throw(1);
|
|
_d_arraysetassign(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 b = NoThrow(1);
|
|
_d_arraysetassign(a[], b);
|
|
foreach (ref e; a)
|
|
assert(e == NoThrow(1));
|
|
}
|
|
catch (Exception)
|
|
{
|
|
didThrow = true;
|
|
}
|
|
assert(!didThrow);
|
|
// The array `a` is destroyed when the `try` block ends.
|
|
assert(counter == 4);
|
|
}
|