summaryrefslogtreecommitdiff
path: root/libphobos/libdruntime/core/internal/lifetime.d
diff options
context:
space:
mode:
Diffstat (limited to 'libphobos/libdruntime/core/internal/lifetime.d')
-rw-r--r--libphobos/libdruntime/core/internal/lifetime.d213
1 files changed, 213 insertions, 0 deletions
diff --git a/libphobos/libdruntime/core/internal/lifetime.d b/libphobos/libdruntime/core/internal/lifetime.d
new file mode 100644
index 00000000000..7e9b5f2ad48
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/lifetime.d
@@ -0,0 +1,213 @@
+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.
++/
+template emplaceInitializer(T)
+if (!is(T == const) && !is(T == immutable) && !is(T == inout))
+{
+ import core.internal.traits : hasElaborateAssign, Unqual;
+
+ // Avoid stack allocation by hacking to get to the struct/union init symbol.
+ static if (is(T == struct) || is(T == union))
+ {
+ pragma(mangle, "_D" ~ Unqual!T.mangleof[1..$] ~ "6__initZ")
+ __gshared extern immutable T initializer;
+ }
+
+ void emplaceInitializer(scope ref T chunk) nothrow pure @trusted
+ {
+ 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;
+ memcpy(cast(void*)&chunk, &initializer, T.sizeof);
+ }
+ }
+}
+
+@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);
+ assert(dst is shared(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);
+}