xref: /netbsd-src/external/gpl3/gcc/dist/libphobos/libdruntime/core/internal/lifetime.d (revision 0a3071956a3a9fdebdbf7f338cf2d439b45fc728)
1 module core.internal.lifetime;
2 
3 import core.lifetime : forward;
4 
5 /+
6 emplaceRef is a package function for druntime internal use. It works like
7 emplace, but takes its argument by ref (as opposed to "by pointer").
8 This makes it easier to use, easier to be safe, and faster in a non-inline
9 build.
10 Furthermore, emplaceRef optionally takes a type parameter, which specifies
11 the type we want to build. This helps to build qualified objects on mutable
12 buffer, without breaking the type system with unsafe casts.
13 +/
emplaceRef(T,UT,Args...)14 void 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
80 static import core.internal.traits;
81 void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args)
82 if (is(UT == core.internal.traits.Unqual!UT))
83 {
84     emplaceRef!(UT, UT)(chunk, forward!args);
85 }
86 
87 /+
88 Emplaces T.init.
89 In contrast to `emplaceRef(chunk)`, there are no checks for disabled default
90 constructors etc.
91 +/
92 void emplaceInitializer(T)(scope ref T chunk) nothrow pure @trusted
93 if (!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 {
testInitializer(T)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;
thisElaborateAndZero151         this(this) {}
152     }
153 
154     static struct ElaborateAndNonZero
155     {
156         int a = 42;
this(this)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 /*
195 Simple swap function.
196 */
swap(T)197 void 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