1 /** 2 * Generic tagged union and algebraic data type implementations. 3 * 4 * Copyright: Copyright 2015-2019, Sönke Ludwig. 5 * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 6 * Authors: Sönke Ludwig 7 */ 8 module taggedalgebraic.taggedunion; 9 10 // scheduled for deprecation 11 static import taggedalgebraic.visit; 12 13 alias visit = taggedalgebraic.visit.visit; 14 15 import std.algorithm.mutation : move, swap; 16 import std.meta; 17 import std.range : isOutputRange; 18 import std.traits : EnumMembers, FieldNameTuple, Unqual, hasUDA, isInstanceOf; 19 20 /** Implements a generic tagged union type. 21 22 This struct takes a `union` or `struct` declaration as an input and builds 23 an algebraic data type from its fields, using an automatically generated 24 `Kind` enumeration to identify which field of the union is currently used. 25 Multiple fields with the same value are supported. 26 27 For each field defined by `U` a number of convenience members are generated. 28 For a given field "foo", these fields are: 29 30 $(UL 31 $(LI `static foo(value)` - returns a new tagged union with the specified value) 32 $(LI `isFoo` - equivalent to `kind == Kind.foo`) 33 $(LI `setFoo(value)` - equivalent to `set!(Kind.foo)(value)`) 34 $(LI `getFoo` - equivalent to `get!(Kind.foo)`) 35 ) 36 */ 37 template TaggedUnion(U) if (is(U == union) || is(U == struct) || is(U == enum)) 38 { 39 align(commonAlignment!(UnionKindTypes!(UnionFieldEnum!U))) struct TaggedUnion 40 { 41 import std.traits : FieldTypeTuple, FieldNameTuple, Largest, 42 hasElaborateCopyConstructor, hasElaborateDestructor, isCopyable; 43 import std.meta : templateOr; 44 import std.ascii : toUpper; 45 46 alias FieldDefinitionType = U; 47 48 /// A type enum that identifies the type of value currently stored. 49 alias Kind = UnionFieldEnum!U; 50 51 alias FieldTypes = UnionKindTypes!Kind; 52 alias fieldNames = UnionKindNames!Kind; 53 54 static assert(FieldTypes.length > 0, 55 "The TaggedUnions's union type must have at least one field."); 56 static assert(FieldTypes.length == fieldNames.length); 57 58 package alias FieldTypeByName(string name) = FieldTypes[__traits(getMember, Kind, name)]; 59 60 private 61 { 62 static if (isUnitType!(FieldTypes[0]) || __VERSION__ < 2072) 63 { 64 void[Largest!FieldTypes.sizeof] m_data; 65 } 66 else 67 { 68 union Dummy 69 { 70 FieldTypes[0] initField; 71 void[Largest!FieldTypes.sizeof] data; 72 alias data this; 73 } 74 75 Dummy m_data = {initField: FieldTypes[0].init}; 76 } 77 Kind m_kind; 78 } 79 80 this(TaggedUnion other) 81 { 82 rawSwap(this, other); 83 } 84 85 void opAssign(TaggedUnion other) 86 { 87 rawSwap(this, other); 88 } 89 90 static foreach (ti; UniqueTypes!FieldTypes) 91 static if (!is(FieldTypes[ti] == void)) 92 { 93 this(FieldTypes[ti] value) 94 { 95 static if (isUnitType!(FieldTypes[ti])) 96 set!(cast(Kind) ti)(); 97 else 98 set!(cast(Kind) ti)(.move(value)); 99 } 100 101 void opAssign(FieldTypes[ti] value) 102 { 103 static if (isUnitType!(FieldTypes[ti])) 104 set!(cast(Kind) ti)(); 105 else 106 set!(cast(Kind) ti)(.move(value)); 107 } 108 } 109 110 // disable default construction if first type is not a null/Void type 111 static if (!isUnitType!(FieldTypes[0]) && __VERSION__ < 2072) 112 { 113 @disable this(); 114 } 115 116 // postblit constructor 117 static if (!allSatisfy!(templateOr!(isCopyable, isUnitType), FieldTypes)) 118 { 119 @disable this(this); 120 } 121 else static if (anySatisfy!(hasElaborateCopyConstructor, FieldTypes)) 122 { 123 this(this) 124 { 125 switch (m_kind) 126 { 127 default: 128 break; 129 foreach (i, tname; fieldNames) 130 { 131 alias T = FieldTypes[i]; 132 static if (hasElaborateCopyConstructor!T) 133 { 134 case __traits(getMember, Kind, tname): 135 static if (hasUDA!(U, typeof(forceNothrowPostblit()))) 136 { 137 try 138 typeid(T).postblit(cast(void*)&trustedGet!T()); 139 catch (Exception e) 140 assert(false, e.msg); 141 } 142 else 143 { 144 typeid(T).postblit(cast(void*)&trustedGet!T()); 145 } 146 return; 147 } 148 } 149 } 150 } 151 } 152 153 // destructor 154 static if (anySatisfy!(hasElaborateDestructor, FieldTypes)) 155 { 156 ~this() 157 { 158 final switch (m_kind) 159 { 160 foreach (i, tname; fieldNames) 161 { 162 alias T = FieldTypes[i]; 163 case __traits(getMember, Kind, tname): 164 static if (hasElaborateDestructor!T) 165 { 166 .destroy(trustedGet!T); 167 } 168 return; 169 } 170 } 171 } 172 } 173 174 /// Enables conversion or extraction of the stored value. 175 T opCast(T)() 176 { 177 import std.conv : to; 178 179 final switch (m_kind) 180 { 181 foreach (i, FT; FieldTypes) 182 { 183 case __traits(getMember, Kind, fieldNames[i]): 184 static if (is(typeof(trustedGet!FT) : T)) 185 return trustedGet!FT; 186 else static if (is(typeof(to!T(trustedGet!FT)))) 187 { 188 return to!T(trustedGet!FT); 189 } 190 else 191 { 192 assert(false, 193 "Cannot cast a " ~ fieldNames[i] ~ " value of type " 194 ~ FT.stringof ~ " to " ~ T.stringof); 195 } 196 } 197 } 198 assert(false); // never reached 199 } 200 /// ditto 201 T opCast(T)() const 202 { 203 // this method needs to be duplicated because inout doesn't work with to!() 204 import std.conv : to; 205 206 final switch (m_kind) 207 { 208 foreach (i, FT; FieldTypes) 209 { 210 case __traits(getMember, Kind, fieldNames[i]): 211 static if (is(typeof(trustedGet!FT) : T)) 212 return trustedGet!FT; 213 else static if (is(typeof(to!T(trustedGet!FT)))) 214 { 215 return to!T(trustedGet!FT); 216 } 217 else 218 { 219 assert(false, 220 "Cannot cast a " ~ fieldNames[i] ~ " value of type" 221 ~ FT.stringof ~ " to " ~ T.stringof); 222 } 223 } 224 } 225 assert(false); // never reached 226 } 227 228 /// Enables equality comparison with the stored value. 229 bool opEquals()(auto ref inout(TaggedUnion) other) inout 230 { 231 if (this.kind != other.kind) 232 return false; 233 234 final switch (this.kind) 235 { 236 foreach (i, fname; TaggedUnion!U.fieldNames) 237 case __traits(getMember, Kind, fname): 238 return trustedGet!(FieldTypes[i]) == other.trustedGet!(FieldTypes[i]); 239 } 240 assert(false); // never reached 241 } 242 243 /// The type ID of the currently stored value. 244 @property Kind kind() const 245 { 246 return m_kind; 247 } 248 249 static foreach (i, name; fieldNames) 250 { 251 // NOTE: using getX/setX here because using just x would be prone to 252 // misuse (attempting to "get" a value for modification when 253 // a different kind is set instead of assigning a new value) 254 mixin("alias set" ~ pascalCase(name) ~ " = set!(Kind." ~ name ~ ");"); 255 mixin("@property bool is" ~ pascalCase( 256 name) ~ "() const { return m_kind == Kind." ~ name ~ "; }"); 257 258 static if (name[$ - 1] == '_') 259 { 260 mixin("alias set" ~ pascalCase(name[0 .. $ - 1]) ~ " = set!(Kind." ~ name ~ ");"); 261 mixin("@property bool is" ~ pascalCase( 262 name[0 .. $ - 1]) ~ "() const { return m_kind == Kind." ~ name ~ "; }"); 263 } 264 265 static if (!isUnitType!(FieldTypes[i])) 266 { 267 mixin("alias " ~ name ~ "Value = value!(Kind." ~ name ~ ");"); 268 269 // remove trailing underscore from names like "int_" 270 static if (name[$ - 1] == '_') 271 mixin("alias " ~ name[0 .. $ - 1] ~ "Value = value!(Kind." ~ name ~ ");"); 272 273 mixin("static TaggedUnion " ~ name ~ "(FieldTypes[" ~ i.stringof ~ "] value)" 274 ~ "{ TaggedUnion tu; tu.set!(Kind." ~ name ~ ")(.move(value)); return tu; }"); 275 276 // TODO: define assignment operator for unique types 277 } 278 else 279 { 280 mixin( 281 "static @property TaggedUnion " ~ name 282 ~ "() { TaggedUnion tu; tu.set!(Kind." ~ name ~ "); return tu; }"); 283 } 284 } 285 286 /** Checks whether the currently stored value has a given type. 287 */ 288 @property bool hasType(T)() const 289 { 290 static assert(staticIndexOf!(T, FieldTypes) >= 0, 291 "Type " ~ T.stringof ~ " not part of " ~ FieldTypes.stringof); 292 293 final switch (this.kind) 294 { 295 static foreach (i, n; fieldNames) 296 { 297 case __traits(getMember, Kind, n): 298 return is(FieldTypes[i] == T); 299 } 300 } 301 } 302 303 /** Accesses the contained value by reference. 304 305 The specified `kind` must equal the current value of the `this.kind` 306 property. Setting a different type must be done with `set` or `opAssign` 307 instead. 308 309 See_Also: `set`, `opAssign` 310 */ 311 @property ref inout(FieldTypes[kind]) value(Kind kind)() inout 312 { 313 if (this.kind != kind) 314 { 315 enum msg(.string k_is) = "Attempt to get kind " ~ kind.stringof 316 ~ " from tagged union with kind " ~ k_is; 317 final switch (this.kind) 318 { 319 static foreach (i, n; fieldNames) 320 case __traits(getMember, Kind, n): 321 assert(false, msg!n); 322 } 323 } 324 //return trustedGet!(FieldTypes[kind]); 325 return *() @trusted { 326 return cast(const(FieldTypes[kind])*) m_data.ptr; 327 }(); 328 } 329 330 /** Accesses the contained value by reference. 331 332 The specified type `T` must equal the type of the currently set value. 333 Setting a different type must be done with `set` or `opAssign` instead. 334 335 See_Also: `set`, `opAssign` 336 */ 337 @property ref inout(T) value(T)() inout 338 { 339 static assert(staticIndexOf!(T, FieldTypes) >= 0, 340 "Type " ~ T.stringof ~ " not part of " ~ FieldTypes.stringof); 341 342 final switch (this.kind) 343 { 344 static foreach (i, n; fieldNames) 345 { 346 case __traits(getMember, Kind, n): 347 static if (is(FieldTypes[i] == T)) 348 return trustedGet!T; 349 else 350 assert(false, "Attempting to get type " ~ T.stringof ~ " from a TaggedUnion with type " 351 ~ FieldTypes[__traits(getMember, Kind, n)].stringof); 352 } 353 } 354 } 355 356 /** Sets a new value of the specified `kind`. 357 */ 358 ref FieldTypes[kind] set(Kind kind)(FieldTypes[kind] value) 359 if (!isUnitType!(FieldTypes[kind])) 360 { 361 if (m_kind != kind) 362 { 363 destroy(this); 364 m_data.rawEmplace(value); 365 } 366 else 367 { 368 rawSwap(trustedGet!(FieldTypes[kind]), value); 369 } 370 m_kind = kind; 371 372 return trustedGet!(FieldTypes[kind]); 373 } 374 375 /** Sets a `void` value of the specified kind. 376 */ 377 void set(Kind kind)() if (isUnitType!(FieldTypes[kind])) 378 { 379 if (m_kind != kind) 380 { 381 destroy(this); 382 } 383 m_kind = kind; 384 } 385 386 /** Converts the contained value to a string. 387 388 The format output by this method is "(kind: value)", where "kind" is 389 the enum name of the currently stored type and "value" is the string 390 representation of the stored value. 391 */ 392 void toString(W)(ref W w) const if (isOutputRange!(W, char)) 393 { 394 import std.range.primitives : put; 395 import std.conv : text; 396 import std.format : FormatSpec, formatValue; 397 398 final switch (m_kind) 399 { 400 foreach (i, v; EnumMembers!Kind) 401 { 402 case v: 403 enum vstr = text(v); 404 static if (isUnitType!(FieldTypes[i])) 405 put(w, vstr); 406 else 407 { 408 // NOTE: using formatValue instead of formattedWrite 409 // because formattedWrite does not work for 410 // non-copyable types 411 enum prefix = "(" ~ vstr ~ ": "; 412 enum suffix = ")"; 413 put(w, prefix); 414 FormatSpec!char spec; 415 formatValue(w, value!v, spec); 416 put(w, suffix); 417 } 418 break; 419 } 420 } 421 } 422 423 package @trusted @property ref inout(T) trustedGet(T)() inout 424 { 425 return *cast(inout(T)*) m_data.ptr; 426 } 427 } 428 } 429 430 /// 431 @safe nothrow unittest 432 { 433 union Kinds 434 { 435 int count; 436 string text; 437 } 438 439 alias TU = TaggedUnion!Kinds; 440 441 // default initialized to the first field defined 442 TU tu; 443 assert(tu.kind == TU.Kind.count); 444 assert(tu.isCount); // qequivalent to the line above 445 assert(!tu.isText); 446 assert(tu.value!(TU.Kind.count) == int.init); 447 448 // set to a specific count 449 tu.setCount(42); 450 assert(tu.isCount); 451 assert(tu.countValue == 42); 452 assert(tu.value!(TU.Kind.count) == 42); 453 assert(tu.value!int == 42); // can also get by type 454 assert(tu.countValue == 42); 455 456 // assign a new tagged algebraic value 457 tu = TU.count(43); 458 459 // test equivalence with other tagged unions 460 assert(tu == TU.count(43)); 461 assert(tu != TU.count(42)); 462 assert(tu != TU.text("hello")); 463 464 // modify by reference 465 tu.countValue++; 466 assert(tu.countValue == 44); 467 468 // set the second field 469 tu.setText("hello"); 470 assert(!tu.isCount); 471 assert(tu.isText); 472 assert(tu.kind == TU.Kind.text); 473 assert(tu.textValue == "hello"); 474 475 // unique types can also be directly constructed 476 tu = TU(12); 477 assert(tu.countValue == 12); 478 tu = TU("foo"); 479 assert(tu.textValue == "foo"); 480 } 481 482 /// 483 @safe nothrow unittest 484 { 485 // Enum annotations supported since DMD 2.082.0. The mixin below is 486 // necessary to keep the parser happy on older versions. 487 static if (__VERSION__ >= 2082) 488 { 489 alias myint = int; 490 // tagged unions can be defined in terms of an annotated enum 491 mixin(q{enum E { 492 none, 493 @string text 494 }}); 495 496 alias TU = TaggedUnion!E; 497 static assert(is(TU.Kind == E)); 498 499 TU tu; 500 assert(tu.isNone); 501 assert(tu.kind == E.none); 502 503 tu.setText("foo"); 504 assert(tu.kind == E.text); 505 assert(tu.textValue == "foo"); 506 } 507 } 508 509 unittest 510 { // test for name clashes 511 union U 512 { 513 .string string; 514 } 515 516 alias TU = TaggedUnion!U; 517 TU tu; 518 tu = TU..string("foo"); 519 assert(tu.isString); 520 assert(tu.stringValue == "foo"); 521 } 522 523 unittest 524 { // test woraround for Phobos issue 19696 525 struct T 526 { 527 struct F 528 { 529 int num; 530 } 531 532 alias Payload = TaggedUnion!F; 533 Payload payload; 534 alias payload this; 535 } 536 537 struct U 538 { 539 T t; 540 } 541 542 alias TU = TaggedUnion!U; 543 static assert(is(TU.FieldTypes[0] == T)); 544 } 545 546 unittest 547 { // non-copyable types 548 import std.traits : isCopyable; 549 550 struct S 551 { 552 @disable this(this); 553 } 554 555 struct U 556 { 557 int i; 558 S s; 559 } 560 561 alias TU = TaggedUnion!U; 562 static assert(!isCopyable!TU); 563 564 auto tu = TU(42); 565 tu.setS(S.init); 566 } 567 568 unittest 569 { // alignment 570 union S1 571 { 572 int v; 573 } 574 575 union S2 576 { 577 ulong v; 578 } 579 580 union S3 581 { 582 void* v; 583 } 584 585 // sanity check for the actual checks - this may differ on non-x86 architectures 586 static assert(S1.alignof == 4); 587 static assert(S2.alignof == 8); 588 version (D_LP64) 589 static assert(S3.alignof == 8); 590 else 591 static assert(S3.alignof == 4); 592 593 // test external struct alignment 594 static assert(TaggedUnion!S1.alignof == 4); 595 static assert(TaggedUnion!S2.alignof == 8); 596 version (D_LP64) 597 static assert(TaggedUnion!S3.alignof == 8); 598 else 599 static assert(TaggedUnion!S3.alignof == 4); 600 601 // test internal struct alignment 602 TaggedUnion!S1 s1; 603 assert((cast(ubyte*)&s1.vValue() - cast(ubyte*)&s1) % 4 == 0); 604 TaggedUnion!S1 s2; 605 assert((cast(ubyte*)&s2.vValue() - cast(ubyte*)&s2) % 8 == 0); 606 TaggedUnion!S1 s3; 607 version (D_LP64) 608 assert((cast(ubyte*)&s3.vValue() - cast(ubyte*)&s3) % 8 == 0); 609 else 610 assert((cast(ubyte*)&s3.vValue() - cast(ubyte*)&s3) % 4 == 0); 611 } 612 613 unittest 614 { // toString 615 import std.conv : to; 616 617 static struct NoCopy 618 { 619 @disable this(this); 620 string toString() const 621 { 622 return "foo"; 623 } 624 } 625 626 union U 627 { 628 Void empty; 629 int i; 630 NoCopy noCopy; 631 } 632 633 TaggedUnion!U val; 634 assert(val.to!string == "empty"); 635 val.setI(42); 636 assert(val.to!string == "(i: 42)"); 637 val.setNoCopy(NoCopy.init); 638 assert(val.to!string == "(noCopy: foo)"); 639 } 640 641 unittest 642 { // null members should be assignable 643 union U 644 { 645 int i; 646 typeof(null) Null; 647 } 648 649 TaggedUnion!U val; 650 val = null; 651 assert(val.kind == val.Kind.Null); 652 val = TaggedUnion!U(null); 653 assert(val.kind == val.Kind.Null); 654 } 655 656 unittest 657 { // make sure field names don't conflict with function names 658 union U 659 { 660 int to; 661 int move; 662 int swap; 663 } 664 665 TaggedUnion!U val; 666 val.setMove(1); 667 } 668 669 unittest 670 { // support trailing underscores properly 671 union U 672 { 673 int int_; 674 } 675 676 TaggedUnion!U val; 677 678 val = TaggedUnion!U.int_(10); 679 assert(val.int_Value == 10); 680 assert(val.intValue == 10); 681 assert(val.isInt_); 682 assert(val.isInt); 683 val.setInt_(20); 684 val.setInt(20); 685 assert(val.intValue == 20); 686 } 687 688 @property auto forceNothrowPostblit() 689 { 690 if (!__ctfe) 691 assert(false, "@forceNothrowPostblit must only be used as a UDA."); 692 static struct R 693 { 694 } 695 696 return R.init; 697 } 698 699 nothrow unittest 700 { 701 static struct S 702 { 703 this(this) nothrow 704 { 705 } 706 } 707 708 @forceNothrowPostblit struct U 709 { 710 S s; 711 } 712 713 alias TU = TaggedUnion!U; 714 715 TU tu, tv; 716 tu = S.init; 717 tv = tu; 718 } 719 720 enum isUnitType(T) = is(T == Void) || is(T == void) || is(T == typeof(null)); 721 722 private string pascalCase(string camel_case) 723 { 724 if (!__ctfe) 725 assert(false); 726 import std.ascii : toUpper; 727 728 return camel_case[0].toUpper ~ camel_case[1 .. $]; 729 } 730 731 /** Maps a kind enumeration value to the corresponding field type. 732 733 `kind` must be a value of the `TaggedAlgebraic!T.Kind` enumeration. 734 */ 735 template TypeOf(alias kind) if (is(typeof(kind) == enum)) 736 { 737 import std.traits : FieldTypeTuple, TemplateArgsOf; 738 import std.typecons : ReplaceType; 739 740 static if (isInstanceOf!(UnionFieldEnum, typeof(kind))) 741 { 742 alias U = TemplateArgsOf!(typeof(kind)); 743 alias FT = FieldTypeTuple!U[kind]; 744 } 745 else 746 { 747 alias U = typeof(kind); 748 alias Types = UnionKindTypes!(typeof(kind)); 749 alias uda = AliasSeq!(__traits(getAttributes, kind)); 750 static if (uda.length == 0) 751 alias FT = void; 752 else 753 alias FT = uda[0]; 754 } 755 756 // NOTE: ReplaceType has issues with certain types, such as a class 757 // declaration like this: class C : D!C {} 758 // For this reason, we test first if it compiles and only then use it. 759 // It also replaces a type with the contained "alias this" type under 760 // certain conditions, so we make a second check to see heuristically 761 // if This is actually present in FT 762 // 763 // Phobos issues: 19696, 19697 764 static if (is(ReplaceType!(This, U, FT)) && !is(ReplaceType!(This, void, FT))) 765 alias TypeOf = ReplaceType!(This, U, FT); 766 else 767 alias TypeOf = FT; 768 } 769 770 /// 771 unittest 772 { 773 static struct S 774 { 775 int a; 776 string b; 777 string c; 778 } 779 780 alias TU = TaggedUnion!S; 781 782 static assert(is(TypeOf!(TU.Kind.a) == int)); 783 static assert(is(TypeOf!(TU.Kind.b) == string)); 784 static assert(is(TypeOf!(TU.Kind.c) == string)); 785 } 786 787 unittest 788 { 789 struct S 790 { 791 TaggedUnion!This[] test; 792 } 793 794 alias TU = TaggedUnion!S; 795 796 TypeOf!(TU.Kind.test) a; 797 798 static assert(is(TypeOf!(TU.Kind.test) == TaggedUnion!S[])); 799 } 800 801 /// Convenience type that can be used for union fields that have no value (`void` is not allowed). 802 struct Void 803 { 804 } 805 806 /** Special type used as a placeholder for `U` within the definition of `U` to 807 enable self-referential types. 808 809 Note that this is recognized only if used as the first argument to a 810 template type. 811 */ 812 struct This 813 { 814 Void nothing; 815 } 816 817 /// 818 unittest 819 { 820 union U 821 { 822 TaggedUnion!This[] list; 823 int number; 824 string text; 825 } 826 827 alias Node = TaggedUnion!U; 828 829 auto n = Node([Node(12), Node("foo")]); 830 assert(n.isList); 831 assert(n.listValue == [Node(12), Node("foo")]); 832 } 833 834 package template UnionFieldEnum(U) 835 { 836 static if (is(U == enum)) 837 alias UnionFieldEnum = U; 838 else 839 { 840 import std.array : join; 841 import std.traits : FieldNameTuple; 842 843 mixin("enum UnionFieldEnum { " ~ [FieldNameTuple!U].join(", ") ~ " }"); 844 } 845 } 846 847 deprecated alias TypeEnum(U) = UnionFieldEnum!U; 848 849 package alias UnionKindTypes(FieldEnum) = staticMap!(TypeOf, EnumMembers!FieldEnum); 850 package alias UnionKindNames(FieldEnum) = AliasSeq!(__traits(allMembers, FieldEnum)); 851 852 package template UniqueTypes(Types...) 853 { 854 template impl(size_t i) 855 { 856 static if (i < Types.length) 857 { 858 alias T = Types[i]; 859 static if (staticIndexOf!(T, Types) == i && staticIndexOf!(T, Types[i + 1 .. $]) < 0) 860 alias impl = AliasSeq!(i, impl!(i + 1)); 861 else 862 alias impl = AliasSeq!(impl!(i + 1)); 863 } 864 else 865 alias impl = AliasSeq!(); 866 } 867 868 alias UniqueTypes = impl!0; 869 } 870 871 package template AmbiguousTypes(Types...) 872 { 873 template impl(size_t i) 874 { 875 static if (i < Types.length) 876 { 877 alias T = Types[i]; 878 static if (staticIndexOf!(T, Types) == i && staticIndexOf!(T, Types[i + 1 .. $]) >= 0) 879 alias impl = AliasSeq!(i, impl!(i + 1)); 880 else 881 alias impl = impl!(i + 1); 882 } 883 else 884 alias impl = AliasSeq!(); 885 } 886 887 alias AmbiguousTypes = impl!0; 888 } 889 890 /// Computes the minimum alignment necessary to align all types correctly 891 private size_t commonAlignment(TYPES...)() 892 { 893 import std.numeric : gcd; 894 895 size_t ret = 1; 896 foreach (T; TYPES) 897 ret = (T.alignof * ret) / gcd(T.alignof, ret); 898 return ret; 899 } 900 901 unittest 902 { 903 align(2) struct S1 904 { 905 ubyte x; 906 } 907 908 align(4) struct S2 909 { 910 ubyte x; 911 } 912 913 align(8) struct S3 914 { 915 ubyte x; 916 } 917 918 static if (__VERSION__ > 2076) 919 { // DMD 2.076 ignores the alignment 920 assert(commonAlignment!S1 == 2); 921 assert(commonAlignment!S2 == 4); 922 assert(commonAlignment!S3 == 8); 923 assert(commonAlignment!(S1, S3) == 8); 924 assert(commonAlignment!(S1, S2, S3) == 8); 925 assert(commonAlignment!(S2, S2, S1) == 4); 926 } 927 } 928 929 package void rawEmplace(T)(void[] dst, ref T src) 930 { 931 T[] tdst = () @trusted { return cast(T[]) dst[0 .. T.sizeof]; }(); 932 static if (is(T == class)) 933 { 934 tdst[0] = src; 935 } 936 else 937 { 938 import std.conv : emplace; 939 940 emplace!T(&tdst[0]); 941 rawSwap(tdst[0], src); 942 } 943 } 944 945 // std.algorithm.mutation.swap sometimes fails to compile due to 946 // internal errors in hasElaborateAssign!T/isAssignable!T. This is probably 947 // caused by cyclic dependencies. However, there is no reason to do these 948 // checks in this context, so we just directly move the raw memory. 949 package void rawSwap(T)(ref T a, ref T b) @trusted 950 { 951 void[T.sizeof] tmp = void; 952 void[] ab = (cast(void*)&a)[0 .. T.sizeof]; 953 void[] bb = (cast(void*)&b)[0 .. T.sizeof]; 954 tmp[] = ab[]; 955 ab[] = bb[]; 956 bb[] = tmp[]; 957 }