1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /** 6 * Lifetime management functions, types and related exceptions. 7 * 8 * Copyright: Eugene Wissner 2019-2020. 9 * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 10 * Mozilla Public License, v. 2.0). 11 * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 12 * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/middle/tanya/memory/lifetime.d, 13 * tanya/memory/lifetime.d) 14 */ 15 module tanya.memory.lifetime; 16 17 import tanya.memory.allocator; 18 import tanya.meta.metafunction; 19 import tanya.meta.trait; 20 21 package(tanya) void destroyAllImpl(R, E)(R p) 22 { 23 static if (hasElaborateDestructor!E) 24 { 25 foreach (ref e; p) 26 { 27 destroy(e); 28 } 29 } 30 } 31 32 /** 33 * Constructs a new object of type $(D_PARAM T) in $(D_PARAM memory) with the 34 * given arguments. 35 * 36 * If $(D_PARAM T) is a $(D_KEYWORD class), emplace returns a class reference 37 * of type $(D_PARAM T), otherwise a pointer to the constructed object is 38 * returned. 39 * 40 * If $(D_PARAM T) is a nested class inside another class, $(D_PARAM outer) 41 * should be an instance of the outer class. 42 * 43 * $(D_PARAM args) are arguments for the constructor of $(D_PARAM T). If 44 * $(D_PARAM T) isn't an aggregate type and doesn't have a constructor, 45 * $(D_PARAM memory) can be initialized to `args[0]` if `Args.length == 1`, 46 * `Args[0]` should be implicitly convertible to $(D_PARAM T) then. 47 * 48 * Params: 49 * T = Constructed type. 50 * U = Type of the outer class if $(D_PARAM T) is a nested class. 51 * Args = Types of the constructor arguments if $(D_PARAM T) has a constructor 52 * or the type of the initial value. 53 * outer = Outer class instance if $(D_PARAM T) is a nested class. 54 * args = Constructor arguments if $(D_PARAM T) has a constructor or the 55 * initial value. 56 * 57 * Returns: New instance of type $(D_PARAM T) constructed in $(D_PARAM memory). 58 * 59 * Precondition: `memory.length == stateSize!T`. 60 * Postcondition: $(D_PARAM memory) and the result point to the same memory. 61 */ 62 T emplace(T, U, Args...)(void[] memory, U outer, auto ref Args args) 63 if (!isAbstractClass!T && isInnerClass!T && is(typeof(T.outer) == U)) 64 in 65 { 66 assert(memory.length >= stateSize!T); 67 } 68 out (result) 69 { 70 assert(memory.ptr is (() @trusted => cast(void*) result)()); 71 } 72 do 73 { 74 import tanya.memory.op : copy; 75 76 copy(typeid(T).initializer, memory); 77 78 auto result = (() @trusted => cast(T) memory.ptr)(); 79 result.outer = outer; 80 81 static if (is(typeof(result.__ctor(args)))) 82 { 83 result.__ctor(args); 84 } 85 86 return result; 87 } 88 89 /// ditto 90 T emplace(T, Args...)(void[] memory, auto ref Args args) 91 if (is(T == class) && !isAbstractClass!T && !isInnerClass!T) 92 in 93 { 94 assert(memory.length == stateSize!T); 95 } 96 out (result) 97 { 98 assert(memory.ptr is (() @trusted => cast(void*) result)()); 99 } 100 do 101 { 102 import tanya.memory.op : copy; 103 104 copy(typeid(T).initializer, memory); 105 106 auto result = (() @trusted => cast(T) memory.ptr)(); 107 static if (is(typeof(result.__ctor(args)))) 108 { 109 result.__ctor(args); 110 } 111 return result; 112 } 113 114 /// 115 @nogc nothrow pure @safe unittest 116 { 117 class C 118 { 119 int i = 5; 120 class Inner 121 { 122 int i; 123 124 this(int param) pure nothrow @safe @nogc 125 { 126 this.i = param; 127 } 128 } 129 } 130 ubyte[stateSize!C] memory1; 131 ubyte[stateSize!(C.Inner)] memory2; 132 133 auto c = emplace!C(memory1); 134 assert(c.i == 5); 135 136 auto inner = emplace!(C.Inner)(memory2, c, 8); 137 assert(c.i == 5); 138 assert(inner.i == 8); 139 assert(inner.outer is c); 140 } 141 142 /// ditto 143 T* emplace(T, Args...)(void[] memory, auto ref Args args) 144 if (!isAggregateType!T && (Args.length <= 1)) 145 in 146 { 147 assert(memory.length >= T.sizeof); 148 } 149 out (result) 150 { 151 assert(memory.ptr is result); 152 } 153 do 154 { 155 auto result = (() @trusted => cast(T*) memory.ptr)(); 156 static if (Args.length == 1) 157 { 158 *result = T(args[0]); 159 } 160 else 161 { 162 *result = T.init; 163 } 164 return result; 165 } 166 167 private void initializeOne(T)(ref void[] memory, ref T* result) @trusted 168 { 169 import tanya.memory.op : copy, fill; 170 171 static if (!hasElaborateAssign!T && isAssignable!T) 172 { 173 *result = T.init; 174 } 175 else static if (__VERSION__ >= 2083 // __traits(isZeroInit) available. 176 && __traits(isZeroInit, T)) 177 { 178 memory.ptr[0 .. T.sizeof].fill!0; 179 } 180 else 181 { 182 static immutable T init = T.init; 183 copy((&init)[0 .. 1], memory); 184 } 185 } 186 187 /// ditto 188 T* emplace(T, Args...)(void[] memory, auto ref Args args) 189 if (!isPolymorphicType!T && isAggregateType!T) 190 in 191 { 192 assert(memory.length >= T.sizeof); 193 } 194 out (result) 195 { 196 assert(memory.ptr is result); 197 } 198 do 199 { 200 auto result = (() @trusted => cast(T*) memory.ptr)(); 201 202 static if (Args.length == 0) 203 { 204 static assert(is(typeof({ static T t; })), 205 "Default constructor is disabled"); 206 initializeOne(memory, result); 207 } 208 else static if (is(typeof(result.__ctor(args)))) 209 { 210 initializeOne(memory, result); 211 result.__ctor(args); 212 } 213 else static if (Args.length == 1 && is(typeof({ T t = args[0]; }))) 214 { 215 import tanya.memory.op : copy; 216 217 ((ref arg) @trusted => 218 copy((cast(void*) &arg)[0 .. T.sizeof], memory))(args[0]); 219 static if (hasElaborateCopyConstructor!T) 220 { 221 result.__postblit(); 222 } 223 } 224 else static if (is(typeof({ T t = T(args); }))) 225 { 226 auto init = T(args); 227 (() @trusted => moveEmplace(init, *result))(); 228 } 229 else 230 { 231 static assert(false, 232 "Unable to construct value with the given arguments"); 233 } 234 return result; 235 } 236 237 /// 238 @nogc nothrow pure @safe unittest 239 { 240 ubyte[4] memory; 241 242 auto i = emplace!int(memory); 243 static assert(is(typeof(i) == int*)); 244 assert(*i == 0); 245 246 i = emplace!int(memory, 5); 247 assert(*i == 5); 248 249 static struct S 250 { 251 int i; 252 @disable this(); 253 @disable this(this); 254 this(int i) @nogc nothrow pure @safe 255 { 256 this.i = i; 257 } 258 } 259 auto s = emplace!S(memory, 8); 260 static assert(is(typeof(s) == S*)); 261 assert(s.i == 8); 262 } 263 264 private void deinitialize(bool zero, T)(ref T value) 265 { 266 static if (is(T == U[S], U, size_t S)) 267 { 268 foreach (ref e; value) 269 { 270 deinitialize!zero(e); 271 } 272 } 273 else 274 { 275 import tanya.memory.op : copy, fill; 276 277 static if (isNested!T) 278 { 279 // Don't override the context pointer. 280 enum size_t size = T.sizeof - (void*).sizeof; 281 } 282 else 283 { 284 enum size_t size = T.sizeof; 285 } 286 static if (zero) 287 { 288 fill!0((cast(void*) &value)[0 .. size]); 289 } 290 else 291 { 292 copy(typeid(T).initializer()[0 .. size], (&value)[0 .. 1]); 293 } 294 } 295 } 296 297 /** 298 * Moves $(D_PARAM source) into $(D_PARAM target) assuming that 299 * $(D_PARAM target) isn't initialized. 300 * 301 * Moving the $(D_PARAM source) copies it into the $(D_PARAM target) and places 302 * the $(D_PARAM source) into a valid but unspecified state, which means that 303 * after moving $(D_PARAM source) can be destroyed or assigned a new value, but 304 * accessing it yields an unspecified value. No postblits or destructors are 305 * called. If the $(D_PARAM target) should be destroyed before, use 306 * $(D_PSYMBOL move). 307 * 308 * $(D_PARAM source) and $(D_PARAM target) must be different objects. 309 * 310 * Params: 311 * T = Object type. 312 * source = Source object. 313 * target = Target object. 314 * 315 * See_Also: $(D_PSYMBOL move), 316 * $(D_PSYMBOL hasElaborateCopyConstructor), 317 * $(D_PSYMBOL hasElaborateDestructor). 318 * 319 * Precondition: `&source !is &target`. 320 */ 321 void moveEmplace(T)(ref T source, ref T target) @system 322 in 323 { 324 assert(&source !is &target, "Source and target must be different"); 325 } 326 do 327 { 328 static if (is(T == struct) || isStaticArray!T) 329 { 330 import tanya.memory.op : copy; 331 332 copy((&source)[0 .. 1], (&target)[0 .. 1]); 333 334 static if (hasElaborateCopyConstructor!T || hasElaborateDestructor!T) 335 { 336 static if (__VERSION__ >= 2083) // __traits(isZeroInit) available. 337 { 338 deinitialize!(__traits(isZeroInit, T))(source); 339 } 340 else 341 { 342 if (typeid(T).initializer().ptr is null) 343 { 344 deinitialize!true(source); 345 } 346 else 347 { 348 deinitialize!false(source); 349 } 350 } 351 } 352 } 353 else 354 { 355 target = source; 356 } 357 } 358 359 /// 360 @nogc nothrow pure @system unittest 361 { 362 static struct S 363 { 364 int member = 5; 365 366 this(this) @nogc nothrow pure @safe 367 { 368 assert(false); 369 } 370 } 371 S source, target = void; 372 moveEmplace(source, target); 373 assert(target.member == 5); 374 375 int x1 = 5, x2; 376 moveEmplace(x1, x2); 377 assert(x2 == 5); 378 } 379 380 /** 381 * Moves $(D_PARAM source) into $(D_PARAM target) assuming that 382 * $(D_PARAM target) isn't initialized. 383 * 384 * Moving the $(D_PARAM source) copies it into the $(D_PARAM target) and places 385 * the $(D_PARAM source) into a valid but unspecified state, which means that 386 * after moving $(D_PARAM source) can be destroyed or assigned a new value, but 387 * accessing it yields an unspecified value. $(D_PARAM target) is destroyed before 388 * the new value is assigned. If $(D_PARAM target) isn't initialized and 389 * therefore shouldn't be destroyed, $(D_PSYMBOL moveEmplace) can be used. 390 * 391 * If $(D_PARAM target) isn't specified, $(D_PSYMBOL move) returns the source 392 * as rvalue without calling its copy constructor or destructor. 393 * 394 * $(D_PARAM source) and $(D_PARAM target) are the same object, 395 * $(D_PSYMBOL move) does nothing. 396 * 397 * Params: 398 * T = Object type. 399 * source = Source object. 400 * target = Target object. 401 * 402 * See_Also: $(D_PSYMBOL moveEmplace). 403 */ 404 void move(T)(ref T source, ref T target) 405 { 406 if ((() @trusted => &source is &target)()) 407 { 408 return; 409 } 410 static if (hasElaborateDestructor!T) 411 { 412 target.__xdtor(); 413 } 414 (() @trusted => moveEmplace(source, target))(); 415 } 416 417 /// ditto 418 T move(T)(ref T source) @trusted 419 { 420 static if (hasElaborateCopyConstructor!T || hasElaborateDestructor!T) 421 { 422 T target = void; 423 moveEmplace(source, target); 424 return target; 425 } 426 else 427 { 428 return source; 429 } 430 } 431 432 /// 433 @nogc nothrow pure @safe unittest 434 { 435 static struct S 436 { 437 int member = 5; 438 439 this(this) @nogc nothrow pure @safe 440 { 441 assert(false); 442 } 443 } 444 S source, target = void; 445 move(source, target); 446 assert(target.member == 5); 447 assert(move(target).member == 5); 448 449 int x1 = 5, x2; 450 move(x1, x2); 451 assert(x2 == 5); 452 assert(move(x2) == 5); 453 } 454 455 /** 456 * Exchanges the values of $(D_PARAM a) and $(D_PARAM b). 457 * 458 * $(D_PSYMBOL swap) moves the contents of $(D_PARAM a) and $(D_PARAM b) 459 * without calling its postblits or destructors. 460 * 461 * Params: 462 * a = The first object. 463 * b = The second object. 464 */ 465 void swap(T)(ref T a, ref T b) @trusted 466 { 467 T tmp = void; 468 moveEmplace(a, tmp); 469 moveEmplace(b, a); 470 moveEmplace(tmp, b); 471 } 472 473 /// 474 @nogc nothrow pure @safe unittest 475 { 476 int a = 3, b = 5; 477 swap(a, b); 478 assert(a == 5); 479 assert(b == 3); 480 } 481 482 /** 483 * Forwards its argument list preserving $(D_KEYWORD ref) and $(D_KEYWORD out) 484 * storage classes. 485 * 486 * $(D_PSYMBOL forward) accepts a list of variables or literals. It returns an 487 * argument list of the same length that can be for example passed to a 488 * function accepting the arguments of this type. 489 * 490 * Params: 491 * args = Argument list. 492 * 493 * Returns: $(D_PARAM args) with their original storage classes. 494 */ 495 template forward(args...) 496 { 497 static if (args.length == 0) 498 { 499 alias forward = AliasSeq!(); 500 } 501 else static if (__traits(isRef, args[0]) || __traits(isOut, args[0])) 502 { 503 static if (args.length == 1) 504 { 505 alias forward = args[0]; 506 } 507 else 508 { 509 alias forward = AliasSeq!(args[0], forward!(args[1 .. $])); 510 } 511 } 512 else 513 { 514 @property auto forwardOne() 515 { 516 return move(args[0]); 517 } 518 static if (args.length == 1) 519 { 520 alias forward = forwardOne; 521 } 522 else 523 { 524 alias forward = AliasSeq!(forwardOne, forward!(args[1 .. $])); 525 } 526 } 527 } 528 529 /// 530 @nogc nothrow pure @safe unittest 531 { 532 static assert(is(typeof((int i) { int v = forward!i; }))); 533 static assert(is(typeof((ref int i) { int v = forward!i; }))); 534 static assert(is(typeof({ 535 void f(int i, ref int j, out int k) 536 { 537 f(forward!(i, j, k)); 538 } 539 }))); 540 }