1251875Spetermodule core.internal.lifetime;
2251875Speter
3251875Speterimport core.lifetime : forward;
4251875Speter
5251875Speter/+
6251875SpeteremplaceRef is a package function for druntime internal use. It works like
7251875Speteremplace, but takes its argument by ref (as opposed to "by pointer").
8251875SpeterThis makes it easier to use, easier to be safe, and faster in a non-inline
9251875Speterbuild.
10251875SpeterFurthermore, emplaceRef optionally takes a type parameter, which specifies
11251875Speterthe type we want to build. This helps to build qualified objects on mutable
12251875Speterbuffer, without breaking the type system with unsafe casts.
13251875Speter+/
14251875Spetervoid emplaceRef(T, UT, Args...)(ref UT chunk, auto ref Args args)
15251875Speter{
16251875Speter    static if (args.length == 0)
17251875Speter    {
18251875Speter        static assert(is(typeof({static T i;})),
19251875Speter            "Cannot emplace a " ~ T.stringof ~ " because " ~ T.stringof ~
20251875Speter            ".this() is annotated with @disable.");
21251875Speter        static if (is(T == class)) static assert(!__traits(isAbstractClass, T),
22251875Speter            T.stringof ~ " is abstract and it can't be emplaced");
23251875Speter        emplaceInitializer(chunk);
24251875Speter    }
25251875Speter    else static if (
26251875Speter        !is(T == struct) && Args.length == 1 /* primitives, enums, arrays */
27251875Speter        ||
28251875Speter        Args.length == 1 && is(typeof({T t = forward!(args[0]);})) /* conversions */
29251875Speter        ||
30251875Speter        is(typeof(T(forward!args))) /* general constructors */)
31251875Speter    {
32251875Speter        static struct S
33251875Speter        {
34251875Speter            T payload;
35251875Speter            this()(auto ref Args args)
36251875Speter            {
37251875Speter                static if (__traits(compiles, payload = forward!args))
38251875Speter                    payload = forward!args;
39251875Speter                else
40251875Speter                    payload = T(forward!args);
41251875Speter            }
42251875Speter        }
43251875Speter        if (__ctfe)
44251875Speter        {
45251875Speter            static if (__traits(compiles, chunk = T(forward!args)))
46251875Speter                chunk = T(forward!args);
47251875Speter            else static if (args.length == 1 && __traits(compiles, chunk = forward!(args[0])))
48251875Speter                chunk = forward!(args[0]);
49251875Speter            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