mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 12:00:11 -05:00
Synchronizing with the upstream release of v2.108.0. D front-end changes: - Import dmd v2.108.0. D runtime changes: - Import druntime v2.108.0. Phobos changes: - Import phobos v2.108.0. gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd b65767825f. * dmd/VERSION: Bump version to v2.108.0. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime b65767825f. * src/MERGE: Merge upstream phobos 92dc5a4e9.
371 lines
9.0 KiB
D
371 lines
9.0 KiB
D
/**
|
|
The `.dup` and `.idup` properties for Associative Arrays and Dynamic Arrays
|
|
|
|
Copyright: Copyright Digital Mars 2000 - 2022.
|
|
License: Distributed under the $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
|
|
(See accompanying file LICENSE)
|
|
Source: $(DRUNTIMESRC core/internal/_array/_duplication.d)
|
|
*/
|
|
module core.internal.array.duplication;
|
|
|
|
U[] _dup(T, U)(scope T[] a) pure nothrow @trusted if (__traits(isPOD, T))
|
|
{
|
|
if (__ctfe)
|
|
return _dupCtfe!(T, U)(a);
|
|
|
|
version (D_BetterC)
|
|
{
|
|
return _dupCtfe!(T, U)(a);
|
|
}
|
|
else
|
|
{
|
|
import core.stdc.string : memcpy;
|
|
import core.internal.array.construction: _d_newarrayUPureNothrow;
|
|
auto arr = _d_newarrayUPureNothrow!U(a.length, is(U == shared));
|
|
memcpy(cast(void*) arr.ptr, cast(const(void)*) a.ptr, T.sizeof * a.length);
|
|
return arr;
|
|
}
|
|
}
|
|
|
|
U[] _dupCtfe(T, U)(scope T[] a)
|
|
{
|
|
static if (is(T : void))
|
|
assert(0, "Cannot dup a void[] array at compile time.");
|
|
else
|
|
{
|
|
U[] res;
|
|
foreach (ref e; a)
|
|
res ~= e;
|
|
return res;
|
|
}
|
|
}
|
|
|
|
U[] _dup(T, U)(T[] a) if (!__traits(isPOD, T))
|
|
{
|
|
// note: copyEmplace is `@system` inside a `@trusted` block, so the __ctfe branch
|
|
// has the extra duty to infer _dup `@system` when the copy-constructor is `@system`.
|
|
if (__ctfe)
|
|
return _dupCtfe!(T, U)(a);
|
|
|
|
version (D_BetterC)
|
|
{
|
|
return _dupCtfe!(T, U)(a);
|
|
}
|
|
else
|
|
{
|
|
import core.lifetime: copyEmplace;
|
|
import core.internal.array.construction: _d_newarrayU;
|
|
U[] res = () @trusted {
|
|
auto arr = cast(U*) _d_newarrayU!T(a.length, is(T == shared));
|
|
size_t i;
|
|
scope (failure)
|
|
{
|
|
import core.internal.lifetime: emplaceInitializer;
|
|
// Initialize all remaining elements to not destruct garbage
|
|
foreach (j; i .. a.length)
|
|
emplaceInitializer(cast() arr[j]);
|
|
}
|
|
for (; i < a.length; i++)
|
|
{
|
|
copyEmplace(a.ptr[i], arr[i]);
|
|
}
|
|
return cast(U[])(arr[0..a.length]);
|
|
} ();
|
|
|
|
return res;
|
|
}
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=22107
|
|
@safe unittest
|
|
{
|
|
static int i;
|
|
@safe struct S
|
|
{
|
|
this(this) { i++; }
|
|
}
|
|
|
|
void fun(scope S[] values...) @safe
|
|
{
|
|
values.dup;
|
|
}
|
|
}
|
|
|
|
@safe unittest
|
|
{
|
|
static struct S1 { int* p; }
|
|
static struct S2 { @disable this(); }
|
|
static struct S3 { @disable this(this); }
|
|
|
|
int dg1() pure nothrow @safe
|
|
{
|
|
{
|
|
char[] m;
|
|
string i;
|
|
m = m.dup;
|
|
i = i.idup;
|
|
m = i.dup;
|
|
i = m.idup;
|
|
}
|
|
{
|
|
S1[] m;
|
|
immutable(S1)[] i;
|
|
m = m.dup;
|
|
i = i.idup;
|
|
static assert(!is(typeof(m.idup)));
|
|
static assert(!is(typeof(i.dup)));
|
|
}
|
|
{
|
|
S3[] m;
|
|
immutable(S3)[] i;
|
|
static assert(!is(typeof(m.dup)));
|
|
static assert(!is(typeof(i.idup)));
|
|
}
|
|
{
|
|
shared(S1)[] m;
|
|
m = m.dup;
|
|
static assert(!is(typeof(m.idup)));
|
|
}
|
|
{
|
|
int[] a = (inout(int)) { inout(const(int))[] a; return a.dup; }(0);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int dg2() pure nothrow @safe
|
|
{
|
|
{
|
|
S2[] m = [S2.init, S2.init];
|
|
immutable(S2)[] i = [S2.init, S2.init];
|
|
m = m.dup;
|
|
m = i.dup;
|
|
i = m.idup;
|
|
i = i.idup;
|
|
}
|
|
return 2;
|
|
}
|
|
|
|
enum a = dg1();
|
|
enum b = dg2();
|
|
assert(dg1() == a);
|
|
assert(dg2() == b);
|
|
}
|
|
|
|
@system unittest
|
|
{
|
|
static struct Sunpure { this(this) @safe nothrow {} }
|
|
static struct Sthrow { this(this) @safe pure {} }
|
|
static struct Sunsafe { this(this) @system pure nothrow {} }
|
|
static struct Snocopy { @disable this(this); }
|
|
|
|
[].dup!Sunpure;
|
|
[].dup!Sthrow;
|
|
cast(void) [].dup!Sunsafe;
|
|
static assert(!__traits(compiles, () pure { [].dup!Sunpure; }));
|
|
static assert(!__traits(compiles, () nothrow { [].dup!Sthrow; }));
|
|
static assert(!__traits(compiles, () @safe { [].dup!Sunsafe; }));
|
|
static assert(!__traits(compiles, () { [].dup!Snocopy; }));
|
|
|
|
[].idup!Sunpure;
|
|
[].idup!Sthrow;
|
|
[].idup!Sunsafe;
|
|
static assert(!__traits(compiles, () pure { [].idup!Sunpure; }));
|
|
static assert(!__traits(compiles, () nothrow { [].idup!Sthrow; }));
|
|
static assert(!__traits(compiles, () @safe { [].idup!Sunsafe; }));
|
|
static assert(!__traits(compiles, () { [].idup!Snocopy; }));
|
|
}
|
|
|
|
@safe unittest
|
|
{
|
|
// test that the copy-constructor is called with .dup
|
|
static struct ArrElem
|
|
{
|
|
int a;
|
|
this(int a)
|
|
{
|
|
this.a = a;
|
|
}
|
|
this(ref const ArrElem)
|
|
{
|
|
a = 2;
|
|
}
|
|
this(ref ArrElem) immutable
|
|
{
|
|
a = 3;
|
|
}
|
|
}
|
|
|
|
auto arr = [ArrElem(1), ArrElem(1)];
|
|
|
|
ArrElem[] b = arr.dup;
|
|
assert(b[0].a == 2 && b[1].a == 2);
|
|
|
|
immutable ArrElem[] c = arr.idup;
|
|
assert(c[0].a == 3 && c[1].a == 3);
|
|
}
|
|
|
|
@system unittest
|
|
{
|
|
static struct Sunpure { this(ref const typeof(this)) @safe nothrow {} }
|
|
static struct Sthrow { this(ref const typeof(this)) @safe pure {} }
|
|
static struct Sunsafe { this(ref const typeof(this)) @system pure nothrow {} }
|
|
[].dup!Sunpure;
|
|
[].dup!Sthrow;
|
|
cast(void) [].dup!Sunsafe;
|
|
static assert(!__traits(compiles, () pure { [].dup!Sunpure; }));
|
|
static assert(!__traits(compiles, () nothrow { [].dup!Sthrow; }));
|
|
static assert(!__traits(compiles, () @safe { [].dup!Sunsafe; }));
|
|
|
|
// for idup to work on structs that have copy constructors, it is necessary
|
|
// that the struct defines a copy constructor that creates immutable objects
|
|
static struct ISunpure { this(ref const typeof(this)) immutable @safe nothrow {} }
|
|
static struct ISthrow { this(ref const typeof(this)) immutable @safe pure {} }
|
|
static struct ISunsafe { this(ref const typeof(this)) immutable @system pure nothrow {} }
|
|
[].idup!ISunpure;
|
|
[].idup!ISthrow;
|
|
[].idup!ISunsafe;
|
|
static assert(!__traits(compiles, () pure { [].idup!ISunpure; }));
|
|
static assert(!__traits(compiles, () nothrow { [].idup!ISthrow; }));
|
|
static assert(!__traits(compiles, () @safe { [].idup!ISunsafe; }));
|
|
}
|
|
|
|
@safe unittest
|
|
{
|
|
static int*[] pureFoo() pure { return null; }
|
|
{ char[] s; immutable x = s.dup; }
|
|
{ immutable x = (cast(int*[])null).dup; }
|
|
{ immutable x = pureFoo(); }
|
|
{ immutable x = pureFoo().dup; }
|
|
}
|
|
|
|
@safe unittest
|
|
{
|
|
auto a = [1, 2, 3];
|
|
auto b = a.dup;
|
|
debug(SENTINEL) {} else
|
|
assert(b.capacity >= 3);
|
|
}
|
|
|
|
@system unittest
|
|
{
|
|
// Bugzilla 12580
|
|
void[] m = [0];
|
|
shared(void)[] s = [cast(shared)1];
|
|
immutable(void)[] i = [cast(immutable)2];
|
|
|
|
s = s.dup;
|
|
static assert(is(typeof(s.dup) == shared(void)[]));
|
|
|
|
m = i.dup;
|
|
i = m.dup;
|
|
i = i.idup;
|
|
i = m.idup;
|
|
i = s.idup;
|
|
i = s.dup;
|
|
static assert(!__traits(compiles, m = s.dup));
|
|
}
|
|
|
|
@safe unittest
|
|
{
|
|
// Bugzilla 13809
|
|
static struct S
|
|
{
|
|
this(this) {}
|
|
~this() {}
|
|
}
|
|
|
|
S[] arr;
|
|
auto a = arr.dup;
|
|
}
|
|
|
|
@system unittest
|
|
{
|
|
// Bugzilla 16504
|
|
static struct S
|
|
{
|
|
__gshared int* gp;
|
|
int* p;
|
|
// postblit and hence .dup could escape
|
|
this(this) { gp = p; }
|
|
}
|
|
|
|
int p;
|
|
scope S[1] arr = [S(&p)];
|
|
auto a = arr.dup; // dup does escape
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=21983
|
|
// dup/idup destroys partially constructed arrays on failure
|
|
@safe unittest
|
|
{
|
|
static struct SImpl(bool postblit)
|
|
{
|
|
int num;
|
|
long l = 0xDEADBEEF;
|
|
|
|
static if (postblit)
|
|
{
|
|
this(this)
|
|
{
|
|
if (this.num == 3)
|
|
throw new Exception("");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this(scope ref const SImpl other)
|
|
{
|
|
if (other.num == 3)
|
|
throw new Exception("");
|
|
|
|
this.num = other.num;
|
|
this.l = other.l;
|
|
}
|
|
}
|
|
|
|
~this() @trusted
|
|
{
|
|
if (l != 0xDEADBEEF)
|
|
{
|
|
import core.stdc.stdio;
|
|
printf("Unexpected value: %lld\n", l);
|
|
fflush(stdout);
|
|
assert(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
alias Postblit = SImpl!true;
|
|
alias Copy = SImpl!false;
|
|
|
|
static int test(S)()
|
|
{
|
|
S[4] arr = [ S(1), S(2), S(3), S(4) ];
|
|
try
|
|
{
|
|
arr.dup();
|
|
assert(false);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static assert(test!Postblit());
|
|
assert(test!Postblit());
|
|
|
|
static assert(test!Copy());
|
|
assert(test!Copy());
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=24453
|
|
@safe unittest
|
|
{
|
|
static inout(char)[] foo(ref scope return inout(char)[] s)
|
|
{
|
|
auto bla = s.idup;
|
|
return s;
|
|
}
|
|
}
|