1 /++ 2 [SumType] is a generic discriminated union implementation that uses 3 design-by-introspection to generate safe and efficient code. Its features 4 include: 5 6 $(LIST 7 * [match|Pattern matching.] 8 * Support for self-referential types. 9 * Full attribute correctness (`pure`, `@safe`, `@nogc`, and `nothrow` are 10 inferred whenever possible). 11 * A type-safe and memory-safe API compatible with DIP 1000 (`scope`). 12 * No dependency on runtime type information (`TypeInfo`). 13 * Compatibility with BetterC. 14 ) 15 16 License: Boost License 1.0 17 Authors: Paul Backus 18 +/ 19 module sumtype; 20 21 /// $(H3 Basic usage) 22 version (D_BetterC) {} else 23 @safe unittest { 24 import std.math: isClose; 25 26 struct Fahrenheit { double degrees; } 27 struct Celsius { double degrees; } 28 struct Kelvin { double degrees; } 29 30 alias Temperature = SumType!(Fahrenheit, Celsius, Kelvin); 31 32 // Construct from any of the member types. 33 Temperature t1 = Fahrenheit(98.6); 34 Temperature t2 = Celsius(100); 35 Temperature t3 = Kelvin(273); 36 37 // Use pattern matching to access the value. 38 Fahrenheit toFahrenheit(Temperature t) 39 { 40 return Fahrenheit( 41 t.match!( 42 (Fahrenheit f) => f.degrees, 43 (Celsius c) => c.degrees * 9.0/5 + 32, 44 (Kelvin k) => k.degrees * 9.0/5 - 459.4 45 ) 46 ); 47 } 48 49 assert(toFahrenheit(t1).degrees.isClose(98.6)); 50 assert(toFahrenheit(t2).degrees.isClose(212)); 51 assert(toFahrenheit(t3).degrees.isClose(32)); 52 53 // Use ref to modify the value in place. 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.isClose(32)); 65 66 // Use a catch-all handler to give a default result. 67 bool isFahrenheit(Temperature t) 68 { 69 return t.match!( 70 (Fahrenheit f) => true, 71 _ => false 72 ); 73 } 74 75 assert(isFahrenheit(t1)); 76 assert(!isFahrenheit(t2)); 77 assert(!isFahrenheit(t3)); 78 } 79 80 /** $(H3 Introspection-based matching) 81 * 82 * In the `length` and `horiz` functions below, the handlers for `match` do not 83 * specify the types of their arguments. Instead, matching is done based on how 84 * the argument is used in the body of the handler: any type with `x` and `y` 85 * properties will be matched by the `rect` handlers, and any type with `r` and 86 * `theta` properties will be matched by the `polar` handlers. 87 */ 88 version (D_BetterC) {} else 89 @safe unittest { 90 import std.math: isClose, cos, PI, sqrt; 91 92 struct Rectangular { double x, y; } 93 struct Polar { double r, theta; } 94 alias Vector = SumType!(Rectangular, Polar); 95 96 double length(Vector v) 97 { 98 return v.match!( 99 rect => sqrt(rect.x^^2 + rect.y^^2), 100 polar => polar.r 101 ); 102 } 103 104 double horiz(Vector v) 105 { 106 return v.match!( 107 rect => rect.x, 108 polar => polar.r * cos(polar.theta) 109 ); 110 } 111 112 Vector u = Rectangular(1, 1); 113 Vector v = Polar(1, PI/4); 114 115 assert(length(u).isClose(sqrt(2.0))); 116 assert(length(v).isClose(1)); 117 assert(horiz(u).isClose(1)); 118 assert(horiz(v).isClose(sqrt(0.5))); 119 } 120 121 /** $(H3 Arithmetic expression evaluator) 122 * 123 * This example makes use of the special placeholder type `This` to define a 124 * [https://en.wikipedia.org/wiki/Recursive_data_type|recursive data type]: an 125 * [https://en.wikipedia.org/wiki/Abstract_syntax_tree|abstract syntax tree] for 126 * representing simple arithmetic expressions. 127 */ 128 version (D_BetterC) {} else 129 @system unittest { 130 import std.functional: partial; 131 import std.traits: EnumMembers; 132 import std.typecons: Tuple; 133 134 enum Op : string 135 { 136 Plus = "+", 137 Minus = "-", 138 Times = "*", 139 Div = "/" 140 } 141 142 // An expression is either 143 // - a number, 144 // - a variable, or 145 // - a binary operation combining two sub-expressions. 146 alias Expr = SumType!( 147 double, 148 string, 149 Tuple!(Op, "op", This*, "lhs", This*, "rhs") 150 ); 151 152 // Shorthand for Tuple!(Op, "op", Expr*, "lhs", Expr*, "rhs"), 153 // the Tuple type above with Expr substituted for This. 154 alias BinOp = Expr.Types[2]; 155 156 // Factory function for number expressions 157 Expr* num(double value) 158 { 159 return new Expr(value); 160 } 161 162 // Factory function for variable expressions 163 Expr* var(string name) 164 { 165 return new Expr(name); 166 } 167 168 // Factory function for binary operation expressions 169 Expr* binOp(Op op, Expr* lhs, Expr* rhs) 170 { 171 return new Expr(BinOp(op, lhs, rhs)); 172 } 173 174 // Convenience wrappers for creating BinOp expressions 175 alias sum = partial!(binOp, Op.Plus); 176 alias diff = partial!(binOp, Op.Minus); 177 alias prod = partial!(binOp, Op.Times); 178 alias quot = partial!(binOp, Op.Div); 179 180 // Evaluate expr, looking up variables in env 181 double eval(Expr expr, double[string] env) 182 { 183 return expr.match!( 184 (double num) => num, 185 (string var) => env[var], 186 (BinOp bop) { 187 double lhs = eval(*bop.lhs, env); 188 double rhs = eval(*bop.rhs, env); 189 final switch(bop.op) { 190 static foreach(op; EnumMembers!Op) { 191 case op: 192 return mixin("lhs" ~ op ~ "rhs"); 193 } 194 } 195 } 196 ); 197 } 198 199 // Return a "pretty-printed" representation of expr 200 string pprint(Expr expr) 201 { 202 import std.format; 203 204 return expr.match!( 205 (double num) => "%g".format(num), 206 (string var) => var, 207 (BinOp bop) => "(%s %s %s)".format( 208 pprint(*bop.lhs), 209 cast(string) bop.op, 210 pprint(*bop.rhs) 211 ) 212 ); 213 } 214 215 Expr* myExpr = sum(var("a"), prod(num(2), var("b"))); 216 double[string] myEnv = ["a":3, "b":4, "c":7]; 217 218 assert(eval(*myExpr, myEnv) == 11); 219 assert(pprint(*myExpr) == "(a + (2 * b))"); 220 } 221 222 import std.format: FormatSpec, singleSpec; 223 import std.meta: AliasSeq, Filter, IndexOf = staticIndexOf, Map = staticMap; 224 import std.meta: NoDuplicates; 225 import std.meta: anySatisfy, allSatisfy; 226 import std.traits: hasElaborateCopyConstructor, hasElaborateDestructor; 227 import std.traits: isAssignable, isCopyable, isStaticArray; 228 import std.traits: ConstOf, ImmutableOf, InoutOf, TemplateArgsOf; 229 import std.traits: CommonType; 230 import std.typecons: ReplaceTypeUnless; 231 import std.typecons: Flag; 232 233 /// Placeholder used to refer to the enclosing [SumType]. 234 struct This {} 235 236 // Converts an unsigned integer to a compile-time string constant. 237 private enum toCtString(ulong n) = n.stringof[0 .. $ - "LU".length]; 238 239 // Check that .stringof does what we expect, since it's not guaranteed by the 240 // lanugage spec. 241 @safe unittest { 242 assert(toCtString!0 == "0"); 243 assert(toCtString!123456 == "123456"); 244 } 245 246 // True if a variable of type T can appear on the lhs of an assignment 247 private enum isAssignableTo(T) = 248 isAssignable!T || (!isCopyable!T && isRvalueAssignable!T); 249 250 // toHash is required by the language spec to be nothrow and @safe 251 private enum isHashable(T) = __traits(compiles, 252 () nothrow @safe { hashOf(T.init); } 253 ); 254 255 private enum hasPostblit(T) = __traits(hasPostblit, T); 256 257 /** 258 * A [tagged union](https://en.wikipedia.org/wiki/Tagged_union) that can hold a 259 * single value from any of a specified set of types. 260 * 261 * The value in a `SumType` can be operated on using [match|pattern matching]. 262 * 263 * To avoid ambiguity, duplicate types are not allowed (but see the 264 * [sumtype#basic-usage|"basic usage" example] for a workaround). 265 * 266 * The special type `This` can be used as a placeholder to create 267 * self-referential types, just like with `Algebraic`. See the 268 * [sumtype#arithmetic-expression-evaluator|"Arithmetic expression evaluator" example] for 269 * usage. 270 * 271 * A `SumType` is initialized by default to hold the `.init` value of its 272 * first member type, just like a regular union. The version identifier 273 * `SumTypeNoDefaultCtor` can be used to disable this behavior. 274 * 275 * See_Also: `std.variant.Algebraic` 276 */ 277 struct SumType(Types...) 278 if (is(NoDuplicates!Types == Types) && Types.length > 0) 279 { 280 /// The types a `SumType` can hold. 281 alias Types = AliasSeq!( 282 ReplaceTypeUnless!(isSumTypeInstance, This, typeof(this), TemplateArgsOf!SumType) 283 ); 284 285 private: 286 287 enum bool canHoldTag(T) = Types.length <= T.max; 288 alias unsignedInts = AliasSeq!(ubyte, ushort, uint, ulong); 289 290 alias Tag = Filter!(canHoldTag, unsignedInts)[0]; 291 292 union Storage 293 { 294 // Workaround for dlang issue 20068 295 template memberName(T) 296 if (IndexOf!(T, Types) >= 0) 297 { 298 enum tid = IndexOf!(T, Types); 299 mixin("enum memberName = `values_", toCtString!tid, "`;"); 300 } 301 302 static foreach (T; Types) { 303 mixin("T ", memberName!T, ";"); 304 } 305 } 306 307 Storage storage; 308 Tag tag; 309 310 /** 311 * Accesses the value stored in a SumType. 312 * 313 * This method is memory-safe, provided that: 314 * 315 * 1. A SumType's tag is always accurate. 316 * 2. A SumType cannot be assigned to in @safe code if that assignment 317 * could cause unsafe aliasing. 318 * 319 * All code that accesses a SumType's tag or storage directly, including 320 * @safe code in this module, must be manually checked to ensure that it 321 * does not violate either of the above requirements. 322 */ 323 @trusted 324 ref inout(T) get(T)() inout 325 if (IndexOf!(T, Types) >= 0) 326 { 327 enum tid = IndexOf!(T, Types); 328 assert(tag == tid, 329 "This `" ~ SumType.stringof ~ 330 "` does not contain a(n) `" ~ T.stringof ~ "`" 331 ); 332 return __traits(getMember, storage, Storage.memberName!T); 333 } 334 335 public: 336 337 static foreach (tid, T; Types) { 338 /// Constructs a `SumType` holding a specific value. 339 this(T value) 340 { 341 import core.lifetime: forward; 342 343 // Workaround for dlang issue 21229 344 storage = () { 345 static if (isCopyable!T) { 346 mixin("Storage newStorage = { ", 347 // Workaround for dlang issue 21542 348 Storage.memberName!T, ": (__ctfe ? value : forward!value)", 349 " };"); 350 } else { 351 mixin("Storage newStorage = { ", 352 Storage.memberName!T, " : forward!value", 353 " };"); 354 } 355 356 return newStorage; 357 }(); 358 359 tag = tid; 360 } 361 362 static if (isCopyable!T) { 363 // Avoid defining the same constructor multiple times 364 static if (IndexOf!(const(T), Map!(ConstOf, Types)) == tid) { 365 /// ditto 366 this(const(T) value) const 367 { 368 storage = () { 369 mixin("const(Storage) newStorage = { ", 370 Storage.memberName!T, ": value", 371 " };"); 372 373 return newStorage; 374 }(); 375 376 tag = tid; 377 } 378 } 379 380 static if (IndexOf!(immutable(T), Map!(ImmutableOf, Types)) == tid) { 381 /// ditto 382 this(immutable(T) value) immutable 383 { 384 storage = () { 385 mixin("immutable(Storage) newStorage = { ", 386 Storage.memberName!T, ": value", 387 " };"); 388 389 return newStorage; 390 }(); 391 392 tag = tid; 393 } 394 } 395 } else { 396 @disable this(const(T) value) const; 397 @disable this(immutable(T) value) immutable; 398 } 399 } 400 401 static if (anySatisfy!(hasElaborateCopyConstructor, Types)) { 402 static if ( 403 allSatisfy!(isCopyable, Map!(InoutOf, Types)) 404 && !anySatisfy!(hasPostblit, Map!(InoutOf, Types)) 405 ) { 406 /// Constructs a `SumType` that's a copy of another `SumType`. 407 this(ref inout(SumType) other) inout 408 { 409 storage = other.match!((ref value) { 410 alias OtherTypes = Map!(InoutOf, Types); 411 enum tid = IndexOf!(typeof(value), OtherTypes); 412 alias T = Types[tid]; 413 414 mixin("inout(Storage) newStorage = { ", 415 Storage.memberName!T, ": value", 416 " };"); 417 418 return newStorage; 419 }); 420 421 tag = other.tag; 422 } 423 } else { 424 static if (allSatisfy!(isCopyable, Types)) { 425 /// ditto 426 this(ref SumType other) 427 { 428 storage = other.match!((ref value) { 429 alias T = typeof(value); 430 431 mixin("Storage newStorage = { ", 432 Storage.memberName!T, ": value", 433 " };"); 434 435 return newStorage; 436 }); 437 438 tag = other.tag; 439 } 440 } else { 441 @disable this(ref SumType other); 442 } 443 444 static if (allSatisfy!(isCopyable, Map!(ConstOf, Types))) { 445 /// ditto 446 this(ref const(SumType) other) const 447 { 448 storage = other.match!((ref value) { 449 alias OtherTypes = Map!(ConstOf, Types); 450 enum tid = IndexOf!(typeof(value), OtherTypes); 451 alias T = Types[tid]; 452 453 mixin("const(Storage) newStorage = { ", 454 Storage.memberName!T, ": value", 455 " };"); 456 457 return newStorage; 458 }); 459 460 tag = other.tag; 461 } 462 } else { 463 @disable this(ref const(SumType) other) const; 464 } 465 466 static if (allSatisfy!(isCopyable, Map!(ImmutableOf, Types))) { 467 /// ditto 468 this(ref immutable(SumType) other) immutable 469 { 470 storage = other.match!((ref value) { 471 alias OtherTypes = Map!(ImmutableOf, Types); 472 enum tid = IndexOf!(typeof(value), OtherTypes); 473 alias T = Types[tid]; 474 475 mixin("immutable(Storage) newStorage = { ", 476 Storage.memberName!T, ": value", 477 " };"); 478 479 return newStorage; 480 }); 481 482 tag = other.tag; 483 } 484 } else { 485 @disable this(ref immutable(SumType) other) immutable; 486 } 487 } 488 } 489 490 version (SumTypeNoDefaultCtor) { 491 @disable this(); 492 } 493 494 static foreach (tid, T; Types) { 495 static if (isAssignableTo!T) { 496 /** 497 * Assigns a value to a `SumType`. 498 * 499 * Assigning to a `SumType` is `@system` if any of the 500 * `SumType`'s members contain pointers or references, since 501 * those members may be reachable through external references, 502 * and overwriting them could therefore lead to memory 503 * corruption. 504 * 505 * An individual assignment can be `@trusted` if the caller can 506 * guarantee that there are no outstanding references to $(I any) 507 * of the `SumType`'s members when the assignment occurs. 508 */ 509 ref SumType opAssign(T rhs) 510 { 511 import core.lifetime: forward; 512 import std.traits: hasIndirections, hasNested; 513 import std.meta: Or = templateOr; 514 515 enum mayContainPointers = 516 anySatisfy!(Or!(hasIndirections, hasNested), Types); 517 518 static if (mayContainPointers) { 519 cast(void) () @system {}(); 520 } 521 522 this.match!destroyIfOwner; 523 524 mixin("Storage newStorage = { ", 525 Storage.memberName!T, ": forward!rhs", 526 " };"); 527 528 storage = newStorage; 529 tag = tid; 530 531 return this; 532 } 533 } 534 } 535 536 static if (allSatisfy!(isAssignableTo, Types)) { 537 static if (allSatisfy!(isCopyable, Types)) { 538 /** 539 * Copies the value from another `SumType` into this one. 540 * 541 * See the value-assignment overload for details on `@safe`ty. 542 * 543 * Copy assignment is `@disable`d if any of `Types` is non-copyable. 544 */ 545 ref SumType opAssign(ref SumType rhs) 546 { 547 rhs.match!((ref value) { this = value; }); 548 return this; 549 } 550 } else { 551 @disable ref SumType opAssign(ref SumType rhs); 552 } 553 554 /** 555 * Moves the value from another `SumType` into this one. 556 * 557 * See the value-assignment overload for details on `@safe`ty. 558 */ 559 ref SumType opAssign(SumType rhs) 560 { 561 import core.lifetime: move; 562 563 rhs.match!((ref value) { this = move(value); }); 564 return this; 565 } 566 } 567 568 /** 569 * Compares two `SumType`s for equality. 570 * 571 * Two `SumType`s are equal if they are the same kind of `SumType`, they 572 * contain values of the same type, and those values are equal. 573 */ 574 bool opEquals(this This, Rhs)(auto ref Rhs rhs) 575 if (!is(CommonType!(This, Rhs) == void)) 576 { 577 static if (is(This == Rhs)) { 578 return AliasSeq!(this, rhs).match!((ref value, ref rhsValue) { 579 static if (is(typeof(value) == typeof(rhsValue))) { 580 return value == rhsValue; 581 } else { 582 return false; 583 } 584 }); 585 } else { 586 alias CommonSumType = CommonType!(This, Rhs); 587 return cast(CommonSumType) this == cast(CommonSumType) rhs; 588 } 589 } 590 591 // Workaround for dlang issue 19407 592 static if (__traits(compiles, anySatisfy!(hasElaborateDestructor, Types))) { 593 // If possible, include the destructor only when it's needed 594 private enum includeDtor = anySatisfy!(hasElaborateDestructor, Types); 595 } else { 596 // If we can't tell, always include it, even when it does nothing 597 private enum includeDtor = true; 598 } 599 600 static if (includeDtor) { 601 /// Calls the destructor of the `SumType`'s current value. 602 ~this() 603 { 604 this.match!destroyIfOwner; 605 } 606 } 607 608 invariant { 609 this.match!((ref value) { 610 static if (is(typeof(value) == class)) { 611 if (value !is null) { 612 assert(value); 613 } 614 } else static if (is(typeof(value) == struct)) { 615 assert(&value); 616 } 617 }); 618 } 619 620 version (D_BetterC) {} else 621 /** 622 * Returns a string representation of the `SumType`'s current value. 623 * 624 * Not available when compiled with `-betterC`. 625 */ 626 string toString(this This)() 627 { 628 import std.conv: to; 629 630 return this.match!(to!string); 631 } 632 633 version (D_BetterC) {} else 634 /** 635 * Handles formatted writing of the `SumType`'s current value. 636 * 637 * Not available when compiled with `-betterC`. 638 * 639 * Params: 640 * sink = Output range to write to. 641 * fmt = Format specifier to use. 642 * 643 * See_Also: `std.format.formatValue` 644 */ 645 void toString(this This, Sink, Char)(ref Sink sink, const ref FormatSpec!Char fmt) 646 { 647 import std.format: formatValue; 648 649 this.match!((ref value) { 650 formatValue(sink, value, fmt); 651 }); 652 } 653 654 static if (allSatisfy!(isHashable, Map!(ConstOf, Types))) { 655 // Workaround for dlang issue 20095 656 version (D_BetterC) {} else 657 /** 658 * Returns the hash of the `SumType`'s current value. 659 * 660 * Not available when compiled with `-betterC`. 661 */ 662 size_t toHash() const 663 { 664 return this.match!hashOf; 665 } 666 } 667 668 /** 669 * Returns the index of the type of the `SumType`'s current value in the 670 * `SumType`'s [Types]. 671 * 672 * If the `SumType` is qualified, then its qualifiers are applied to 673 * [Types] before determining the index. 674 */ 675 size_t typeIndex() const 676 { 677 return tag; 678 } 679 } 680 681 // Construction 682 @safe unittest { 683 alias MySum = SumType!(int, float); 684 685 assert(__traits(compiles, MySum(42))); 686 assert(__traits(compiles, MySum(3.14))); 687 } 688 689 // Assignment 690 @safe unittest { 691 alias MySum = SumType!(int, float); 692 693 MySum x = MySum(42); 694 695 assert(__traits(compiles, x = 3.14)); 696 } 697 698 // Self assignment 699 @safe unittest { 700 alias MySum = SumType!(int, float); 701 702 MySum x = MySum(42); 703 MySum y = MySum(3.14); 704 705 assert(__traits(compiles, y = x)); 706 } 707 708 // Equality 709 @safe unittest { 710 alias MySum = SumType!(int, float); 711 712 assert(MySum(123) == MySum(123)); 713 assert(MySum(123) != MySum(456)); 714 assert(MySum(123) != MySum(123.0)); 715 assert(MySum(123) != MySum(456.0)); 716 717 } 718 719 // Equality of differently-qualified SumTypes 720 // Disabled in BetterC due to use of dynamic arrays 721 version (D_BetterC) {} else 722 @safe unittest { 723 alias SumA = SumType!(int, float); 724 alias SumB = SumType!(const(int[]), int[]); 725 alias SumC = SumType!(int[], const(int[])); 726 727 int[] ma = [1, 2, 3]; 728 const(int[]) ca = [1, 2, 3]; 729 730 assert(const(SumA)(123) == SumA(123)); 731 assert(const(SumB)(ma[]) == SumB(ca[])); 732 assert(const(SumC)(ma[]) == SumC(ca[])); 733 } 734 735 // Imported types 736 @safe unittest { 737 import std.typecons: Tuple; 738 739 assert(__traits(compiles, { 740 alias MySum = SumType!(Tuple!(int, int)); 741 })); 742 } 743 744 // const and immutable types 745 @safe unittest { 746 assert(__traits(compiles, { 747 alias MySum = SumType!(const(int[]), immutable(float[])); 748 })); 749 } 750 751 // Recursive types 752 @safe unittest { 753 alias MySum = SumType!(This*); 754 assert(is(MySum.Types[0] == MySum*)); 755 } 756 757 // Allowed types 758 @safe unittest { 759 import std.meta: AliasSeq; 760 761 alias MySum = SumType!(int, float, This*); 762 763 assert(is(MySum.Types == AliasSeq!(int, float, MySum*))); 764 } 765 766 // Types with destructors and postblits 767 @system unittest { 768 int copies; 769 770 static struct Test 771 { 772 bool initialized = false; 773 int* copiesPtr; 774 775 this(this) { (*copiesPtr)++; } 776 ~this() { if (initialized) (*copiesPtr)--; } 777 } 778 779 alias MySum = SumType!(int, Test); 780 781 Test t = Test(true, &copies); 782 783 { 784 MySum x = t; 785 assert(copies == 1); 786 } 787 assert(copies == 0); 788 789 { 790 MySum x = 456; 791 assert(copies == 0); 792 } 793 assert(copies == 0); 794 795 { 796 MySum x = t; 797 assert(copies == 1); 798 x = 456; 799 assert(copies == 0); 800 } 801 802 { 803 MySum x = 456; 804 assert(copies == 0); 805 x = t; 806 assert(copies == 1); 807 } 808 809 { 810 MySum x = t; 811 MySum y = x; 812 assert(copies == 2); 813 } 814 815 { 816 MySum x = t; 817 MySum y; 818 y = x; 819 assert(copies == 2); 820 } 821 } 822 823 // Doesn't destroy reference types 824 // Disabled in BetterC due to use of classes 825 version (D_BetterC) {} else 826 @system unittest { 827 bool destroyed; 828 829 class C 830 { 831 ~this() 832 { 833 destroyed = true; 834 } 835 } 836 837 struct S 838 { 839 ~this() {} 840 } 841 842 alias MySum = SumType!(S, C); 843 844 C c = new C(); 845 { 846 MySum x = c; 847 destroyed = false; 848 } 849 assert(!destroyed); 850 851 { 852 MySum x = c; 853 destroyed = false; 854 x = S(); 855 assert(!destroyed); 856 } 857 } 858 859 // Types with @disable this() 860 @safe unittest { 861 static struct NoInit 862 { 863 @disable this(); 864 } 865 866 alias MySum = SumType!(NoInit, int); 867 868 assert(!__traits(compiles, MySum())); 869 assert(__traits(compiles, MySum(42))); 870 auto x = MySum(42); 871 } 872 873 // const SumTypes 874 @safe unittest { 875 assert(__traits(compiles, 876 const(SumType!(int[]))([1, 2, 3]) 877 )); 878 } 879 880 // Equality of const SumTypes 881 @safe unittest { 882 alias MySum = SumType!int; 883 884 assert(__traits(compiles, 885 const(MySum)(123) == const(MySum)(456) 886 )); 887 } 888 889 // Compares reference types using value equality 890 @safe unittest { 891 import std.array: staticArray; 892 893 static struct Field {} 894 static struct Struct { Field[] fields; } 895 alias MySum = SumType!Struct; 896 897 static arr1 = staticArray([Field()]); 898 static arr2 = staticArray([Field()]); 899 900 auto a = MySum(Struct(arr1[])); 901 auto b = MySum(Struct(arr2[])); 902 903 assert(a == b); 904 } 905 906 // toString 907 // Disabled in BetterC due to use of std.conv.text 908 version (D_BetterC) {} else 909 @safe unittest { 910 import std.conv: text; 911 912 static struct Int { int i; } 913 static struct Double { double d; } 914 alias Sum = SumType!(Int, Double); 915 916 assert(Sum(Int(42)).text == Int(42).text, Sum(Int(42)).text); 917 assert(Sum(Double(33.3)).text == Double(33.3).text, Sum(Double(33.3)).text); 918 assert((const(Sum)(Int(42))).text == (const(Int)(42)).text, (const(Sum)(Int(42))).text); 919 } 920 921 // string formatting 922 // Disabled in BetterC due to use of std.format.format 923 version (D_BetterC) {} else 924 @safe unittest { 925 import std.format: format; 926 927 SumType!int x = 123; 928 929 assert(format!"%s"(x) == format!"%s"(123)); 930 assert(format!"%x"(x) == format!"%x"(123)); 931 } 932 933 // string formatting of qualified SumTypes 934 // Disabled in BetterC due to use of std.format.format and dynamic arrays 935 version (D_BetterC) {} else 936 @safe unittest { 937 import std.format: format; 938 939 int[] a = [1, 2, 3]; 940 const(SumType!(int[])) x = a; 941 942 assert(format!"%(%d, %)"(x) == format!"%(%s, %)"(a)); 943 } 944 945 // Github issue #16 946 // Disabled in BetterC due to use of dynamic arrays 947 version (D_BetterC) {} else 948 @safe unittest { 949 alias Node = SumType!(This[], string); 950 951 // override inference of @system attribute for cyclic functions 952 assert((() @trusted => 953 Node([Node([Node("x")])]) 954 == 955 Node([Node([Node("x")])]) 956 )()); 957 } 958 959 // Github issue #16 with const 960 // Disabled in BetterC due to use of dynamic arrays 961 version (D_BetterC) {} else 962 @safe unittest { 963 alias Node = SumType!(const(This)[], string); 964 965 // override inference of @system attribute for cyclic functions 966 assert((() @trusted => 967 Node([Node([Node("x")])]) 968 == 969 Node([Node([Node("x")])]) 970 )()); 971 } 972 973 // Stale pointers 974 // Disabled in BetterC due to use of dynamic arrays 975 version (D_BetterC) {} else 976 @system unittest { 977 alias MySum = SumType!(ubyte, void*[2]); 978 979 MySum x = [null, cast(void*) 0x12345678]; 980 void** p = &x.get!(void*[2])[1]; 981 x = ubyte(123); 982 983 assert(*p != cast(void*) 0x12345678); 984 } 985 986 // Exception-safe assignment 987 // Disabled in BetterC due to use of exceptions 988 version (D_BetterC) {} else 989 @safe unittest { 990 static struct A 991 { 992 int value = 123; 993 } 994 995 static struct B 996 { 997 int value = 456; 998 this(this) { throw new Exception("oops"); } 999 } 1000 1001 alias MySum = SumType!(A, B); 1002 1003 MySum x; 1004 try { 1005 x = B(); 1006 } catch (Exception e) {} 1007 1008 assert( 1009 (x.tag == 0 && x.get!A.value == 123) || 1010 (x.tag == 1 && x.get!B.value == 456) 1011 ); 1012 } 1013 1014 // Types with @disable this(this) 1015 @safe unittest { 1016 import core.lifetime: move; 1017 1018 static struct NoCopy 1019 { 1020 @disable this(this); 1021 } 1022 1023 alias MySum = SumType!NoCopy; 1024 1025 NoCopy lval = NoCopy(); 1026 1027 MySum x = NoCopy(); 1028 MySum y = NoCopy(); 1029 1030 assert(__traits(compiles, SumType!NoCopy(NoCopy()))); 1031 assert(!__traits(compiles, SumType!NoCopy(lval))); 1032 1033 assert(__traits(compiles, y = NoCopy())); 1034 assert(__traits(compiles, y = move(x))); 1035 assert(!__traits(compiles, y = lval)); 1036 assert(!__traits(compiles, y = x)); 1037 1038 assert(__traits(compiles, x == y)); 1039 } 1040 1041 // Github issue #22 1042 // Disabled in BetterC due to use of std.typecons.Nullable 1043 version (D_BetterC) {} else 1044 @safe unittest { 1045 import std.typecons; 1046 assert(__traits(compiles, { 1047 static struct A { 1048 SumType!(Nullable!int) a = Nullable!int.init; 1049 } 1050 })); 1051 } 1052 1053 // Static arrays of structs with postblits 1054 // Disabled in BetterC due to use of dynamic arrays 1055 version (D_BetterC) {} else 1056 @safe unittest { 1057 static struct S 1058 { 1059 int n; 1060 this(this) { n++; } 1061 } 1062 1063 assert(__traits(compiles, SumType!(S[1])())); 1064 1065 SumType!(S[1]) x = [S(0)]; 1066 SumType!(S[1]) y = x; 1067 1068 auto xval = x.get!(S[1])[0].n; 1069 auto yval = y.get!(S[1])[0].n; 1070 1071 assert(xval != yval); 1072 } 1073 1074 // Replacement does not happen inside SumType 1075 // Disabled in BetterC due to use of associative arrays 1076 version (D_BetterC) {} else 1077 @safe unittest { 1078 import std.typecons : Tuple, ReplaceTypeUnless; 1079 alias A = Tuple!(This*,SumType!(This*))[SumType!(This*,string)[This]]; 1080 alias TR = ReplaceTypeUnless!(isSumTypeInstance, This, int, A); 1081 static assert(is(TR == Tuple!(int*,SumType!(This*))[SumType!(This*, string)[int]])); 1082 } 1083 1084 // Supports nested self-referential SumTypes 1085 @safe unittest { 1086 import std.typecons : Tuple, Flag; 1087 alias Nat = SumType!(Flag!"0", Tuple!(This*)); 1088 static assert(__traits(compiles, SumType!(Nat))); 1089 static assert(__traits(compiles, SumType!(Nat*, Tuple!(This*, This*)))); 1090 } 1091 1092 // Self-referential SumTypes inside Algebraic 1093 // Disabled in BetterC due to use of std.variant.Algebraic 1094 version (D_BetterC) {} else 1095 @safe unittest { 1096 import std.variant: Algebraic; 1097 1098 alias T = Algebraic!(SumType!(This*)); 1099 1100 assert(is(T.AllowedTypes[0].Types[0] == T.AllowedTypes[0]*)); 1101 } 1102 1103 // Doesn't call @system postblits in @safe code 1104 @safe unittest { 1105 static struct SystemCopy { @system this(this) {} } 1106 SystemCopy original; 1107 1108 assert(!__traits(compiles, () @safe { 1109 SumType!SystemCopy copy = original; 1110 })); 1111 1112 assert(!__traits(compiles, () @safe { 1113 SumType!SystemCopy copy; copy = original; 1114 })); 1115 } 1116 1117 // Doesn't overwrite pointers in @safe code 1118 @safe unittest { 1119 alias MySum = SumType!(int*, int); 1120 1121 MySum x; 1122 1123 assert(!__traits(compiles, () @safe { 1124 x = 123; 1125 })); 1126 1127 assert(!__traits(compiles, () @safe { 1128 x = MySum(123); 1129 })); 1130 } 1131 1132 // Types with invariants 1133 // Disabled in BetterC due to use of exceptions 1134 version (D_BetterC) {} else 1135 @system unittest { 1136 import std.exception: assertThrown; 1137 import core.exception: AssertError; 1138 1139 struct S 1140 { 1141 int i; 1142 invariant { assert(i >= 0); } 1143 } 1144 1145 class C 1146 { 1147 int i; 1148 invariant { assert(i >= 0); } 1149 } 1150 1151 SumType!S x; 1152 x.match!((ref v) { v.i = -1; }); 1153 assertThrown!AssertError(assert(&x)); 1154 1155 SumType!C y = new C(); 1156 y.match!((ref v) { v.i = -1; }); 1157 assertThrown!AssertError(assert(&y)); 1158 } 1159 1160 // Calls value postblit on self-assignment 1161 @safe unittest { 1162 static struct S 1163 { 1164 int n; 1165 this(this) { n++; } 1166 } 1167 1168 SumType!S x = S(); 1169 SumType!S y; 1170 y = x; 1171 1172 auto xval = x.get!S.n; 1173 auto yval = y.get!S.n; 1174 1175 assert(xval != yval); 1176 } 1177 1178 // Github issue #29 1179 @safe unittest { 1180 assert(__traits(compiles, () @safe { 1181 alias A = SumType!string; 1182 1183 @safe A createA(string arg) { 1184 return A(arg); 1185 } 1186 1187 @safe void test() { 1188 A a = createA(""); 1189 } 1190 })); 1191 } 1192 1193 // SumTypes as associative array keys 1194 // Disabled in BetterC due to use of associative arrays 1195 version (D_BetterC) {} else 1196 @safe unittest { 1197 assert(__traits(compiles, { 1198 int[SumType!(int, string)] aa; 1199 })); 1200 } 1201 1202 // toString with non-copyable types 1203 // Disabled in BetterC due to use of std.conv.to (in toString) 1204 version (D_BetterC) {} else 1205 @safe unittest { 1206 struct NoCopy 1207 { 1208 @disable this(this); 1209 } 1210 1211 SumType!NoCopy x; 1212 1213 assert(__traits(compiles, x.toString())); 1214 } 1215 1216 // Can use the result of assignment 1217 @safe unittest { 1218 alias MySum = SumType!(int, float); 1219 1220 MySum a = MySum(123); 1221 MySum b = MySum(3.14); 1222 1223 assert((a = b) == b); 1224 assert((a = MySum(123)) == MySum(123)); 1225 assert((a = 3.14) == MySum(3.14)); 1226 assert(((a = b) = MySum(123)) == MySum(123)); 1227 } 1228 1229 // Types with copy constructors 1230 @safe unittest { 1231 static struct S 1232 { 1233 int n; 1234 1235 this(ref return scope inout S other) inout 1236 { 1237 n = other.n + 1; 1238 } 1239 } 1240 1241 SumType!S x = S(); 1242 SumType!S y = x; 1243 1244 auto xval = x.get!S.n; 1245 auto yval = y.get!S.n; 1246 1247 assert(xval != yval); 1248 } 1249 1250 // Copyable by generated copy constructors 1251 @safe unittest { 1252 static struct Inner 1253 { 1254 ref this(ref inout Inner other) {} 1255 } 1256 1257 static struct Outer 1258 { 1259 SumType!Inner inner; 1260 } 1261 1262 Outer x; 1263 Outer y = x; 1264 } 1265 1266 // Types with disabled opEquals 1267 @safe unittest { 1268 static struct S 1269 { 1270 @disable bool opEquals(const S rhs) const; 1271 } 1272 1273 assert(__traits(compiles, SumType!S(S()))); 1274 } 1275 1276 // Types with non-const opEquals 1277 @safe unittest { 1278 static struct S 1279 { 1280 int i; 1281 bool opEquals(S rhs) { return i == rhs.i; } 1282 } 1283 1284 assert(__traits(compiles, SumType!S(S(123)))); 1285 } 1286 1287 // Incomparability of different SumTypes 1288 @safe unittest { 1289 SumType!(int, string) x = 123; 1290 SumType!(string, int) y = 123; 1291 1292 assert(!__traits(compiles, x != y)); 1293 } 1294 1295 // Self-reference in return/parameter type of function pointer member 1296 @safe unittest { 1297 assert(__traits(compiles, { 1298 alias T = SumType!(int, This delegate(This)); 1299 })); 1300 } 1301 1302 // Construction and assignment from implicitly-convertible lvalue 1303 @safe unittest { 1304 alias MySum = SumType!bool; 1305 1306 const(bool) b = true; 1307 1308 assert(__traits(compiles, { MySum x = b; })); 1309 assert(__traits(compiles, { MySum x; x = b; })); 1310 } 1311 1312 // Type index 1313 @safe unittest { 1314 alias MySum = SumType!(int, float); 1315 1316 static bool isIndexOf(Target, Types...)(size_t i) 1317 { 1318 switch (i) { 1319 static foreach (tid, T; Types) 1320 case tid: return is(T == Target); 1321 default: return false; 1322 } 1323 } 1324 1325 assert(isIndexOf!(int, MySum.Types)(MySum(42).typeIndex)); 1326 assert(isIndexOf!(float, MySum.Types)(MySum(3.14).typeIndex)); 1327 } 1328 1329 // Type index for qualified SumTypes 1330 // Disabled in BetterC due to use of dynamic arrays 1331 version (D_BetterC) {} else 1332 @safe unittest { 1333 alias MySum = SumType!(const(int[]), int[]); 1334 1335 static bool isIndexOf(Target, Types...)(size_t i) 1336 { 1337 switch (i) { 1338 static foreach (tid, T; Types) 1339 case tid: return is(T == Target); 1340 default: return false; 1341 } 1342 } 1343 1344 int[] ma = [1, 2, 3]; 1345 // Construct as mutable and convert to const to get mismatched type + tag 1346 auto x = MySum(ma); 1347 const y = MySum(ma); 1348 auto z = const(MySum)(ma); 1349 1350 assert(isIndexOf!(int[], MySum.Types)(x.typeIndex)); 1351 assert(isIndexOf!(const(int[]), Map!(ConstOf, MySum.Types))(y.typeIndex)); 1352 assert(isIndexOf!(const(int[]), Map!(ConstOf, MySum.Types))(z.typeIndex)); 1353 } 1354 1355 // Type index for differently-qualified versions of the same SumType 1356 // Disabled in BetterC due to use of dynamic arrays 1357 version (D_BetterC) {} else 1358 @safe unittest { 1359 alias MySum = SumType!(const(int[]), int[]); 1360 1361 int[] ma = [1, 2, 3]; 1362 auto x = MySum(ma); 1363 const y = x; 1364 1365 assert(x.typeIndex == y.typeIndex); 1366 } 1367 1368 /// True if `T` is an instance of the `SumType` template, otherwise false. 1369 private enum bool isSumTypeInstance(T) = is(T == SumType!Args, Args...); 1370 1371 @safe unittest { 1372 static struct Wrapper 1373 { 1374 SumType!int s; 1375 alias s this; 1376 } 1377 1378 assert(isSumTypeInstance!(SumType!int)); 1379 assert(!isSumTypeInstance!Wrapper); 1380 } 1381 1382 /// True if `T` is a [SumType] or implicitly converts to one, otherwise false. 1383 enum bool isSumType(T) = is(T : SumType!Args, Args...); 1384 1385 /// 1386 @safe unittest { 1387 static struct ConvertsToSumType 1388 { 1389 SumType!int payload; 1390 alias payload this; 1391 } 1392 1393 static struct ContainsSumType 1394 { 1395 SumType!int payload; 1396 } 1397 1398 assert(isSumType!(SumType!int)); 1399 assert(isSumType!ConvertsToSumType); 1400 assert(!isSumType!ContainsSumType); 1401 } 1402 1403 /** 1404 * Calls a type-appropriate function with the value held in a [SumType]. 1405 * 1406 * For each possible type the [SumType] can hold, the given handlers are 1407 * checked, in order, to see whether they accept a single argument of that type. 1408 * The first one that does is chosen as the match for that type. (Note that the 1409 * first match may not always be the most exact match. 1410 * See [#avoiding-unintentional-matches|"Avoiding unintentional matches"] for 1411 * one common pitfall.) 1412 * 1413 * Every type must have a matching handler, and every handler must match at 1414 * least one type. This is enforced at compile time. 1415 * 1416 * Handlers may be functions, delegates, or objects with `opCall` overloads. If 1417 * a function with more than one overload is given as a handler, all of the 1418 * overloads are considered as potential matches. 1419 * 1420 * Templated handlers are also accepted, and will match any type for which they 1421 * can be [implicitly instantiated](https://dlang.org/glossary.html#ifti). See 1422 * [sumtype#introspection-based-matching|"Introspection-based matching"] for an 1423 * example of templated handler usage. 1424 * 1425 * If multiple [SumType]s are passed to `match`, their values are passed to the 1426 * handlers as separate arguments, and matching is done for each possible 1427 * combination of value types. See [#multiple-dispatch|"Multiple dispatch"] for 1428 * an example. 1429 * 1430 * Returns: 1431 * The value returned from the handler that matches the currently-held type. 1432 * 1433 * See_Also: `std.variant.visit` 1434 */ 1435 template match(handlers...) 1436 { 1437 import std.typecons: Yes; 1438 1439 /** 1440 * The actual `match` function. 1441 * 1442 * Params: 1443 * args = One or more [SumType] objects. 1444 */ 1445 auto ref match(SumTypes...)(auto ref SumTypes args) 1446 if (allSatisfy!(isSumType, SumTypes) && args.length > 0) 1447 { 1448 return matchImpl!(Yes.exhaustive, handlers)(args); 1449 } 1450 } 1451 1452 /** $(H3 Avoiding unintentional matches) 1453 * 1454 * Sometimes, implicit conversions may cause a handler to match more types than 1455 * intended. The example below shows two solutions to this problem. 1456 */ 1457 @safe unittest { 1458 alias Number = SumType!(double, int); 1459 1460 Number x; 1461 1462 // Problem: because int implicitly converts to double, the double 1463 // handler is used for both types, and the int handler never matches. 1464 assert(!__traits(compiles, 1465 x.match!( 1466 (double d) => "got double", 1467 (int n) => "got int" 1468 ) 1469 )); 1470 1471 // Solution 1: put the handler for the "more specialized" type (in this 1472 // case, int) before the handler for the type it converts to. 1473 assert(__traits(compiles, 1474 x.match!( 1475 (int n) => "got int", 1476 (double d) => "got double" 1477 ) 1478 )); 1479 1480 // Solution 2: use a template that only accepts the exact type it's 1481 // supposed to match, instead of any type that implicitly converts to it. 1482 alias exactly(T, alias fun) = function (arg) { 1483 static assert(is(typeof(arg) == T)); 1484 return fun(arg); 1485 }; 1486 1487 // Now, even if we put the double handler first, it will only be used for 1488 // doubles, not ints. 1489 assert(__traits(compiles, 1490 x.match!( 1491 exactly!(double, d => "got double"), 1492 exactly!(int, n => "got int") 1493 ) 1494 )); 1495 } 1496 1497 /** $(H3 Multiple dispatch) 1498 * 1499 * Pattern matching can be performed on multiple `SumType`s at once by passing 1500 * handlers with multiple arguments. This usually leads to more concise code 1501 * than using nested calls to `match`, as show below. 1502 */ 1503 @safe unittest { 1504 struct Point2D { double x, y; } 1505 struct Point3D { double x, y, z; } 1506 1507 alias Point = SumType!(Point2D, Point3D); 1508 1509 version (none) { 1510 // This function works, but the code is ugly and repetitive. 1511 // It uses three separate calls to match! 1512 @safe pure nothrow @nogc 1513 bool sameDimensions(Point p1, Point p2) 1514 { 1515 return p1.match!( 1516 (Point2D _) => p2.match!( 1517 (Point2D _) => true, 1518 _ => false 1519 ), 1520 (Point3D _) => p2.match!( 1521 (Point3D _) => true, 1522 _ => false 1523 ) 1524 ); 1525 } 1526 } 1527 1528 // This version is much nicer. 1529 @safe pure nothrow @nogc 1530 bool sameDimensions(Point p1, Point p2) 1531 { 1532 alias doMatch = match!( 1533 (Point2D _1, Point2D _2) => true, 1534 (Point3D _1, Point3D _2) => true, 1535 (_1, _2) => false 1536 ); 1537 1538 return doMatch(p1, p2); 1539 } 1540 1541 Point a = Point2D(1, 2); 1542 Point b = Point2D(3, 4); 1543 Point c = Point3D(5, 6, 7); 1544 Point d = Point3D(8, 9, 0); 1545 1546 assert( sameDimensions(a, b)); 1547 assert( sameDimensions(c, d)); 1548 assert(!sameDimensions(a, c)); 1549 assert(!sameDimensions(d, b)); 1550 } 1551 1552 /** 1553 * Attempts to call a type-appropriate function with the value held in a 1554 * [SumType], and throws on failure. 1555 * 1556 * Matches are chosen using the same rules as [match], but are not required to 1557 * be exhaustive—in other words, a type (or combination of types) is allowed to 1558 * have no matching handler. If a type without a handler is encountered at 1559 * runtime, a [MatchException] is thrown. 1560 * 1561 * Not available when compiled with `-betterC`. 1562 * 1563 * Returns: 1564 * The value returned from the handler that matches the currently-held type, 1565 * if a handler was given for that type. 1566 * 1567 * Throws: 1568 * [MatchException], if the currently-held type has no matching handler. 1569 * 1570 * See_Also: `std.variant.tryVisit` 1571 */ 1572 version (D_Exceptions) 1573 template tryMatch(handlers...) 1574 { 1575 import std.typecons: No; 1576 1577 /** 1578 * The actual `tryMatch` function. 1579 * 1580 * Params: 1581 * args = One or more [SumType] objects. 1582 */ 1583 auto ref tryMatch(SumTypes...)(auto ref SumTypes args) 1584 if (allSatisfy!(isSumType, SumTypes) && args.length > 0) 1585 { 1586 return matchImpl!(No.exhaustive, handlers)(args); 1587 } 1588 } 1589 1590 /** 1591 * Thrown by [tryMatch] when an unhandled type is encountered. 1592 * 1593 * Not available when compiled with `-betterC`. 1594 */ 1595 version (D_Exceptions) 1596 class MatchException : Exception 1597 { 1598 /// 1599 pure @safe @nogc nothrow 1600 this(string msg, string file = __FILE__, size_t line = __LINE__) 1601 { 1602 super(msg, file, line); 1603 } 1604 } 1605 1606 /** 1607 * True if `handler` is a potential match for `Ts`, otherwise false. 1608 * 1609 * See the documentation for [match] for a full explanation of how matches are 1610 * chosen. 1611 */ 1612 template canMatch(alias handler, Ts...) 1613 if (Ts.length > 0) 1614 { 1615 enum canMatch = is(typeof((Ts args) => handler(args))); 1616 } 1617 1618 /// 1619 @safe unittest { 1620 alias handleInt = (int i) => "got an int"; 1621 1622 assert( canMatch!(handleInt, int)); 1623 assert(!canMatch!(handleInt, string)); 1624 } 1625 1626 // Includes all overloads of the given handler 1627 @safe unittest { 1628 static struct OverloadSet 1629 { 1630 static void fun(int n) {} 1631 static void fun(double d) {} 1632 } 1633 1634 assert(canMatch!(OverloadSet.fun, int)); 1635 assert(canMatch!(OverloadSet.fun, double)); 1636 } 1637 1638 // Like aliasSeqOf!(iota(n)), but works in BetterC 1639 private template Iota(size_t n) 1640 { 1641 static if (n == 0) { 1642 alias Iota = AliasSeq!(); 1643 } else { 1644 alias Iota = AliasSeq!(Iota!(n - 1), n - 1); 1645 } 1646 } 1647 1648 @safe unittest { 1649 assert(is(Iota!0 == AliasSeq!())); 1650 assert(Iota!1 == AliasSeq!(0)); 1651 assert(Iota!3 == AliasSeq!(0, 1, 2)); 1652 } 1653 1654 /* The number that the dim-th argument's tag is multiplied by when 1655 * converting TagTuples to and from case indices ("caseIds"). 1656 * 1657 * Named by analogy to the stride that the dim-th index into a 1658 * multidimensional static array is multiplied by to calculate the 1659 * offset of a specific element. 1660 */ 1661 private size_t stride(size_t dim, lengths...)() 1662 { 1663 import core.checkedint: mulu; 1664 1665 size_t result = 1; 1666 bool overflow = false; 1667 1668 static foreach (i; 0 .. dim) { 1669 result = mulu(result, lengths[i], overflow); 1670 } 1671 1672 /* The largest number matchImpl uses, numCases, is calculated with 1673 * stride!(SumTypes.length), so as long as this overflow check 1674 * passes, we don't need to check for overflow anywhere else. 1675 */ 1676 assert(!overflow, "Integer overflow"); 1677 return result; 1678 } 1679 1680 private template matchImpl(Flag!"exhaustive" exhaustive, handlers...) 1681 { 1682 auto matchImpl(SumTypes...)(auto ref SumTypes args) 1683 if (allSatisfy!(isSumType, SumTypes) && args.length > 0) 1684 { 1685 enum typeCount(SumType) = SumType.Types.length; 1686 alias stride(size_t i) = .stride!(i, Map!(typeCount, SumTypes)); 1687 1688 /* A TagTuple represents a single possible set of tags that `args` 1689 * could have at runtime. 1690 * 1691 * Because D does not allow a struct to be the controlling expression 1692 * of a switch statement, we cannot dispatch on the TagTuple directly. 1693 * Instead, we must map each TagTuple to a unique integer and generate 1694 * a case label for each of those integers. 1695 * 1696 * This mapping is implemented in `fromCaseId` and `toCaseId`. It uses 1697 * the same technique that's used to map index tuples to memory offsets 1698 * in a multidimensional static array. 1699 * 1700 * For example, when `args` consists of two SumTypes with two member 1701 * types each, the TagTuples corresponding to each case label are: 1702 * 1703 * case 0: TagTuple([0, 0]) 1704 * case 1: TagTuple([1, 0]) 1705 * case 2: TagTuple([0, 1]) 1706 * case 3: TagTuple([1, 1]) 1707 * 1708 * When there is only one argument, the caseId is equal to that 1709 * argument's tag. 1710 */ 1711 static struct TagTuple 1712 { 1713 size_t[SumTypes.length] tags; 1714 alias tags this; 1715 1716 invariant { 1717 static foreach (i; 0 .. tags.length) { 1718 assert(tags[i] < SumTypes[i].Types.length); 1719 } 1720 } 1721 1722 this(ref const(SumTypes) args) 1723 { 1724 static foreach (i; 0 .. tags.length) { 1725 tags[i] = args[i].tag; 1726 } 1727 } 1728 1729 static TagTuple fromCaseId(size_t caseId) 1730 { 1731 TagTuple result; 1732 1733 // Most-significant to least-significant 1734 static foreach_reverse (i; 0 .. result.length) { 1735 result[i] = caseId / stride!i; 1736 caseId %= stride!i; 1737 } 1738 1739 return result; 1740 } 1741 1742 size_t toCaseId() 1743 { 1744 size_t result; 1745 1746 static foreach (i; 0 .. tags.length) { 1747 result += tags[i] * stride!i; 1748 } 1749 1750 return result; 1751 } 1752 } 1753 1754 /* 1755 * A list of arguments to be passed to a handler needed for the case 1756 * labeled with `caseId`. 1757 */ 1758 template handlerArgs(size_t caseId) 1759 { 1760 enum tags = TagTuple.fromCaseId(caseId); 1761 enum argsFrom(size_t i: tags.length) = ""; 1762 enum argsFrom(size_t i) = "args[" ~ toCtString!i ~ "].get!(SumTypes[" ~ toCtString!i ~ "]" ~ 1763 ".Types[" ~ toCtString!(tags[i]) ~ "])(), " ~ argsFrom!(i + 1); 1764 enum handlerArgs = argsFrom!0; 1765 } 1766 1767 /* An AliasSeq of the types of the member values in the argument list 1768 * returned by `handlerArgs!caseId`. 1769 * 1770 * Note that these are the actual (that is, qualified) types of the 1771 * member values, which may not be the same as the types listed in 1772 * the arguments' `.Types` properties. 1773 */ 1774 template valueTypes(size_t caseId) 1775 { 1776 enum tags = TagTuple.fromCaseId(caseId); 1777 1778 template getType(size_t i) 1779 { 1780 enum tid = tags[i]; 1781 alias T = SumTypes[i].Types[tid]; 1782 alias getType = typeof(args[i].get!T()); 1783 } 1784 1785 alias valueTypes = Map!(getType, Iota!(tags.length)); 1786 } 1787 1788 /* The total number of cases is 1789 * 1790 * Π SumTypes[i].Types.length for 0 ≤ i < SumTypes.length 1791 * 1792 * Conveniently, this is equal to stride!(SumTypes.length), so we can 1793 * use that function to compute it. 1794 */ 1795 enum numCases = stride!(SumTypes.length); 1796 1797 /* Guaranteed to never be a valid handler index, since 1798 * handlers.length <= size_t.max. 1799 */ 1800 enum noMatch = size_t.max; 1801 1802 // An array that maps caseIds to handler indices ("hids"). 1803 enum matches = () { 1804 size_t[numCases] matches; 1805 1806 // Workaround for dlang issue 19561 1807 foreach (ref match; matches) { 1808 match = noMatch; 1809 } 1810 1811 static foreach (caseId; 0 .. numCases) { 1812 static foreach (hid, handler; handlers) { 1813 static if (canMatch!(handler, valueTypes!caseId)) { 1814 if (matches[caseId] == noMatch) { 1815 matches[caseId] = hid; 1816 } 1817 } 1818 } 1819 } 1820 1821 return matches; 1822 }(); 1823 1824 import std.algorithm.searching: canFind; 1825 1826 // Check for unreachable handlers 1827 static foreach (hid, handler; handlers) { 1828 static assert(matches[].canFind(hid), 1829 "`handlers[" ~ toCtString!hid ~ "]` " ~ 1830 "of type `" ~ ( __traits(isTemplate, handler) 1831 ? "template" 1832 : typeof(handler).stringof 1833 ) ~ "` " ~ 1834 "never matches" 1835 ); 1836 } 1837 1838 // Workaround for dlang issue 19993 1839 enum handlerName(size_t hid) = "handler" ~ toCtString!hid; 1840 1841 static foreach (size_t hid, handler; handlers) { 1842 mixin("alias ", handlerName!hid, " = handler;"); 1843 } 1844 1845 immutable argsId = TagTuple(args).toCaseId; 1846 1847 final switch (argsId) { 1848 static foreach (caseId; 0 .. numCases) { 1849 case caseId: 1850 static if (matches[caseId] != noMatch) { 1851 return mixin(handlerName!(matches[caseId]), "(", handlerArgs!caseId, ")"); 1852 } else { 1853 static if(exhaustive) { 1854 static assert(false, 1855 "No matching handler for types `" ~ valueTypes!caseId.stringof ~ "`"); 1856 } else { 1857 throw new MatchException( 1858 "No matching handler for types `" ~ valueTypes!caseId.stringof ~ "`"); 1859 } 1860 } 1861 } 1862 } 1863 1864 assert(false, "unreachable"); 1865 } 1866 } 1867 1868 // Matching 1869 @safe unittest { 1870 alias MySum = SumType!(int, float); 1871 1872 MySum x = MySum(42); 1873 MySum y = MySum(3.14); 1874 1875 assert(x.match!((int v) => true, (float v) => false)); 1876 assert(y.match!((int v) => false, (float v) => true)); 1877 } 1878 1879 // Missing handlers 1880 @safe unittest { 1881 alias MySum = SumType!(int, float); 1882 1883 MySum x = MySum(42); 1884 1885 assert(!__traits(compiles, x.match!((int x) => true))); 1886 assert(!__traits(compiles, x.match!())); 1887 } 1888 1889 // Handlers with qualified parameters 1890 // Disabled in BetterC due to use of dynamic arrays 1891 version (D_BetterC) {} else 1892 @safe unittest { 1893 alias MySum = SumType!(int[], float[]); 1894 1895 MySum x = MySum([1, 2, 3]); 1896 MySum y = MySum([1.0, 2.0, 3.0]); 1897 1898 assert(x.match!((const(int[]) v) => true, (const(float[]) v) => false)); 1899 assert(y.match!((const(int[]) v) => false, (const(float[]) v) => true)); 1900 } 1901 1902 // Handlers for qualified types 1903 // Disabled in BetterC due to use of dynamic arrays 1904 version (D_BetterC) {} else 1905 @safe unittest { 1906 alias MySum = SumType!(immutable(int[]), immutable(float[])); 1907 1908 MySum x = MySum([1, 2, 3]); 1909 1910 assert(x.match!((immutable(int[]) v) => true, (immutable(float[]) v) => false)); 1911 assert(x.match!((const(int[]) v) => true, (const(float[]) v) => false)); 1912 // Tail-qualified parameters 1913 assert(x.match!((immutable(int)[] v) => true, (immutable(float)[] v) => false)); 1914 assert(x.match!((const(int)[] v) => true, (const(float)[] v) => false)); 1915 // Generic parameters 1916 assert(x.match!((immutable v) => true)); 1917 assert(x.match!((const v) => true)); 1918 // Unqualified parameters 1919 assert(!__traits(compiles, 1920 x.match!((int[] v) => true, (float[] v) => false) 1921 )); 1922 } 1923 1924 // Delegate handlers 1925 // Disabled in BetterC due to use of closures 1926 version (D_BetterC) {} else 1927 @safe unittest { 1928 alias MySum = SumType!(int, float); 1929 1930 int answer = 42; 1931 MySum x = MySum(42); 1932 MySum y = MySum(3.14); 1933 1934 assert(x.match!((int v) => v == answer, (float v) => v == answer)); 1935 assert(!y.match!((int v) => v == answer, (float v) => v == answer)); 1936 } 1937 1938 version (unittest) { 1939 version (D_BetterC) { 1940 // std.math.isClose depends on core.runtime.math, so use a 1941 // libc-based version for testing with -betterC 1942 @safe pure @nogc nothrow 1943 private bool isClose(double lhs, double rhs) 1944 { 1945 import core.stdc.math: fabs; 1946 1947 return fabs(lhs - rhs) < 1e-5; 1948 } 1949 } else { 1950 import std.math: isClose; 1951 } 1952 } 1953 1954 // Generic handler 1955 @safe unittest { 1956 alias MySum = SumType!(int, float); 1957 1958 MySum x = MySum(42); 1959 MySum y = MySum(3.14); 1960 1961 assert(x.match!(v => v*2) == 84); 1962 assert(y.match!(v => v*2).isClose(6.28)); 1963 } 1964 1965 // Fallback to generic handler 1966 // Disabled in BetterC due to use of std.conv.to 1967 version (D_BetterC) {} else 1968 @safe unittest { 1969 import std.conv: to; 1970 1971 alias MySum = SumType!(int, float, string); 1972 1973 MySum x = MySum(42); 1974 MySum y = MySum("42"); 1975 1976 assert(x.match!((string v) => v.to!int, v => v*2) == 84); 1977 assert(y.match!((string v) => v.to!int, v => v*2) == 42); 1978 } 1979 1980 // Multiple non-overlapping generic handlers 1981 @safe unittest { 1982 import std.array: staticArray; 1983 1984 alias MySum = SumType!(int, float, int[], char[]); 1985 1986 static ints = staticArray([1, 2, 3]); 1987 static chars = staticArray(['a', 'b', 'c']); 1988 1989 MySum x = MySum(42); 1990 MySum y = MySum(3.14); 1991 MySum z = MySum(ints[]); 1992 MySum w = MySum(chars[]); 1993 1994 assert(x.match!(v => v*2, v => v.length) == 84); 1995 assert(y.match!(v => v*2, v => v.length).isClose(6.28)); 1996 assert(w.match!(v => v*2, v => v.length) == 3); 1997 assert(z.match!(v => v*2, v => v.length) == 3); 1998 } 1999 2000 // Structural matching 2001 @safe unittest { 2002 static struct S1 { int x; } 2003 static struct S2 { int y; } 2004 alias MySum = SumType!(S1, S2); 2005 2006 MySum a = MySum(S1(0)); 2007 MySum b = MySum(S2(0)); 2008 2009 assert(a.match!(s1 => s1.x + 1, s2 => s2.y - 1) == 1); 2010 assert(b.match!(s1 => s1.x + 1, s2 => s2.y - 1) == -1); 2011 } 2012 2013 // Separate opCall handlers 2014 @safe unittest { 2015 static struct IntHandler 2016 { 2017 bool opCall(int arg) 2018 { 2019 return true; 2020 } 2021 } 2022 2023 static struct FloatHandler 2024 { 2025 bool opCall(float arg) 2026 { 2027 return false; 2028 } 2029 } 2030 2031 alias MySum = SumType!(int, float); 2032 2033 MySum x = MySum(42); 2034 MySum y = MySum(3.14); 2035 2036 assert(x.match!(IntHandler.init, FloatHandler.init)); 2037 assert(!y.match!(IntHandler.init, FloatHandler.init)); 2038 } 2039 2040 // Compound opCall handler 2041 @safe unittest { 2042 static struct CompoundHandler 2043 { 2044 bool opCall(int arg) 2045 { 2046 return true; 2047 } 2048 2049 bool opCall(float arg) 2050 { 2051 return false; 2052 } 2053 } 2054 2055 alias MySum = SumType!(int, float); 2056 2057 MySum x = MySum(42); 2058 MySum y = MySum(3.14); 2059 2060 assert(x.match!(CompoundHandler.init)); 2061 assert(!y.match!(CompoundHandler.init)); 2062 } 2063 2064 // Ordered matching 2065 @safe unittest { 2066 alias MySum = SumType!(int, float); 2067 2068 MySum x = MySum(42); 2069 2070 assert(x.match!((int v) => true, v => false)); 2071 } 2072 2073 // Non-exhaustive matching 2074 version (D_Exceptions) 2075 @system unittest { 2076 import std.exception: assertThrown, assertNotThrown; 2077 2078 alias MySum = SumType!(int, float); 2079 2080 MySum x = MySum(42); 2081 MySum y = MySum(3.14); 2082 2083 assertNotThrown!MatchException(x.tryMatch!((int n) => true)); 2084 assertThrown!MatchException(y.tryMatch!((int n) => true)); 2085 } 2086 2087 // Non-exhaustive matching in @safe code 2088 version (D_Exceptions) 2089 @safe unittest { 2090 SumType!(int, float) x; 2091 2092 assert(__traits(compiles, 2093 x.tryMatch!( 2094 (int n) => n + 1, 2095 ) 2096 )); 2097 2098 } 2099 2100 // Handlers with ref parameters 2101 @safe unittest { 2102 alias Value = SumType!(long, double); 2103 2104 auto value = Value(3.14); 2105 2106 value.match!( 2107 (long) {}, 2108 (ref double d) { d *= 2; } 2109 ); 2110 2111 assert(value.get!double.isClose(6.28)); 2112 } 2113 2114 // Unreachable handlers 2115 @safe unittest { 2116 alias MySum = SumType!(int, string); 2117 2118 MySum s; 2119 2120 assert(!__traits(compiles, 2121 s.match!( 2122 (int _) => 0, 2123 (string _) => 1, 2124 (double _) => 2 2125 ) 2126 )); 2127 2128 assert(!__traits(compiles, 2129 s.match!( 2130 _ => 0, 2131 (int _) => 1 2132 ) 2133 )); 2134 } 2135 2136 // Unsafe handlers 2137 @system unittest { 2138 SumType!int x; 2139 alias unsafeHandler = (int x) @system { return; }; 2140 2141 assert(!__traits(compiles, () @safe { 2142 x.match!unsafeHandler; 2143 })); 2144 2145 assert(__traits(compiles, () @system { 2146 return x.match!unsafeHandler; 2147 })); 2148 } 2149 2150 // Overloaded handlers 2151 @safe unittest { 2152 static struct OverloadSet 2153 { 2154 static string fun(int i) { return "int"; } 2155 static string fun(double d) { return "double"; } 2156 } 2157 2158 alias MySum = SumType!(int, double); 2159 2160 MySum a = 42; 2161 MySum b = 3.14; 2162 2163 assert(a.match!(OverloadSet.fun) == "int"); 2164 assert(b.match!(OverloadSet.fun) == "double"); 2165 } 2166 2167 // Overload sets that include SumType arguments 2168 @safe unittest { 2169 alias Inner = SumType!(int, double); 2170 alias Outer = SumType!(Inner, string); 2171 2172 static struct OverloadSet 2173 { 2174 @safe: 2175 static string fun(int i) { return "int"; } 2176 static string fun(double d) { return "double"; } 2177 static string fun(string s) { return "string"; } 2178 static string fun(Inner i) { return i.match!fun; } 2179 static string fun(Outer o) { return o.match!fun; } 2180 } 2181 2182 Outer a = Inner(42); 2183 Outer b = Inner(3.14); 2184 Outer c = "foo"; 2185 2186 assert(OverloadSet.fun(a) == "int"); 2187 assert(OverloadSet.fun(b) == "double"); 2188 assert(OverloadSet.fun(c) == "string"); 2189 } 2190 2191 // Overload sets with ref arguments 2192 @safe unittest { 2193 static struct OverloadSet 2194 { 2195 static void fun(ref int i) { i = 42; } 2196 static void fun(ref double d) { d = 3.14; } 2197 } 2198 2199 alias MySum = SumType!(int, double); 2200 2201 MySum x = 0; 2202 MySum y = 0.0; 2203 2204 x.match!(OverloadSet.fun); 2205 y.match!(OverloadSet.fun); 2206 2207 assert(x.match!((value) => is(typeof(value) == int) && value == 42)); 2208 assert(y.match!((value) => is(typeof(value) == double) && value == 3.14)); 2209 } 2210 2211 // Overload sets with templates 2212 @safe unittest { 2213 import std.traits: isNumeric; 2214 2215 static struct OverloadSet 2216 { 2217 static string fun(string arg) 2218 { 2219 return "string"; 2220 } 2221 2222 static string fun(T)(T arg) 2223 if (isNumeric!T) 2224 { 2225 return "numeric"; 2226 } 2227 } 2228 2229 alias MySum = SumType!(int, string); 2230 2231 MySum x = 123; 2232 MySum y = "hello"; 2233 2234 assert(x.match!(OverloadSet.fun) == "numeric"); 2235 assert(y.match!(OverloadSet.fun) == "string"); 2236 } 2237 2238 // Github issue #24 2239 @safe unittest { 2240 assert(__traits(compiles, () @nogc { 2241 int acc = 0; 2242 SumType!int(1).match!((int x) => acc += x); 2243 })); 2244 } 2245 2246 // Github issue #31 2247 @safe unittest { 2248 assert(__traits(compiles, () @nogc { 2249 int acc = 0; 2250 2251 SumType!(int, string)(1).match!( 2252 (int x) => acc += x, 2253 (string _) => 0, 2254 ); 2255 })); 2256 } 2257 2258 // Types that `alias this` a SumType 2259 @safe unittest { 2260 static struct A {} 2261 static struct B {} 2262 static struct D { SumType!(A, B) value; alias value this; } 2263 2264 assert(__traits(compiles, D().match!(_ => true))); 2265 } 2266 2267 // Multiple dispatch 2268 @safe unittest { 2269 alias MySum = SumType!(int, string); 2270 2271 static int fun(MySum x, MySum y) 2272 { 2273 import std.meta: Args = AliasSeq; 2274 2275 return Args!(x, y).match!( 2276 (int xv, int yv) => 0, 2277 (string xv, int yv) => 1, 2278 (int xv, string yv) => 2, 2279 (string xv, string yv) => 3 2280 ); 2281 } 2282 2283 assert(fun(MySum(0), MySum(0)) == 0); 2284 assert(fun(MySum(""), MySum(0)) == 1); 2285 assert(fun(MySum(0), MySum("")) == 2); 2286 assert(fun(MySum(""), MySum("")) == 3); 2287 } 2288 2289 // inout SumTypes 2290 @safe unittest { 2291 assert(__traits(compiles, { 2292 inout(int[]) fun(inout(SumType!(int[])) x) 2293 { 2294 return x.match!((inout(int[]) a) => a); 2295 } 2296 })); 2297 } 2298 2299 static if (__traits(compiles, { import std.traits: isRvalueAssignable; })) { 2300 import std.traits: isRvalueAssignable; 2301 } else private { 2302 enum isRvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, lvalueOf!Lhs = rvalueOf!Rhs); 2303 struct __InoutWorkaroundStruct{} 2304 @property T rvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init); 2305 @property ref T lvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init); 2306 } 2307 2308 private void destroyIfOwner(T)(ref T value) 2309 { 2310 static if (hasElaborateDestructor!T) { 2311 destroy(value); 2312 } 2313 }