1 /** 2 * Algebraic data type implementation based on a tagged union. 3 * 4 * Copyright: Copyright 2015-2016, 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; 9 10 import std.typetuple; 11 import std.traits : Unqual, isInstanceOf; 12 13 // TODO: 14 // - distinguish between @property and non@-property methods. 15 // - verify that static methods are handled properly 16 17 /** Implements a generic algebraic type using an enum to identify the stored type. 18 19 This struct takes a `union` or `struct` declaration as an input and builds 20 an algebraic data type from its fields, using an automatically generated 21 `Kind` enumeration to identify which field of the union is currently used. 22 Multiple fields with the same value are supported. 23 24 All operators and methods are transparently forwarded to the contained 25 value. The caller has to make sure that the contained value supports the 26 requested operation. Failure to do so will result in an assertion failure. 27 28 The return value of forwarded operations is determined as follows: 29 $(UL 30 $(LI If the type can be uniquely determined, it is used as the return 31 value) 32 $(LI If there are multiple possible return values and all of them match 33 the unique types defined in the `TaggedAlgebraic`, a 34 `TaggedAlgebraic` is returned.) 35 $(LI If there are multiple return values and none of them is a 36 `Variant`, an `Algebraic` of the set of possible return types is 37 returned.) 38 $(LI If any of the possible operations returns a `Variant`, this is used 39 as the return value.) 40 ) 41 */ 42 struct TaggedAlgebraic(U) if (is(U == union) || is(U == struct)) 43 { 44 import std.algorithm : among; 45 import std.string : format; 46 import std.traits : FieldTypeTuple, FieldNameTuple, Largest, 47 hasElaborateCopyConstructor, hasElaborateDestructor; 48 49 /// Alias of the type used for defining the possible storage types/kinds. 50 alias Union = U; 51 52 private alias FieldTypes = FieldTypeTuple!U; 53 private alias fieldNames = FieldNameTuple!U; 54 55 static assert(FieldTypes.length > 0, 56 "The TaggedAlgebraic's union type must have at least one field."); 57 static assert(FieldTypes.length == fieldNames.length); 58 59 private 60 { 61 static if (is(FieldTypes[0] == typeof(null)) || is(FieldTypes[0] == Void) 62 || __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: 76 FieldTypes[0].init}; 77 } 78 Kind m_kind; 79 } 80 81 /// A type enum that identifies the type of value currently stored. 82 alias Kind = TypeEnum!U; 83 84 /// Compatibility alias 85 deprecated("Use 'Kind' instead.") alias Type = Kind; 86 87 /// The type ID of the currently stored value. 88 @property Kind kind() const 89 { 90 return m_kind; 91 } 92 93 // Compatibility alias 94 deprecated("Use 'kind' instead.") alias typeID = kind; 95 96 // constructors 97 //pragma(msg, generateConstructors!U()); 98 mixin(generateConstructors!U); 99 100 this(TaggedAlgebraic other) 101 { 102 rawSwap(this, other); 103 } 104 105 void opAssign(TaggedAlgebraic other) 106 { 107 rawSwap(this, other); 108 } 109 110 // postblit constructor 111 static if (anySatisfy!(hasElaborateCopyConstructor, FieldTypes)) 112 { 113 this(this) 114 { 115 switch (m_kind) 116 { 117 default: 118 break; 119 foreach (i, tname; fieldNames) 120 { 121 alias T = typeof(__traits(getMember, U, tname)); 122 static if (hasElaborateCopyConstructor!T) 123 { 124 case __traits(getMember, Kind, tname): 125 typeid(T).postblit(cast(void*)&trustedGet!tname()); 126 return; 127 } 128 } 129 } 130 } 131 } 132 133 // destructor 134 static if (anySatisfy!(hasElaborateDestructor, FieldTypes)) 135 { 136 ~this() 137 { 138 final switch (m_kind) 139 { 140 foreach (i, tname; fieldNames) 141 { 142 alias T = typeof(__traits(getMember, U, tname)); 143 case __traits(getMember, Kind, tname): 144 static if (hasElaborateDestructor!T) 145 { 146 .destroy(trustedGet!tname); 147 } 148 return; 149 } 150 } 151 } 152 } 153 154 /// Enables conversion or extraction of the stored value. 155 T opCast(T)() 156 { 157 import std.conv : to; 158 159 final switch (m_kind) 160 { 161 foreach (i, FT; FieldTypes) 162 { 163 case __traits(getMember, Kind, fieldNames[i]): 164 static if (is(typeof(trustedGet!(fieldNames[i])) : T)) 165 return trustedGet!(fieldNames[i]); 166 else static if (is(typeof(to!T(trustedGet!(fieldNames[i]))))) 167 { 168 return to!T(trustedGet!(fieldNames[i])); 169 } 170 else 171 { 172 assert(false, 173 "Cannot cast a " ~ fieldNames[i] ~ " value of type " 174 ~ FT.stringof ~ " to " ~ T.stringof); 175 } 176 } 177 } 178 assert(false); // never reached 179 } 180 /// ditto 181 T opCast(T)() const 182 { 183 // this method needs to be duplicated because inout doesn't work with to!() 184 import std.conv : to; 185 186 final switch (m_kind) 187 { 188 foreach (i, FT; FieldTypes) 189 { 190 case __traits(getMember, Kind, fieldNames[i]): 191 static if (is(typeof(trustedGet!(fieldNames[i])) : T)) 192 return trustedGet!(fieldNames[i]); 193 else static if (is(typeof(to!T(trustedGet!(fieldNames[i]))))) 194 { 195 return to!T(trustedGet!(fieldNames[i])); 196 } 197 else 198 { 199 assert(false, 200 "Cannot cast a " ~ fieldNames[i] ~ " value of type" 201 ~ FT.stringof ~ " to " ~ T.stringof); 202 } 203 } 204 } 205 assert(false); // never reached 206 } 207 208 /// Uses `cast(string)`/`to!string` to return a string representation of the enclosed value. 209 string toString() const 210 { 211 return cast(string) this; 212 } 213 214 // NOTE: "this TA" is used here as the functional equivalent of inout, 215 // just that it generates one template instantiation per modifier 216 // combination, so that we can actually decide what to do for each 217 // case. 218 219 /// Enables the invocation of methods of the stored value. 220 auto opDispatch(string name, this TA, ARGS...)(auto ref ARGS args) 221 if (hasOp!(TA, OpKind.method, name, ARGS)) 222 { 223 return implementOp!(OpKind.method, name)(this, args); 224 } 225 /// Enables accessing properties/fields of the stored value. 226 @property auto opDispatch(string name, this TA, ARGS...)(auto ref ARGS args) 227 if (hasOp!(TA, OpKind.field, name, ARGS) && !hasOp!(TA, OpKind.method, name, ARGS)) 228 { 229 return implementOp!(OpKind.field, name)(this, args); 230 } 231 /// Enables equality comparison with the stored value. 232 auto opEquals(T, this TA)(auto ref T other) 233 if (is(Unqual!T == TaggedAlgebraic) || hasOp!(TA, OpKind.binary, "==", T)) 234 { 235 static if (is(Unqual!T == TaggedAlgebraic)) 236 { 237 if (this.kind != other.kind) 238 return false; 239 final switch (this.kind) foreach (i, fname; fieldNames) 240 case __traits(getMember, Kind, fname): 241 return trustedGet!fname == other.trustedGet!fname; 242 assert(false); // never reached 243 } 244 else 245 return implementOp!(OpKind.binary, "==")(this, other); 246 } 247 /// Enables relational comparisons with the stored value. 248 auto opCmp(T, this TA)(auto ref T other) if (hasOp!(TA, OpKind.binary, "<", T)) 249 { 250 assert(false, "TODO!"); 251 } 252 /// Enables the use of unary operators with the stored value. 253 auto opUnary(string op, this TA)() if (hasOp!(TA, OpKind.unary, op)) 254 { 255 return implementOp!(OpKind.unary, op)(this); 256 } 257 /// Enables the use of binary operators with the stored value. 258 auto opBinary(string op, T, this TA)(auto ref T other) 259 if (hasOp!(TA, OpKind.binary, op, T)) 260 { 261 return implementOp!(OpKind.binary, op)(this, other); 262 } 263 /// Enables the use of binary operators with the stored value. 264 auto opBinaryRight(string op, T, this TA)(auto ref T other) 265 if (hasOp!(TA, OpKind.binaryRight, op, T)) 266 { 267 return implementOp!(OpKind.binaryRight, op)(this, other); 268 } 269 /// Enables operator assignments on the stored value. 270 auto opOpAssign(string op, T, this TA)(auto ref T other) 271 if (hasOp!(TA, OpKind.binary, op ~ "=", T)) 272 { 273 return implementOp!(OpKind.binary, op ~ "=")(this, other); 274 } 275 /// Enables indexing operations on the stored value. 276 auto opIndex(this TA, ARGS...)(auto ref ARGS args) 277 if (hasOp!(TA, OpKind.index, null, ARGS)) 278 { 279 return implementOp!(OpKind.index, null)(this, args); 280 } 281 /// Enables index assignments on the stored value. 282 auto opIndexAssign(this TA, ARGS...)(auto ref ARGS args) 283 if (hasOp!(TA, OpKind.indexAssign, null, ARGS)) 284 { 285 return implementOp!(OpKind.indexAssign, null)(this, args); 286 } 287 /// Enables call syntax operations on the stored value. 288 auto opCall(this TA, ARGS...)(auto ref ARGS args) 289 if (hasOp!(TA, OpKind.call, null, ARGS)) 290 { 291 return implementOp!(OpKind.call, null)(this, args); 292 } 293 294 private @trusted @property ref inout(typeof(__traits(getMember, U, f))) trustedGet(string f)() inout 295 { 296 return trustedGet!(inout(typeof(__traits(getMember, U, f)))); 297 } 298 299 private @trusted @property ref inout(T) trustedGet(T)() inout 300 { 301 return *cast(inout(T)*) m_data.ptr; 302 } 303 } 304 305 /// 306 @safe unittest 307 { 308 import taggedalgebraic; 309 310 struct Foo 311 { 312 string name; 313 void bar() @safe 314 { 315 } 316 } 317 318 union Base 319 { 320 int i; 321 string str; 322 Foo foo; 323 } 324 325 alias Tagged = TaggedAlgebraic!Base; 326 327 // Instantiate 328 Tagged taggedInt = 5; 329 Tagged taggedString = "Hello"; 330 Tagged taggedFoo = Foo(); 331 Tagged taggedAny = taggedInt; 332 taggedAny = taggedString; 333 taggedAny = taggedFoo; 334 335 // Check type: Tagged.Kind is an enum 336 assert(taggedInt.kind == Tagged.Kind.i); 337 assert(taggedString.kind == Tagged.Kind.str); 338 assert(taggedFoo.kind == Tagged.Kind.foo); 339 assert(taggedAny.kind == Tagged.Kind.foo); 340 341 // In most cases, can simply use as-is 342 auto num = 4 + taggedInt; 343 auto msg = taggedString ~ " World!"; 344 taggedFoo.bar(); 345 if (taggedAny.kind == Tagged.Kind.foo) // Make sure to check type first! 346 taggedAny.bar(); 347 //taggedString.bar(); // AssertError: Not a Foo! 348 349 // Convert back by casting 350 auto i = cast(int) taggedInt; 351 auto str = cast(string) taggedString; 352 auto foo = cast(Foo) taggedFoo; 353 if (taggedAny.kind == Tagged.Kind.foo) // Make sure to check type first! 354 auto foo2 = cast(Foo) taggedAny; 355 //cast(Foo) taggedString; // AssertError! 356 357 // Kind is an enum, so final switch is supported: 358 final switch (taggedAny.kind) 359 { 360 case Tagged.Kind.i: 361 // It's "int i" 362 break; 363 364 case Tagged.Kind.str: 365 // It's "string str" 366 break; 367 368 case Tagged.Kind.foo: 369 // It's "Foo foo" 370 break; 371 } 372 } 373 374 /** Operators and methods of the contained type can be used transparently. 375 */ 376 @safe unittest 377 { 378 static struct S 379 { 380 int v; 381 int test() 382 { 383 return v / 2; 384 } 385 } 386 387 static union Test 388 { 389 typeof(null) null_; 390 int integer; 391 string text; 392 string[string] dictionary; 393 S custom; 394 } 395 396 alias TA = TaggedAlgebraic!Test; 397 398 TA ta; 399 assert(ta.kind == TA.Kind.null_); 400 401 ta = 12; 402 assert(ta.kind == TA.Kind.integer); 403 assert(ta == 12); 404 assert(cast(int) ta == 12); 405 assert(cast(long) ta == 12); 406 assert(cast(short) ta == 12); 407 408 ta += 12; 409 assert(ta == 24); 410 assert(ta - 10 == 14); 411 412 ta = ["foo" : "bar"]; 413 assert(ta.kind == TA.Kind.dictionary); 414 assert(ta["foo"] == "bar"); 415 416 ta["foo"] = "baz"; 417 assert(ta["foo"] == "baz"); 418 419 ta = S(8); 420 assert(ta.test() == 4); 421 } 422 423 unittest 424 { // std.conv integration 425 import std.conv : to; 426 427 static struct S 428 { 429 int v; 430 int test() 431 { 432 return v / 2; 433 } 434 } 435 436 static union Test 437 { 438 typeof(null) null_; 439 int number; 440 string text; 441 } 442 443 alias TA = TaggedAlgebraic!Test; 444 445 TA ta; 446 assert(ta.kind == TA.Kind.null_); 447 ta = "34"; 448 assert(ta == "34"); 449 assert(to!int(ta) == 34, to!string(to!int(ta))); 450 assert(to!string(ta) == "34", to!string(ta)); 451 } 452 453 /** Multiple fields are allowed to have the same type, in which case the type 454 ID enum is used to disambiguate. 455 */ 456 @safe unittest 457 { 458 static union Test 459 { 460 typeof(null) null_; 461 int count; 462 int difference; 463 } 464 465 alias TA = TaggedAlgebraic!Test; 466 467 TA ta = TA(12, TA.Kind.count); 468 assert(ta.kind == TA.Kind.count); 469 assert(ta == 12); 470 471 ta = null; 472 assert(ta.kind == TA.Kind.null_); 473 } 474 475 @safe unittest 476 { // comparison of whole TAs 477 static union Test 478 { 479 typeof(null) a; 480 typeof(null) b; 481 Void c; 482 Void d; 483 int e; 484 int f; 485 } 486 487 alias TA = TaggedAlgebraic!Test; 488 489 assert(TA(null, TA.Kind.a) == TA(null, TA.Kind.a)); 490 assert(TA(null, TA.Kind.a) != TA(null, TA.Kind.b)); 491 assert(TA(null, TA.Kind.a) != TA(Void.init, TA.Kind.c)); 492 assert(TA(null, TA.Kind.a) != TA(0, TA.Kind.e)); 493 assert(TA(Void.init, TA.Kind.c) == TA(Void.init, TA.Kind.c)); 494 assert(TA(Void.init, TA.Kind.c) != TA(Void.init, TA.Kind.d)); 495 assert(TA(1, TA.Kind.e) == TA(1, TA.Kind.e)); 496 assert(TA(1, TA.Kind.e) != TA(2, TA.Kind.e)); 497 assert(TA(1, TA.Kind.e) != TA(1, TA.Kind.f)); 498 } 499 500 unittest 501 { 502 // test proper type modifier support 503 static struct S 504 { 505 void test() 506 { 507 } 508 509 void testI() immutable 510 { 511 } 512 513 void testC() const 514 { 515 } 516 517 void testS() shared 518 { 519 } 520 521 void testSC() shared const 522 { 523 } 524 } 525 526 static union U 527 { 528 S s; 529 } 530 531 auto u = TaggedAlgebraic!U(S.init); 532 const uc = u; 533 immutable ui = cast(immutable) u; 534 //const shared usc = cast(shared)u; 535 //shared us = cast(shared)u; 536 537 static assert(is(typeof(u.test()))); 538 static assert(!is(typeof(u.testI()))); 539 static assert(is(typeof(u.testC()))); 540 static assert(!is(typeof(u.testS()))); 541 static assert(!is(typeof(u.testSC()))); 542 543 static assert(!is(typeof(uc.test()))); 544 static assert(!is(typeof(uc.testI()))); 545 static assert(is(typeof(uc.testC()))); 546 static assert(!is(typeof(uc.testS()))); 547 static assert(!is(typeof(uc.testSC()))); 548 549 static assert(!is(typeof(ui.test()))); 550 static assert(is(typeof(ui.testI()))); 551 static assert(is(typeof(ui.testC()))); 552 static assert(!is(typeof(ui.testS()))); 553 static assert(is(typeof(ui.testSC()))); 554 555 /*static assert(!is(typeof(us.test()))); 556 static assert(!is(typeof(us.testI()))); 557 static assert(!is(typeof(us.testC()))); 558 static assert( is(typeof(us.testS()))); 559 static assert( is(typeof(us.testSC()))); 560 561 static assert(!is(typeof(usc.test()))); 562 static assert(!is(typeof(usc.testI()))); 563 static assert(!is(typeof(usc.testC()))); 564 static assert(!is(typeof(usc.testS()))); 565 static assert( is(typeof(usc.testSC())));*/ 566 } 567 568 unittest 569 { 570 // test attributes on contained values 571 import std.typecons : Rebindable, rebindable; 572 573 class C 574 { 575 void test() 576 { 577 } 578 579 void testC() const 580 { 581 } 582 583 void testI() immutable 584 { 585 } 586 } 587 588 union U 589 { 590 Rebindable!(immutable(C)) c; 591 } 592 593 auto ta = TaggedAlgebraic!U(rebindable(new immutable C)); 594 static assert(!is(typeof(ta.test()))); 595 static assert(is(typeof(ta.testC()))); 596 static assert(is(typeof(ta.testI()))); 597 } 598 599 // test recursive definition using a wrapper dummy struct 600 // (needed to avoid "no size yet for forward reference" errors) 601 unittest 602 { 603 static struct TA 604 { 605 union U 606 { 607 TA[] children; 608 int value; 609 } 610 611 TaggedAlgebraic!U u; 612 alias u this; 613 this(ARGS...)(ARGS args) 614 { 615 u = TaggedAlgebraic!U(args); 616 } 617 } 618 619 auto ta = TA(null); 620 ta ~= TA(0); 621 ta ~= TA(1); 622 ta ~= TA([TA(2)]); 623 assert(ta[0] == 0); 624 assert(ta[1] == 1); 625 assert(ta[2][0] == 2); 626 } 627 628 unittest 629 { // postblit/destructor test 630 static struct S 631 { 632 static int i = 0; 633 bool initialized = false; 634 this(bool) 635 { 636 initialized = true; 637 i++; 638 } 639 640 this(this) 641 { 642 if (initialized) 643 i++; 644 } 645 646 ~this() 647 { 648 if (initialized) 649 i--; 650 } 651 } 652 653 static struct U 654 { 655 S s; 656 int t; 657 } 658 659 alias TA = TaggedAlgebraic!U; 660 { 661 assert(S.i == 0); 662 auto ta = TA(S(true)); 663 assert(S.i == 1); 664 { 665 auto tb = ta; 666 assert(S.i == 2); 667 ta = tb; 668 assert(S.i == 2); 669 ta = 1; 670 assert(S.i == 1); 671 ta = S(true); 672 assert(S.i == 2); 673 } 674 assert(S.i == 1); 675 } 676 assert(S.i == 0); 677 678 static struct U2 679 { 680 S a; 681 S b; 682 } 683 684 alias TA2 = TaggedAlgebraic!U2; 685 { 686 auto ta2 = TA2(S(true), TA2.Kind.a); 687 assert(S.i == 1); 688 } 689 assert(S.i == 0); 690 } 691 692 unittest 693 { 694 static struct S 695 { 696 union U 697 { 698 int i; 699 string s; 700 U[] a; 701 } 702 703 alias TA = TaggedAlgebraic!U; 704 TA p; 705 alias p this; 706 } 707 708 S s = S(S.TA("hello")); 709 assert(cast(string) s == "hello"); 710 } 711 712 unittest 713 { // multiple operator choices 714 union U 715 { 716 int i; 717 double d; 718 } 719 720 alias TA = TaggedAlgebraic!U; 721 TA ta = 12; 722 static assert(is(typeof(ta + 10) == TA)); // ambiguous, could be int or double 723 assert((ta + 10).kind == TA.Kind.i); 724 assert(ta + 10 == 22); 725 static assert(is(typeof(ta + 10.5) == double)); 726 assert(ta + 10.5 == 22.5); 727 } 728 729 unittest 730 { // Binary op between two TaggedAlgebraic values 731 union U 732 { 733 int i; 734 } 735 736 alias TA = TaggedAlgebraic!U; 737 738 TA a = 1, b = 2; 739 static assert(is(typeof(a + b) == int)); 740 assert(a + b == 3); 741 } 742 743 unittest 744 { // Ambiguous binary op between two TaggedAlgebraic values 745 union U 746 { 747 int i; 748 double d; 749 } 750 751 alias TA = TaggedAlgebraic!U; 752 753 TA a = 1, b = 2; 754 static assert(is(typeof(a + b) == TA)); 755 assert((a + b).kind == TA.Kind.i); 756 assert(a + b == 3); 757 } 758 759 unittest 760 { 761 struct S 762 { 763 union U 764 { 765 @disableIndex string str; 766 S[] array; 767 S[string] object; 768 } 769 770 alias TA = TaggedAlgebraic!U; 771 TA payload; 772 alias payload this; 773 } 774 775 S a = S(S.TA("hello")); 776 S b = S(S.TA(["foo" : a])); 777 S c = S(S.TA([a])); 778 assert(b["foo"] == a); 779 assert(b["foo"] == "hello"); 780 assert(c[0] == a); 781 assert(c[0] == "hello"); 782 } 783 784 static if (__VERSION__ >= 2072) 785 unittest 786 { // default initialization 787 struct S 788 { 789 int i = 42; 790 } 791 792 union U 793 { 794 S s; 795 int j; 796 } 797 798 TaggedAlgebraic!U ta; 799 assert(ta.i == 42); 800 } 801 802 unittest 803 { 804 import std.meta : AliasSeq; 805 806 union U 807 { 808 int[int] a; 809 } 810 811 foreach (TA; AliasSeq!(TaggedAlgebraic!U, const(TaggedAlgebraic!U))) 812 { 813 TA ta = [1 : 2]; 814 assert(cast(int[int]) ta == [1 : 2]); 815 } 816 } 817 818 static if (__VERSION__ >= 2072) 819 { 820 unittest 821 { // issue #8 822 static struct Result(T, E) 823 { 824 static union U 825 { 826 T ok; 827 E err; 828 } 829 830 alias TA = TaggedAlgebraic!U; 831 TA payload; 832 alias payload this; 833 834 this(T ok) 835 { 836 payload = ok; 837 } 838 839 this(E err) 840 { 841 payload = err; 842 } 843 } 844 845 static struct Option(T) 846 { 847 static union U 848 { 849 T some; 850 typeof(null) none; 851 } 852 853 alias TA = TaggedAlgebraic!U; 854 TA payload; 855 alias payload this; 856 857 this(T some) 858 { 859 payload = some; 860 } 861 862 this(typeof(null) none) 863 { 864 payload = null; 865 } 866 } 867 868 Result!(Option!size_t, int) foo() 869 { 870 return Result!(Option!size_t, int)(42); 871 } 872 873 assert(foo() == 42); 874 } 875 } 876 877 unittest 878 { // issue #13 879 struct S1 880 { 881 Void dummy; 882 int foo; 883 } 884 885 struct S 886 { 887 struct T 888 { 889 TaggedAlgebraic!S1 foo() 890 { 891 return TaggedAlgebraic!S1(42); 892 } 893 } 894 895 struct U 896 { 897 string foo() 898 { 899 return "foo"; 900 } 901 } 902 903 Void dummy; 904 T t; 905 U u; 906 } 907 908 alias TA = TaggedAlgebraic!S; 909 auto ta = TA(S.T.init); 910 assert(ta.foo().get!(TaggedAlgebraic!S1) == 42); 911 912 ta = TA(S.U.init); 913 assert(ta.foo() == "foo"); 914 } 915 916 /** Tests if the algebraic type stores a value of a certain data type. 917 */ 918 bool hasType(T, U)(in ref TaggedAlgebraic!U ta) 919 { 920 alias Fields = Filter!(fieldMatchesType!(U, T), ta.fieldNames); 921 static assert(Fields.length > 0, 922 "Type " ~ T.stringof ~ " cannot be stored in a " ~ (TaggedAlgebraic!U).stringof ~ "."); 923 924 switch (ta.kind) 925 { 926 default: 927 return false; 928 foreach (i, fname; Fields) 929 case __traits(getMember, ta.Kind, fname): 930 return true; 931 } 932 assert(false); // never reached 933 } 934 /// ditto 935 bool hasType(T, U)(in TaggedAlgebraic!U ta) 936 { 937 return hasType!(T, U)(ta); 938 } 939 940 /// 941 unittest 942 { 943 union Fields 944 { 945 int number; 946 string text; 947 } 948 949 TaggedAlgebraic!Fields ta = "test"; 950 951 assert(ta.hasType!string); 952 assert(!ta.hasType!int); 953 954 ta = 42; 955 assert(ta.hasType!int); 956 assert(!ta.hasType!string); 957 } 958 959 unittest 960 { // issue #1 961 union U 962 { 963 int a; 964 int b; 965 } 966 967 alias TA = TaggedAlgebraic!U; 968 969 TA ta = TA(0, TA.Kind.b); 970 static assert(!is(typeof(ta.hasType!double))); 971 assert(ta.hasType!int); 972 } 973 974 unittest 975 { 976 union U 977 { 978 int a; 979 float b; 980 } 981 982 alias TA = TaggedAlgebraic!U; 983 984 const(TA) test() 985 { 986 return TA(12); 987 } 988 989 assert(test().hasType!int); 990 } 991 992 static if (__VERSION__ >= 2072) 993 { 994 /** Maps a kind enumeration value to the corresponding field type. 995 996 `kind` must be a value of the `TaggedAlgebraic!T.Kind` enumeration. 997 */ 998 template TypeOf(alias kind) if (isInstanceOf!(TypeEnum, typeof(kind))) 999 { 1000 import std.traits : FieldTypeTuple, TemplateArgsOf; 1001 1002 alias U = TemplateArgsOf!(typeof(kind)); 1003 alias TypeOf = FieldTypeTuple!U[kind]; 1004 } 1005 1006 /// 1007 unittest 1008 { 1009 static struct S 1010 { 1011 int a; 1012 string b; 1013 string c; 1014 } 1015 1016 alias TA = TaggedAlgebraic!S; 1017 1018 static assert(is(TypeOf!(TA.Kind.a) == int)); 1019 static assert(is(TypeOf!(TA.Kind.b) == string)); 1020 static assert(is(TypeOf!(TA.Kind.c) == string)); 1021 } 1022 } 1023 1024 /** Gets the value stored in an algebraic type based on its data type. 1025 */ 1026 ref inout(T) get(T, U)(ref inout(TaggedAlgebraic!U) ta) 1027 { 1028 import std.format : format; 1029 1030 assert(hasType!(T, U)(ta), "Type mismatch!"); 1031 return ta.trustedGet!T; 1032 } 1033 /// ditto 1034 inout(T) get(T, U)(inout(TaggedAlgebraic!U) ta) 1035 { 1036 import std.format : format; 1037 1038 assert(hasType!(T, U)(ta), "Type mismatch!"); 1039 return ta.trustedGet!T; 1040 } 1041 1042 @nogc @safe nothrow unittest 1043 { 1044 struct Fields 1045 { 1046 int a; 1047 float b; 1048 } 1049 1050 alias TA = TaggedAlgebraic!Fields; 1051 auto ta = TA(1); 1052 assert(ta.get!int == 1); 1053 ta.get!int = 2; 1054 assert(ta.get!int == 2); 1055 ta = TA(1.0); 1056 assert(ta.get!float == 1.0); 1057 } 1058 1059 /** Calls a the given callback with the static type of the contained value. 1060 1061 The `handler` callback must be a lambda or a single-argument template 1062 function that accepts all possible types that the given `TaggedAlgebraic` 1063 can hold. 1064 1065 Returns: 1066 If `handler` has a non-void return value, its return value gets 1067 forwarded to the caller. 1068 */ 1069 auto apply(alias handler, TA)(TA ta) if (isInstanceOf!(TaggedAlgebraic, TA)) 1070 { 1071 final switch (ta.kind) 1072 { 1073 foreach (i, fn; TA.fieldNames) 1074 { 1075 case __traits(getMember, ta.Kind, fn): 1076 return handler(get!(TA.FieldTypes[i])(ta)); 1077 } 1078 } 1079 static if (__VERSION__ <= 2068) 1080 assert(false); 1081 } 1082 /// ditto 1083 auto apply(alias handler, T)(T value) if (!isInstanceOf!(TaggedAlgebraic, T)) 1084 { 1085 return handler(value); 1086 } 1087 1088 /// 1089 unittest 1090 { 1091 union U 1092 { 1093 int i; 1094 string s; 1095 } 1096 1097 alias TA = TaggedAlgebraic!U; 1098 1099 assert(TA(12).apply!((v) { 1100 static if (is(typeof(v) == int)) 1101 { 1102 assert(v == 12); 1103 return 1; 1104 } 1105 else 1106 { 1107 return 0; 1108 } 1109 }) == 1); 1110 1111 assert(TA("foo").apply!((v) { 1112 static if (is(typeof(v) == string)) 1113 { 1114 assert(v == "foo"); 1115 return 2; 1116 } 1117 else 1118 { 1119 return 0; 1120 } 1121 }) == 2); 1122 1123 "baz".apply!((v) { assert(v == "baz"); }); 1124 } 1125 1126 /// Convenience type that can be used for union fields that have no value (`void` is not allowed). 1127 struct Void 1128 { 1129 } 1130 1131 /// User-defined attibute to disable `opIndex` forwarding for a particular tagged union member. 1132 @property auto disableIndex() 1133 { 1134 assert(__ctfe, "disableIndex must only be used as an attribute."); 1135 return DisableOpAttribute(OpKind.index, null); 1136 } 1137 1138 private struct DisableOpAttribute 1139 { 1140 OpKind kind; 1141 string name; 1142 } 1143 1144 private template hasOp(TA, OpKind kind, string name, ARGS...) 1145 { 1146 import std.traits : CopyTypeQualifiers; 1147 1148 alias UQ = CopyTypeQualifiers!(TA, TA.Union); 1149 enum hasOp = TypeTuple!(OpInfo!(UQ, kind, name, ARGS).fields).length > 0; 1150 } 1151 1152 unittest 1153 { 1154 static struct S 1155 { 1156 void m(int i) 1157 { 1158 } 1159 1160 bool opEquals(int i) 1161 { 1162 return true; 1163 } 1164 1165 bool opEquals(S s) 1166 { 1167 return true; 1168 } 1169 } 1170 1171 static union U 1172 { 1173 int i; 1174 string s; 1175 S st; 1176 } 1177 1178 alias TA = TaggedAlgebraic!U; 1179 1180 static assert(hasOp!(TA, OpKind.binary, "+", int)); 1181 static assert(hasOp!(TA, OpKind.binary, "~", string)); 1182 static assert(hasOp!(TA, OpKind.binary, "==", int)); 1183 static assert(hasOp!(TA, OpKind.binary, "==", string)); 1184 static assert(hasOp!(TA, OpKind.binary, "==", int)); 1185 static assert(hasOp!(TA, OpKind.binary, "==", S)); 1186 static assert(hasOp!(TA, OpKind.method, "m", int)); 1187 static assert(hasOp!(TA, OpKind.binary, "+=", int)); 1188 static assert(!hasOp!(TA, OpKind.binary, "~", int)); 1189 static assert(!hasOp!(TA, OpKind.binary, "~", int)); 1190 static assert(!hasOp!(TA, OpKind.method, "m", string)); 1191 static assert(!hasOp!(TA, OpKind.method, "m")); 1192 static assert(!hasOp!(const(TA), OpKind.binary, "+=", int)); 1193 static assert(!hasOp!(const(TA), OpKind.method, "m", int)); 1194 } 1195 1196 unittest 1197 { 1198 struct S 1199 { 1200 union U 1201 { 1202 string s; 1203 S[] arr; 1204 S[string] obj; 1205 } 1206 1207 alias TA = TaggedAlgebraic!(S.U); 1208 TA payload; 1209 alias payload this; 1210 } 1211 1212 static assert(hasOp!(S.TA, OpKind.index, null, size_t)); 1213 static assert(hasOp!(S.TA, OpKind.index, null, int)); 1214 static assert(hasOp!(S.TA, OpKind.index, null, string)); 1215 static assert(hasOp!(S.TA, OpKind.field, "length")); 1216 } 1217 1218 unittest 1219 { // "in" operator 1220 union U 1221 { 1222 string[string] dict; 1223 } 1224 1225 alias TA = TaggedAlgebraic!U; 1226 auto ta = TA(["foo" : "bar"]); 1227 assert("foo" in ta); 1228 assert(*("foo" in ta) == "bar"); 1229 } 1230 1231 private static auto implementOp(OpKind kind, string name, T, ARGS...)(ref T self, auto ref ARGS args) 1232 { 1233 import std.array : join; 1234 import std.traits : CopyTypeQualifiers; 1235 import std.variant : Algebraic, Variant; 1236 1237 alias UQ = CopyTypeQualifiers!(T, T.Union); 1238 1239 alias info = OpInfo!(UQ, kind, name, ARGS); 1240 1241 static assert(hasOp!(T, kind, name, ARGS)); 1242 1243 static assert(info.fields.length > 0, 1244 "Implementing operator that has no valid implementation for any supported type."); 1245 1246 //pragma(msg, "Fields for "~kind.stringof~" "~name~", "~T.stringof~": "~info.fields.stringof); 1247 //pragma(msg, "Return types for "~kind.stringof~" "~name~", "~T.stringof~": "~info.ReturnTypes.stringof); 1248 //pragma(msg, typeof(T.Union.tupleof)); 1249 //import std.meta : staticMap; pragma(msg, staticMap!(isMatchingUniqueType!(T.Union), info.ReturnTypes)); 1250 1251 switch (self.m_kind) 1252 { 1253 enum assert_msg = "Operator " ~ name ~ " (" ~ kind.stringof 1254 ~ ") can only be used on values of the following types: " ~ [info.fields].join(", "); 1255 default: 1256 assert(false, assert_msg); 1257 foreach (i, f; info.fields) 1258 { 1259 alias FT = typeof(__traits(getMember, T.Union, f)); 1260 case __traits(getMember, T.Kind, f): 1261 static if (NoDuplicates!(info.ReturnTypes).length == 1) 1262 return info.perform(self.trustedGet!FT, args); 1263 else static if (allSatisfy!(isMatchingUniqueType!(T.Union), info.ReturnTypes)) 1264 return TaggedAlgebraic!(T.Union)(info.perform(self.trustedGet!FT, args)); 1265 else static if (allSatisfy!(isNoVariant, info.ReturnTypes)) 1266 { 1267 alias Alg = Algebraic!(NoDuplicates!(info.ReturnTypes)); 1268 info.ReturnTypes[i] ret = info.perform(self.trustedGet!FT, args); 1269 import std.traits : isInstanceOf; 1270 1271 return Alg(ret); 1272 } 1273 else static if (is(FT == Variant)) 1274 return info.perform(self.trustedGet!FT, args); 1275 else 1276 return Variant(info.perform(self.trustedGet!FT, args)); 1277 } 1278 } 1279 1280 assert(false); // never reached 1281 } 1282 1283 unittest 1284 { // opIndex on recursive TA with closed return value set 1285 static struct S 1286 { 1287 union U 1288 { 1289 char ch; 1290 string str; 1291 S[] arr; 1292 } 1293 1294 alias TA = TaggedAlgebraic!U; 1295 TA payload; 1296 alias payload this; 1297 1298 this(T)(T t) 1299 { 1300 this.payload = t; 1301 } 1302 } 1303 1304 S a = S("foo"); 1305 S s = S([a]); 1306 1307 assert(implementOp!(OpKind.field, "length")(s.payload) == 1); 1308 static assert(is(typeof(implementOp!(OpKind.index, null)(s.payload, 0)) == S.TA)); 1309 assert(implementOp!(OpKind.index, null)(s.payload, 0) == "foo"); 1310 } 1311 1312 unittest 1313 { // opIndex on recursive TA with closed return value set using @disableIndex 1314 static struct S 1315 { 1316 union U 1317 { 1318 @disableIndex string str; 1319 S[] arr; 1320 } 1321 1322 alias TA = TaggedAlgebraic!U; 1323 TA payload; 1324 alias payload this; 1325 1326 this(T)(T t) 1327 { 1328 this.payload = t; 1329 } 1330 } 1331 1332 S a = S("foo"); 1333 S s = S([a]); 1334 1335 assert(implementOp!(OpKind.field, "length")(s.payload) == 1); 1336 static assert(is(typeof(implementOp!(OpKind.index, null)(s.payload, 0)) == S)); 1337 assert(implementOp!(OpKind.index, null)(s.payload, 0) == "foo"); 1338 } 1339 1340 private auto performOpRaw(U, OpKind kind, string name, T, ARGS...)(ref T value, /*auto ref*/ ARGS args) 1341 { 1342 static if (kind == OpKind.binary) 1343 return mixin("value " ~ name ~ " args[0]"); 1344 else static if (kind == OpKind.binaryRight) 1345 return mixin("args[0] " ~ name ~ " value"); 1346 else static if (kind == OpKind.unary) 1347 return mixin("name " ~ value); 1348 else static if (kind == OpKind.method) 1349 return __traits(getMember, value, name)(args); 1350 else static if (kind == OpKind.field) 1351 return __traits(getMember, value, name); 1352 else static if (kind == OpKind.index) 1353 return value[args]; 1354 else static if (kind == OpKind.indexAssign) 1355 return value[args[1 .. $]] = args[0]; 1356 else static if (kind == OpKind.call) 1357 return value(args); 1358 else 1359 static assert(false, "Unsupported kind of operator: " ~ kind.stringof); 1360 } 1361 1362 unittest 1363 { 1364 union U 1365 { 1366 int i; 1367 string s; 1368 } 1369 1370 { 1371 int v = 1; 1372 assert(performOpRaw!(U, OpKind.binary, "+")(v, 3) == 4); 1373 } 1374 { 1375 string v = "foo"; 1376 assert(performOpRaw!(U, OpKind.binary, "~")(v, "bar") == "foobar"); 1377 } 1378 } 1379 1380 private auto performOp(U, OpKind kind, string name, T, ARGS...)(ref T value, /*auto ref*/ ARGS args) 1381 { 1382 import std.traits : isInstanceOf; 1383 1384 static if (ARGS.length > 0 && isInstanceOf!(TaggedAlgebraic, ARGS[0])) 1385 { 1386 static if (is(typeof(performOpRaw!(U, kind, name, T, ARGS)(value, args)))) 1387 { 1388 return performOpRaw!(U, kind, name, T, ARGS)(value, args); 1389 } 1390 else 1391 { 1392 alias TA = ARGS[0]; 1393 template MTypesImpl(size_t i) 1394 { 1395 static if (i < TA.FieldTypes.length) 1396 { 1397 alias FT = TA.FieldTypes[i]; 1398 static if (is(typeof(&performOpRaw!(U, kind, name, T, FT, ARGS[1 .. $])))) 1399 alias MTypesImpl = TypeTuple!(FT, MTypesImpl!(i + 1)); 1400 else 1401 alias MTypesImpl = TypeTuple!(MTypesImpl!(i + 1)); 1402 } 1403 else 1404 alias MTypesImpl = TypeTuple!(); 1405 } 1406 1407 alias MTypes = NoDuplicates!(MTypesImpl!0); 1408 static assert(MTypes.length > 0, 1409 "No type of the TaggedAlgebraic parameter matches any function declaration."); 1410 static if (MTypes.length == 1) 1411 { 1412 if (args[0].hasType!(MTypes[0])) 1413 return performOpRaw!(U, kind, name)(value, 1414 args[0].get!(MTypes[0]), args[1 .. $]); 1415 } 1416 else 1417 { 1418 // TODO: allow all return types (fall back to Algebraic or Variant) 1419 foreach (FT; MTypes) 1420 { 1421 if (args[0].hasType!FT) 1422 return ARGS[0](performOpRaw!(U, kind, name)(value, 1423 args[0].get!FT, args[1 .. $])); 1424 } 1425 } 1426 throw new /*InvalidAgument*/ Exception("Algebraic parameter type mismatch"); 1427 } 1428 } 1429 else 1430 return performOpRaw!(U, kind, name, T, ARGS)(value, args); 1431 } 1432 1433 unittest 1434 { 1435 union U 1436 { 1437 int i; 1438 double d; 1439 string s; 1440 } 1441 1442 { 1443 int v = 1; 1444 assert(performOp!(U, OpKind.binary, "+")(v, 3) == 4); 1445 } 1446 { 1447 string v = "foo"; 1448 assert(performOp!(U, OpKind.binary, "~")(v, "bar") == "foobar"); 1449 } 1450 { 1451 string v = "foo"; 1452 assert(performOp!(U, OpKind.binary, "~")(v, TaggedAlgebraic!U("bar")) == "foobar"); 1453 } 1454 { 1455 int v = 1; 1456 assert(performOp!(U, OpKind.binary, "+")(v, TaggedAlgebraic!U(3)) == 4); 1457 } 1458 } 1459 1460 private template OpInfo(U, OpKind kind, string name, ARGS...) 1461 { 1462 import std.traits : CopyTypeQualifiers, FieldTypeTuple, FieldNameTuple, ReturnType; 1463 1464 private alias FieldTypes = FieldTypeTuple!U; 1465 private alias fieldNames = FieldNameTuple!U; 1466 1467 private template isOpEnabled(string field) 1468 { 1469 alias attribs = TypeTuple!(__traits(getAttributes, __traits(getMember, U, field))); 1470 template impl(size_t i) 1471 { 1472 static if (i < attribs.length) 1473 { 1474 static if (is(typeof(attribs[i]) == DisableOpAttribute)) 1475 { 1476 static if (kind == attribs[i].kind && name == attribs[i].name) 1477 enum impl = false; 1478 else 1479 enum impl = impl!(i + 1); 1480 } 1481 else 1482 enum impl = impl!(i + 1); 1483 } 1484 else 1485 enum impl = true; 1486 } 1487 1488 enum isOpEnabled = impl!0; 1489 } 1490 1491 template fieldsImpl(size_t i) 1492 { 1493 static if (i < FieldTypes.length) 1494 { 1495 static if (isOpEnabled!(fieldNames[i]) 1496 && is(typeof(&performOp!(U, kind, name, FieldTypes[i], ARGS)))) 1497 { 1498 alias fieldsImpl = TypeTuple!(fieldNames[i], fieldsImpl!(i + 1)); 1499 } 1500 else 1501 alias fieldsImpl = fieldsImpl!(i + 1); 1502 } 1503 else 1504 alias fieldsImpl = TypeTuple!(); 1505 } 1506 1507 alias fields = fieldsImpl!0; 1508 1509 template ReturnTypesImpl(size_t i) 1510 { 1511 static if (i < fields.length) 1512 { 1513 alias FT = CopyTypeQualifiers!(U, typeof(__traits(getMember, U, fields[i]))); 1514 alias ReturnTypesImpl = TypeTuple!(ReturnType!(performOp!(U, 1515 kind, name, FT, ARGS)), ReturnTypesImpl!(i + 1)); 1516 } 1517 else 1518 alias ReturnTypesImpl = TypeTuple!(); 1519 } 1520 1521 alias ReturnTypes = ReturnTypesImpl!0; 1522 1523 static auto perform(T)(ref T value, auto ref ARGS args) 1524 { 1525 return performOp!(U, kind, name)(value, args); 1526 } 1527 } 1528 1529 private template ImplicitUnqual(T) 1530 { 1531 import std.traits : Unqual, hasAliasing; 1532 1533 static if (is(T == void)) 1534 alias ImplicitUnqual = void; 1535 else 1536 { 1537 private static struct S 1538 { 1539 T t; 1540 } 1541 1542 static if (hasAliasing!S) 1543 alias ImplicitUnqual = T; 1544 else 1545 alias ImplicitUnqual = Unqual!T; 1546 } 1547 } 1548 1549 private enum OpKind 1550 { 1551 binary, 1552 binaryRight, 1553 unary, 1554 method, 1555 field, 1556 index, 1557 indexAssign, 1558 call 1559 } 1560 1561 private template TypeEnum(U) 1562 { 1563 import std.array : join; 1564 import std.traits : FieldNameTuple; 1565 1566 mixin("enum TypeEnum { " ~ [FieldNameTuple!U].join(", ") ~ " }"); 1567 } 1568 1569 private string generateConstructors(U)() 1570 { 1571 import std.algorithm : map; 1572 import std.array : join; 1573 import std.string : format; 1574 import std.traits : FieldTypeTuple; 1575 1576 string ret; 1577 1578 static if (__VERSION__ < 2072) 1579 { 1580 // disable default construction if first type is not a null/Void type 1581 static if (!is(FieldTypeTuple!U[0] == typeof(null)) && !is(FieldTypeTuple!U[0] == Void)) 1582 { 1583 ret ~= q{ 1584 @disable this(); 1585 }; 1586 } 1587 } 1588 1589 // normal type constructors 1590 foreach (tname; UniqueTypeFields!U) 1591 ret ~= q{ 1592 this(typeof(U.%s) value) 1593 { 1594 m_data.rawEmplace(value); 1595 m_kind = Kind.%s; 1596 } 1597 1598 void opAssign(typeof(U.%s) value) 1599 { 1600 if (m_kind != Kind.%s) { 1601 // NOTE: destroy(this) doesn't work for some opDispatch-related reason 1602 static if (is(typeof(&this.__xdtor))) 1603 this.__xdtor(); 1604 m_data.rawEmplace(value); 1605 } else { 1606 trustedGet!"%s" = value; 1607 } 1608 m_kind = Kind.%s; 1609 } 1610 }.format(tname, tname, tname, tname, tname, tname); 1611 1612 // type constructors with explicit type tag 1613 foreach (tname; TypeTuple!(UniqueTypeFields!U, AmbiguousTypeFields!U)) 1614 ret ~= q{ 1615 this(typeof(U.%s) value, Kind type) 1616 { 1617 assert(type.among!(%s), format("Invalid type ID for type %%s: %%s", typeof(U.%s).stringof, type)); 1618 m_data.rawEmplace(value); 1619 m_kind = type; 1620 } 1621 }.format(tname, [SameTypeFields!(U, 1622 tname)].map!(f => "Kind." ~ f).join(", "), tname); 1623 1624 return ret; 1625 } 1626 1627 private template UniqueTypeFields(U) 1628 { 1629 import std.traits : FieldTypeTuple, FieldNameTuple; 1630 1631 alias Types = FieldTypeTuple!U; 1632 1633 template impl(size_t i) 1634 { 1635 static if (i < Types.length) 1636 { 1637 enum name = FieldNameTuple!U[i]; 1638 alias T = Types[i]; 1639 static if (staticIndexOf!(T, Types) == i && staticIndexOf!(T, Types[i + 1 .. $]) < 0) 1640 alias impl = TypeTuple!(name, impl!(i + 1)); 1641 else 1642 alias impl = TypeTuple!(impl!(i + 1)); 1643 } 1644 else 1645 alias impl = TypeTuple!(); 1646 } 1647 1648 alias UniqueTypeFields = impl!0; 1649 } 1650 1651 private template AmbiguousTypeFields(U) 1652 { 1653 import std.traits : FieldTypeTuple, FieldNameTuple; 1654 1655 alias Types = FieldTypeTuple!U; 1656 1657 template impl(size_t i) 1658 { 1659 static if (i < Types.length) 1660 { 1661 enum name = FieldNameTuple!U[i]; 1662 alias T = Types[i]; 1663 static if (staticIndexOf!(T, Types) == i && staticIndexOf!(T, Types[i + 1 .. $]) >= 0) 1664 alias impl = TypeTuple!(name, impl!(i + 1)); 1665 else 1666 alias impl = impl!(i + 1); 1667 } 1668 else 1669 alias impl = TypeTuple!(); 1670 } 1671 1672 alias AmbiguousTypeFields = impl!0; 1673 } 1674 1675 unittest 1676 { 1677 union U 1678 { 1679 int a; 1680 string b; 1681 int c; 1682 double d; 1683 } 1684 1685 static assert([UniqueTypeFields!U] == ["b", "d"]); 1686 static assert([AmbiguousTypeFields!U] == ["a"]); 1687 } 1688 1689 private template SameTypeFields(U, string field) 1690 { 1691 import std.traits : FieldTypeTuple, FieldNameTuple; 1692 1693 alias Types = FieldTypeTuple!U; 1694 1695 alias T = typeof(__traits(getMember, U, field)); 1696 template impl(size_t i) 1697 { 1698 static if (i < Types.length) 1699 { 1700 enum name = FieldNameTuple!U[i]; 1701 static if (is(Types[i] == T)) 1702 alias impl = TypeTuple!(name, impl!(i + 1)); 1703 else 1704 alias impl = TypeTuple!(impl!(i + 1)); 1705 } 1706 else 1707 alias impl = TypeTuple!(); 1708 } 1709 1710 alias SameTypeFields = impl!0; 1711 } 1712 1713 private template MemberType(U) 1714 { 1715 template MemberType(string name) 1716 { 1717 alias MemberType = typeof(__traits(getMember, U, name)); 1718 } 1719 } 1720 1721 private template isMatchingType(U) 1722 { 1723 import std.traits : FieldTypeTuple; 1724 1725 enum isMatchingType(T) = staticIndexOf!(T, FieldTypeTuple!U) >= 0; 1726 } 1727 1728 private template isMatchingUniqueType(U) 1729 { 1730 import std.traits : staticMap; 1731 1732 alias UniqueTypes = staticMap!(FieldTypeOf!U, UniqueTypeFields!U); 1733 template isMatchingUniqueType(T) 1734 { 1735 static if (is(T : TaggedAlgebraic!U)) 1736 enum isMatchingUniqueType = true; 1737 else 1738 enum isMatchingUniqueType = staticIndexOfImplicit!(T, UniqueTypes) >= 0; 1739 } 1740 } 1741 1742 private template fieldMatchesType(U, T) 1743 { 1744 enum fieldMatchesType(string field) = is(typeof(__traits(getMember, U, field)) == T); 1745 } 1746 1747 private template FieldTypeOf(U) 1748 { 1749 template FieldTypeOf(string name) 1750 { 1751 alias FieldTypeOf = typeof(__traits(getMember, U, name)); 1752 } 1753 } 1754 1755 private template staticIndexOfImplicit(T, Types...) 1756 { 1757 template impl(size_t i) 1758 { 1759 static if (i < Types.length) 1760 { 1761 static if (is(T : Types[i])) 1762 enum impl = i; 1763 else 1764 enum impl = impl!(i + 1); 1765 } 1766 else 1767 enum impl = -1; 1768 } 1769 1770 enum staticIndexOfImplicit = impl!0; 1771 } 1772 1773 unittest 1774 { 1775 static assert(staticIndexOfImplicit!(immutable(char), char) == 0); 1776 static assert(staticIndexOfImplicit!(int, long) == 0); 1777 static assert(staticIndexOfImplicit!(long, int) < 0); 1778 static assert(staticIndexOfImplicit!(int, int, double) == 0); 1779 static assert(staticIndexOfImplicit!(double, int, double) == 1); 1780 } 1781 1782 private template isNoVariant(T) 1783 { 1784 import std.variant : Variant; 1785 1786 enum isNoVariant = !is(T == Variant); 1787 } 1788 1789 private void rawEmplace(T)(void[] dst, ref T src) 1790 { 1791 T[] tdst = () @trusted { return cast(T[]) dst[0 .. T.sizeof]; }(); 1792 static if (is(T == class)) 1793 { 1794 tdst[0] = src; 1795 } 1796 else 1797 { 1798 import std.conv : emplace; 1799 1800 emplace!T(&tdst[0]); 1801 tdst[0] = src; 1802 } 1803 } 1804 1805 // std.algorithm.mutation.swap sometimes fails to compile due to 1806 // internal errors in hasElaborateAssign!T/isAssignable!T. This is probably 1807 // caused by cyclic dependencies. However, there is no reason to do these 1808 // checks in this context, so we just directly move the raw memory. 1809 private void rawSwap(T)(ref T a, ref T b) @trusted 1810 { 1811 void[T.sizeof] tmp = void; 1812 void[] ab = (cast(void*)&a)[0 .. T.sizeof]; 1813 void[] bb = (cast(void*)&b)[0 .. T.sizeof]; 1814 tmp[] = ab[]; 1815 ab[] = bb[]; 1816 bb[] = tmp[]; 1817 } 1818 1819 unittest 1820 { 1821 struct TU 1822 { 1823 int i; 1824 } 1825 1826 alias TA = TaggedAlgebraic!TU; 1827 1828 auto ta = TA(12); 1829 static assert(!is(typeof(ta.put(12)))); 1830 }