| module core.internal.lifetime; | |
| import core.lifetime : forward; | |
| /+ | |
| emplaceRef is a package function for druntime internal use. It works like | |
| emplace, but takes its argument by ref (as opposed to "by pointer"). | |
| This makes it easier to use, easier to be safe, and faster in a non-inline | |
| build. | |
| Furthermore, emplaceRef optionally takes a type parameter, which specifies | |
| the type we want to build. This helps to build qualified objects on mutable | |
| buffer, without breaking the type system with unsafe casts. | |
| +/ | |
| void emplaceRef(T, UT, Args...)(ref UT chunk, auto ref Args args) | |
| { | |
| static if (args.length == 0) | |
| { | |
| static assert(is(typeof({static T i;})), | |
| "Cannot emplace a " ~ T.stringof ~ " because " ~ T.stringof ~ | |
| ".this() is annotated with @disable."); | |
| static if (is(T == class)) static assert(!__traits(isAbstractClass, T), | |
| T.stringof ~ " is abstract and it can't be emplaced"); | |
| emplaceInitializer(chunk); | |
| } | |
| else static if ( | |
| !is(T == struct) && Args.length == 1 /* primitives, enums, arrays */ | |
| || | |
| Args.length == 1 && is(typeof({T t = forward!(args[0]);})) /* conversions */ | |
| || | |
| is(typeof(T(forward!args))) /* general constructors */) | |
| { | |
| static struct S | |
| { | |
| T payload; | |
| this()(auto ref Args args) | |
| { | |
| static if (__traits(compiles, payload = forward!args)) | |
| payload = forward!args; | |
| else | |
| payload = T(forward!args); | |
| } | |
| } | |
| if (__ctfe) | |
| { | |
| static if (__traits(compiles, chunk = T(forward!args))) | |
| chunk = T(forward!args); | |
| else static if (args.length == 1 && __traits(compiles, chunk = forward!(args[0]))) | |
| chunk = forward!(args[0]); | |
| else assert(0, "CTFE emplace doesn't support " | |
| ~ T.stringof ~ " from " ~ Args.stringof); | |
| } | |
| else | |
| { | |
| S* p = () @trusted { return cast(S*) &chunk; }(); | |
| static if (UT.sizeof > 0) | |
| emplaceInitializer(*p); | |
| p.__ctor(forward!args); | |
| } | |
| } | |
| else static if (is(typeof(chunk.__ctor(forward!args)))) | |
| { | |
| // This catches the rare case of local types that keep a frame pointer | |
| emplaceInitializer(chunk); | |
| chunk.__ctor(forward!args); | |
| } | |
| else | |
| { | |
| //We can't emplace. Try to diagnose a disabled postblit. | |
| static assert(!(Args.length == 1 && is(Args[0] : T)), | |
| "Cannot emplace a " ~ T.stringof ~ " because " ~ T.stringof ~ | |
| ".this(this) is annotated with @disable."); | |
| //We can't emplace. | |
| static assert(false, | |
| T.stringof ~ " cannot be emplaced from " ~ Args[].stringof ~ "."); | |
| } | |
| } | |
| // ditto | |
| static import core.internal.traits; | |
| void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args) | |
| if (is(UT == core.internal.traits.Unqual!UT)) | |
| { | |
| emplaceRef!(UT, UT)(chunk, forward!args); | |
| } | |
| /+ | |
| Emplaces T.init. | |
| In contrast to `emplaceRef(chunk)`, there are no checks for disabled default | |
| constructors etc. | |
| +/ | |
| void emplaceInitializer(T)(scope ref T chunk) nothrow pure @trusted | |
| if (!is(T == const) && !is(T == immutable) && !is(T == inout)) | |
| { | |
| import core.internal.traits : hasElaborateAssign; | |
| static if (is(T == shared U, U)) | |
| { | |
| // Initialization happens before the shared object is published, so the | |
| // helper has to operate on the backing storage instead of performing an | |
| // ordinary shared write that `-preview=nosharedaccess` rejects. | |
| emplaceInitializer(*cast(U*) &chunk); | |
| } | |
| else static if (__traits(isZeroInit, T)) | |
| { | |
| import core.stdc.string : memset; | |
| memset(cast(void*) &chunk, 0, T.sizeof); | |
| } | |
| else static if (__traits(isScalar, T) || | |
| T.sizeof <= 16 && !hasElaborateAssign!T && __traits(compiles, (){ T chunk; chunk = T.init; })) | |
| { | |
| chunk = T.init; | |
| } | |
| else static if (__traits(isStaticArray, T)) | |
| { | |
| // For static arrays there is no initializer symbol created. Instead, we emplace elements one-by-one. | |
| foreach (i; 0 .. T.length) | |
| { | |
| emplaceInitializer(chunk[i]); | |
| } | |
| } | |
| else | |
| { | |
| import core.stdc.string : memcpy; | |
| const initializer = __traits(initSymbol, T); | |
| memcpy(cast(void*)&chunk, initializer.ptr, initializer.length); | |
| } | |
| } | |
| @safe unittest | |
| { | |
| static void testInitializer(T)() | |
| { | |
| // mutable T | |
| { | |
| T dst = void; | |
| emplaceInitializer(dst); | |
| assert(dst is T.init); | |
| } | |
| // shared T | |
| { | |
| shared T dst = void; | |
| emplaceInitializer(dst); | |
| // The initializer has not been published yet, so the test may read | |
| // the backing storage directly to verify that emplace wrote T.init. | |
| assert((() @trusted => (*cast(T*) &dst) is T.init)()); | |
| } | |
| // const T | |
| { | |
| const T dst = void; | |
| static assert(!__traits(compiles, emplaceInitializer(dst))); | |
| } | |
| } | |
| static struct ElaborateAndZero | |
| { | |
| int a; | |
| this(this) {} | |
| } | |
| static struct ElaborateAndNonZero | |
| { | |
| int a = 42; | |
| this(this) {} | |
| } | |
| static union LargeNonZeroUnion | |
| { | |
| byte[128] a = 1; | |
| } | |
| testInitializer!int(); | |
| testInitializer!double(); | |
| testInitializer!ElaborateAndZero(); | |
| testInitializer!ElaborateAndNonZero(); | |
| testInitializer!LargeNonZeroUnion(); | |
| static if (is(__vector(double[4]))) | |
| { | |
| // DMD 2.096 and GDC 11.1 can't compare vectors with `is` so can't use | |
| // testInitializer. | |
| enum VE : __vector(double[4]) | |
| { | |
| a = [1.0, 2.0, 3.0, double.nan], | |
| b = [4.0, 5.0, 6.0, double.nan], | |
| } | |
| const VE expected = VE.a; | |
| VE dst = VE.b; | |
| shared VE sharedDst = VE.b; | |
| emplaceInitializer(dst); | |
| emplaceInitializer(sharedDst); | |
| () @trusted { | |
| import core.stdc.string : memcmp; | |
| assert(memcmp(&expected, &dst, VE.sizeof) == 0); | |
| assert(memcmp(&expected, cast(void*) &sharedDst, VE.sizeof) == 0); | |
| }(); | |
| static assert(!__traits(compiles, emplaceInitializer(expected))); | |
| } | |
| } | |
| /* | |
| Simple swap function. | |
| */ | |
| void swap(T)(ref T lhs, ref T rhs) | |
| { | |
| import core.lifetime : move, moveEmplace; | |
| T tmp = move(lhs); | |
| moveEmplace(rhs, lhs); | |
| moveEmplace(tmp, rhs); | |
| } | |
| void __doPostblit(T)(T[] arr) | |
| { | |
| // infer static postblit type, run postblit if any | |
| static if (__traits(hasPostblit, T)) | |
| { | |
| static if (__traits(isStaticArray, T) && is(T : E[], E)) | |
| __doPostblit(cast(E[]) arr); | |
| else | |
| { | |
| import core.internal.traits : Unqual; | |
| foreach (ref elem; (() @trusted => cast(Unqual!T[]) arr)()) | |
| elem.__xpostblit(); | |
| } | |
| } | |
| } | |
| // ditto, but with an index to keep track of how many elements have been postblitted | |
| void __doPostblit(T)(T[] arr, ref size_t i) | |
| { | |
| // infer static postblit type, run postblit if any | |
| static if (__traits(hasPostblit, T)) | |
| { | |
| static if (__traits(isStaticArray, T) && is(T : E[], E)) | |
| __doPostblit(cast(E[]) arr, i); | |
| else | |
| { | |
| i = 0; | |
| import core.internal.traits : Unqual; | |
| for(auto eptr = cast(Unqual!T*)&arr[0]; i < arr.length; ++i, ++eptr) | |
| eptr.__xpostblit(); | |
| } | |
| } | |
| } |