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