1module core.internal.lifetime;
2
3import core.lifetime : forward;
4
5/+
6emplaceRef is a package function for druntime internal use. It works like
7emplace, but takes its argument by ref (as opposed to "by pointer").
8This makes it easier to use, easier to be safe, and faster in a non-inline
9build.
10Furthermore, emplaceRef optionally takes a type parameter, which specifies
11the type we want to build. This helps to build qualified objects on mutable
12buffer, without breaking the type system with unsafe casts.
13+/
14void emplaceRef(T, UT, Args...)(ref UT chunk, auto ref Args args)
15{
16    static if (args.length == 0)
17    {
18        static assert(is(typeof({static T i;})),
19            "Cannot emplace a " ~ T.stringof ~ " because " ~ T.stringof ~
20            ".this() is annotated with @disable.");
21        static if (is(T == class)) static assert(!__traits(isAbstractClass, T),
22            T.stringof ~ " is abstract and it can't be emplaced");
23        emplaceInitializer(chunk);
24    }
25    else static if (
26        !is(T == struct) && Args.length == 1 /* primitives, enums, arrays */
27        ||
28        Args.length == 1 && is(typeof({T t = forward!(args[0]);})) /* conversions */
29        ||
30        is(typeof(T(forward!args))) /* general constructors */)
31    {
32        static struct S
33        {
34            T payload;
35            this()(auto ref Args args)
36            {
37                static if (__traits(compiles, payload = forward!args))
38                    payload = forward!args;
39                else
40                    payload = T(forward!args);
41            }
42        }
43        if (__ctfe)
44        {
45            static if (__traits(compiles, chunk = T(forward!args)))
46                chunk = T(forward!args);
47            else static if (args.length == 1 && __traits(compiles, chunk = forward!(args[0])))
48                chunk = forward!(args[0]);
49            else assert(0, "CTFE emplace doesn't support "
50                ~ T.stringof ~ " from " ~ Args.stringof);
51        }
52        else
53        {
54            S* p = () @trusted { return cast(S*) &chunk; }();
55            static if (UT.sizeof > 0)
56                emplaceInitializer(*p);
57            p.__ctor(forward!args);
58        }
59    }
60    else static if (is(typeof(chunk.__ctor(forward!args))))
61    {
62        // This catches the rare case of local types that keep a frame pointer
63        emplaceInitializer(chunk);
64        chunk.__ctor(forward!args);
65    }
66    else
67    {
68        //We can't emplace. Try to diagnose a disabled postblit.
69        static assert(!(Args.length == 1 && is(Args[0] : T)),
70            "Cannot emplace a " ~ T.stringof ~ " because " ~ T.stringof ~
71            ".this(this) is annotated with @disable.");
72
73        //We can't emplace.
74        static assert(false,
75            T.stringof ~ " cannot be emplaced from " ~ Args[].stringof ~ ".");
76    }
77}
78
79// ditto
80static import core.internal.traits;
81void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args)
82if (is(UT == core.internal.traits.Unqual!UT))
83{
84    emplaceRef!(UT, UT)(chunk, forward!args);
85}
86
87/+
88Emplaces T.init.
89In contrast to `emplaceRef(chunk)`, there are no checks for disabled default
90constructors etc.
91+/
92void emplaceInitializer(T)(scope ref T chunk) nothrow pure @trusted
93if (!is(T == const) && !is(T == immutable) && !is(T == inout))
94{
95    import core.internal.traits : hasElaborateAssign;
96
97    static if (__traits(isZeroInit, T))
98    {
99        import core.stdc.string : memset;
100        memset(cast(void*) &chunk, 0, T.sizeof);
101    }
102    else static if (__traits(isScalar, T) ||
103                    T.sizeof <= 16 && !hasElaborateAssign!T && __traits(compiles, (){ T chunk; chunk = T.init; }))
104    {
105        chunk = T.init;
106    }
107    else static if (__traits(isStaticArray, T))
108    {
109        // For static arrays there is no initializer symbol created. Instead, we emplace elements one-by-one.
110        foreach (i; 0 .. T.length)
111        {
112            emplaceInitializer(chunk[i]);
113        }
114    }
115    else
116    {
117        import core.stdc.string : memcpy;
118        const initializer = __traits(initSymbol, T);
119        memcpy(cast(void*)&chunk, initializer.ptr, initializer.length);
120    }
121}
122
123@safe unittest
124{
125    static void testInitializer(T)()
126    {
127        // mutable T
128        {
129            T dst = void;
130            emplaceInitializer(dst);
131            assert(dst is T.init);
132        }
133
134        // shared T
135        {
136            shared T dst = void;
137            emplaceInitializer(dst);
138            assert(dst is shared(T).init);
139        }
140
141        // const T
142        {
143            const T dst = void;
144            static assert(!__traits(compiles, emplaceInitializer(dst)));
145        }
146    }
147
148    static struct ElaborateAndZero
149    {
150        int a;
151        this(this) {}
152    }
153
154    static struct ElaborateAndNonZero
155    {
156        int a = 42;
157        this(this) {}
158    }
159
160    static union LargeNonZeroUnion
161    {
162        byte[128] a = 1;
163    }
164
165    testInitializer!int();
166    testInitializer!double();
167    testInitializer!ElaborateAndZero();
168    testInitializer!ElaborateAndNonZero();
169    testInitializer!LargeNonZeroUnion();
170
171    static if (is(__vector(double[4])))
172    {
173        // DMD 2.096 and GDC 11.1 can't compare vectors with `is` so can't use
174        // testInitializer.
175        enum VE : __vector(double[4])
176        {
177            a = [1.0, 2.0, 3.0, double.nan],
178            b = [4.0, 5.0, 6.0, double.nan],
179        }
180        const VE expected = VE.a;
181        VE dst = VE.b;
182        shared VE sharedDst = VE.b;
183        emplaceInitializer(dst);
184        emplaceInitializer(sharedDst);
185        () @trusted {
186            import core.stdc.string : memcmp;
187            assert(memcmp(&expected, &dst, VE.sizeof) == 0);
188            assert(memcmp(&expected, cast(void*) &sharedDst, VE.sizeof) == 0);
189        }();
190        static assert(!__traits(compiles, emplaceInitializer(expected)));
191    }
192}
193
194/*
195Simple swap function.
196*/
197void swap(T)(ref T lhs, ref T rhs)
198{
199    import core.lifetime : move, moveEmplace;
200
201    T tmp = move(lhs);
202    moveEmplace(rhs, lhs);
203    moveEmplace(tmp, rhs);
204}
205