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