1 /* 2 TODO 3 4 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 5 Authors: Adam Búš 6 */ 7 module btl.lifetime; 8 9 10 import std.meta : AliasSeq; 11 12 import core.lifetime : forward, move; 13 import std.range : isInputRange, ElementEncodingType; 14 import std.traits : isDynamicArray; 15 16 import btl.traits.range : isBtlInputRange; 17 18 /* 19 Type used as parameter for function pointer returned from `DestructorType`. 20 */ 21 public struct Evoid{ 22 23 } 24 25 //generate `DestructorTypes` alias 26 /+version(D_BetterC){}else 27 private string genDestructorTypes(){ 28 string result; 29 result.reserve(16*40); 30 31 import std.range : empty; 32 foreach(string _pure; ["pure", ""]) 33 foreach(string _nothrow; ["nothrow", ""]) 34 foreach(string _safe; ["@safe", "@system"]) 35 foreach(string _nogc; ["@nogc", ""]) 36 result ~= "void function(void* )" ~ _pure 37 ~ (_pure.empty?"":" ") ~ _nothrow 38 ~ ((_pure.empty && _nothrow.empty)?"":" ") ~ _safe 39 ~ ((_pure.empty && _nothrow.empty && _safe.empty)?"":" ") ~ _nogc 40 ~ ",\n"; 41 42 return result; 43 }+/ 44 45 46 //create all possible DestructorType types, DestructorType can return type with some hidden information and comparsion with it can fail (bug in D compiler). 47 //If type is created before calling DestructorType then DestructorType return existing type free of hidden informations and comparsion is ok. 48 public alias DtorTypes = AliasSeq!( 49 void function(Evoid* )pure nothrow @safe @nogc, 50 void function(Evoid* )pure nothrow @safe, 51 void function(Evoid* )pure nothrow @system @nogc, 52 void function(Evoid* )pure nothrow @system, 53 void function(Evoid* )pure @safe @nogc, 54 void function(Evoid* )pure @safe, 55 void function(Evoid* )pure @system @nogc, 56 void function(Evoid* )pure @system, 57 void function(Evoid* )nothrow @safe @nogc, 58 void function(Evoid* )nothrow @safe, 59 void function(Evoid* )nothrow @system @nogc, 60 void function(Evoid* )nothrow @system, 61 void function(Evoid* )@safe @nogc, 62 void function(Evoid* )@safe, 63 void function(Evoid* )@system @nogc, 64 void function(Evoid* )@system, 65 ); 66 67 68 69 /* 70 Check if type `Type` is of type destructor type (is(void function(Evoid* )pure nothrow @safe @nogc : Type)) 71 */ 72 public template isDtorType(Type){ 73 enum bool isDtorType = is( 74 void function(Evoid* )pure nothrow @safe @nogc : Type 75 ); 76 } 77 78 // 79 unittest{ 80 static assert(isDtorType!(void function(Evoid* )pure)); 81 static assert(isDtorType!(DtorType!long)); 82 static assert(!isDtorType!(long)); 83 } 84 85 86 87 public template ClassDtorType(Type) 88 if(is(Type == class)){ 89 import std.traits : Unqual, BaseClassesTuple; 90 import std.meta : AliasSeq; 91 92 alias Get(T) = T; 93 static void impl()(Evoid*){ 94 // generate a body that calls all the destructors in the chain, 95 // compiler should infer the intersection of attributes 96 foreach (B; AliasSeq!(Type, BaseClassesTuple!Type)) { 97 alias UB = Unqual!B; 98 99 // __dtor, i.e. B.~this 100 static if (__traits(hasMember, B, "__dtor")) 101 () { UB obj; obj.__dtor; } (); 102 // __xdtor, i.e. dtors for all RAII members 103 static if (__traits(hasMember, B, "__xdtor")) 104 () { UB obj; obj.__xdtor; } (); 105 } 106 } 107 108 alias ClassDtorType = Get!(typeof(&impl!())); 109 } 110 111 public template DtorType(Type){ 112 113 import std.traits : Unqual, isDynamicArray, BaseClassesTuple; 114 import std.range : ElementEncodingType; 115 import std.meta : AliasSeq; 116 117 alias Get(T) = T; 118 static void impl()(Evoid*){ 119 static if(is(Unqual!Type == void)){ 120 //nothing 121 } 122 else static if(is(Type == interface) || is(Type == class)){ 123 //nothing 124 } 125 else{ 126 Unqual!Type tmp; 127 } 128 } 129 130 alias DtorType = Get!(typeof(&impl!())); 131 } 132 133 134 135 //emplaceImpl & emplaceRangeImpl & emplaceClassImpl 136 public{ 137 void emplaceImpl(T, Args...)(ref T elm, auto ref Args args){ 138 static if(is(T == class) || is(T == interface)){ 139 import std.traits : Unqual; 140 141 static if(Args.length == 0){ 142 *(()@trusted => cast(Unqual!T*)&elm )() = null; 143 } 144 else static if(Args.length == 1 && is(Args[0] : T)){ 145 T src = args[0]; 146 147 ()@trusted{ 148 *cast(Unqual!T*)&elm = cast(Unqual!T)src; 149 }(); 150 } 151 else{ 152 static assert(0, "cannot emplace class with parameters " ~ args.stringof ~ ", try emplaceClassImpl instead"); 153 } 154 } 155 else{ 156 import core.lifetime : forward; 157 import core.internal.lifetime : emplaceRef; 158 159 emplaceRef!T(elm, forward!args); 160 } 161 } 162 163 void emplaceImpl(T, S)(ref T elm, return ref S src) 164 if(is(T == struct) && is(S == struct) && is(immutable T == immutable S)){ 165 import core.internal.lifetime : emplaceRef; 166 167 emplaceRef!T(elm, src); 168 169 } 170 171 void emplaceRangeImpl(T, Args...)(T[] slice, auto ref Args args){ 172 foreach(ref elm; slice) 173 emplaceImpl(elm, args); 174 175 } 176 177 void emplaceClassImpl(T, Args...)(T elm, auto ref Args args) 178 if(is(T == class)){ 179 import core.lifetime : forward, emplace; 180 181 emplace(elm, forward!args); 182 } 183 } 184 185 //destructImpl & destructRangeImpl & destructClassImpl 186 public{ 187 //destructImpl: 188 void destructImpl(bool initialize = false, T)(ref T obj){ 189 destructImpl!(initialize, DtorType!T)(obj); 190 } 191 void destructImpl(bool initialize, DestructorType, T)(ref T obj) 192 if(isDtorType!DestructorType){ 193 import std.traits : Unqual; 194 import btl.traits.assume; 195 196 if(false){ 197 DestructorType dt; 198 dt(null); 199 } 200 Unqual!T* mutableObj = (()@trusted => cast(Unqual!T*)&obj )(); 201 202 static if(is(T == class) || is(T == interface)){ 203 static if(initialize) 204 *mutableObj = null; 205 } 206 else{ 207 assumePureNoGcNothrow((Unqual!T* o)@trusted{ 208 destroy!initialize(*o); 209 })(mutableObj); 210 } 211 } 212 213 214 //destructRangeImpl: 215 void destructRangeImpl(bool initialize = false, T)(T[] slice){ 216 destructRangeImpl!(initialize, DtorType!T)(slice); 217 218 } 219 void destructRangeImpl(bool initialize, DestructorType, T)(T[] slice) 220 if(isDtorType!DestructorType){ 221 222 static if(is(T == void)){ 223 if(false){ 224 DestructorType dt; 225 dt(null); 226 } 227 228 static if(initialize){} //TODO 229 } 230 else{ 231 foreach(ref elm; slice) 232 destructImpl!(initialize, DestructorType, T)(elm); 233 234 } 235 } 236 237 238 //class destructor 239 private extern(C) void rt_finalize2(void* p, bool det = true, bool resetMemory = false)nothrow @safe @nogc pure; 240 241 //destructClassImpl: 242 void destructClassImpl(bool initialize, DestructorType, T)(T obj) 243 if(is(T == class) && isDtorType!DestructorType){ 244 import std.traits : Unqual; 245 import btl.traits.assume; 246 247 if(false){ 248 DestructorType dt; 249 dt(null); 250 } 251 252 ///C++ class 253 static if (__traits(getLinkage, T) == "C++"){ 254 Unqual!T mutableObj = (()@trusted => cast(Unqual!T)obj )(); 255 256 assumePureNoGcNothrow((Unqual!T o)@trusted{ 257 destroy!initialize(o); 258 })(mutableObj); 259 } 260 ///D class 261 static if(__traits(getLinkage, T) == "D"){ 262 263 assumePureNoGcNothrow((T o)@trusted{ 264 //resetMemory must be false because intrusiv pointers can contains control block with weak references. 265 rt_finalize2(cast(void*)o, true, initialize); 266 })(obj); 267 } 268 else static assert(0, "no impl"); 269 } 270 void destructClassImpl(bool initialize = false, T)(T obj) 271 if(is(T == class)){ 272 destructClassImpl!(initialize, ClassDtorType!T)(obj); 273 } 274 } 275 276 //transferEmplaceImpl 277 private{ 278 void transferEmplaceImpl(bool move, S, T)(scope ref T target, return scope ref S source) 279 if(is(immutable S == immutable T)){ 280 static if(move) 281 moveEmplaceImpl(target, source); 282 else 283 copyEmplaceImpl(target, source); 284 } 285 286 void transferEmplaceImpl(bool move, S, T)(scope ref T target, return scope ref S source) 287 if(!is(immutable S == immutable T) && is(immutable S : immutable T)){ 288 import std.range : ElementEncodingType; 289 import std.traits : 290 CopyTypeQualifiers, Unqual, 291 isDynamicArray, isStaticArray, 292 isPointer, PointerTarget, 293 isFunctionPointer, isDelegate; 294 295 296 static if(is(S == struct) || is(S == union)){ 297 transferEmplaceImpl!(move, CopyTypeQualifiers!(S, Unqual!T), T)(target, source); 298 } 299 else static if(isStaticArray!S){ 300 static if(S.length){ 301 transferEmplaceRangeImpl!move( 302 (()@trusted => target.ptr )(), 303 (()@trusted => source.ptr )(), 304 T.length 305 ); 306 } 307 } 308 else static if(is(S == class) || is(S == interface)){ 309 CopyTypeQualifiers!(S, Unqual!T) src = source; 310 transferEmplaceImpl!move(target, src); 311 } 312 else static if(isDynamicArray!S){ 313 auto src = ()@trusted{ 314 return cast(CopyTypeQualifiers!(ElementEncodingType!S, Unqual!(ElementEncodingType!T))[] )source; 315 }(); 316 transferEmplaceImpl!move(target, src); 317 } 318 else static if(isPointer!S){ 319 auto src = ()@trusted{ 320 return cast(CopyTypeQualifiers!(PointerTarget!S, Unqual!(PointerTarget!T))* )source; 321 }(); 322 transferEmplaceImpl!move(target, src); 323 } 324 else{ 325 CopyTypeQualifiers!(S, Unqual!T) src = source; 326 transferEmplaceImpl!move(target, src); 327 } 328 } 329 330 void transferEmplaceRangeImpl(bool move, S, T)(T* target, S* source, size_t length) 331 if(is(immutable S[] : immutable T[])){ 332 static if(move) 333 moveEmplaceRangeImpl!false(target, source, length); 334 else 335 copyEmplaceRangeImpl(target, source, length); 336 337 338 } 339 340 void transferAssignRangeImpl(bool move, S, T)(T* target, S* source, size_t length) 341 if(is(immutable S[] : immutable T[])){ 342 import std.traits : Unqual, hasElaborateAssign; 343 344 if(length == 0) 345 return; 346 347 static if (hasElaborateAssign!T){ 348 for (size_t i = 0; i < length; i++){ 349 static if(move) 350 *(()@trusted => target + i )() = .move(*(()@trusted => source + i )()); 351 else 352 *(()@trusted => target + i )() = *(()@trusted => source + i )(); 353 354 } 355 } 356 else // trivial copy 357 { 358 static assert(T.sizeof == S.sizeof); 359 360 () @trusted{ 361 import core.stdc.string : memcpy; 362 363 memcpy( 364 cast(Unqual!T*) target, 365 cast(Unqual!T*) source, 366 T.sizeof * length 367 ); 368 }(); 369 } 370 } 371 372 void transferAssignRange(bool move, T, S)(T[] target, ref S[] source){ 373 import btl.internal.traits : min; 374 375 assert(target.length <= source.length); 376 377 transferAssignRangeImpl!move( 378 (()@trusted => target.ptr )(), 379 (()@trusted => source.ptr )(), 380 target.length//min(target.length, source.length) 381 ); 382 } 383 384 void transferAssignRange(bool move, T, R)(T[] target, ref R source) 385 if(isBtlInputRange!R && is(immutable ElementEncodingType!R[] : immutable T[]) && !isDynamicArray!R){ 386 import std.range ; popFront; 387 import core.lifetime : mv = move; 388 389 foreach(ref elm; target){ 390 assert(!source.empty); 391 392 static if(move) 393 elm = mv(source.front); 394 else 395 elm = source.front; 396 397 source.popFront; 398 } 399 } 400 } 401 402 //moveEmplaceImpl 403 public{ 404 // https://github.com/dlang/druntime/blob/master/src/core/lifetime.d : moveEmplaceImpl 405 // target must be first-parameter, because in void-functions DMD + dip1000 allows it to take the place of a return-scope 406 void moveEmplaceImpl(S, T)(scope ref T target, return scope ref S source) 407 if(is(immutable S == immutable T)){ 408 // TODO: this assert pulls in half of phobos. we need to work out an alternative assert strategy. 409 // static if (!is(T == class) && hasAliasing!T) if (!__ctfe) 410 // { 411 // import std.exception : doesPointTo; 412 // assert(!doesPointTo(source, source) && !hasElaborateMove!T), 413 // "Cannot move object with internal pointer unless `opPostMove` is defined."); 414 // } 415 416 import core.internal.traits : hasElaborateAssign, isAssignable, hasElaborateMove, 417 hasElaborateDestructor, hasElaborateCopyConstructor, Unqual; 418 419 Unqual!T* mutableTarget = (()@trusted => cast(Unqual!T*)&target )(); 420 Unqual!S* mutableSource = (()@trusted => cast(Unqual!S*)&source )(); 421 422 static if (is(T == struct)) 423 { 424 425 // Unsafe when compiling without -preview=dip1000 426 assert((() @trusted => &source !is &target)(), "source and target must not be identical"); 427 428 429 430 static if (hasElaborateAssign!T || !isAssignable!T) 431 { 432 import core.stdc.string : memcpy; 433 () @trusted{ 434 memcpy( 435 mutableTarget, 436 mutableSource, 437 T.sizeof 438 ); 439 }(); 440 } 441 else 442 *mutableTarget = *mutableSource; 443 444 static if (hasElaborateMove!T) 445 __move_post_blt(target, source); 446 447 // If the source defines a destructor or a postblit hook, we must obliterate the 448 // object in order to avoid double freeing and undue aliasing 449 static if (hasElaborateDestructor!T || hasElaborateCopyConstructor!T) 450 { 451 // If there are members that are nested structs, we must take care 452 // not to erase any context pointers, so we might have to recurse 453 static if (__traits(isZeroInit, T)) 454 wipe(source); 455 else 456 wipe(source, ref () @trusted { return *cast(immutable(T)*) __traits(initSymbol, T).ptr; } ()); 457 } 458 } 459 else static if (__traits(isStaticArray, T)) 460 { 461 static if (T.length) 462 { 463 /+moveEmplaceRangeImpl!false( 464 (()@trusted => target.ptr )(), 465 (()@trusted => source.ptr )(), 466 T.length 467 );+/ 468 static if (!hasElaborateMove!T && 469 !hasElaborateDestructor!T && 470 !hasElaborateCopyConstructor!T) 471 { 472 // Single blit if no special per-instance handling is required 473 () @trusted 474 { 475 assert(source.ptr !is target.ptr, "source and target must not be identical"); 476 *cast(ubyte[T.sizeof]*) &target = *cast(ubyte[T.sizeof]*) &source; 477 } (); 478 } 479 else 480 { 481 for (size_t i = 0; i < source.length; ++i) 482 moveEmplaceImpl(target[i], source[i]); 483 } 484 485 } 486 } 487 else 488 { 489 // Primitive data (including pointers and arrays) or class - 490 // assignment works great 491 static if(is(S : T)){ 492 493 *mutableTarget = *mutableSource; 494 495 } 496 else 497 static assert(0, "no impl"); 498 } 499 } 500 501 void moveEmplaceImpl(S, T)(scope ref T target, return scope ref S source) 502 if(!is(immutable S == immutable T) && is(immutable S : immutable T)){ 503 transferEmplaceImpl!true(target, source); 504 } 505 506 void moveEmplaceRangeImpl(bool overlap = true, S, T)(T* target, S* source, size_t length) 507 if(is(immutable S[] : immutable T[])){ 508 import std.traits : hasElaborateMove, hasElaborateDestructor, hasElaborateCopyConstructor, Unqual; 509 510 if(length == 0) 511 return; 512 513 static if(is(immutable S* : immutable T*) 514 && !hasElaborateMove!T 515 && !hasElaborateDestructor!T 516 && !hasElaborateCopyConstructor!T 517 ){ 518 // Single blit if no special per-instance handling is required 519 /+() @trusted{ 520 assert(source.ptr !is target.ptr, "source and target must not be identical"); 521 *cast(ubyte[T.sizeof]*) target = *cast(ubyte[T.sizeof]*) source; 522 }();+/ 523 524 static assert(T.sizeof == S.sizeof); 525 526 () @trusted{ 527 import core.stdc.string : memcpy, memmove; 528 static if(overlap) 529 memmove( 530 cast(Unqual!T*) target, 531 cast(Unqual!T*) source, 532 T.sizeof * length 533 ); 534 else 535 memcpy( 536 cast(Unqual!T*) target, 537 cast(Unqual!T*) source, 538 T.sizeof * length 539 ); 540 }(); 541 } 542 else{ 543 544 if(!overlap || target < source){ 545 for(size_t i = 0; i < length; ++i){ 546 //moveEmplace(source[i], target[i]); 547 moveEmplaceImpl( 548 *(()@trusted => target + i )(), 549 *(()@trusted => source + i )() 550 ); 551 } 552 } 553 else{ 554 size_t i = length; 555 do{ 556 i -= 1; 557 moveEmplaceImpl( 558 *(()@trusted => target + i )(), 559 *(()@trusted => source + i )() 560 ); 561 562 }while(i > 0); 563 } 564 } 565 566 } 567 568 void moveAssignRangeImpl(S, T)(T* target, S* source, size_t length) 569 if(is(immutable S[] : immutable T[])){ 570 transferAssignRangeImpl!true(target, source, length); 571 } 572 573 void moveAssignRange(T, R)(T[] target, ref R source) 574 if(isBtlInputRange!R && is(immutable ElementEncodingType!R[] : immutable T[])){ 575 transferAssignRange!true(target, source); 576 } 577 578 @safe pure nothrow @nogc unittest{ 579 { 580 const int trg; 581 int src; 582 moveEmplaceImpl(trg, src); 583 } 584 { 585 const long trg; 586 int src; 587 moveEmplaceImpl(trg, src); 588 } 589 { 590 const int* trg; 591 int* src; 592 moveEmplaceImpl(trg, src); 593 } 594 { 595 const int* trg; 596 int* src; 597 moveEmplaceImpl(trg, src); 598 } 599 { 600 struct S{ 601 void* p; 602 int i; 603 } 604 const S trg; 605 S src; 606 moveEmplaceImpl(trg, src); 607 } 608 { 609 struct S{ 610 void* p; 611 int i; 612 ~this(){} 613 } 614 const S trg; 615 S src; 616 moveEmplaceImpl(trg, src); 617 } 618 { 619 struct S{ 620 void* p; 621 int i; 622 ~this(){} 623 } 624 const S[4] trg; 625 S[4] src; 626 moveEmplaceImpl(trg, src); 627 } 628 { 629 static class B{} 630 static class D : B{} 631 { 632 const B trg; 633 D src; 634 moveEmplaceImpl(trg, src); 635 } 636 { 637 const B[] trg; 638 D[] src; 639 moveEmplaceImpl(trg, src); 640 } 641 { 642 const B* trg; 643 D* src; 644 moveEmplaceImpl(trg, src); 645 } 646 { 647 const B[4] trg; 648 D[4] src; 649 moveEmplaceImpl(trg, src); 650 } 651 } 652 653 { 654 void delegate()@system trg; 655 void delegate()@safe src; 656 moveEmplaceImpl(trg, src); 657 658 } 659 660 } 661 662 // https://github.com/dlang/druntime/blob/master/src/core/lifetime.d : wipe 663 // wipes source after moving 664 pragma(inline, true) 665 private void wipe(T, Init...)(return scope ref T source, ref const scope Init initializer) @trusted 666 if (!Init.length || 667 ((Init.length == 1) && (is(immutable T == immutable Init[0])))) 668 { 669 static if (__traits(isStaticArray, T) && hasContextPointers!T) 670 { 671 for (auto i = 0; i < T.length; i++) 672 static if (Init.length) 673 wipe(source[i], initializer[0][i]); 674 else 675 wipe(source[i]); 676 } 677 else static if (is(T == struct) && hasContextPointers!T) 678 { 679 import core.internal.traits : anySatisfy; 680 static if (anySatisfy!(hasContextPointers, typeof(T.tupleof))) 681 { 682 static foreach (i; 0 .. T.tupleof.length - __traits(isNested, T)) 683 static if (Init.length) 684 wipe(source.tupleof[i], initializer[0].tupleof[i]); 685 else 686 wipe(source.tupleof[i]); 687 } 688 else 689 { 690 static if (__traits(isNested, T)) 691 enum sz = T.tupleof[$-1].offsetof; 692 else 693 enum sz = T.sizeof; 694 695 static if (Init.length) 696 *cast(ubyte[sz]*) &source = *cast(ubyte[sz]*) &initializer[0]; 697 else 698 *cast(ubyte[sz]*) &source = 0; 699 } 700 } 701 else 702 { 703 import core.internal.traits : hasElaborateAssign, isAssignable; 704 static if (Init.length) 705 { 706 static if (hasElaborateAssign!T || !isAssignable!T) 707 *cast(ubyte[T.sizeof]*) &source = *cast(ubyte[T.sizeof]*) &initializer[0]; 708 else 709 source = *cast(T*) &initializer[0]; 710 } 711 else 712 { 713 *cast(ubyte[T.sizeof]*) &source = 0; 714 } 715 } 716 } 717 718 // https://github.com/dlang/druntime/blob/master/src/core/lifetime.d : hasContextPointers 719 private enum bool hasContextPointers(T) = { 720 static if (__traits(isStaticArray, T)) 721 { 722 return hasContextPointers!(typeof(T.init[0])); 723 } 724 else static if (is(T == struct)) 725 { 726 import core.internal.traits : anySatisfy; 727 return __traits(isNested, T) || anySatisfy!(hasContextPointers, typeof(T.tupleof)); 728 } 729 else return false; 730 } (); 731 } 732 733 //copyEmplaceImpl 734 public{ 735 // https://github.com/dlang/druntime/blob/master/src/core/lifetime.d : copyEmplace 736 void copyEmplaceImpl(S, T)(ref T target, return auto ref S source) 737 if (is(immutable S == immutable T)){ 738 emplaceImpl(target, source); 739 /+import core.internal.traits : BaseElemOf, hasElaborateCopyConstructor, Unconst, Unqual; 740 741 // cannot have the following as simple template constraint due to nested-struct special case... 742 static if (!__traits(compiles, (ref S src) { T tgt = src; })) 743 { 744 alias B = BaseElemOf!T; 745 enum isNestedStruct = is(B == struct) && __traits(isNested, B); 746 static assert(isNestedStruct, "cannot copy-construct " ~ T.stringof ~ " from " ~ S.stringof); 747 } 748 749 Unqual!T* mutableTarget = (()@trusted => cast(Unqual!T*)&target )(); 750 Unqual!S* mutableSource = (()@trusted => cast(Unqual!S*)&source )(); 751 752 void blit() 753 { 754 import core.stdc.string : memcpy; 755 ()@trusted{ 756 memcpy(mutableTarget, mutableSource, T.sizeof); 757 }(); 758 } 759 760 static if (is(T == struct)) 761 { 762 static if (__traits(hasPostblit, T)) 763 { 764 blit(); 765 (cast() target).__xpostblit(); 766 } 767 else static if (__traits(hasCopyConstructor, T)) 768 { 769 emplace(cast(Unqual!(T)*) &target); // blit T.init 770 static if (__traits(isNested, T)) 771 { 772 // copy context pointer 773 *(cast(void**) &target.tupleof[$-1]) = cast(void*) source.tupleof[$-1]; 774 } 775 target.__ctor(source); // invoke copy ctor 776 } 777 else 778 { 779 blit(); // no opAssign 780 } 781 } 782 else static if (is(T == E[n], E, size_t n)) 783 { 784 static if(n){ 785 copyEmplaceRangeImpl( 786 (()@trusted => target.ptr )(), 787 (()@trusted => source.ptr )(), 788 T.length 789 ); 790 } 791 /+static if (hasElaborateCopyConstructor!E) 792 { 793 size_t i; 794 scope(failure){ 795 // destroy, in reverse order, what we've constructed so far 796 while (i--) 797 destroy(*cast(Unconst!(E)*) &target[i]); 798 799 } 800 for (i = 0; i < n; i++) 801 copyEmplaceImpl(target[i], source[i]); 802 } 803 else // trivial copy 804 { 805 blit(); // all elements at once 806 }+/ 807 } 808 else 809 { 810 *mutableTarget = *mutableSource; 811 }+/ 812 } 813 814 void copyEmplaceImpl(S, T)(ref T target, return ref S source) 815 if(!is(immutable S == immutable T) && is(immutable S : immutable T)){ 816 transferEmplaceImpl!false(target, source); 817 } 818 819 void copyEmplaceRangeImpl(S, T)(T* target, S* source, size_t length) 820 if(is(immutable S[] : immutable T[])){ 821 import std.traits : Unqual, hasElaborateCopyConstructor; 822 823 if(length == 0) 824 return; 825 826 static if (hasElaborateCopyConstructor!T){ 827 size_t i; 828 scope(failure){ 829 // destroy, in reverse order, what we've constructed so far 830 while (i--) 831 destroy(*cast(Unconst!T*) &target[i]); 832 833 } 834 for (i = 0; i < length; i++) 835 copyEmplaceImpl( 836 *(()@trusted => target + i )(), 837 *(()@trusted => source + i )() 838 ); 839 } 840 else // trivial copy 841 { 842 static assert(T.sizeof == S.sizeof); 843 844 () @trusted{ 845 import core.stdc.string : memcpy; 846 847 memcpy( 848 cast(Unqual!T*) target, 849 cast(Unqual!T*) source, 850 T.sizeof * length 851 ); 852 }(); 853 } 854 } 855 856 void copyAssignRangeImpl(S, T)(T* target, S* source, size_t length) 857 if(is(immutable S[] : immutable T[])){ 858 transferAssignRangeImpl!false(target, source, length); 859 } 860 861 void copyAssignRange(T, R)(T[] target, ref R source) 862 if(isBtlInputRange!R && is(immutable ElementEncodingType!R[] : immutable T[])){ 863 transferAssignRange!false(target, source); 864 } 865 866 @safe pure nothrow @nogc unittest{ 867 { 868 const int trg; 869 int src; 870 copyEmplaceImpl(trg, src); 871 } 872 { 873 const long trg; 874 int src; 875 copyEmplaceImpl(trg, src); 876 } 877 { 878 const int* trg; 879 int* src; 880 copyEmplaceImpl(trg, src); 881 } 882 { 883 const int* trg; 884 int* src; 885 copyEmplaceImpl(trg, src); 886 } 887 { 888 struct S{ 889 void* p; 890 int i; 891 } 892 const S trg; 893 S src; 894 copyEmplaceImpl(trg, src); 895 } 896 { 897 struct S{ 898 void* p; 899 int i; 900 ~this(){} 901 } 902 const S trg; 903 S src; 904 copyEmplaceImpl(trg, src); 905 } 906 { 907 struct S{ 908 void* p; 909 int i; 910 ~this(){} 911 } 912 const S[4] trg; 913 S[4] src; 914 copyEmplaceImpl(trg, src); 915 } 916 { 917 static class B{} 918 static class D : B{} 919 { 920 const B trg; 921 D src; 922 copyEmplaceImpl(trg, src); 923 } 924 { 925 const B[] trg; 926 D[] src; 927 copyEmplaceImpl(trg, src); 928 } 929 { 930 const B* trg; 931 D* src; 932 copyEmplaceImpl(trg, src); 933 } 934 { 935 const B[4] trg; 936 D[4] src; 937 copyEmplaceImpl(trg, src); 938 } 939 } 940 941 { 942 void delegate()@system trg; 943 void delegate()@safe src; 944 copyEmplaceImpl(trg, src); 945 946 } 947 948 } 949 } 950 951 952 953