module core.lifetime; import core.internal.attributes : betterC; // emplace /** Given a pointer `chunk` to uninitialized memory (but already typed as `T`), constructs an object of non-`class` type `T` at that address. If `T` is a class, initializes the class reference to null. Returns: A pointer to the newly constructed object (which is the same as `chunk`). */ T* emplace(T)(T* chunk) @safe pure nothrow { import core.internal.lifetime : emplaceRef; emplaceRef!T(*chunk); return chunk; } /// @betterC @system unittest { static struct S { int i = 42; } S[2] s2 = void; emplace(&s2); assert(s2[0].i == 42 && s2[1].i == 42); } /// @system unittest { interface I {} class K : I {} K k = void; emplace(&k); assert(k is null); I i = void; emplace(&i); assert(i is null); } /** Given a pointer `chunk` to uninitialized memory (but already typed as a non-class type `T`), constructs an object of type `T` at that address from arguments `args`. If `T` is a class, initializes the class reference to `args[0]`. This function can be `@trusted` if the corresponding constructor of `T` is `@safe`. Returns: A pointer to the newly constructed object (which is the same as `chunk`). */ T* emplace(T, Args...)(T* chunk, auto ref Args args) if (is(T == struct) || Args.length == 1) { import core.internal.lifetime : emplaceRef; emplaceRef!T(*chunk, forward!args); return chunk; } /// @betterC @system unittest { int a; int b = 42; assert(*emplace!int(&a, b) == 42); } @betterC @system unittest { shared int i; emplace(&i, 42); assert(i == 42); } /** Given a raw memory area `chunk` (but already typed as a class type `T`), constructs an object of `class` type `T` at that address. The constructor is passed the arguments `Args`. If `T` is an inner class whose `outer` field can be used to access an instance of the enclosing class, then `Args` must not be empty, and the first member of it must be a valid initializer for that `outer` field. Correct initialization of this field is essential to access members of the outer class inside `T` methods. Note: This function is `@safe` if the corresponding constructor of `T` is `@safe`. Returns: The newly constructed object. */ T emplace(T, Args...)(T chunk, auto ref Args args) if (is(T == class)) { import core.internal.traits : isInnerClass; static assert(!__traits(isAbstractClass, T), T.stringof ~ " is abstract and it can't be emplaced"); // Initialize the object in its pre-ctor state const initializer = __traits(initSymbol, T); (() @trusted { (cast(void*) chunk)[0 .. initializer.length] = initializer[]; })(); static if (isInnerClass!T) { static assert(Args.length > 0, "Initializing an inner class requires a pointer to the outer class"); static assert(is(Args[0] : typeof(T.outer)), "The first argument must be a pointer to the outer class"); chunk.outer = args[0]; alias args1 = args[1..$]; } else alias args1 = args; // Call the ctor if any static if (is(typeof(chunk.__ctor(forward!args1)))) { // T defines a genuine constructor accepting args // Go the classic route: write .init first, then call ctor chunk.__ctor(forward!args1); } else { static assert(args1.length == 0 && !is(typeof(&T.__ctor)), "Don't know how to initialize an object of type " ~ T.stringof ~ " with arguments " ~ typeof(args1).stringof); } return chunk; } /// @safe unittest { () @safe { class SafeClass { int x; @safe this(int x) { this.x = x; } } auto buf = new void[__traits(classInstanceSize, SafeClass)]; auto support = (() @trusted => cast(SafeClass)(buf.ptr))(); auto safeClass = emplace!SafeClass(support, 5); assert(safeClass.x == 5); class UnsafeClass { int x; @system this(int x) { this.x = x; } } auto buf2 = new void[__traits(classInstanceSize, UnsafeClass)]; auto support2 = (() @trusted => cast(UnsafeClass)(buf2.ptr))(); static assert(!__traits(compiles, emplace!UnsafeClass(support2, 5))); static assert(!__traits(compiles, emplace!UnsafeClass(buf2, 5))); }(); } @safe unittest { class Outer { int i = 3; class Inner { @safe auto getI() { return i; } } } auto outerBuf = new void[__traits(classInstanceSize, Outer)]; auto outerSupport = (() @trusted => cast(Outer)(outerBuf.ptr))(); auto innerBuf = new void[__traits(classInstanceSize, Outer.Inner)]; auto innerSupport = (() @trusted => cast(Outer.Inner)(innerBuf.ptr))(); auto inner = innerSupport.emplace!(Outer.Inner)(outerSupport.emplace!Outer); assert(inner.getI == 3); } /** Given a raw memory area `chunk`, constructs an object of `class` type `T` at that address. The constructor is passed the arguments `Args`. If `T` is an inner class whose `outer` field can be used to access an instance of the enclosing class, then `Args` must not be empty, and the first member of it must be a valid initializer for that `outer` field. Correct initialization of this field is essential to access members of the outer class inside `T` methods. Preconditions: `chunk` must be at least as large as `T` needs and should have an alignment multiple of `T`'s alignment. (The size of a `class` instance is obtained by using $(D __traits(classInstanceSize, T))). Note: This function can be `@trusted` if the corresponding constructor of `T` is `@safe`. Returns: The newly constructed object. */ T emplace(T, Args...)(void[] chunk, auto ref Args args) if (is(T == class)) { import core.internal.traits : maxAlignment; enum classSize = __traits(classInstanceSize, T); assert(chunk.length >= classSize, "chunk size too small."); enum alignment = maxAlignment!(void*, typeof(T.tupleof)); assert((cast(size_t) chunk.ptr) % alignment == 0, "chunk is not aligned."); return emplace!T(cast(T)(chunk.ptr), forward!args); } /// @system unittest { static class C { int i; this(int i){this.i = i;} } auto buf = new void[__traits(classInstanceSize, C)]; auto c = emplace!C(buf, 5); assert(c.i == 5); } /// @betterC @nogc pure nothrow @system unittest { // works with -betterC too: static extern (C++) class C { @nogc pure nothrow @safe: int i = 3; this(int i) { assert(this.i == 3); this.i = i; } int virtualGetI() { return i; } } import core.internal.traits : classInstanceAlignment; align(classInstanceAlignment!C) byte[__traits(classInstanceSize, C)] buffer; C c = emplace!C(buffer[], 42); assert(c.virtualGetI() == 42); } @system unittest { class Outer { int i = 3; class Inner { auto getI() { return i; } } } auto outerBuf = new void[__traits(classInstanceSize, Outer)]; auto innerBuf = new void[__traits(classInstanceSize, Outer.Inner)]; auto inner = innerBuf.emplace!(Outer.Inner)(outerBuf.emplace!Outer); assert(inner.getI == 3); } @nogc pure nothrow @safe unittest { static class __conv_EmplaceTestClass { @nogc @safe pure nothrow: int i = 3; this(int i) { assert(this.i == 3); this.i = 10 + i; } this(ref int i) { assert(this.i == 3); this.i = 20 + i; } this(int i, ref int j) { assert(this.i == 3 && i == 5 && j == 6); this.i = i; ++j; } } int var = 6; align(__conv_EmplaceTestClass.alignof) ubyte[__traits(classInstanceSize, __conv_EmplaceTestClass)] buf; auto support = (() @trusted => cast(__conv_EmplaceTestClass)(buf.ptr))(); auto fromRval = emplace!__conv_EmplaceTestClass(support, 1); assert(fromRval.i == 11); auto fromLval = emplace!__conv_EmplaceTestClass(support, var); assert(fromLval.i == 26); auto k = emplace!__conv_EmplaceTestClass(support, 5, var); assert(k.i == 5); assert(var == 7); } /** Given a raw memory area `chunk`, constructs an object of non-$(D class) type `T` at that address. The constructor is passed the arguments `args`, if any. Preconditions: `chunk` must be at least as large as `T` needs and should have an alignment multiple of `T`'s alignment. Note: This function can be `@trusted` if the corresponding constructor of `T` is `@safe`. Returns: A pointer to the newly constructed object. */ T* emplace(T, Args...)(void[] chunk, auto ref Args args) if (!is(T == class)) { import core.internal.traits : Unqual; import core.internal.lifetime : emplaceRef; assert(chunk.length >= T.sizeof, "chunk size too small."); assert((cast(size_t) chunk.ptr) % T.alignof == 0, "emplace: Chunk is not aligned."); emplaceRef!(T, Unqual!T)(*cast(Unqual!T*) chunk.ptr, forward!args); return cast(T*) chunk.ptr; } /// @betterC @system unittest { struct S { int a, b; } void[S.sizeof] buf = void; S s; s.a = 42; s.b = 43; auto s1 = emplace!S(buf, s); assert(s1.a == 42 && s1.b == 43); } // Bulk of emplace unittests starts here @betterC @system unittest /* unions */ { static union U { string a; int b; struct { long c; int[] d; } } U u1 = void; U u2 = { "hello" }; emplace(&u1, u2); assert(u1.a == "hello"); } @system unittest // bugzilla 15772 { abstract class Foo {} class Bar: Foo {} void[] memory; // test in emplaceInitializer static assert(!is(typeof(emplace!Foo(cast(Foo*) memory.ptr)))); static assert( is(typeof(emplace!Bar(cast(Bar*) memory.ptr)))); // test in the emplace overload that takes void[] static assert(!is(typeof(emplace!Foo(memory)))); static assert( is(typeof(emplace!Bar(memory)))); } @betterC @system unittest { struct S { @disable this(); } S s = void; static assert(!__traits(compiles, emplace(&s))); emplace(&s, S.init); } @betterC @system unittest { struct S1 {} struct S2 { void opAssign(S2); } S1 s1 = void; S2 s2 = void; S1[2] as1 = void; S2[2] as2 = void; emplace(&s1); emplace(&s2); emplace(&as1); emplace(&as2); } @system unittest { static struct S1 { this(this) @disable; } static struct S2 { this() @disable; } S1[2] ss1 = void; S2[2] ss2 = void; emplace(&ss1); static assert(!__traits(compiles, emplace(&ss2))); S1 s1 = S1.init; S2 s2 = S2.init; static assert(!__traits(compiles, emplace(&ss1, s1))); emplace(&ss2, s2); } @system unittest { struct S { immutable int i; } S s = void; S[2] ss1 = void; S[2] ss2 = void; emplace(&s, 5); assert(s.i == 5); emplace(&ss1, s); assert(ss1[0].i == 5 && ss1[1].i == 5); emplace(&ss2, ss1); assert(ss2 == ss1); } //Start testing emplace-args here @system unittest { interface I {} class K : I {} K k = null, k2 = new K; assert(k !is k2); emplace!K(&k, k2); assert(k is k2); I i = null; assert(i !is k); emplace!I(&i, k); assert(i is k); } @system unittest { static struct S { int i = 5; void opAssign(S){assert(0);} } S[2] sa = void; S[2] sb; emplace(&sa, sb); assert(sa[0].i == 5 && sa[1].i == 5); } //Start testing emplace-struct here // Test constructor branch @betterC @system unittest { struct S { double x = 5, y = 6; this(int a, int b) { assert(x == 5 && y == 6); x = a; y = b; } } void[S.sizeof] s1 = void; auto s2 = S(42, 43); assert(*emplace!S(cast(S*) s1.ptr, s2) == s2); assert(*emplace!S(cast(S*) s1, 44, 45) == S(44, 45)); } @system unittest { static struct __conv_EmplaceTest { int i = 3; this(int i) { assert(this.i == 3 && i == 5); this.i = i; } this(int i, ref int j) { assert(i == 5 && j == 6); this.i = i; ++j; } @disable: this(); this(this); void opAssign(); } __conv_EmplaceTest k = void; emplace(&k, 5); assert(k.i == 5); int var = 6; __conv_EmplaceTest x = void; emplace(&x, 5, var); assert(x.i == 5); assert(var == 7); var = 6; auto z = emplace!__conv_EmplaceTest(new void[__conv_EmplaceTest.sizeof], 5, var); assert(z.i == 5); assert(var == 7); } // Test matching fields branch @betterC @system unittest { struct S { uint n; } S s; emplace!S(&s, 2U); assert(s.n == 2); } @betterC @safe unittest { struct S { int a, b; this(int){} } S s; static assert(!__traits(compiles, emplace!S(&s, 2, 3))); } @betterC @system unittest { struct S { int a, b = 7; } S s1 = void, s2 = void; emplace!S(&s1, 2); assert(s1.a == 2 && s1.b == 7); emplace!S(&s2, 2, 3); assert(s2.a == 2 && s2.b == 3); } //opAssign @betterC @system unittest { static struct S { int i = 5; void opAssign(int){assert(0);} void opAssign(S){assert(0);} } S sa1 = void; S sa2 = void; S sb1 = S(1); emplace(&sa1, sb1); emplace(&sa2, 2); assert(sa1.i == 1); assert(sa2.i == 2); } //postblit precedence @betterC @system unittest { //Works, but breaks in "-w -O" because of @@@9332@@@. //Uncomment test when 9332 is fixed. static struct S { int i; this(S other){assert(false);} this(int i){this.i = i;} this(this){} } S a = void; assert(is(typeof({S b = a;}))); //Postblit assert(is(typeof({S b = S(a);}))); //Constructor auto b = S(5); emplace(&a, b); assert(a.i == 5); static struct S2 { int* p; this(const S2){} } static assert(!is(immutable S2 : S2)); S2 s2 = void; immutable is2 = (immutable S2).init; emplace(&s2, is2); } //nested structs and postblit @system unittest { static struct S { int* p; this(int i){p = [i].ptr;} this(this) { if (p) p = [*p].ptr; } } static struct SS { S s; void opAssign(const SS) { assert(0); } } SS ssa = void; SS ssb = SS(S(5)); emplace(&ssa, ssb); assert(*ssa.s.p == 5); assert(ssa.s.p != ssb.s.p); } //disabled postblit @betterC @system unittest { static struct S1 { int i; @disable this(this); } S1 s1 = void; emplace(&s1, 1); assert(s1.i == 1); static assert(!__traits(compiles, emplace(&s1, s1))); // copy disabled static assert(__traits(compiles, emplace(&s1, move(s1)))); // move not affected static struct S2 { int i; @disable this(this); this(ref S2){} } S2 s2 = void; //static assert(!__traits(compiles, emplace(&s2, 1))); emplace(&s2, S2.init); static struct SS1 { S1 s; } SS1 ss1 = void; emplace(&ss1); static assert(!__traits(compiles, emplace(&ss1, ss1))); // copying disabled static assert(__traits(compiles, emplace(&ss1, move(ss1)))); // move unaffected static struct SS2 { S2 s; } SS2 ss2 = void; emplace(&ss2); static assert(!__traits(compiles, emplace(&ss2, ss2))); // copying disabled static assert(__traits(compiles, emplace(&ss2, SS2.init))); // move is OK // SS1 sss1 = s1; //This doesn't compile // SS1 sss1 = SS1(s1); //This doesn't compile // So emplace shouldn't compile either static assert(!__traits(compiles, emplace(&sss1, s1))); static assert(!__traits(compiles, emplace(&sss2, s2))); } //Imutability @betterC @system unittest { //Castable immutability { static struct S1 { int i; } static assert(is( immutable(S1) : S1)); S1 sa = void; auto sb = immutable(S1)(5); emplace(&sa, sb); assert(sa.i == 5); } //Un-castable immutability { static struct S2 { int* p; } static assert(!is(immutable(S2) : S2)); S2 sa = void; auto sb = immutable(S2)(null); assert(!__traits(compiles, emplace(&sa, sb))); } } @betterC @system unittest { static struct S { immutable int i; immutable(int)* j; } S s = void; emplace(&s, 1, null); emplace(&s, 2, &s.i); assert(s is S(2, &s.i)); } //Context pointer @system unittest { int i = 0; { struct S1 { void foo(){++i;} } S1 sa = void; S1 sb; emplace(&sa, sb); sa.foo(); assert(i == 1); } { struct S2 { void foo(){++i;} this(this){} } S2 sa = void; S2 sb; emplace(&sa, sb); sa.foo(); assert(i == 2); } } //Alias this @betterC @system unittest { static struct S { int i; } //By Ref { static struct SS1 { int j; S s; alias s this; } S s = void; SS1 ss = SS1(1, S(2)); emplace(&s, ss); assert(s.i == 2); } //By Value { static struct SS2 { int j; S s; S foo() @property{return s;} alias foo this; } S s = void; SS2 ss = SS2(1, S(2)); emplace(&s, ss); assert(s.i == 2); } } version (CoreUnittest) { //Ambiguity private struct __std_conv_S { int i; this(__std_conv_SS ss) {assert(0);} static opCall(__std_conv_SS ss) { __std_conv_S s; s.i = ss.j; return s; } } private struct __std_conv_SS { int j; __std_conv_S s; ref __std_conv_S foo() return @property {s.i = j; return s;} alias foo this; } } @system unittest { static assert(is(__std_conv_SS : __std_conv_S)); __std_conv_S s = void; __std_conv_SS ss = __std_conv_SS(1); __std_conv_S sTest1 = ss; //this calls "SS alias this" (and not "S.this(SS)") emplace(&s, ss); //"alias this" should take precedence in emplace over "opCall" assert(s.i == 1); } //Nested classes @system unittest { class A{} static struct S { A a; } S s1 = void; S s2 = S(new A); emplace(&s1, s2); assert(s1.a is s2.a); } //safety & nothrow & CTFE @betterC @system unittest { //emplace should be safe for anything with no elaborate opassign static struct S1 { int i; } static struct S2 { int i; this(int j)@safe nothrow{i = j;} } int i; S1 s1 = void; S2 s2 = void; auto pi = &i; auto ps1 = &s1; auto ps2 = &s2; void foo() @safe nothrow { emplace(pi); emplace(pi, 5); emplace(ps1); emplace(ps1, 5); emplace(ps1, S1.init); emplace(ps2); emplace(ps2, 5); emplace(ps2, S2.init); } foo(); T bar(T)() @property { T t/+ = void+/; //CTFE void illegal emplace(&t, 5); return t; } // CTFE enum a = bar!int; static assert(a == 5); enum b = bar!S1; static assert(b.i == 5); enum c = bar!S2; static assert(c.i == 5); // runtime auto aa = bar!int; assert(aa == 5); auto bb = bar!S1; assert(bb.i == 5); auto cc = bar!S2; assert(cc.i == 5); } @betterC @system unittest { struct S { int[2] get(){return [1, 2];} alias get this; } struct SS { int[2] ii; } struct ISS { int[2] ii; } S s; SS ss = void; ISS iss = void; emplace(&ss, s); emplace(&iss, s); assert(ss.ii == [1, 2]); assert(iss.ii == [1, 2]); } //disable opAssign @betterC @system unittest { static struct S { @disable void opAssign(S); } S s; emplace(&s, S.init); } //opCall @betterC @system unittest { int i; //Without constructor { static struct S1 { int i; static S1 opCall(int*){assert(0);} } S1 s = void; static assert(!__traits(compiles, emplace(&s, 1))); } //With constructor { static struct S2 { int i = 0; static S2 opCall(int*){assert(0);} static S2 opCall(int){assert(0);} this(int i){this.i = i;} } S2 s = void; emplace(&s, 1); assert(s.i == 1); } //With postblit ambiguity { static struct S3 { int i = 0; static S3 opCall(ref S3){assert(0);} } S3 s = void; emplace(&s, S3.init); } } //static arrays @system unittest { static struct S { int[2] ii; } static struct IS { immutable int[2] ii; } int[2] ii; S s = void; IS ims = void; ubyte ub = 2; emplace(&s, ub); emplace(&s, ii); emplace(&ims, ub); emplace(&ims, ii); uint[2] uu; static assert(!__traits(compiles, {S ss = S(uu);})); static assert(!__traits(compiles, emplace(&s, uu))); } @system unittest { int[2] sii; int[2] sii2; uint[2] uii; uint[2] uii2; emplace(&sii, 1); emplace(&sii, 1U); emplace(&uii, 1); emplace(&uii, 1U); emplace(&sii, sii2); //emplace(&sii, uii2); //Sorry, this implementation doesn't know how to... //emplace(&uii, sii2); //Sorry, this implementation doesn't know how to... emplace(&uii, uii2); emplace(&sii, sii2[]); //emplace(&sii, uii2[]); //Sorry, this implementation doesn't know how to... //emplace(&uii, sii2[]); //Sorry, this implementation doesn't know how to... emplace(&uii, uii2[]); } @system unittest { bool allowDestruction = false; struct S { int i; this(this){} ~this(){assert(allowDestruction);} } S s = S(1); S[2] ss1 = void; S[2] ss2 = void; S[2] ss3 = void; emplace(&ss1, s); emplace(&ss2, ss1); emplace(&ss3, ss2[]); assert(ss1[1] == s); assert(ss2[1] == s); assert(ss3[1] == s); allowDestruction = true; } @system unittest { //Checks postblit, construction, and context pointer int count = 0; struct S { this(this) { ++count; } ~this() { --count; } } S s; { S[4] ss = void; emplace(&ss, s); assert(count == 4); } assert(count == 0); } @system unittest { struct S { int i; } S s; S[2][2][2] sss = void; emplace(&sss, s); } @system unittest //Constness { import core.internal.lifetime : emplaceRef; int a = void; emplaceRef!(const int)(a, 5); immutable i = 5; const(int)* p = void; emplaceRef!(const int*)(p, &i); struct S { int* p; } alias IS = immutable(S); S s = void; emplaceRef!IS(s, IS()); S[2] ss = void; emplaceRef!(IS[2])(ss, IS()); IS[2] iss = IS.init; emplaceRef!(IS[2])(ss, iss); emplaceRef!(IS[2])(ss, iss[]); } @betterC pure nothrow @safe @nogc unittest { import core.internal.lifetime : emplaceRef; int i; emplaceRef(i); emplaceRef!int(i); emplaceRef(i, 5); emplaceRef!int(i, 5); } // Test attribute propagation for UDTs pure nothrow @safe /* @nogc */ unittest { import core.internal.lifetime : emplaceRef; static struct Safe { this(this) pure nothrow @safe @nogc {} } Safe safe = void; emplaceRef(safe, Safe()); Safe[1] safeArr = [Safe()]; Safe[1] uninitializedSafeArr = void; emplaceRef(uninitializedSafeArr, safe); emplaceRef(uninitializedSafeArr, safeArr); static struct Unsafe { this(this) @system {} } Unsafe unsafe = void; static assert(!__traits(compiles, emplaceRef(unsafe, unsafe))); Unsafe[1] unsafeArr = [Unsafe()]; Unsafe[1] uninitializedUnsafeArr = void; static assert(!__traits(compiles, emplaceRef(uninitializedUnsafeArr, unsafe))); static assert(!__traits(compiles, emplaceRef(uninitializedUnsafeArr, unsafeArr))); } @betterC @system unittest { // Issue 15313 static struct Node { int payload; Node* next; uint refs; } import core.stdc.stdlib : malloc; void[] buf = malloc(Node.sizeof)[0 .. Node.sizeof]; const Node* n = emplace!(const Node)(buf, 42, null, 10); assert(n.payload == 42); assert(n.next == null); assert(n.refs == 10); } @system unittest { class A { int x = 5; int y = 42; this(int z) { assert(x == 5 && y == 42); x = y = z; } } void[] buf; static align(A.alignof) byte[__traits(classInstanceSize, A)] sbuf; buf = sbuf[]; auto a = emplace!A(buf, 55); assert(a.x == 55 && a.y == 55); // emplace in bigger buffer buf = new byte[](__traits(classInstanceSize, A) + 10); a = emplace!A(buf, 55); assert(a.x == 55 && a.y == 55); // need ctor args static assert(!is(typeof(emplace!A(buf)))); } //constructor arguments forwarding @betterC @system unittest { static struct S { this()(auto ref long arg) { // assert that arg is an lvalue static assert(__traits(isRef, arg)); } this()(auto ref double arg) // assert that arg is an rvalue { static assert(!__traits(isRef, arg)); } } S obj = void; long i; emplace(&obj, i); // lvalue emplace(&obj, 0.0); // rvalue } // Bulk of emplace unittests ends here /** * Emplaces a copy of the specified source value into uninitialized memory, * i.e., simulates `T target = source` copy-construction for cases where the * target memory is already allocated and to be initialized with a copy. * * Params: * source = value to be copied into target * target = uninitialized value to be initialized with a copy of source */ void copyEmplace(S, T)(ref S source, ref T target) @system if (is(immutable S == immutable T)) { import core.internal.traits : BaseElemOf, hasElaborateCopyConstructor, Unconst, Unqual; // cannot have the following as simple template constraint due to nested-struct special case... static if (!__traits(compiles, (ref S src) { T tgt = src; })) { alias B = BaseElemOf!T; enum isNestedStruct = is(B == struct) && __traits(isNested, B); static assert(isNestedStruct, "cannot copy-construct " ~ T.stringof ~ " from " ~ S.stringof); } void blit() { import core.stdc.string : memcpy; memcpy(cast(Unqual!(T)*) &target, cast(Unqual!(T)*) &source, T.sizeof); } static if (is(T == struct)) { static if (__traits(hasPostblit, T)) { blit(); (cast() target).__xpostblit(); } else static if (__traits(hasCopyConstructor, T)) { // https://issues.dlang.org/show_bug.cgi?id=22766 import core.internal.lifetime : emplaceInitializer; emplaceInitializer(*(cast(Unqual!T*)&target)); static if (__traits(isNested, T)) { // copy context pointer *(cast(void**) &target.tupleof[$-1]) = cast(void*) source.tupleof[$-1]; } target.__ctor(source); // invoke copy ctor } else { blit(); // no opAssign } } else static if (is(T == E[n], E, size_t n)) { static if (hasElaborateCopyConstructor!E) { size_t i; try { for (i = 0; i < n; i++) copyEmplace(source[i], target[i]); } catch (Exception e) { // destroy, in reverse order, what we've constructed so far while (i--) destroy(*cast(Unconst!(E)*) &target[i]); throw e; } } else // trivial copy { blit(); // all elements at once } } else { *cast(Unconst!(T)*) &target = *cast(Unconst!(T)*) &source; } } /// @betterC @system pure nothrow @nogc unittest { int source = 123; int target = void; copyEmplace(source, target); assert(target == 123); } /// @betterC @system pure nothrow @nogc unittest { immutable int[1][1] source = [ [123] ]; immutable int[1][1] target = void; copyEmplace(source, target); assert(target[0][0] == 123); } /// @betterC @system pure nothrow @nogc unittest { struct S { int x; void opAssign(const scope ref S rhs) @safe pure nothrow @nogc { assert(0); } } S source = S(42); S target = void; copyEmplace(source, target); assert(target.x == 42); } // preserve shared-ness @system pure nothrow unittest { auto s = new Object(); auto ss = new shared Object(); Object t; shared Object st; copyEmplace(s, t); assert(t is s); copyEmplace(ss, st); assert(st is ss); static assert(!__traits(compiles, copyEmplace(s, st))); static assert(!__traits(compiles, copyEmplace(ss, t))); } // https://issues.dlang.org/show_bug.cgi?id=22766 @system pure nothrow @nogc unittest { static struct S { @disable this(); this(int) @safe pure nothrow @nogc{} this(ref const(S) other) @safe pure nothrow @nogc {} } S s1 = S(1); S s2 = void; copyEmplace(s1, s2); assert(s2 == S(1)); } version (DigitalMars) version (X86) version (Posix) version = DMD_X86_Posix; // don't violate immutability for reference types @system pure nothrow unittest { auto s = new Object(); auto si = new immutable Object(); Object t; immutable Object ti; copyEmplace(s, t); assert(t is s); copyEmplace(si, ti); version (DMD_X86_Posix) { /* wrongly fails without -O */ } else assert(ti is si); static assert(!__traits(compiles, copyEmplace(s, ti))); static assert(!__traits(compiles, copyEmplace(si, t))); } version (CoreUnittest) { private void testCopyEmplace(S, T)(const scope T* expected = null) { S source; T target = void; copyEmplace(source, target); if (expected) assert(target == *expected); else { T expectedCopy = source; assert(target == expectedCopy); } } } // postblit @system pure nothrow @nogc unittest { static struct S { @safe pure nothrow @nogc: int x = 42; this(this) { x += 10; } } testCopyEmplace!(S, S)(); testCopyEmplace!(immutable S, S)(); testCopyEmplace!(S, immutable S)(); testCopyEmplace!(immutable S, immutable S)(); testCopyEmplace!(S[1], S[1])(); testCopyEmplace!(immutable S[1], S[1])(); // copying to an immutable static array works, but `T expected = source` // wrongly ignores the postblit: https://issues.dlang.org/show_bug.cgi?id=8950 immutable S[1] expectedImmutable = [S(52)]; testCopyEmplace!(S[1], immutable S[1])(&expectedImmutable); testCopyEmplace!(immutable S[1], immutable S[1])(&expectedImmutable); } // copy constructors @system pure nothrow @nogc unittest { static struct S { @safe pure nothrow @nogc: int x = 42; this(int x) { this.x = x; } this(const scope ref S rhs) { x = rhs.x + 10; } this(const scope ref S rhs) immutable { x = rhs.x + 20; } } testCopyEmplace!(S, S)(); testCopyEmplace!(immutable S, S)(); testCopyEmplace!(S, immutable S)(); testCopyEmplace!(immutable S, immutable S)(); // static arrays work, but `T expected = source` wrongly ignores copy ctors // https://issues.dlang.org/show_bug.cgi?id=20365 S[1] expectedMutable = [S(52)]; immutable S[1] expectedImmutable = [immutable S(62)]; testCopyEmplace!(S[1], S[1])(&expectedMutable); testCopyEmplace!(immutable S[1], S[1])(&expectedMutable); testCopyEmplace!(S[1], immutable S[1])(&expectedImmutable); testCopyEmplace!(immutable S[1], immutable S[1])(&expectedImmutable); } // copy constructor in nested struct @system pure nothrow unittest { int copies; struct S { @safe pure nothrow @nogc: size_t x = 42; this(size_t x) { this.x = x; } this(const scope ref S rhs) { assert(x == 42); // T.init x = rhs.x; ++copies; } } { copies = 0; S source = S(123); immutable S target = void; copyEmplace(source, target); assert(target is source); assert(copies == 1); } { copies = 0; immutable S[1] source = [immutable S(456)]; S[1] target = void; copyEmplace(source, target); assert(target[0] is source[0]); assert(copies == 1); } } // destruction of partially copied static array @system unittest { static struct S { __gshared int[] deletions; int x; this(this) { if (x == 5) throw new Exception(""); } ~this() { deletions ~= x; } } alias T = immutable S[3][2]; T source = [ [S(1), S(2), S(3)], [S(4), S(5), S(6)] ]; T target = void; try { copyEmplace(source, target); assert(0); } catch (Exception) { static immutable expectedDeletions = [ 4, 3, 2, 1 ]; version (DigitalMars) { assert(S.deletions == expectedDeletions || S.deletions == [ 4 ]); // FIXME: happens with -O } else assert(S.deletions == expectedDeletions); } } /** Forwards function arguments while keeping `out`, `ref`, and `lazy` on the parameters. Params: args = a parameter list or an $(REF AliasSeq,std,meta). Returns: An `AliasSeq` of `args` with `out`, `ref`, and `lazy` saved. */ template forward(args...) { import core.internal.traits : AliasSeq; template fwd(alias arg) { // by ref || lazy || const/immutable static if (__traits(isRef, arg) || __traits(isOut, arg) || __traits(isLazy, arg) || !is(typeof(move(arg)))) alias fwd = arg; // (r)value else @property auto fwd(){ pragma(inline, true); return move(arg); } } alias Result = AliasSeq!(); static foreach (arg; args) Result = AliasSeq!(Result, fwd!arg); static if (Result.length == 1) alias forward = Result[0]; else alias forward = Result; } /// @safe unittest { class C { static int foo(int n) { return 1; } static int foo(ref int n) { return 2; } } // with forward int bar()(auto ref int x) { return C.foo(forward!x); } // without forward int baz()(auto ref int x) { return C.foo(x); } int i; assert(bar(1) == 1); assert(bar(i) == 2); assert(baz(1) == 2); assert(baz(i) == 2); } /// @safe unittest { void foo(int n, ref string s) { s = null; foreach (i; 0 .. n) s ~= "Hello"; } // forwards all arguments which are bound to parameter tuple void bar(Args...)(auto ref Args args) { return foo(forward!args); } // forwards all arguments with swapping order void baz(Args...)(auto ref Args args) { return foo(forward!args[$/2..$], forward!args[0..$/2]); } string s; bar(1, s); assert(s == "Hello"); baz(s, 2); assert(s == "HelloHello"); } @safe unittest { auto foo(TL...)(auto ref TL args) { string result = ""; foreach (i, _; args) { //pragma(msg, "[",i,"] ", __traits(isRef, args[i]) ? "L" : "R"); result ~= __traits(isRef, args[i]) ? "L" : "R"; } return result; } string bar(TL...)(auto ref TL args) { return foo(forward!args); } string baz(TL...)(auto ref TL args) { int x; return foo(forward!args[3], forward!args[2], 1, forward!args[1], forward!args[0], x); } struct S {} S makeS(){ return S(); } int n; string s; assert(bar(S(), makeS(), n, s) == "RRLL"); assert(baz(S(), makeS(), n, s) == "LLRRRL"); } @betterC @safe unittest { ref int foo(return ref int a) { return a; } ref int bar(Args)(auto ref Args args) { return foo(forward!args); } static assert(!__traits(compiles, { auto x1 = bar(3); })); // case of NG int value = 3; auto x2 = bar(value); // case of OK } /// @betterC @safe unittest { struct X { int i; this(this) { ++i; } } struct Y { private X x_; this()(auto ref X x) { x_ = forward!x; } } struct Z { private const X x_; this()(auto ref X x) { x_ = forward!x; } this()(auto const ref X x) { x_ = forward!x; } } X x; const X cx; auto constX = (){ const X x; return x; }; static assert(__traits(compiles, { Y y = x; })); static assert(__traits(compiles, { Y y = X(); })); static assert(!__traits(compiles, { Y y = cx; })); static assert(!__traits(compiles, { Y y = constX(); })); static assert(__traits(compiles, { Z z = x; })); static assert(__traits(compiles, { Z z = X(); })); static assert(__traits(compiles, { Z z = cx; })); static assert(__traits(compiles, { Z z = constX(); })); Y y1 = x; // ref lvalue, copy assert(y1.x_.i == 1); Y y2 = X(); // rvalue, move assert(y2.x_.i == 0); Z z1 = x; // ref lvalue, copy assert(z1.x_.i == 1); Z z2 = X(); // rvalue, move assert(z2.x_.i == 0); Z z3 = cx; // ref const lvalue, copy assert(z3.x_.i == 1); Z z4 = constX(); // const rvalue, copy assert(z4.x_.i == 1); } // lazy -> lazy @betterC @safe unittest { int foo1(lazy int i) { return i; } int foo2(A)(auto ref A i) { return foo1(forward!i); } int foo3(lazy int i) { return foo2(i); } int numCalls = 0; assert(foo3({ ++numCalls; return 42; }()) == 42); assert(numCalls == 1); } // lazy -> non-lazy @betterC @safe unittest { int foo1(int a, int b) { return a + b; } int foo2(A...)(auto ref A args) { return foo1(forward!args); } int foo3(int a, lazy int b) { return foo2(a, b); } int numCalls; assert(foo3(11, { ++numCalls; return 31; }()) == 42); assert(numCalls == 1); } // non-lazy -> lazy @betterC @safe unittest { int foo1(int a, lazy int b) { return a + b; } int foo2(A...)(auto ref A args) { return foo1(forward!args); } int foo3(int a, int b) { return foo2(a, b); } assert(foo3(11, 31) == 42); } // out @betterC @safe unittest { void foo1(int a, out int b) { b = a; } void foo2(A...)(auto ref A args) { foo1(forward!args); } void foo3(int a, out int b) { foo2(a, b); } int b; foo3(42, b); assert(b == 42); } // move /** Moves `source` into `target`, via a destructive copy when necessary. If `T` is a struct with a destructor or postblit defined, source is reset to its `.init` value after it is moved into target, otherwise it is left unchanged. Preconditions: If source has internal pointers that point to itself and doesn't define opPostMove, it cannot be moved, and will trigger an assertion failure. Params: source = Data to copy. target = Where to copy into. The destructor, if any, is invoked before the copy is performed. */ void move(T)(ref T source, ref T target) { moveImpl(target, source); } /// For non-struct types, `move` just performs `target = source`: @safe unittest { Object obj1 = new Object; Object obj2 = obj1; Object obj3; move(obj2, obj3); assert(obj3 is obj1); // obj2 unchanged assert(obj2 is obj1); } /// pure nothrow @safe @nogc unittest { // Structs without destructors are simply copied struct S1 { int a = 1; int b = 2; } S1 s11 = { 10, 11 }; S1 s12; move(s11, s12); assert(s12 == S1(10, 11)); assert(s11 == s12); // But structs with destructors or postblits are reset to their .init value // after copying to the target. struct S2 { int a = 1; int b = 2; ~this() pure nothrow @safe @nogc { } } S2 s21 = { 3, 4 }; S2 s22; move(s21, s22); assert(s21 == S2(1, 2)); assert(s22 == S2(3, 4)); } @safe unittest { import core.internal.traits; assertCTFEable!((){ Object obj1 = new Object; Object obj2 = obj1; Object obj3; move(obj2, obj3); assert(obj3 is obj1); static struct S1 { int a = 1, b = 2; } S1 s11 = { 10, 11 }; S1 s12; move(s11, s12); assert(s11.a == 10 && s11.b == 11 && s12.a == 10 && s12.b == 11); static struct S2 { int a = 1; int * b; } S2 s21 = { 10, null }; s21.b = new int; S2 s22; move(s21, s22); assert(s21 == s22); }); // Issue 5661 test(1) static struct S3 { static struct X { int n = 0; ~this(){n = 0;} } X x; } static assert(hasElaborateDestructor!S3); S3 s31, s32; s31.x.n = 1; move(s31, s32); assert(s31.x.n == 0); assert(s32.x.n == 1); // Issue 5661 test(2) static struct S4 { static struct X { int n = 0; this(this){n = 0;} } X x; } static assert(hasElaborateCopyConstructor!S4); S4 s41, s42; s41.x.n = 1; move(s41, s42); assert(s41.x.n == 0); assert(s42.x.n == 1); // Issue 13990 test class S5; S5 s51; S5 s52 = s51; S5 s53; move(s52, s53); assert(s53 is s51); } /// Ditto T move(T)(return scope ref T source) { return moveImpl(source); } /// Non-copyable structs can still be moved: pure nothrow @safe @nogc unittest { struct S { int a = 1; @disable this(this); ~this() pure nothrow @safe @nogc {} } S s1; s1.a = 2; S s2 = move(s1); assert(s1.a == 1); assert(s2.a == 2); } // https://issues.dlang.org/show_bug.cgi?id=20869 // `move` should propagate the attributes of `opPostMove` @system unittest { static struct S { void opPostMove(const ref S old) nothrow @system { __gshared int i; new int(i++); // Force @gc impure @system } } alias T = void function() @system nothrow; static assert(is(typeof({ S s; move(s); }) == T)); static assert(is(typeof({ S s; move(s, s); }) == T)); } private void moveImpl(T)(scope ref T target, return scope ref T source) { import core.internal.traits : hasElaborateDestructor; static if (is(T == struct)) { // Unsafe when compiling without -preview=dip1000 if ((() @trusted => &source == &target)()) return; // Destroy target before overwriting it static if (hasElaborateDestructor!T) target.__xdtor(); } // move and emplace source into target moveEmplaceImpl(target, source); } private T moveImpl(T)(return scope ref T source) { // Properly infer safety from moveEmplaceImpl as the implementation below // might void-initialize pointers in result and hence needs to be @trusted if (false) moveEmplaceImpl(source, source); return trustedMoveImpl(source); } private T trustedMoveImpl(T)(return scope ref T source) @trusted { T result = void; moveEmplaceImpl(result, source); return result; } @safe unittest { import core.internal.traits; assertCTFEable!((){ Object obj1 = new Object; Object obj2 = obj1; Object obj3 = move(obj2); assert(obj3 is obj1); static struct S1 { int a = 1, b = 2; } S1 s11 = { 10, 11 }; S1 s12 = move(s11); assert(s11.a == 10 && s11.b == 11 && s12.a == 10 && s12.b == 11); static struct S2 { int a = 1; int * b; } S2 s21 = { 10, null }; s21.b = new int; S2 s22 = move(s21); assert(s21 == s22); }); // Issue 5661 test(1) static struct S3 { static struct X { int n = 0; ~this(){n = 0;} } X x; } static assert(hasElaborateDestructor!S3); S3 s31; s31.x.n = 1; S3 s32 = move(s31); assert(s31.x.n == 0); assert(s32.x.n == 1); // Issue 5661 test(2) static struct S4 { static struct X { int n = 0; this(this){n = 0;} } X x; } static assert(hasElaborateCopyConstructor!S4); S4 s41; s41.x.n = 1; S4 s42 = move(s41); assert(s41.x.n == 0); assert(s42.x.n == 1); // Issue 13990 test class S5; S5 s51; S5 s52 = s51; S5 s53; s53 = move(s52); assert(s53 is s51); } @betterC @system unittest { static struct S { int n = 0; ~this() @system { n = 0; } } S a, b; static assert(!__traits(compiles, () @safe { move(a, b); })); static assert(!__traits(compiles, () @safe { move(a); })); a.n = 1; () @trusted { move(a, b); }(); assert(a.n == 0); a.n = 1; () @trusted { move(a); }(); assert(a.n == 0); } /+ this can't be tested in druntime, tests are still run in phobos @safe unittest//Issue 6217 { import std.algorithm.iteration : map; auto x = map!"a"([1,2,3]); x = move(x); } +/ @betterC @safe unittest// Issue 8055 { static struct S { int x; ~this() { assert(x == 0); } } S foo(S s) { return move(s); } S a; a.x = 0; auto b = foo(a); assert(b.x == 0); } @system unittest// Issue 8057 { int n = 10; struct S { int x; ~this() { // Access to enclosing scope assert(n == 10); } } S foo(S s) { // Move nested struct return move(s); } S a; a.x = 1; auto b = foo(a); assert(b.x == 1); // Regression 8171 static struct Array(T) { // nested struct has no member struct Payload { ~this() {} } } Array!int.Payload x = void; move(x); move(x, x); } private enum bool hasContextPointers(T) = { static if (__traits(isStaticArray, T)) { return hasContextPointers!(typeof(T.init[0])); } else static if (is(T == struct)) { import core.internal.traits : anySatisfy; return __traits(isNested, T) || anySatisfy!(hasContextPointers, typeof(T.tupleof)); } else return false; } (); @safe @nogc nothrow pure unittest { static assert(!hasContextPointers!int); static assert(!hasContextPointers!(void*)); static struct S {} static assert(!hasContextPointers!S); static assert(!hasContextPointers!(S[1])); struct Nested { void foo() {} } static assert(hasContextPointers!Nested); static assert(hasContextPointers!(Nested[1])); static struct OneLevel { int before; Nested n; int after; } static assert(hasContextPointers!OneLevel); static assert(hasContextPointers!(OneLevel[1])); static struct TwoLevels { int before; OneLevel o; int after; } static assert(hasContextPointers!TwoLevels); static assert(hasContextPointers!(TwoLevels[1])); union U { Nested n; } // unions can have false positives, so this query ignores them static assert(!hasContextPointers!U); } // target must be first-parameter, because in void-functions DMD + dip1000 allows it to take the place of a return-scope private void moveEmplaceImpl(T)(scope ref T target, return scope ref T source) { // TODO: this assert pulls in half of phobos. we need to work out an alternative assert strategy. // static if (!is(T == class) && hasAliasing!T) if (!__ctfe) // { // import std.exception : doesPointTo; // assert(!doesPointTo(source, source) && !hasElaborateMove!T), // "Cannot move object with internal pointer unless `opPostMove` is defined."); // } import core.internal.traits : hasElaborateAssign, isAssignable, hasElaborateMove, hasElaborateDestructor, hasElaborateCopyConstructor; static if (is(T == struct)) { // Unsafe when compiling without -preview=dip1000 assert((() @trusted => &source !is &target)(), "source and target must not be identical"); static if (hasElaborateAssign!T || !isAssignable!T) { import core.stdc.string : memcpy; () @trusted { memcpy(&target, &source, T.sizeof); }(); } else target = source; static if (hasElaborateMove!T) __move_post_blt(target, source); // If the source defines a destructor or a postblit hook, we must obliterate the // object in order to avoid double freeing and undue aliasing static if (hasElaborateDestructor!T || hasElaborateCopyConstructor!T) { // If there are members that are nested structs, we must take care // not to erase any context pointers, so we might have to recurse static if (__traits(isZeroInit, T)) wipe(source); else wipe(source, ref () @trusted { return *cast(immutable(T)*) __traits(initSymbol, T).ptr; } ()); } } else static if (__traits(isStaticArray, T)) { static if (T.length) { static if (!hasElaborateMove!T && !hasElaborateDestructor!T && !hasElaborateCopyConstructor!T) { // Single blit if no special per-instance handling is required () @trusted { assert(source.ptr !is target.ptr, "source and target must not be identical"); *cast(ubyte[T.sizeof]*) &target = *cast(ubyte[T.sizeof]*) &source; } (); } else { for (size_t i = 0; i < source.length; ++i) moveEmplaceImpl(target[i], source[i]); } } } else { // Primitive data (including pointers and arrays) or class - // assignment works great target = source; } } /** * Similar to $(LREF move) but assumes `target` is uninitialized. This * is more efficient because `source` can be blitted over `target` * without destroying or initializing it first. * * Params: * source = value to be moved into target * target = uninitialized value to be filled by source */ void moveEmplace(T)(ref T source, ref T target) @system { moveEmplaceImpl(target, source); } /// @betterC pure nothrow @nogc @system unittest { static struct Foo { pure nothrow @nogc: this(int* ptr) { _ptr = ptr; } ~this() { if (_ptr) ++*_ptr; } int* _ptr; } int val; Foo foo1 = void; // uninitialized auto foo2 = Foo(&val); // initialized assert(foo2._ptr is &val); // Using `move(foo2, foo1)` would have an undefined effect because it would destroy // the uninitialized foo1. // moveEmplace directly overwrites foo1 without destroying or initializing it first. moveEmplace(foo2, foo1); assert(foo1._ptr is &val); assert(foo2._ptr is null); assert(val == 0); } @betterC pure nothrow @nogc @system unittest { static struct Foo { pure nothrow @nogc: this(int* ptr) { _ptr = ptr; } ~this() { if (_ptr) ++*_ptr; } int* _ptr; } int val; { Foo[1] foo1 = void; // uninitialized Foo[1] foo2 = [Foo(&val)];// initialized assert(foo2[0]._ptr is &val); // Using `move(foo2, foo1)` would have an undefined effect because it would destroy // the uninitialized foo1. // moveEmplace directly overwrites foo1 without destroying or initializing it first. moveEmplace(foo2, foo1); assert(foo1[0]._ptr is &val); assert(foo2[0]._ptr is null); assert(val == 0); } assert(val == 1); } // issue 18913 @safe unittest { static struct NoCopy { int payload; ~this() { } @disable this(this); } static void f(NoCopy[2]) { } NoCopy[2] ncarray = [ NoCopy(1), NoCopy(2) ]; static assert(!__traits(compiles, f(ncarray))); f(move(ncarray)); } //debug = PRINTF; debug(PRINTF) { import core.stdc.stdio; } /// Implementation of `_d_delstruct` and `_d_delstructTrace` template _d_delstructImpl(T) { private void _d_delstructImpure(ref T p) { debug(PRINTF) printf("_d_delstruct(%p)\n", p); import core.memory : GC; destroy(*p); GC.free(p); p = null; } /** * This is called for a delete statement where the value being deleted is a * pointer to a struct with a destructor but doesn't have an overloaded * `delete` operator. * * Params: * p = pointer to the value to be deleted * * Bugs: * This function template was ported from a much older runtime hook that * bypassed safety, purity, and throwabilty checks. To prevent breaking * existing code, this function template is temporarily declared * `@trusted` until the implementation can be brought up to modern D * expectations. */ void _d_delstruct(ref T p) @trusted @nogc pure nothrow { if (p) { alias Type = void function(ref T P) @nogc pure nothrow; (cast(Type) &_d_delstructImpure)(p); } } import core.internal.array.utils : _d_HookTraceImpl; private enum errorMessage = "Cannot delete struct if compiling without support for runtime type information!"; /** * TraceGC wrapper around $(REF _d_delstruct, core,lifetime,_d_delstructImpl). * * Bugs: * This function template was ported from a much older runtime hook that * bypassed safety, purity, and throwabilty checks. To prevent breaking * existing code, this function template is temporarily declared * `@trusted` until the implementation can be brought up to modern D * expectations. */ alias _d_delstructTrace = _d_HookTraceImpl!(T, _d_delstruct, errorMessage); } @system pure nothrow unittest { int dtors = 0; struct S { ~this() nothrow { ++dtors; } } S *s = new S(); _d_delstructImpl!(typeof(s))._d_delstruct(s); assert(s == null); assert(dtors == 1); } @system pure unittest { int innerDtors = 0; int outerDtors = 0; struct Inner { ~this() { ++innerDtors; } } struct Outer { Inner *i1; Inner *i2; this(int x) { i1 = new Inner(); i2 = new Inner(); } ~this() { ++outerDtors; _d_delstructImpl!(typeof(i1))._d_delstruct(i1); assert(i1 == null); _d_delstructImpl!(typeof(i2))._d_delstruct(i2); assert(i2 == null); } } Outer *o = new Outer(0); _d_delstructImpl!(typeof(o))._d_delstruct(o); assert(o == null); assert(innerDtors == 2); assert(outerDtors == 1); } // issue 25552 pure nothrow @system unittest { int i; struct Nested { pure nothrow @nogc: char[1] arr; // char.init is not 0 ~this() { ++i; } } { Nested[1] dst = void; Nested[1] src = [Nested(['a'])]; moveEmplace(src, dst); assert(i == 0); assert(dst[0].arr == ['a']); assert(src[0].arr == [char.init]); assert(dst[0].tupleof[$-1] is src[0].tupleof[$-1]); } assert(i == 2); } // issue 25552 @safe unittest { int i; struct Nested { ~this() { ++i; } } static struct NotNested { Nested n; } static struct Deep { NotNested nn; } static struct Deeper { NotNested[1] nn; } static assert(__traits(isZeroInit, Nested)); static assert(__traits(isZeroInit, NotNested)); static assert(__traits(isZeroInit, Deep)); static assert(__traits(isZeroInit, Deeper)); { auto a = NotNested(Nested()); assert(a.n.tupleof[$-1]); auto b = move(a); assert(b.n.tupleof[$-1]); assert(a.n.tupleof[$-1] is b.n.tupleof[$-1]); auto c = Deep(NotNested(Nested())); auto d = move(c); assert(d.nn.n.tupleof[$-1]); assert(c.nn.n.tupleof[$-1] is d.nn.n.tupleof[$-1]); auto e = Deeper([NotNested(Nested())]); auto f = move(e); assert(f.nn[0].n.tupleof[$-1]); assert(e.nn[0].n.tupleof[$-1] is f.nn[0].n.tupleof[$-1]); } assert(i == 6); } // issue 25552 @safe unittest { int i; struct Nested { align(32) // better still find context pointer correctly! int[3] stuff = [0, 1, 2]; ~this() { ++i; } } static struct NoAssign { int value; @disable void opAssign(typeof(this)); } static struct NotNested { int before = 42; align(Nested.alignof * 4) // better still find context pointer correctly! Nested n; auto after = NoAssign(43); } static struct Deep { NotNested nn; } static struct Deeper { NotNested[1] nn; } static assert(!__traits(isZeroInit, Nested)); static assert(!__traits(isZeroInit, NotNested)); static assert(!__traits(isZeroInit, Deep)); static assert(!__traits(isZeroInit, Deeper)); { auto a = NotNested(1, Nested([3, 4, 5]), NoAssign(2)); auto b = move(a); assert(b.n.tupleof[$-1]); assert(a.n.tupleof[$-1] is b.n.tupleof[$-1]); assert(a.n.stuff == [0, 1, 2]); assert(a.before == 42); assert(a.after == NoAssign(43)); auto c = Deep(NotNested(1, Nested([3, 4, 5]), NoAssign(2))); auto d = move(c); assert(d.nn.n.tupleof[$-1]); assert(c.nn.n.tupleof[$-1] is d.nn.n.tupleof[$-1]); assert(c.nn.n.stuff == [0, 1, 2]); assert(c.nn.before == 42); assert(c.nn.after == NoAssign(43)); auto e = Deeper([NotNested(1, Nested([3, 4, 5]), NoAssign(2))]); auto f = move(e); assert(f.nn[0].n.tupleof[$-1]); assert(e.nn[0].n.tupleof[$-1] is f.nn[0].n.tupleof[$-1]); assert(e.nn[0].n.stuff == [0, 1, 2]); assert(e.nn[0].before == 42); assert(e.nn[0].after == NoAssign(43)); } assert(i == 6); } // wipes source after moving pragma(inline, true) private void wipe(T, Init...)(return scope ref T source, ref const scope Init initializer) @trusted if (!Init.length || ((Init.length == 1) && (is(immutable T == immutable Init[0])))) { static if (__traits(isStaticArray, T) && hasContextPointers!T) { for (auto i = 0; i < T.length; i++) static if (Init.length) wipe(source[i], initializer[0][i]); else wipe(source[i]); } else static if (is(T == struct) && hasContextPointers!T) { import core.internal.traits : anySatisfy; static if (anySatisfy!(hasContextPointers, typeof(T.tupleof))) { static foreach (i; 0 .. T.tupleof.length - __traits(isNested, T)) static if (Init.length) wipe(source.tupleof[i], initializer[0].tupleof[i]); else wipe(source.tupleof[i]); } else { static if (__traits(isNested, T)) enum sz = T.tupleof[$-1].offsetof; else enum sz = T.sizeof; static if (Init.length) *cast(ubyte[sz]*) &source = *cast(ubyte[sz]*) &initializer[0]; else *cast(ubyte[sz]*) &source = 0; } } else { import core.internal.traits : hasElaborateAssign, isAssignable; static if (Init.length) { static if (hasElaborateAssign!T || !isAssignable!T) *cast(ubyte[T.sizeof]*) &source = *cast(ubyte[T.sizeof]*) &initializer[0]; else source = *cast(T*) &initializer[0]; } else { *cast(ubyte[T.sizeof]*) &source = 0; } } } /** * Allocate an exception of type `T` from the exception pool and call its constructor. * It has the same interface as `rt.lifetime._d_newclass()`. * `T` must be Throwable or derived from it, must declare an explicit ctor * and cannot be a COM or C++ class. * Returns: * constructed instance of the type */ T _d_newThrowable(T, Args...)(auto ref Args args) @trusted if (is(T : Throwable) && is(typeof(T.__ctor(forward!args))) && __traits(getLinkage, T) == "D") { debug(PRINTF) printf("_d_newThrowable(%s)\n", cast(char*) T.stringof); import core.memory : pureMalloc; auto init = __traits(initSymbol, T); void* p = pureMalloc(init.length); if (!p) { import core.exception : onOutOfMemoryError; onOutOfMemoryError(); } debug(PRINTF) printf(" p = %p\n", p); // initialize it p[0 .. init.length] = init[]; import core.internal.traits : hasIndirections; if (hasIndirections!T) { // Inform the GC about the pointers in the object instance import core.memory : GC; GC.addRange(p, init.length); } debug(PRINTF) printf("initialization done\n"); (cast(Throwable) p).refcount() = 1; auto t = cast(T) p; t.__ctor(forward!args); return t; } @system unittest { class E : Exception { int x; this(int x, string msg = "", Throwable nextInChain = null) { super(msg, nextInChain); this.x = x; } } auto exc = _d_newThrowable!Exception("Exception"); assert(exc.refcount() == 1); assert(exc.msg == "Exception"); static assert(!__traits(compiles, _d_newThrowable!E())); auto e = _d_newThrowable!E(42, "E", null); assert(e.refcount() == 1); assert(e.x == 42); assert(e.msg == "E"); }