| /** |
| 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 : fflush, printf, stdout; |
| 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; |
| } |
| } |