1 /++ 2 A sum type for modern D. 3 4 [SumType] is an alternative to `std.variant.Algebraic` that features: 5 6 $(LIST 7 * [match|Improved pattern-matching.] 8 * Full attribute correctness (`pure`, `@safe`, `@nogc`, and `nothrow` are 9 inferred whenever possible). 10 * A type-safe and memory-safe API compatible with DIP 1000 (`scope`). 11 * No dependency on runtime type information (`TypeInfo`). 12 ) 13 14 License: MIT 15 Authors: Paul Backus, Atila Neves 16 +/ 17 module spasm.sumtype; 18 19 /+/// $(H3 Basic usage) 20 version (D_BetterC) {} else 21 @safe unittest { 22 import std.math: approxEqual; 23 24 struct Fahrenheit { double degrees; } 25 struct Celsius { double degrees; } 26 struct Kelvin { double degrees; } 27 28 alias Temperature = SumType!(Fahrenheit, Celsius, Kelvin); 29 30 // Construct from any of the member types. 31 Temperature t1 = Fahrenheit(98.6); 32 Temperature t2 = Celsius(100); 33 Temperature t3 = Kelvin(273); 34 35 // Use pattern matching to access the value. 36 pure @safe @nogc nothrow 37 Fahrenheit toFahrenheit(Temperature t) 38 { 39 return Fahrenheit( 40 t.match!( 41 (Fahrenheit f) => f.degrees, 42 (Celsius c) => c.degrees * 9.0/5 + 32, 43 (Kelvin k) => k.degrees * 9.0/5 - 459.4 44 ) 45 ); 46 } 47 48 assert(toFahrenheit(t1).degrees.approxEqual(98.6)); 49 assert(toFahrenheit(t2).degrees.approxEqual(212)); 50 assert(toFahrenheit(t3).degrees.approxEqual(32)); 51 52 // Use ref to modify the value in place. 53 pure @safe @nogc nothrow 54 void freeze(ref Temperature t) 55 { 56 t.match!( 57 (ref Fahrenheit f) => f.degrees = 32, 58 (ref Celsius c) => c.degrees = 0, 59 (ref Kelvin k) => k.degrees = 273 60 ); 61 } 62 63 freeze(t1); 64 assert(toFahrenheit(t1).degrees.approxEqual(32)); 65 66 // Use a catch-all handler to give a default result. 67 pure @safe @nogc nothrow 68 bool isFahrenheit(Temperature t) 69 { 70 return t.match!( 71 (Fahrenheit f) => true, 72 _ => false 73 ); 74 } 75 76 assert(isFahrenheit(t1)); 77 assert(!isFahrenheit(t2)); 78 assert(!isFahrenheit(t3)); 79 } 80 81 /** $(H3 Introspection-based matching) 82 * 83 * In the `length` and `horiz` functions below, the handlers for `match` do not 84 * specify the types of their arguments. Instead, matching is done based on how 85 * the argument is used in the body of the handler: any type with `x` and `y` 86 * properties will be matched by the `rect` handlers, and any type with `r` and 87 * `theta` properties will be matched by the `polar` handlers. 88 */ 89 version (D_BetterC) {} else 90 @safe unittest { 91 import std.math: approxEqual, cos, PI, sqrt; 92 93 struct Rectangular { double x, y; } 94 struct Polar { double r, theta; } 95 alias Vector = SumType!(Rectangular, Polar); 96 97 pure @safe @nogc nothrow 98 double length(Vector v) 99 { 100 return v.match!( 101 rect => sqrt(rect.x^^2 + rect.y^^2), 102 polar => polar.r 103 ); 104 } 105 106 pure @safe @nogc nothrow 107 double horiz(Vector v) 108 { 109 return v.match!( 110 rect => rect.x, 111 polar => polar.r * cos(polar.theta) 112 ); 113 } 114 115 Vector u = Rectangular(1, 1); 116 Vector v = Polar(1, PI/4); 117 118 assert(length(u).approxEqual(sqrt(2.0))); 119 assert(length(v).approxEqual(1)); 120 assert(horiz(u).approxEqual(1)); 121 assert(horiz(v).approxEqual(sqrt(0.5))); 122 } 123 +/ 124 /// `This` placeholder, for use in self-referential types. 125 public import std.variant: This; 126 127 import std.meta: NoDuplicates; 128 129 /** 130 * A tagged union that can hold a single value from any of a specified set of 131 * types. 132 * 133 * The value in a `SumType` can be operated on using [match|pattern matching]. 134 * 135 * To avoid ambiguity, duplicate types are not allowed (but see the 136 * [sumtype#basic-usage|"basic usage" example] for a workaround). 137 * 138 * The special type `This` can be used as a placeholder to create 139 * self-referential types, just like with `Algebraic`. See the 140 * [sumtype#arithmetic-expression-evaluator|"Arithmetic expression evaluator" example] for 141 * usage. 142 * 143 * A `SumType` is initialized by default to hold the `.init` value of its 144 * first member type, just like a regular union. The version identifier 145 * `SumTypeNoDefaultCtor` can be used to disable this behavior. 146 * 147 * Bugs: 148 * Types with `@disable`d `opEquals` overloads cannot be members of a 149 * `SumType`. 150 * 151 * See_Also: `std.variant.Algebraic` 152 */ 153 template FlattenSumTypes(Types...) { 154 import std.meta : staticMap; 155 static template Impl(Type) { 156 static if (is(Type : SumType!(Ts), Ts)) 157 alias Impl = Type.Types; 158 else 159 alias Impl = Type; 160 } 161 alias FlattenSumTypes = staticMap!(Impl, Types); 162 } 163 164 struct SumType(TypeArgs...) 165 if (is(NoDuplicates!TypeArgs == TypeArgs) && TypeArgs.length > 0) 166 { 167 import std.meta: AliasSeq, Filter, anySatisfy, allSatisfy, staticIndexOf; 168 import std.traits: hasElaborateCopyConstructor, hasElaborateDestructor; 169 import std.traits: isAssignable, isCopyable, isStaticArray; 170 171 /// The types a `SumType` can hold. 172 alias Types = FlattenSumTypes!(TypeArgs);//AliasSeq!(ReplaceTypeUnless!(isSumType, This, typeof(this), TypeArgs)); 173 174 private: 175 176 enum bool canHoldTag(T) = Types.length <= T.max; 177 alias unsignedInts = AliasSeq!(ubyte, ushort, uint, ulong); 178 179 alias Tag = Filter!(canHoldTag, unsignedInts)[0]; 180 181 union Storage 182 { 183 template memberName(T) 184 if (staticIndexOf!(T, Types) >= 0) 185 { 186 mixin("enum memberName = `values_", staticIndexOf!(T, Types), "`;"); 187 } 188 189 static foreach (T; Types) { 190 mixin("T ", memberName!T, ";"); 191 } 192 } 193 194 Tag tag; 195 Storage storage; 196 197 @trusted 198 ref inout(T) get(T)() inout 199 if (staticIndexOf!(T, Types) >= 0) 200 { 201 enum tid = staticIndexOf!(T, Types); 202 assert(tag == tid); 203 return __traits(getMember, storage, Storage.memberName!T); 204 } 205 206 public: 207 208 @trusted 209 ref inout(T) trustedGet(T)() inout 210 if (staticIndexOf!(T, Types) >= 0) 211 { 212 enum tid = staticIndexOf!(T, Types); 213 assert(tag == tid); 214 return __traits(getMember, storage, Storage.memberName!T); 215 } 216 217 static foreach (tid, T; Types) { 218 /// Constructs a `SumType` holding a specific value. 219 this()(auto ref T value) 220 { 221 import core.lifetime: forward; 222 223 static if (isCopyable!T) { 224 mixin("Storage newStorage = { ", Storage.memberName!T, ": value };"); 225 } else { 226 mixin("Storage newStorage = { ", Storage.memberName!T, " : forward!value };"); 227 } 228 229 storage = newStorage; 230 tag = tid; 231 } 232 233 static if (isCopyable!T) { 234 /// ditto 235 this()(auto ref const(T) value) const 236 { 237 mixin("const(Storage) newStorage = { ", Storage.memberName!T, ": value };"); 238 storage = newStorage; 239 tag = tid; 240 } 241 242 /// ditto 243 this()(auto ref immutable(T) value) immutable 244 { 245 mixin("immutable(Storage) newStorage = { ", Storage.memberName!T, ": value };"); 246 storage = newStorage; 247 tag = tid; 248 } 249 } else { 250 @disable this(const(T) value) const; 251 @disable this(immutable(T) value) immutable; 252 } 253 } 254 255 static if (allSatisfy!(isCopyable, Types)) { 256 static if (anySatisfy!(hasElaborateCopyConstructor, Types)) { 257 /// Constructs a `SumType` that's a copy of another `SumType` 258 this(ref SumType other) 259 { 260 storage = other.match!((ref value) { 261 alias T = typeof(value); 262 263 mixin("Storage newStorage = { ", Storage.memberName!T, ": value };"); 264 return newStorage; 265 }); 266 267 tag = other.tag; 268 } 269 270 /// ditto 271 this(ref const(SumType) other) const 272 { 273 import std.meta: staticMap; 274 import std.traits: ConstOf; 275 276 storage = other.match!((ref value) { 277 alias OtherTypes = staticMap!(ConstOf, Types); 278 enum tid = staticIndexOf!(typeof(value), OtherTypes); 279 alias T = Types[tid]; 280 281 mixin("const(Storage) newStorage = { ", Storage.memberName!T, ": value };"); 282 return newStorage; 283 }); 284 285 tag = other.tag; 286 } 287 288 /// ditto 289 this(ref immutable(SumType) other) immutable 290 { 291 import std.meta: staticMap; 292 import std.traits: ImmutableOf; 293 294 storage = other.match!((ref value) { 295 alias OtherTypes = staticMap!(ImmutableOf, Types); 296 enum tid = staticIndexOf!(typeof(value), OtherTypes); 297 alias T = Types[tid]; 298 299 mixin("immutable(Storage) newStorage = { ", Storage.memberName!T, ": value };"); 300 return newStorage; 301 }); 302 303 tag = other.tag; 304 } 305 } 306 } else { 307 /// `@disable`d if any member type is non-copyable. 308 @disable this(this); 309 } 310 311 version(SumTypeNoDefaultCtor) { 312 @disable this(); 313 } 314 315 static foreach (tid, T; Types) { 316 static if (isAssignable!T) { 317 /** 318 * Assigns a value to a `SumType`. 319 * 320 * Assigning to a `SumType` is `@system` if any of the 321 * `SumType`'s members contain pointers or references, since 322 * those members may be reachable through external references, 323 * and overwriting them could therefore lead to memory 324 * corruption. 325 * 326 * An individual assignment can be `@trusted` if the caller can 327 * guarantee that there are no outstanding references to $(I any) 328 * of the `SumType`'s members when the assignment occurs. 329 */ 330 void opAssign()(auto ref T rhs) 331 { 332 import core.lifetime: forward; 333 import std.traits: hasIndirections, hasNested; 334 import std.meta: Or = templateOr; 335 336 enum mayContainPointers = 337 anySatisfy!(Or!(hasIndirections, hasNested), Types); 338 339 static if (mayContainPointers) { 340 cast(void) () @system {}(); 341 } 342 343 this.match!((ref value) { 344 static if (hasElaborateDestructor!(typeof(value))) { 345 destroy(value); 346 } 347 }); 348 349 mixin("Storage newStorage = { ", Storage.memberName!T, ": forward!rhs };"); 350 storage = newStorage; 351 tag = tid; 352 } 353 } 354 } 355 356 static if (allSatisfy!(isAssignable, Types)) { 357 static if (allSatisfy!(isCopyable, Types)) { 358 /** 359 * Copies the value from another `SumType` into this one. 360 * 361 * See the value-assignment overload for details on `@safe`ty. 362 * 363 * Copy assignment is `@disable`d if any of `Types` is non-copyable. 364 */ 365 void opAssign(ref SumType rhs) 366 { 367 rhs.match!((ref value) { this = value; }); 368 } 369 } else { 370 @disable void opAssign(ref SumType rhs); 371 } 372 373 /** 374 * Moves the value from another `SumType` into this one. 375 * 376 * See the value-assignment overload for details on `@safe`ty. 377 */ 378 void opAssign(SumType rhs) 379 { 380 import core.lifetime: move; 381 382 rhs.match!((ref value) { this = move(value); }); 383 } 384 } 385 386 /** 387 * Compares two `SumType`s for equality. 388 * 389 * Two `SumType`s are equal if they are the same kind of `SumType`, they 390 * contain values of the same type, and those values are equal. 391 */ 392 bool opEquals(const SumType rhs) const { 393 return this.match!((ref value) { 394 return rhs.match!((ref rhsValue) { 395 static if (is(typeof(value) == typeof(rhsValue))) { 396 return value == rhsValue; 397 } else { 398 return false; 399 } 400 }); 401 }); 402 } 403 404 // Workaround for dlang issue 19407 405 static if (__traits(compiles, anySatisfy!(hasElaborateDestructor, Types))) { 406 // If possible, include the destructor only when it's needed 407 private enum includeDtor = anySatisfy!(hasElaborateDestructor, Types); 408 } else { 409 // If we can't tell, always include it, even when it does nothing 410 private enum includeDtor = true; 411 } 412 413 static if (includeDtor) { 414 /// Calls the destructor of the `SumType`'s current value. 415 ~this() 416 { 417 this.match!((ref value) { 418 static if (hasElaborateDestructor!(typeof(value))) { 419 destroy(value); 420 } 421 }); 422 } 423 } 424 425 invariant { 426 this.match!((ref value) { 427 static if (is(typeof(value) == class)) { 428 if (value !is null) { 429 assert(value); 430 } 431 } else static if (is(typeof(value) == struct)) { 432 assert(&value); 433 } 434 }); 435 } 436 437 static if (allSatisfy!(isCopyable, Types)) { 438 /** 439 * Returns a string representation of a `SumType`'s value. 440 * 441 * Not available when compiled with `-betterC`. 442 */ 443 version (D_BetterC) {} else 444 string toString(this T)() { 445 import std.conv: text; 446 return this.match!((auto ref value) { 447 return value.text; 448 }); 449 } 450 } 451 } 452 /+ 453 // Construction 454 @safe unittest { 455 alias MySum = SumType!(int, float); 456 457 assert(__traits(compiles, MySum(42))); 458 assert(__traits(compiles, MySum(3.14))); 459 } 460 461 // Assignment 462 @safe unittest { 463 alias MySum = SumType!(int, float); 464 465 MySum x = MySum(42); 466 467 assert(__traits(compiles, x = 3.14)); 468 } 469 470 // Self assignment 471 @safe unittest { 472 alias MySum = SumType!(int, float); 473 474 MySum x = MySum(42); 475 MySum y = MySum(3.14); 476 477 assert(__traits(compiles, y = x)); 478 } 479 480 // Equality 481 @safe unittest { 482 alias MySum = SumType!(int, float); 483 484 MySum x = MySum(123); 485 MySum y = MySum(123); 486 MySum z = MySum(456); 487 MySum w = MySum(123.0); 488 MySum v = MySum(456.0); 489 490 assert(x == y); 491 assert(x != z); 492 assert(x != w); 493 assert(x != v); 494 } 495 496 // Imported types 497 @safe unittest { 498 import std.typecons: Tuple; 499 500 assert(__traits(compiles, { 501 alias MySum = SumType!(Tuple!(int, int)); 502 })); 503 } 504 505 // const and immutable types 506 @safe unittest { 507 assert(__traits(compiles, { 508 alias MySum = SumType!(const(int[]), immutable(float[])); 509 })); 510 } 511 512 // Works alongside Algebraic 513 version (D_BetterC) {} else 514 @safe unittest { 515 import std.variant; 516 517 alias Bar = Algebraic!(This*); 518 519 assert(is(Bar.AllowedTypes[0] == Bar*)); 520 } 521 522 // Types with destructors and postblits 523 @system unittest { 524 int copies; 525 526 static struct Test 527 { 528 bool initialized = false; 529 int* copiesPtr; 530 531 this(this) { (*copiesPtr)++; } 532 ~this() { if (initialized) (*copiesPtr)--; } 533 } 534 535 alias MySum = SumType!(int, Test); 536 537 Test t = Test(true, &copies); 538 539 { 540 MySum x = t; 541 assert(copies == 1); 542 } 543 assert(copies == 0); 544 545 { 546 MySum x = 456; 547 assert(copies == 0); 548 } 549 assert(copies == 0); 550 551 { 552 MySum x = t; 553 assert(copies == 1); 554 x = 456; 555 assert(copies == 0); 556 } 557 558 { 559 MySum x = 456; 560 assert(copies == 0); 561 x = t; 562 assert(copies == 1); 563 } 564 565 { 566 MySum x = t; 567 MySum y = x; 568 assert(copies == 2); 569 } 570 571 { 572 MySum x = t; 573 MySum y; 574 y = x; 575 assert(copies == 2); 576 } 577 } 578 579 // Doesn't destroy reference types 580 version (D_BetterC) {} else 581 @system unittest { 582 bool destroyed; 583 584 class C 585 { 586 ~this() 587 { 588 destroyed = true; 589 } 590 } 591 592 struct S 593 { 594 ~this() {} 595 } 596 597 alias MySum = SumType!(S, C); 598 599 C c = new C(); 600 { 601 MySum x = c; 602 destroyed = false; 603 } 604 assert(!destroyed); 605 606 { 607 MySum x = c; 608 destroyed = false; 609 x = S(); 610 assert(!destroyed); 611 } 612 } 613 614 // Types with @disable this() 615 @safe unittest { 616 static struct NoInit 617 { 618 @disable this(); 619 } 620 621 alias MySum = SumType!(NoInit, int); 622 623 assert(!__traits(compiles, MySum())); 624 assert(__traits(compiles, MySum(42))); 625 } 626 627 // const SumTypes 628 @safe unittest { 629 assert(__traits(compiles, 630 const(SumType!(int[]))([1, 2, 3]) 631 )); 632 } 633 634 // Equality of const SumTypes 635 @safe unittest { 636 alias MySum = SumType!int; 637 638 assert(__traits(compiles, 639 const(MySum)(123) == const(MySum)(456) 640 )); 641 } 642 643 // Compares reference types using value equality 644 @safe unittest { 645 import std.array: staticArray; 646 647 static struct Field {} 648 static struct Struct { Field[] fields; } 649 alias MySum = SumType!Struct; 650 651 static arr1 = staticArray([Field()]); 652 static arr2 = staticArray([Field()]); 653 654 auto a = MySum(Struct(arr1[])); 655 auto b = MySum(Struct(arr2[])); 656 657 assert(a == b); 658 } 659 660 // toString 661 version (D_BetterC) {} else 662 @safe unittest { 663 import std.conv: text; 664 665 static struct Int { int i; } 666 static struct Double { double d; } 667 alias Sum = SumType!(Int, Double); 668 669 assert(Sum(Int(42)).text == Int(42).text, Sum(Int(42)).text); 670 assert(Sum(Double(33.3)).text == Double(33.3).text, Sum(Double(33.3)).text); 671 assert((const(Sum)(Int(42))).text == (const(Int)(42)).text, (const(Sum)(Int(42))).text); 672 } 673 674 // Stale pointers 675 version (D_BetterC) {} else 676 @system unittest { 677 alias MySum = SumType!(ubyte, void*[2]); 678 679 MySum x = [null, cast(void*) 0x12345678]; 680 void** p = &x.get!(void*[2])[1]; 681 x = ubyte(123); 682 683 assert(*p != cast(void*) 0x12345678); 684 } 685 686 // Exception-safe assignment 687 version (D_BetterC) {} else 688 @safe unittest { 689 static struct A 690 { 691 int value = 123; 692 } 693 694 static struct B 695 { 696 int value = 456; 697 this(this) { throw new Exception("oops"); } 698 } 699 700 alias MySum = SumType!(A, B); 701 702 MySum x; 703 try { 704 x = B(); 705 } catch (Exception e) {} 706 707 assert( 708 (x.tag == 0 && x.get!A.value == 123) || 709 (x.tag == 1 && x.get!B.value == 456) 710 ); 711 } 712 713 // Types with @disable this(this) 714 @safe unittest { 715 import std.algorithm.mutation: move; 716 717 static struct NoCopy 718 { 719 @disable this(this); 720 } 721 722 alias MySum = SumType!NoCopy; 723 724 NoCopy lval = NoCopy(); 725 726 MySum x = NoCopy(); 727 MySum y = NoCopy(); 728 729 assert(__traits(compiles, SumType!NoCopy(NoCopy()))); 730 assert(!__traits(compiles, SumType!NoCopy(lval))); 731 732 assert(__traits(compiles, y = NoCopy())); 733 assert(__traits(compiles, y = move(x))); 734 assert(!__traits(compiles, y = lval)); 735 assert(!__traits(compiles, y = x)); 736 } 737 738 // Static arrays of structs with postblits 739 version (D_BetterC) {} else 740 @safe unittest { 741 static struct S 742 { 743 int n; 744 this(this) { n++; } 745 } 746 747 assert(__traits(compiles, SumType!(S[1])())); 748 749 SumType!(S[1]) x = [S(0)]; 750 SumType!(S[1]) y = x; 751 752 auto xval = x.get!(S[1])[0].n; 753 auto yval = y.get!(S[1])[0].n; 754 755 assert(xval != yval); 756 } 757 758 // Replacement does not happen inside SumType 759 version (D_BetterC) {} else 760 @safe unittest { 761 import std.typecons : Tuple; 762 alias A = Tuple!(This*,SumType!(This*))[SumType!(This*,string)[This]]; 763 alias TR = ReplaceTypeUnless!(isSumType, This, int, A); 764 static assert(is(TR == Tuple!(int*,SumType!(This*))[SumType!(This*, string)[int]])); 765 } 766 767 // Supports nested self-referential SumTypes 768 @safe unittest { 769 import std.typecons : Tuple, Flag; 770 alias Nat = SumType!(Flag!"0", Tuple!(This*)); 771 static assert(__traits(compiles, SumType!(Nat))); 772 static assert(__traits(compiles, SumType!(Nat*, Tuple!(This*, This*)))); 773 } 774 775 // Doesn't call @system postblits in @safe code 776 @safe unittest { 777 static struct SystemCopy { @system this(this) {} } 778 SystemCopy original; 779 780 assert(!__traits(compiles, () @safe { 781 SumType!SystemCopy copy = original; 782 })); 783 784 assert(!__traits(compiles, () @safe { 785 SumType!SystemCopy copy; copy = original; 786 })); 787 } 788 789 // Doesn't overwrite pointers in @safe code 790 @safe unittest { 791 alias MySum = SumType!(int*, int); 792 793 MySum x; 794 795 assert(!__traits(compiles, () @safe { 796 x = 123; 797 })); 798 799 assert(!__traits(compiles, () @safe { 800 x = MySum(123); 801 })); 802 } 803 804 // Types with invariants 805 version (D_BetterC) {} else 806 @system unittest { 807 import std.exception: assertThrown; 808 import core.exception: AssertError; 809 810 struct S 811 { 812 int i; 813 invariant { assert(i >= 0); } 814 } 815 816 class C 817 { 818 int i; 819 invariant { assert(i >= 0); } 820 } 821 822 SumType!S x; 823 x.match!((ref v) { v.i = -1; }); 824 assertThrown!AssertError(assert(&x)); 825 826 SumType!C y = new C(); 827 y.match!((ref v) { v.i = -1; }); 828 assertThrown!AssertError(assert(&y)); 829 } 830 831 // Calls value postblit on self-assignment 832 @safe unittest { 833 static struct S 834 { 835 int n; 836 this(this) { n++; } 837 } 838 839 SumType!S x = S(); 840 SumType!S y; 841 y = x; 842 843 auto xval = x.get!S.n; 844 auto yval = y.get!S.n; 845 846 assert(xval != yval); 847 } 848 849 // Github issue #29 850 @safe unittest { 851 assert(__traits(compiles, () @safe { 852 alias A = SumType!string; 853 854 @safe A createA(string arg) { 855 return A(arg); 856 } 857 858 @safe void test() { 859 A a = createA(""); 860 } 861 })); 862 } 863 864 version(none) { 865 // Known bug; needs fix for dlang issue 19902 866 // Types with copy constructors 867 @safe unittest { 868 static struct S 869 { 870 int n; 871 this(ref return scope inout S other) inout { n++; } 872 } 873 874 SumType!S x = S(); 875 SumType!S y = x; 876 877 auto xval = x.get!S.n; 878 auto yval = y.get!S.n; 879 880 assert(xval != yval); 881 } 882 } 883 884 version(none) { 885 // Known bug; needs fix for dlang issue 19458 886 // Types with disabled opEquals 887 @safe unittest { 888 static struct S 889 { 890 @disable bool opEquals(const S rhs) const; 891 } 892 893 assert(__traits(compiles, SumType!S(S()))); 894 } 895 } 896 897 version(none) { 898 // Known bug; needs fix for dlang issue 19458 899 @safe unittest { 900 static struct S 901 { 902 int i; 903 bool opEquals(S rhs) { return i == rhs.i; } 904 } 905 906 assert(__traits(compiles, SumType!S(S(123)))); 907 } 908 } 909 +/ 910 /// True if `T` is an instance of `SumType`, otherwise false. 911 enum isSumType(T) = is(T == SumType!Args, Args...); 912 913 /+ 914 unittest { 915 static struct Wrapper 916 { 917 SumType!int s; 918 alias s this; 919 } 920 921 assert(isSumType!(SumType!int)); 922 assert(!isSumType!Wrapper); 923 } 924 +/ 925 /** 926 * Calls a type-appropriate function with the value held in a [SumType]. 927 * 928 * For each possible type the [SumType] can hold, the given handlers are 929 * checked, in order, to see whether they accept a single argument of that type. 930 * The first one that does is chosen as the match for that type. 931 * 932 * Every type must have a matching handler, and every handler must match at 933 * least one type. This is enforced at compile time. 934 * 935 * Handlers may be functions, delegates, or objects with opCall overloads. If a 936 * function with more than one overload is given as a handler, all of the 937 * overloads are considered as potential matches. 938 * 939 * Templated handlers are also accepted, and will match any type for which they 940 * can be [implicitly instantiated](https://dlang.org/glossary.html#ifti). See 941 * [sumtype#introspection-based-matching|"Introspection-based matching"] for an 942 * example of templated handler usage. 943 * 944 * Returns: 945 * The value returned from the handler that matches the currently-held type. 946 * 947 * See_Also: `std.variant.visit` 948 */ 949 template match(handlers...) 950 { 951 import std.typecons: Yes; 952 953 /** 954 * The actual `match` function. 955 * 956 * Params: 957 * self = A [SumType] object 958 */ 959 auto match(Self)(auto ref Self self) 960 if (is(Self : SumType!TypeArgs, TypeArgs...)) 961 { 962 return self.matchImpl!(Yes.exhaustive, handlers); 963 } 964 } 965 /+ 966 /** 967 * Attempts to call a type-appropriate function with the value held in a 968 * [SumType], and throws on failure. 969 * 970 * Matches are chosen using the same rules as [match], but are not required to 971 * be exhaustive—in other words, a type is allowed to have no matching handler. 972 * If a type without a handler is encountered at runtime, a [MatchException] 973 * is thrown. 974 * 975 * Not available when compiled with `-betterC`. 976 * 977 * Returns: 978 * The value returned from the handler that matches the currently-held type, 979 * if a handler was given for that type. 980 * 981 * Throws: 982 * [MatchException], if the currently-held type has no matching handler. 983 * 984 * See_Also: `std.variant.tryVisit` 985 */ 986 version (D_Exceptions) 987 template tryMatch(handlers...) 988 { 989 import std.typecons: No; 990 991 /** 992 * The actual `tryMatch` function. 993 * 994 * Params: 995 * self = A [SumType] object 996 */ 997 auto tryMatch(Self)(auto ref Self self) 998 if (is(Self : SumType!TypeArgs, TypeArgs...)) 999 { 1000 return self.matchImpl!(No.exhaustive, handlers); 1001 } 1002 } 1003 1004 /** 1005 * Thrown by [tryMatch] when an unhandled type is encountered. 1006 * 1007 * Not available when compiled with `-betterC`. 1008 */ 1009 version (D_Exceptions) 1010 class MatchException : Exception 1011 { 1012 pure @safe @nogc nothrow 1013 this(string msg, string file = __FILE__, size_t line = __LINE__) 1014 { 1015 super(msg, file, line); 1016 } 1017 } 1018 +/ 1019 /** 1020 * True if `handler` is a potential match for `T`, otherwise false. 1021 * 1022 * See the documentation for [match] for a full explanation of how matches are 1023 * chosen. 1024 */ 1025 enum bool canMatch(alias handler, T) = is(typeof((T arg) { handler(arg); })); 1026 1027 // Includes all overloads of the given handler 1028 // @safe unittest { 1029 // static struct OverloadSet 1030 // { 1031 // static void fun(int n) {} 1032 // static void fun(double d) {} 1033 // } 1034 1035 // assert(canMatch!(OverloadSet.fun, int)); 1036 // assert(canMatch!(OverloadSet.fun, double)); 1037 // } 1038 1039 import std.typecons: Flag; 1040 1041 private template matchImpl(Flag!"exhaustive" exhaustive, handlers...) 1042 { 1043 auto matchImpl(Self)(auto ref Self self) 1044 if (is(Self : SumType!TypeArgs, TypeArgs...)) 1045 { 1046 alias Types = self.Types; 1047 enum noMatch = size_t.max; 1048 1049 enum matches = () { 1050 size_t[Types.length] matches; 1051 1052 // Workaround for dlang issue 19561 1053 foreach (ref match; matches) { 1054 match = noMatch; 1055 } 1056 1057 static foreach (tid, T; Types) { 1058 static foreach (hid, handler; handlers) { 1059 static if (canMatch!(handler, typeof(self.get!T()))) { 1060 if (matches[tid] == noMatch) { 1061 matches[tid] = hid; 1062 } 1063 } 1064 } 1065 } 1066 1067 return matches; 1068 }(); 1069 1070 import std.algorithm.searching: canFind; 1071 1072 // Check for unreachable handlers 1073 static foreach (hid, handler; handlers) { 1074 static assert(matches[].canFind(hid), 1075 "handler `" ~ __traits(identifier, handler) ~ "` " ~ 1076 "of type `" ~ ( __traits(isTemplate, handler) 1077 ? "template" 1078 : typeof(handler).stringof 1079 ) ~ "` " ~ 1080 "never matches" 1081 ); 1082 } 1083 1084 // Workaround for dlang issue 19993 1085 static foreach (size_t hid, handler; handlers) { 1086 mixin("alias handler", hid, " = handler;"); 1087 } 1088 1089 final switch (self.tag) { 1090 static foreach (tid, T; Types) { 1091 case tid: 1092 static if (matches[tid] != noMatch) { 1093 return mixin("handler", matches[tid])(self.get!T); 1094 } else { 1095 static if(exhaustive) { 1096 static assert(false, 1097 "No matching handler for type `" ~ T.stringof ~ "`"); 1098 } else { 1099 throw new MatchException( 1100 "No matching handler for type `" ~ T.stringof ~ "`"); 1101 } 1102 } 1103 } 1104 } 1105 1106 assert(false); // unreached 1107 } 1108 } 1109 /+ 1110 // Matching 1111 @safe unittest { 1112 alias MySum = SumType!(int, float); 1113 1114 MySum x = MySum(42); 1115 MySum y = MySum(3.14); 1116 1117 assert(x.match!((int v) => true, (float v) => false)); 1118 assert(y.match!((int v) => false, (float v) => true)); 1119 } 1120 1121 // Missing handlers 1122 @safe unittest { 1123 alias MySum = SumType!(int, float); 1124 1125 MySum x = MySum(42); 1126 1127 assert(!__traits(compiles, x.match!((int x) => true))); 1128 assert(!__traits(compiles, x.match!())); 1129 } 1130 1131 // Handlers with qualified parameters 1132 version (D_BetterC) {} else 1133 @safe unittest { 1134 alias MySum = SumType!(int[], float[]); 1135 1136 MySum x = MySum([1, 2, 3]); 1137 MySum y = MySum([1.0, 2.0, 3.0]); 1138 1139 assert(x.match!((const(int[]) v) => true, (const(float[]) v) => false)); 1140 assert(y.match!((const(int[]) v) => false, (const(float[]) v) => true)); 1141 } 1142 1143 // Handlers for qualified types 1144 version (D_BetterC) {} else 1145 @safe unittest { 1146 alias MySum = SumType!(immutable(int[]), immutable(float[])); 1147 1148 MySum x = MySum([1, 2, 3]); 1149 1150 assert(x.match!((immutable(int[]) v) => true, (immutable(float[]) v) => false)); 1151 assert(x.match!((const(int[]) v) => true, (const(float[]) v) => false)); 1152 // Tail-qualified parameters 1153 assert(x.match!((immutable(int)[] v) => true, (immutable(float)[] v) => false)); 1154 assert(x.match!((const(int)[] v) => true, (const(float)[] v) => false)); 1155 // Generic parameters 1156 assert(x.match!((immutable v) => true)); 1157 assert(x.match!((const v) => true)); 1158 // Unqualified parameters 1159 assert(!__traits(compiles, 1160 x.match!((int[] v) => true, (float[] v) => false) 1161 )); 1162 } 1163 1164 // Delegate handlers 1165 version (D_BetterC) {} else 1166 @safe unittest { 1167 alias MySum = SumType!(int, float); 1168 1169 int answer = 42; 1170 MySum x = MySum(42); 1171 MySum y = MySum(3.14); 1172 1173 assert(x.match!((int v) => v == answer, (float v) => v == answer)); 1174 assert(!y.match!((int v) => v == answer, (float v) => v == answer)); 1175 } 1176 1177 version(unittest) { 1178 version(D_BetterC) { 1179 // std.math.approxEqual depends on core.runtime.math, so use a 1180 // libc-based version for testing with -betterC 1181 @safe pure @nogc nothrow 1182 private bool approxEqual(double lhs, double rhs) 1183 { 1184 import core.stdc.math: fabs; 1185 1186 return (lhs - rhs) < 1e-5; 1187 } 1188 } else { 1189 import std.math: approxEqual; 1190 } 1191 } 1192 1193 // Generic handler 1194 @safe unittest { 1195 alias MySum = SumType!(int, float); 1196 1197 MySum x = MySum(42); 1198 MySum y = MySum(3.14); 1199 1200 assert(x.match!(v => v*2) == 84); 1201 assert(y.match!(v => v*2).approxEqual(6.28)); 1202 } 1203 1204 // Fallback to generic handler 1205 version (D_BetterC) {} else 1206 @safe unittest { 1207 import std.conv: to; 1208 1209 alias MySum = SumType!(int, float, string); 1210 1211 MySum x = MySum(42); 1212 MySum y = MySum("42"); 1213 1214 assert(x.match!((string v) => v.to!int, v => v*2) == 84); 1215 assert(y.match!((string v) => v.to!int, v => v*2) == 42); 1216 } 1217 1218 // Multiple non-overlapping generic handlers 1219 @safe unittest { 1220 import std.array: staticArray; 1221 1222 alias MySum = SumType!(int, float, int[], char[]); 1223 1224 static ints = staticArray([1, 2, 3]); 1225 static chars = staticArray(['a', 'b', 'c']); 1226 1227 MySum x = MySum(42); 1228 MySum y = MySum(3.14); 1229 MySum z = MySum(ints[]); 1230 MySum w = MySum(chars[]); 1231 1232 assert(x.match!(v => v*2, v => v.length) == 84); 1233 assert(y.match!(v => v*2, v => v.length).approxEqual(6.28)); 1234 assert(w.match!(v => v*2, v => v.length) == 3); 1235 assert(z.match!(v => v*2, v => v.length) == 3); 1236 } 1237 1238 // Structural matching 1239 @safe unittest { 1240 static struct S1 { int x; } 1241 static struct S2 { int y; } 1242 alias MySum = SumType!(S1, S2); 1243 1244 MySum a = MySum(S1(0)); 1245 MySum b = MySum(S2(0)); 1246 1247 assert(a.match!(s1 => s1.x + 1, s2 => s2.y - 1) == 1); 1248 assert(b.match!(s1 => s1.x + 1, s2 => s2.y - 1) == -1); 1249 } 1250 1251 // Separate opCall handlers 1252 @safe unittest { 1253 static struct IntHandler 1254 { 1255 bool opCall(int arg) 1256 { 1257 return true; 1258 } 1259 } 1260 1261 static struct FloatHandler 1262 { 1263 bool opCall(float arg) 1264 { 1265 return false; 1266 } 1267 } 1268 1269 alias MySum = SumType!(int, float); 1270 1271 MySum x = MySum(42); 1272 MySum y = MySum(3.14); 1273 1274 assert(x.match!(IntHandler.init, FloatHandler.init)); 1275 assert(!y.match!(IntHandler.init, FloatHandler.init)); 1276 } 1277 1278 // Compound opCall handler 1279 @safe unittest { 1280 static struct CompoundHandler 1281 { 1282 bool opCall(int arg) 1283 { 1284 return true; 1285 } 1286 1287 bool opCall(float arg) 1288 { 1289 return false; 1290 } 1291 } 1292 1293 alias MySum = SumType!(int, float); 1294 1295 MySum x = MySum(42); 1296 MySum y = MySum(3.14); 1297 1298 assert(x.match!(CompoundHandler.init)); 1299 assert(!y.match!(CompoundHandler.init)); 1300 } 1301 1302 // Ordered matching 1303 @safe unittest { 1304 alias MySum = SumType!(int, float); 1305 1306 MySum x = MySum(42); 1307 1308 assert(x.match!((int v) => true, v => false)); 1309 } 1310 1311 // Non-exhaustive matching 1312 version (D_Exceptions) 1313 @system unittest { 1314 import std.exception: assertThrown, assertNotThrown; 1315 1316 alias MySum = SumType!(int, float); 1317 1318 MySum x = MySum(42); 1319 MySum y = MySum(3.14); 1320 1321 assertNotThrown!MatchException(x.tryMatch!((int n) => true)); 1322 assertThrown!MatchException(y.tryMatch!((int n) => true)); 1323 } 1324 1325 // Non-exhaustive matching in @safe code 1326 version (D_Exceptions) 1327 @safe unittest { 1328 SumType!(int, float) x; 1329 1330 assert(__traits(compiles, 1331 x.tryMatch!( 1332 (int n) => n + 1, 1333 ) 1334 )); 1335 1336 } 1337 1338 // Handlers with ref parameters 1339 @safe unittest { 1340 import std.meta: staticIndexOf; 1341 1342 alias Value = SumType!(long, double); 1343 1344 auto value = Value(3.14); 1345 1346 value.match!( 1347 (long) {}, 1348 (ref double d) { d *= 2; } 1349 ); 1350 1351 assert(value.get!double.approxEqual(6.28)); 1352 } 1353 1354 // Unreachable handlers 1355 @safe unittest { 1356 alias MySum = SumType!(int, string); 1357 1358 MySum s; 1359 1360 assert(!__traits(compiles, 1361 s.match!( 1362 (int _) => 0, 1363 (string _) => 1, 1364 (double _) => 2 1365 ) 1366 )); 1367 1368 assert(!__traits(compiles, 1369 s.match!( 1370 _ => 0, 1371 (int _) => 1 1372 ) 1373 )); 1374 } 1375 1376 // Unsafe handlers 1377 unittest { 1378 SumType!int x; 1379 alias unsafeHandler = (int x) @system { return; }; 1380 1381 assert(!__traits(compiles, () @safe { 1382 x.match!unsafeHandler; 1383 })); 1384 1385 assert(__traits(compiles, () @system { 1386 return x.match!unsafeHandler; 1387 })); 1388 } 1389 1390 // Overloaded handlers 1391 @safe unittest { 1392 static struct OverloadSet 1393 { 1394 static string fun(int i) { return "int"; } 1395 static string fun(double d) { return "double"; } 1396 } 1397 1398 alias MySum = SumType!(int, double); 1399 1400 MySum a = 42; 1401 MySum b = 3.14; 1402 1403 assert(a.match!(OverloadSet.fun) == "int"); 1404 assert(b.match!(OverloadSet.fun) == "double"); 1405 } 1406 1407 // Overload sets with ref arguments 1408 @safe unittest { 1409 static struct OverloadSet 1410 { 1411 static void fun(ref int i) { i = 42; } 1412 static void fun(ref double d) { d = 3.14; } 1413 } 1414 1415 alias MySum = SumType!(int, double); 1416 1417 MySum x = 0; 1418 MySum y = 0.0; 1419 1420 x.match!(OverloadSet.fun); 1421 y.match!(OverloadSet.fun); 1422 1423 assert(x.match!((value) => is(typeof(value) == int) && value == 42)); 1424 assert(y.match!((value) => is(typeof(value) == double) && value == 3.14)); 1425 } 1426 1427 // Overload sets with templates 1428 @safe unittest { 1429 import std.traits: isNumeric; 1430 1431 static struct OverloadSet 1432 { 1433 static string fun(string arg) 1434 { 1435 return "string"; 1436 } 1437 1438 static string fun(T)(T arg) 1439 if (isNumeric!T) 1440 { 1441 return "numeric"; 1442 } 1443 } 1444 1445 alias MySum = SumType!(int, string); 1446 1447 MySum x = 123; 1448 MySum y = "hello"; 1449 1450 assert(x.match!(OverloadSet.fun) == "numeric"); 1451 assert(y.match!(OverloadSet.fun) == "string"); 1452 } 1453 1454 // Github issue #24 1455 @safe unittest { 1456 assert(__traits(compiles, () @nogc { 1457 int acc = 0; 1458 SumType!int(1).match!((int x) => acc += x); 1459 })); 1460 } 1461 1462 // Github issue #31 1463 @safe unittest { 1464 assert(__traits(compiles, () @nogc { 1465 int acc = 0; 1466 1467 SumType!(int, string)(1).match!( 1468 (int x) => acc += x, 1469 (string _) => 0, 1470 ); 1471 })); 1472 } 1473 1474 // Types that `alias this` a SumType 1475 @safe unittest { 1476 static struct A {} 1477 static struct B {} 1478 static struct D { SumType!(A, B) value; alias value this; } 1479 1480 assert(__traits(compiles, D().match!(_ => true))); 1481 } 1482 +/ 1483 version(SumTypeTestBetterC) { 1484 version(D_BetterC) {} 1485 else static assert(false, "Must compile with -betterC to run betterC tests"); 1486 1487 version(unittest) {} 1488 else static assert(false, "Must compile with -unittest to run betterC tests"); 1489 1490 extern(C) int main() 1491 { 1492 import core.stdc.stdio: puts; 1493 static foreach (test; __traits(getUnitTests, mixin(__MODULE__))) { 1494 test(); 1495 } 1496 1497 puts("All unit tests have been run successfully."); 1498 return 0; 1499 } 1500 } 1501 1502 static if (__traits(compiles, { import std.typecons: ReplaceTypeUnless; })) { 1503 import std.typecons: ReplaceTypeUnless; 1504 } else { 1505 /** 1506 * Replaces all occurrences of `From` into `To`, in one or more types `T` 1507 * whenever the predicate applied to `T` evaluates to false. For example, $(D 1508 * ReplaceTypeUnless!(isBoolean, int, uint, Tuple!(int, float)[string])) yields 1509 * $(D Tuple!(uint, float)[string]) while $(D ReplaceTypeUnless!(isTuple, int, 1510 * string, Tuple!(int, bool)[int])) yields $(D Tuple!(int, bool)[string]). The 1511 * types in which replacement is performed may be arbitrarily complex, 1512 * including qualifiers, built-in type constructors (pointers, arrays, 1513 * associative arrays, functions, and delegates), and template instantiations; 1514 * replacement proceeds transitively through the type definition. However, 1515 * member types in `struct`s or `class`es are not replaced because there are no 1516 * ways to express the types resulting after replacement. 1517 * 1518 * This is an advanced type manipulation necessary e.g. for replacing the 1519 * placeholder type `This` in $(REF SumType). 1520 * 1521 * This template is a generalised version of the one in 1522 * https://github.com/dlang/phobos/blob/d1c8fb0b69dc12669554d5cb96d3045753549619/std/typecons.d 1523 * 1524 * Returns: `ReplaceTypeUnless` aliases itself to the type(s) that result after 1525 * replacement. 1526 */ 1527 private template ReplaceTypeUnless(alias Pred, From, To, T...) 1528 { 1529 import std.meta; 1530 1531 static if (T.length == 1) 1532 { 1533 static if (Pred!(T[0])) 1534 alias ReplaceTypeUnless = T[0]; 1535 else static if (is(T[0] == From)) 1536 alias ReplaceTypeUnless = To; 1537 else static if (is(T[0] == const(U), U)) 1538 alias ReplaceTypeUnless = const(ReplaceTypeUnless!(Pred, From, To, U)); 1539 else static if (is(T[0] == immutable(U), U)) 1540 alias ReplaceTypeUnless = immutable(ReplaceTypeUnless!(Pred, From, To, U)); 1541 else static if (is(T[0] == shared(U), U)) 1542 alias ReplaceTypeUnless = shared(ReplaceTypeUnless!(Pred, From, To, U)); 1543 else static if (is(T[0] == U*, U)) 1544 { 1545 static if (is(U == function)) 1546 alias ReplaceTypeUnless = replaceTypeInFunctionTypeUnless!(Pred, From, To, T[0]); 1547 else 1548 alias ReplaceTypeUnless = ReplaceTypeUnless!(Pred, From, To, U)*; 1549 } 1550 else static if (is(T[0] == delegate)) 1551 { 1552 alias ReplaceTypeUnless = replaceTypeInFunctionTypeUnless!(Pred, From, To, T[0]); 1553 } 1554 else static if (is(T[0] == function)) 1555 { 1556 static assert(0, "Function types not supported," ~ 1557 " use a function pointer type instead of " ~ T[0].stringof); 1558 } 1559 else static if (is(T[0] == U!V, alias U, V...)) 1560 { 1561 template replaceTemplateArgs(T...) 1562 { 1563 static if (is(typeof(T[0]))) // template argument is value or symbol 1564 enum replaceTemplateArgs = T[0]; 1565 else 1566 alias replaceTemplateArgs = ReplaceTypeUnless!(Pred, From, To, T[0]); 1567 } 1568 alias ReplaceTypeUnless = U!(staticMap!(replaceTemplateArgs, V)); 1569 } 1570 else static if (is(T[0] == struct)) 1571 // don't match with alias this struct below (Issue 15168) 1572 alias ReplaceTypeUnless = T[0]; 1573 else static if (is(T[0] == U[], U)) 1574 alias ReplaceTypeUnless = ReplaceTypeUnless!(Pred, From, To, U)[]; 1575 else static if (is(T[0] == U[n], U, size_t n)) 1576 alias ReplaceTypeUnless = ReplaceTypeUnless!(Pred, From, To, U)[n]; 1577 else static if (is(T[0] == U[V], U, V)) 1578 alias ReplaceTypeUnless = 1579 ReplaceTypeUnless!(Pred, From, To, U)[ReplaceTypeUnless!(Pred, From, To, V)]; 1580 else 1581 alias ReplaceTypeUnless = T[0]; 1582 } 1583 else static if (T.length > 1) 1584 { 1585 alias ReplaceTypeUnless = AliasSeq!(ReplaceTypeUnless!(Pred, From, To, T[0]), 1586 ReplaceTypeUnless!(Pred, From, To, T[1 .. $])); 1587 } 1588 else 1589 { 1590 alias ReplaceTypeUnless = AliasSeq!(); 1591 } 1592 } 1593 1594 1595 private template replaceTypeInFunctionTypeUnless(alias Pred, From, To, fun) 1596 { 1597 import std.traits; 1598 import std.meta: AliasSeq; 1599 1600 alias RX = ReplaceTypeUnless!(Pred, From, To, ReturnType!fun); 1601 alias PX = AliasSeq!(ReplaceTypeUnless!(Pred, From, To, Parameters!fun)); 1602 // Wrapping with AliasSeq is neccesary because ReplaceType doesn't return 1603 // tuple if Parameters!fun.length == 1 1604 1605 string gen() 1606 { 1607 enum linkage = functionLinkage!fun; 1608 alias attributes = functionAttributes!fun; 1609 enum variadicStyle = variadicFunctionStyle!fun; 1610 alias storageClasses = ParameterStorageClassTuple!fun; 1611 1612 string result; 1613 1614 result ~= "extern(" ~ linkage ~ ") "; 1615 static if (attributes & FunctionAttribute.ref_) 1616 { 1617 result ~= "ref "; 1618 } 1619 1620 result ~= "RX"; 1621 static if (is(fun == delegate)) 1622 result ~= " delegate"; 1623 else 1624 result ~= " function"; 1625 1626 result ~= "("; 1627 static foreach (i; 0 .. PX.length) 1628 { 1629 if (i) 1630 result ~= ", "; 1631 if (storageClasses[i] & ParameterStorageClass.scope_) 1632 result ~= "scope "; 1633 if (storageClasses[i] & ParameterStorageClass.out_) 1634 result ~= "out "; 1635 if (storageClasses[i] & ParameterStorageClass.ref_) 1636 result ~= "ref "; 1637 if (storageClasses[i] & ParameterStorageClass.lazy_) 1638 result ~= "lazy "; 1639 if (storageClasses[i] & ParameterStorageClass.return_) 1640 result ~= "return "; 1641 1642 result ~= "PX[" ~ i.stringof ~ "]"; 1643 } 1644 static if (variadicStyle == Variadic.typesafe) 1645 result ~= " ..."; 1646 else static if (variadicStyle != Variadic.no) 1647 result ~= ", ..."; 1648 result ~= ")"; 1649 1650 static if (attributes & FunctionAttribute.pure_) 1651 result ~= " pure"; 1652 static if (attributes & FunctionAttribute.nothrow_) 1653 result ~= " nothrow"; 1654 static if (attributes & FunctionAttribute.property) 1655 result ~= " @property"; 1656 static if (attributes & FunctionAttribute.trusted) 1657 result ~= " @trusted"; 1658 static if (attributes & FunctionAttribute.safe) 1659 result ~= " @safe"; 1660 static if (attributes & FunctionAttribute.nogc) 1661 result ~= " @nogc"; 1662 static if (attributes & FunctionAttribute.system) 1663 result ~= " @system"; 1664 static if (attributes & FunctionAttribute.const_) 1665 result ~= " const"; 1666 static if (attributes & FunctionAttribute.immutable_) 1667 result ~= " immutable"; 1668 static if (attributes & FunctionAttribute.inout_) 1669 result ~= " inout"; 1670 static if (attributes & FunctionAttribute.shared_) 1671 result ~= " shared"; 1672 static if (attributes & FunctionAttribute.return_) 1673 result ~= " return"; 1674 1675 return result; 1676 } 1677 1678 mixin("alias replaceTypeInFunctionTypeUnless = " ~ gen() ~ ";"); 1679 } 1680 1681 // Adapted from: 1682 // https://github.com/dlang/phobos/blob/d1c8fb0b69dc12669554d5cb96d3045753549619/std/typecons.d 1683 @safe unittest { 1684 import std.typecons: Tuple; 1685 enum False(T) = false; 1686 static assert( 1687 is(ReplaceTypeUnless!(False, int, string, int[]) == string[]) && 1688 is(ReplaceTypeUnless!(False, int, string, int[int]) == string[string]) && 1689 is(ReplaceTypeUnless!(False, int, string, const(int)[]) == const(string)[]) && 1690 is(ReplaceTypeUnless!(False, int, string, Tuple!(int[], float)) 1691 == Tuple!(string[], float)) 1692 ); 1693 } 1694 1695 // Adapted from: 1696 // https://github.com/dlang/phobos/blob/d1c8fb0b69dc12669554d5cb96d3045753549619/std/typecons.d 1697 version (D_BetterC) {} else 1698 @safe unittest 1699 { 1700 import std.typecons; 1701 1702 enum False(T) = false; 1703 template Test(Ts...) 1704 { 1705 static if (Ts.length) 1706 { 1707 static assert(is(ReplaceTypeUnless!(False, Ts[0], Ts[1], Ts[2]) == Ts[3]), 1708 "ReplaceTypeUnless!(False, "~Ts[0].stringof~", "~Ts[1].stringof~", " 1709 ~Ts[2].stringof~") == " 1710 ~ReplaceTypeUnless!(False, Ts[0], Ts[1], Ts[2]).stringof); 1711 alias Test = Test!(Ts[4 .. $]); 1712 } 1713 else alias Test = void; 1714 } 1715 1716 import core.stdc.stdio; 1717 alias RefFun1 = ref int function(float, long); 1718 alias RefFun2 = ref float function(float, long); 1719 extern(C) int printf(const char*, ...) nothrow @nogc @system; 1720 extern(C) float floatPrintf(const char*, ...) nothrow @nogc @system; 1721 int func(float); 1722 1723 int x; 1724 struct S1 { void foo() { x = 1; } } 1725 struct S2 { void bar() { x = 2; } } 1726 1727 alias Pass = Test!( 1728 int, float, typeof(&func), float delegate(float), 1729 int, float, typeof(&printf), typeof(&floatPrintf), 1730 int, float, int function(out long, ...), 1731 float function(out long, ...), 1732 int, float, int function(ref float, long), 1733 float function(ref float, long), 1734 int, float, int function(ref int, long), 1735 float function(ref float, long), 1736 int, float, int function(out int, long), 1737 float function(out float, long), 1738 int, float, int function(lazy int, long), 1739 float function(lazy float, long), 1740 int, float, int function(out long, ref const int), 1741 float function(out long, ref const float), 1742 int, int, int, int, 1743 int, float, int, float, 1744 int, float, const int, const float, 1745 int, float, immutable int, immutable float, 1746 int, float, shared int, shared float, 1747 int, float, int*, float*, 1748 int, float, const(int)*, const(float)*, 1749 int, float, const(int*), const(float*), 1750 const(int)*, float, const(int*), const(float), 1751 int*, float, const(int)*, const(int)*, 1752 int, float, int[], float[], 1753 int, float, int[42], float[42], 1754 int, float, const(int)[42], const(float)[42], 1755 int, float, const(int[42]), const(float[42]), 1756 int, float, int[int], float[float], 1757 int, float, int[double], float[double], 1758 int, float, double[int], double[float], 1759 int, float, int function(float, long), float function(float, long), 1760 int, float, int function(float), float function(float), 1761 int, float, int function(float, int), float function(float, float), 1762 int, float, int delegate(float, long), float delegate(float, long), 1763 int, float, int delegate(float), float delegate(float), 1764 int, float, int delegate(float, int), float delegate(float, float), 1765 int, float, Unique!int, Unique!float, 1766 int, float, Tuple!(float, int), Tuple!(float, float), 1767 int, float, RefFun1, RefFun2, 1768 S1, S2, 1769 S1[1][][S1]* function(), 1770 S2[1][][S2]* function(), 1771 int, string, 1772 int[3] function( int[] arr, int[2] ...) pure @trusted, 1773 string[3] function(string[] arr, string[2] ...) pure @trusted, 1774 ); 1775 1776 // Dlang Bugzilla 15168 1777 static struct T1 { string s; alias s this; } 1778 static struct T2 { char[10] s; alias s this; } 1779 static struct T3 { string[string] s; alias s this; } 1780 alias Pass2 = Test!( 1781 ubyte, ubyte, T1, T1, 1782 ubyte, ubyte, T2, T2, 1783 ubyte, ubyte, T3, T3, 1784 ); 1785 } 1786 1787 version (D_BetterC) {} else 1788 @safe unittest // Dlang Bugzilla 17116 1789 { 1790 enum False(T) = false; 1791 alias ConstDg = void delegate(float) const; 1792 alias B = void delegate(int) const; 1793 alias A = ReplaceTypeUnless!(False, float, int, ConstDg); 1794 static assert(is(B == A)); 1795 } 1796 1797 // Github issue #27 1798 @safe unittest 1799 { 1800 enum False(T) = false; 1801 struct A(T) {} 1802 struct B { A!int a; alias a this; } 1803 static assert(is(ReplaceTypeUnless!(False, void, void, B) == B)); 1804 } 1805 }