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 version (D_BetterC) {} else 22 /// $(H3 Basic usage) 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 version (D_BetterC) {} else 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 @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 version (D_BetterC) {} else 122 /** $(H3 Arithmetic expression evaluator) 123 * 124 * This example makes use of the special placeholder type `This` to define a 125 * [https://en.wikipedia.org/wiki/Recursive_data_type|recursive data type]: an 126 * [https://en.wikipedia.org/wiki/Abstract_syntax_tree|abstract syntax tree] for 127 * representing simple arithmetic expressions. 128 */ 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, isRvalueAssignable, 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 private enum isInout(T) = is(T == inout); 258 259 // Workaround for dlang issue 19669 260 private enum haveDip1000 = __traits(compiles, () @safe { 261 int x; 262 int* p; 263 p = &x; 264 }); 265 266 /** 267 * A [tagged union](https://en.wikipedia.org/wiki/Tagged_union) that can hold a 268 * single value from any of a specified set of types. 269 * 270 * The value in a `SumType` can be operated on using [match|pattern matching]. 271 * 272 * To avoid ambiguity, duplicate types are not allowed (but see the 273 * [sumtype#basic-usage|"basic usage" example] for a workaround). 274 * 275 * The special type `This` can be used as a placeholder to create 276 * self-referential types, just like with `Algebraic`. See the 277 * [sumtype#arithmetic-expression-evaluator|"Arithmetic expression evaluator" example] for 278 * usage. 279 * 280 * A `SumType` is initialized by default to hold the `.init` value of its 281 * first member type, just like a regular union. The version identifier 282 * `SumTypeNoDefaultCtor` can be used to disable this behavior. 283 * 284 * See_Also: `std.variant.Algebraic` 285 */ 286 struct SumType(Types...) 287 if (is(NoDuplicates!Types == Types) && Types.length > 0) 288 { 289 /// The types a `SumType` can hold. 290 alias Types = AliasSeq!( 291 ReplaceTypeUnless!(isSumTypeInstance, This, typeof(this), TemplateArgsOf!SumType) 292 ); 293 294 private: 295 296 enum bool canHoldTag(T) = Types.length <= T.max; 297 alias unsignedInts = AliasSeq!(ubyte, ushort, uint, ulong); 298 299 alias Tag = Filter!(canHoldTag, unsignedInts)[0]; 300 301 union Storage 302 { 303 // Workaround for dlang issue 20068 304 template memberName(T) 305 if (IndexOf!(T, Types) >= 0) 306 { 307 enum tid = IndexOf!(T, Types); 308 mixin("enum memberName = `values_", toCtString!tid, "`;"); 309 } 310 311 static foreach (T; Types) { 312 mixin("T ", memberName!T, ";"); 313 } 314 } 315 316 Storage storage; 317 Tag tag; 318 319 /** 320 * Accesses the value stored in a SumType. 321 * 322 * This method is memory-safe, provided that: 323 * 324 * 1. A SumType's tag is always accurate. 325 * 2. A SumType cannot be assigned to in @safe code if that assignment 326 * could cause unsafe aliasing. 327 * 328 * All code that accesses a SumType's tag or storage directly, including 329 * @safe code in this module, must be manually checked to ensure that it 330 * does not violate either of the above requirements. 331 */ 332 @trusted 333 ref inout(T) get(T)() inout 334 if (IndexOf!(T, Types) >= 0) 335 { 336 enum tid = IndexOf!(T, Types); 337 assert(tag == tid, 338 "This `" ~ SumType.stringof ~ 339 "` does not contain a(n) `" ~ T.stringof ~ "`" 340 ); 341 return __traits(getMember, storage, Storage.memberName!T); 342 } 343 344 public: 345 346 static foreach (tid, T; Types) { 347 /// Constructs a `SumType` holding a specific value. 348 this(T value) 349 { 350 import core.lifetime: forward; 351 352 static if (isCopyable!T) { 353 // Workaround for dlang issue 21542 354 __traits(getMember, storage, Storage.memberName!T) = __ctfe ? value : forward!value; 355 } else { 356 __traits(getMember, storage, Storage.memberName!T) = forward!value; 357 } 358 359 tag = tid; 360 } 361 362 static if (isCopyable!(const(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 __traits(getMember, storage, Storage.memberName!T) = value; 369 tag = tid; 370 } 371 } 372 } else { 373 @disable this(const(T) value) const; 374 } 375 376 static if (isCopyable!(immutable(T))) { 377 static if (IndexOf!(immutable(T), Map!(ImmutableOf, Types)) == tid) { 378 /// ditto 379 this(immutable(T) value) immutable 380 { 381 __traits(getMember, storage, Storage.memberName!T) = value; 382 tag = tid; 383 } 384 } 385 } else { 386 @disable this(immutable(T) value) immutable; 387 } 388 389 static if (isCopyable!(inout(T))) { 390 static if (IndexOf!(inout(T), Map!(InoutOf, Types)) == tid) { 391 /// ditto 392 this(Value)(Value value) inout 393 if (is(Value == DeducedParameterType!(inout(T)))) 394 { 395 __traits(getMember, storage, Storage.memberName!T) = value; 396 tag = tid; 397 } 398 } 399 } else { 400 @disable this(Value)(Value value) inout 401 if (is(Value == DeducedParameterType!(inout(T)))); 402 } 403 } 404 405 static if (anySatisfy!(hasElaborateCopyConstructor, Types)) { 406 static if ( 407 allSatisfy!(isCopyable, Map!(InoutOf, Types)) 408 && !anySatisfy!(hasPostblit, Map!(InoutOf, Types)) 409 && allSatisfy!(isInout, Map!(InoutOf, Types)) 410 ) { 411 /// Constructs a `SumType` that's a copy of another `SumType`. 412 this(ref inout(SumType) other) inout 413 { 414 storage = other.match!((ref value) { 415 alias OtherTypes = Map!(InoutOf, Types); 416 enum tid = IndexOf!(typeof(value), OtherTypes); 417 alias T = Types[tid]; 418 419 mixin("inout(Storage) newStorage = { ", 420 Storage.memberName!T, ": value", 421 " };"); 422 423 return newStorage; 424 }); 425 426 tag = other.tag; 427 } 428 } else { 429 static if (allSatisfy!(isCopyable, Types)) { 430 /// ditto 431 this(ref SumType other) 432 { 433 storage = other.match!((ref value) { 434 alias T = typeof(value); 435 436 mixin("Storage newStorage = { ", 437 Storage.memberName!T, ": value", 438 " };"); 439 440 return newStorage; 441 }); 442 443 tag = other.tag; 444 } 445 } else { 446 @disable this(ref SumType other); 447 } 448 449 static if (allSatisfy!(isCopyable, Map!(ConstOf, Types))) { 450 /// ditto 451 this(ref const(SumType) other) const 452 { 453 storage = other.match!((ref value) { 454 alias OtherTypes = Map!(ConstOf, Types); 455 enum tid = IndexOf!(typeof(value), OtherTypes); 456 alias T = Types[tid]; 457 458 mixin("const(Storage) newStorage = { ", 459 Storage.memberName!T, ": value", 460 " };"); 461 462 return newStorage; 463 }); 464 465 tag = other.tag; 466 } 467 } else { 468 @disable this(ref const(SumType) other) const; 469 } 470 471 static if (allSatisfy!(isCopyable, Map!(ImmutableOf, Types))) { 472 /// ditto 473 this(ref immutable(SumType) other) immutable 474 { 475 storage = other.match!((ref value) { 476 alias OtherTypes = Map!(ImmutableOf, Types); 477 enum tid = IndexOf!(typeof(value), OtherTypes); 478 alias T = Types[tid]; 479 480 mixin("immutable(Storage) newStorage = { ", 481 Storage.memberName!T, ": value", 482 " };"); 483 484 return newStorage; 485 }); 486 487 tag = other.tag; 488 } 489 } else { 490 @disable this(ref immutable(SumType) other) immutable; 491 } 492 } 493 } 494 495 version (SumTypeNoDefaultCtor) { 496 @disable this(); 497 } 498 499 static foreach (tid, T; Types) { 500 static if (isAssignableTo!T) { 501 /** 502 * Assigns a value to a `SumType`. 503 * 504 * If any of the `SumType`'s members other than the one being assigned 505 * to contain pointers or references, it is possible for the assignment 506 * to cause memory corruption (see the 507 * ["Memory corruption" example](#memory-corruption) below for an 508 * illustration of how). Therefore, such assignments are considered 509 * `@system`. 510 * 511 * An individual assignment can be `@trusted` if the caller can 512 * guarantee that there are no outstanding references to any `SumType` 513 * members that contain pointers or references at the time the 514 * assignment occurs. 515 * 516 * Examples: 517 * 518 * $(H3 Memory corruption) 519 * 520 * This example shows how assignment to a `SumType` can be used to 521 * cause memory corruption in `@system` code. In `@safe` code, the 522 * assignment `s = 123` would not be allowed. 523 * 524 * --- 525 * SumType!(int*, int) s = new int; 526 * s.tryMatch!( 527 * (ref int* p) { 528 * s = 123; // overwrites `p` 529 * return *p; // undefined behavior 530 * } 531 * ); 532 * --- 533 */ 534 ref SumType opAssign(T rhs) 535 { 536 import core.lifetime: forward; 537 import std.traits: hasIndirections, hasNested; 538 import std.meta: AliasSeq, Or = templateOr; 539 540 alias OtherTypes = 541 AliasSeq!(Types[0 .. tid], Types[tid + 1 .. $]); 542 enum unsafeToOverwrite = 543 anySatisfy!(Or!(hasIndirections, hasNested), OtherTypes); 544 545 static if (unsafeToOverwrite) { 546 cast(void) () @system {}(); 547 } 548 549 this.match!destroyIfOwner; 550 551 static if (isCopyable!T) { 552 mixin("Storage newStorage = { ", 553 Storage.memberName!T, ": __ctfe ? rhs : forward!rhs", 554 " };"); 555 } else { 556 mixin("Storage newStorage = { ", 557 Storage.memberName!T, ": forward!rhs", 558 " };"); 559 } 560 561 storage = newStorage; 562 tag = tid; 563 564 return this; 565 } 566 } 567 } 568 569 static if (allSatisfy!(isAssignableTo, Types)) { 570 static if (allSatisfy!(isCopyable, Types)) { 571 /** 572 * Copies the value from another `SumType` into this one. 573 * 574 * See the value-assignment overload for details on `@safe`ty. 575 * 576 * Copy assignment is `@disable`d if any of `Types` is non-copyable. 577 */ 578 ref SumType opAssign(ref SumType rhs) 579 { 580 rhs.match!((ref value) { this = value; }); 581 return this; 582 } 583 } else { 584 @disable ref SumType opAssign(ref SumType rhs); 585 } 586 587 /** 588 * Moves the value from another `SumType` into this one. 589 * 590 * See the value-assignment overload for details on `@safe`ty. 591 */ 592 ref SumType opAssign(SumType rhs) 593 { 594 import core.lifetime: move; 595 596 rhs.match!((ref value) { 597 static if (isCopyable!(typeof(value))) { 598 // Workaround for dlang issue 21542 599 this = __ctfe ? value : move(value); 600 } else { 601 this = move(value); 602 } 603 }); 604 return this; 605 } 606 } 607 608 /** 609 * Compares two `SumType`s for equality. 610 * 611 * Two `SumType`s are equal if they are the same kind of `SumType`, they 612 * contain values of the same type, and those values are equal. 613 */ 614 bool opEquals(this This, Rhs)(auto ref Rhs rhs) 615 if (!is(CommonType!(This, Rhs) == void)) 616 { 617 static if (is(This == Rhs)) { 618 return AliasSeq!(this, rhs).match!((ref value, ref rhsValue) { 619 static if (is(typeof(value) == typeof(rhsValue))) { 620 return value == rhsValue; 621 } else { 622 return false; 623 } 624 }); 625 } else { 626 alias CommonSumType = CommonType!(This, Rhs); 627 return cast(CommonSumType) this == cast(CommonSumType) rhs; 628 } 629 } 630 631 // Workaround for dlang issue 19407 632 static if (__traits(compiles, anySatisfy!(hasElaborateDestructor, Types))) { 633 // If possible, include the destructor only when it's needed 634 private enum includeDtor = anySatisfy!(hasElaborateDestructor, Types); 635 } else { 636 // If we can't tell, always include it, even when it does nothing 637 private enum includeDtor = true; 638 } 639 640 static if (includeDtor) { 641 /// Calls the destructor of the `SumType`'s current value. 642 ~this() 643 { 644 this.match!destroyIfOwner; 645 } 646 } 647 648 version (D_BetterC) {} else 649 /** 650 * Returns a string representation of the `SumType`'s current value. 651 * 652 * Not available when compiled with `-betterC`. 653 */ 654 string toString(this This)() 655 { 656 import std.conv: to; 657 658 return this.match!(to!string); 659 } 660 661 version (D_BetterC) {} else 662 /** 663 * Handles formatted writing of the `SumType`'s current value. 664 * 665 * Not available when compiled with `-betterC`. 666 * 667 * Params: 668 * sink = Output range to write to. 669 * fmt = Format specifier to use. 670 * 671 * See_Also: `std.format.formatValue` 672 */ 673 void toString(this This, Sink, Char)(ref Sink sink, const ref FormatSpec!Char fmt) 674 { 675 import std.format: formatValue; 676 677 this.match!((ref value) { 678 formatValue(sink, value, fmt); 679 }); 680 } 681 682 static if (allSatisfy!(isHashable, Map!(ConstOf, Types))) { 683 // Workaround for dlang issue 20095 684 version (D_BetterC) {} else 685 /** 686 * Returns the hash of the `SumType`'s current value. 687 * 688 * Not available when compiled with `-betterC`. 689 */ 690 size_t toHash() const 691 { 692 return this.match!hashOf; 693 } 694 } 695 696 /** 697 * Returns the index of the type of the `SumType`'s current value in the 698 * `SumType`'s [Types]. 699 * 700 * If the `SumType` is qualified, returns the index of the type in [Types] 701 * whose qualified version matches the `SumType`'s current value. 702 */ 703 size_t typeIndex() const 704 { 705 return tag; 706 } 707 } 708 709 // Construction 710 @safe unittest { 711 alias MySum = SumType!(int, float); 712 713 assert(__traits(compiles, MySum(42))); 714 assert(__traits(compiles, MySum(3.14))); 715 } 716 717 // Assignment 718 @safe unittest { 719 alias MySum = SumType!(int, float); 720 721 MySum x = MySum(42); 722 723 assert(__traits(compiles, x = 3.14)); 724 } 725 726 // Self assignment 727 @safe unittest { 728 alias MySum = SumType!(int, float); 729 730 MySum x = MySum(42); 731 MySum y = MySum(3.14); 732 733 assert(__traits(compiles, y = x)); 734 } 735 736 // Equality 737 @safe unittest { 738 alias MySum = SumType!(int, float); 739 740 assert(MySum(123) == MySum(123)); 741 assert(MySum(123) != MySum(456)); 742 assert(MySum(123) != MySum(123.0)); 743 assert(MySum(123) != MySum(456.0)); 744 745 } 746 747 // Equality of differently-qualified SumTypes 748 // Disabled in BetterC due to use of dynamic arrays 749 version (D_BetterC) {} else 750 @safe unittest { 751 alias SumA = SumType!(int, float); 752 alias SumB = SumType!(const(int[]), int[]); 753 alias SumC = SumType!(int[], const(int[])); 754 755 int[] ma = [1, 2, 3]; 756 const(int[]) ca = [1, 2, 3]; 757 758 assert(const(SumA)(123) == SumA(123)); 759 assert(const(SumB)(ma[]) == SumB(ca[])); 760 assert(const(SumC)(ma[]) == SumC(ca[])); 761 } 762 763 // Imported types 764 @safe unittest { 765 import std.typecons: Tuple; 766 767 assert(__traits(compiles, { 768 alias MySum = SumType!(Tuple!(int, int)); 769 })); 770 } 771 772 // const and immutable types 773 @safe unittest { 774 assert(__traits(compiles, { 775 alias MySum = SumType!(const(int[]), immutable(float[])); 776 })); 777 } 778 779 // Recursive types 780 @safe unittest { 781 alias MySum = SumType!(This*); 782 assert(is(MySum.Types[0] == MySum*)); 783 } 784 785 // Allowed types 786 @safe unittest { 787 import std.meta: AliasSeq; 788 789 alias MySum = SumType!(int, float, This*); 790 791 assert(is(MySum.Types == AliasSeq!(int, float, MySum*))); 792 } 793 794 // Types with destructors and postblits 795 @system unittest { 796 int copies; 797 798 static struct Test 799 { 800 bool initialized = false; 801 int* copiesPtr; 802 803 this(this) { (*copiesPtr)++; } 804 ~this() { if (initialized) (*copiesPtr)--; } 805 } 806 807 alias MySum = SumType!(int, Test); 808 809 Test t = Test(true, &copies); 810 811 { 812 MySum x = t; 813 assert(copies == 1); 814 } 815 assert(copies == 0); 816 817 { 818 MySum x = 456; 819 assert(copies == 0); 820 } 821 assert(copies == 0); 822 823 { 824 MySum x = t; 825 assert(copies == 1); 826 x = 456; 827 assert(copies == 0); 828 } 829 830 { 831 MySum x = 456; 832 assert(copies == 0); 833 x = t; 834 assert(copies == 1); 835 } 836 837 { 838 MySum x = t; 839 MySum y = x; 840 assert(copies == 2); 841 } 842 843 { 844 MySum x = t; 845 MySum y; 846 y = x; 847 assert(copies == 2); 848 } 849 } 850 851 // Doesn't destroy reference types 852 // Disabled in BetterC due to use of classes 853 version (D_BetterC) {} else 854 @system unittest { 855 bool destroyed; 856 857 class C 858 { 859 ~this() 860 { 861 destroyed = true; 862 } 863 } 864 865 struct S 866 { 867 ~this() {} 868 } 869 870 alias MySum = SumType!(S, C); 871 872 C c = new C(); 873 { 874 MySum x = c; 875 destroyed = false; 876 } 877 assert(!destroyed); 878 879 { 880 MySum x = c; 881 destroyed = false; 882 x = S(); 883 assert(!destroyed); 884 } 885 } 886 887 // Types with @disable this() 888 @safe unittest { 889 static struct NoInit 890 { 891 @disable this(); 892 } 893 894 alias MySum = SumType!(NoInit, int); 895 896 assert(!__traits(compiles, MySum())); 897 assert(__traits(compiles, MySum(42))); 898 auto x = MySum(42); 899 } 900 901 // const SumTypes 902 @safe unittest { 903 assert(__traits(compiles, 904 const(SumType!(int[]))([1, 2, 3]) 905 )); 906 } 907 908 // Equality of const SumTypes 909 @safe unittest { 910 alias MySum = SumType!int; 911 912 assert(__traits(compiles, 913 const(MySum)(123) == const(MySum)(456) 914 )); 915 } 916 917 // Compares reference types using value equality 918 @safe unittest { 919 import std.array: staticArray; 920 921 static struct Field {} 922 static struct Struct { Field[] fields; } 923 alias MySum = SumType!Struct; 924 925 static arr1 = staticArray([Field()]); 926 static arr2 = staticArray([Field()]); 927 928 auto a = MySum(Struct(arr1[])); 929 auto b = MySum(Struct(arr2[])); 930 931 assert(a == b); 932 } 933 934 // toString 935 // Disabled in BetterC due to use of std.conv.text 936 version (D_BetterC) {} else 937 @safe unittest { 938 import std.conv: text; 939 940 static struct Int { int i; } 941 static struct Double { double d; } 942 alias Sum = SumType!(Int, Double); 943 944 assert(Sum(Int(42)).text == Int(42).text, Sum(Int(42)).text); 945 assert(Sum(Double(33.3)).text == Double(33.3).text, Sum(Double(33.3)).text); 946 assert((const(Sum)(Int(42))).text == (const(Int)(42)).text, (const(Sum)(Int(42))).text); 947 } 948 949 // string formatting 950 // Disabled in BetterC due to use of std.format.format 951 version (D_BetterC) {} else 952 @safe unittest { 953 import std.format: format; 954 955 SumType!int x = 123; 956 957 assert(format!"%s"(x) == format!"%s"(123)); 958 assert(format!"%x"(x) == format!"%x"(123)); 959 } 960 961 // string formatting of qualified SumTypes 962 // Disabled in BetterC due to use of std.format.format and dynamic arrays 963 version (D_BetterC) {} else 964 @safe unittest { 965 import std.format: format; 966 967 int[] a = [1, 2, 3]; 968 const(SumType!(int[])) x = a; 969 970 assert(format!"%(%d, %)"(x) == format!"%(%s, %)"(a)); 971 } 972 973 // Github issue #16 974 // Disabled in BetterC due to use of dynamic arrays 975 version (D_BetterC) {} else 976 @safe unittest { 977 alias Node = SumType!(This[], string); 978 979 // override inference of @system attribute for cyclic functions 980 assert((() @trusted => 981 Node([Node([Node("x")])]) 982 == 983 Node([Node([Node("x")])]) 984 )()); 985 } 986 987 // Github issue #16 with const 988 // Disabled in BetterC due to use of dynamic arrays 989 version (D_BetterC) {} else 990 @safe unittest { 991 alias Node = SumType!(const(This)[], string); 992 993 // override inference of @system attribute for cyclic functions 994 assert((() @trusted => 995 Node([Node([Node("x")])]) 996 == 997 Node([Node([Node("x")])]) 998 )()); 999 } 1000 1001 // Stale pointers 1002 // Disabled in BetterC due to use of dynamic arrays 1003 version (D_BetterC) {} else 1004 @system unittest { 1005 alias MySum = SumType!(ubyte, void*[2]); 1006 1007 MySum x = [null, cast(void*) 0x12345678]; 1008 void** p = &x.get!(void*[2])[1]; 1009 x = ubyte(123); 1010 1011 assert(*p != cast(void*) 0x12345678); 1012 } 1013 1014 // Exception-safe assignment 1015 // Disabled in BetterC due to use of exceptions 1016 version (D_BetterC) {} else 1017 @safe unittest { 1018 static struct A 1019 { 1020 int value = 123; 1021 } 1022 1023 static struct B 1024 { 1025 int value = 456; 1026 this(this) { throw new Exception("oops"); } 1027 } 1028 1029 alias MySum = SumType!(A, B); 1030 1031 MySum x; 1032 try { 1033 x = B(); 1034 } catch (Exception e) {} 1035 1036 assert( 1037 (x.tag == 0 && x.get!A.value == 123) || 1038 (x.tag == 1 && x.get!B.value == 456) 1039 ); 1040 } 1041 1042 // Types with @disable this(this) 1043 @safe unittest { 1044 import core.lifetime: move; 1045 1046 static struct NoCopy 1047 { 1048 @disable this(this); 1049 } 1050 1051 alias MySum = SumType!NoCopy; 1052 1053 NoCopy lval = NoCopy(); 1054 1055 MySum x = NoCopy(); 1056 MySum y = NoCopy(); 1057 1058 assert(__traits(compiles, SumType!NoCopy(NoCopy()))); 1059 assert(!__traits(compiles, SumType!NoCopy(lval))); 1060 1061 assert(__traits(compiles, y = NoCopy())); 1062 assert(__traits(compiles, y = move(x))); 1063 assert(!__traits(compiles, y = lval)); 1064 assert(!__traits(compiles, y = x)); 1065 1066 assert(__traits(compiles, x == y)); 1067 } 1068 1069 // Github issue #22 1070 // Disabled in BetterC due to use of std.typecons.Nullable 1071 version (D_BetterC) {} else 1072 @safe unittest { 1073 import std.typecons; 1074 assert(__traits(compiles, { 1075 static struct A { 1076 SumType!(Nullable!int) a = Nullable!int.init; 1077 } 1078 })); 1079 } 1080 1081 // Static arrays of structs with postblits 1082 // Disabled in BetterC due to use of dynamic arrays 1083 version (D_BetterC) {} else 1084 @safe unittest { 1085 static struct S 1086 { 1087 int n; 1088 this(this) { n++; } 1089 } 1090 1091 assert(__traits(compiles, SumType!(S[1])())); 1092 1093 SumType!(S[1]) x = [S(0)]; 1094 SumType!(S[1]) y = x; 1095 1096 auto xval = x.get!(S[1])[0].n; 1097 auto yval = y.get!(S[1])[0].n; 1098 1099 assert(xval != yval); 1100 } 1101 1102 // Replacement does not happen inside SumType 1103 // Disabled in BetterC due to use of associative arrays 1104 version (D_BetterC) {} else 1105 @safe unittest { 1106 import std.typecons : Tuple, ReplaceTypeUnless; 1107 alias A = Tuple!(This*,SumType!(This*))[SumType!(This*,string)[This]]; 1108 alias TR = ReplaceTypeUnless!(isSumTypeInstance, This, int, A); 1109 static assert(is(TR == Tuple!(int*,SumType!(This*))[SumType!(This*, string)[int]])); 1110 } 1111 1112 // Supports nested self-referential SumTypes 1113 @safe unittest { 1114 import std.typecons : Tuple, Flag; 1115 alias Nat = SumType!(Flag!"0", Tuple!(This*)); 1116 static assert(__traits(compiles, SumType!(Nat))); 1117 static assert(__traits(compiles, SumType!(Nat*, Tuple!(This*, This*)))); 1118 } 1119 1120 // Self-referential SumTypes inside Algebraic 1121 // Disabled in BetterC due to use of std.variant.Algebraic 1122 version (D_BetterC) {} else 1123 @safe unittest { 1124 import std.variant: Algebraic; 1125 1126 alias T = Algebraic!(SumType!(This*)); 1127 1128 assert(is(T.AllowedTypes[0].Types[0] == T.AllowedTypes[0]*)); 1129 } 1130 1131 // Doesn't call @system postblits in @safe code 1132 @safe unittest { 1133 static struct SystemCopy { @system this(this) {} } 1134 SystemCopy original; 1135 1136 assert(!__traits(compiles, () @safe { 1137 SumType!SystemCopy copy = original; 1138 })); 1139 1140 assert(!__traits(compiles, () @safe { 1141 SumType!SystemCopy copy; copy = original; 1142 })); 1143 } 1144 1145 // Doesn't overwrite pointers in @safe code 1146 @safe unittest { 1147 alias MySum = SumType!(int*, int); 1148 1149 MySum x; 1150 1151 assert(!__traits(compiles, () @safe { 1152 x = 123; 1153 })); 1154 1155 assert(!__traits(compiles, () @safe { 1156 x = MySum(123); 1157 })); 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 qualified copy constructors 1267 @safe unittest { 1268 static struct ConstCopy 1269 { 1270 int n; 1271 this(inout int n) inout { this.n = n; } 1272 this(ref const typeof(this) other) const { this.n = other.n; } 1273 } 1274 1275 static struct ImmutableCopy 1276 { 1277 int n; 1278 this(inout int n) inout { this.n = n; } 1279 this(ref immutable typeof(this) other) immutable { this.n = other.n; } 1280 } 1281 1282 const SumType!ConstCopy x = const(ConstCopy)(1); 1283 immutable SumType!ImmutableCopy y = immutable(ImmutableCopy)(1); 1284 } 1285 1286 // Types with disabled opEquals 1287 @safe unittest { 1288 static struct S 1289 { 1290 @disable bool opEquals(const S rhs) const; 1291 } 1292 1293 assert(__traits(compiles, SumType!S(S()))); 1294 } 1295 1296 // Types with non-const opEquals 1297 @safe unittest { 1298 static struct S 1299 { 1300 int i; 1301 bool opEquals(S rhs) { return i == rhs.i; } 1302 } 1303 1304 assert(__traits(compiles, SumType!S(S(123)))); 1305 } 1306 1307 // Incomparability of different SumTypes 1308 @safe unittest { 1309 SumType!(int, string) x = 123; 1310 SumType!(string, int) y = 123; 1311 1312 assert(!__traits(compiles, x != y)); 1313 } 1314 1315 // Self-reference in return/parameter type of function pointer member 1316 @safe unittest { 1317 assert(__traits(compiles, { 1318 alias T = SumType!(int, This delegate(This)); 1319 })); 1320 } 1321 1322 // Construction and assignment from implicitly-convertible lvalue 1323 @safe unittest { 1324 alias MySum = SumType!bool; 1325 1326 const(bool) b = true; 1327 1328 assert(__traits(compiles, { MySum x = b; })); 1329 assert(__traits(compiles, { MySum x; x = b; })); 1330 } 1331 1332 // Type index 1333 @safe unittest { 1334 alias MySum = SumType!(int, float); 1335 1336 static bool isIndexOf(Target, Types...)(size_t i) 1337 { 1338 switch (i) { 1339 static foreach (tid, T; Types) 1340 case tid: return is(T == Target); 1341 default: return false; 1342 } 1343 } 1344 1345 assert(isIndexOf!(int, MySum.Types)(MySum(42).typeIndex)); 1346 assert(isIndexOf!(float, MySum.Types)(MySum(3.14).typeIndex)); 1347 } 1348 1349 // Type index for qualified SumTypes 1350 // Disabled in BetterC due to use of dynamic arrays 1351 version (D_BetterC) {} else 1352 @safe unittest { 1353 alias MySum = SumType!(const(int[]), int[]); 1354 1355 static bool isIndexOf(Target, Types...)(size_t i) 1356 { 1357 switch (i) { 1358 static foreach (tid, T; Types) 1359 case tid: return is(T == Target); 1360 default: return false; 1361 } 1362 } 1363 1364 int[] ma = [1, 2, 3]; 1365 // Construct as mutable and convert to const to get mismatched type + tag 1366 auto x = MySum(ma); 1367 const y = MySum(ma); 1368 auto z = const(MySum)(ma); 1369 1370 assert(isIndexOf!(int[], MySum.Types)(x.typeIndex)); 1371 assert(isIndexOf!(const(int[]), Map!(ConstOf, MySum.Types))(y.typeIndex)); 1372 assert(isIndexOf!(const(int[]), Map!(ConstOf, MySum.Types))(z.typeIndex)); 1373 } 1374 1375 // Type index for differently-qualified versions of the same SumType 1376 // Disabled in BetterC due to use of dynamic arrays 1377 version (D_BetterC) {} else 1378 @safe unittest { 1379 alias MySum = SumType!(const(int[]), int[]); 1380 1381 int[] ma = [1, 2, 3]; 1382 auto x = MySum(ma); 1383 const y = x; 1384 1385 assert(x.typeIndex == y.typeIndex); 1386 } 1387 1388 // @safe assignment to the only pointer in a SumType 1389 @safe unittest { 1390 SumType!(string, int) sm = 123; 1391 1392 assert(__traits(compiles, () @safe { 1393 sm = "this should be @safe"; 1394 })); 1395 } 1396 1397 // Pointers to local variables 1398 @safe unittest { 1399 enum haveDip1000 = __traits(compiles, () @safe { 1400 int n; 1401 int* p = &n; 1402 }); 1403 1404 static if (haveDip1000) { 1405 int n = 123; 1406 immutable int ni = 456; 1407 1408 SumType!(int*) s = &n; 1409 const SumType!(int*) sc = &n; 1410 immutable SumType!(int*) si = ∋ 1411 } 1412 } 1413 1414 // Dlang issue 22572: immutable member type with copy constructor 1415 @safe unittest { 1416 static struct CopyConstruct 1417 { 1418 this(ref inout CopyConstruct other) inout {} 1419 } 1420 1421 static immutable struct Value 1422 { 1423 CopyConstruct c; 1424 } 1425 1426 SumType!Value s; 1427 } 1428 1429 // Construction of inout-qualified SumTypes 1430 @safe unittest { 1431 static inout(SumType!(int[])) example(inout(int[]) arr) 1432 { 1433 return inout(SumType!(int[]))(arr); 1434 } 1435 } 1436 1437 // Assignment of struct with overloaded opAssign in CTFE 1438 @safe unittest { 1439 static struct HasOpAssign 1440 { 1441 void opAssign(HasOpAssign rhs) {} 1442 } 1443 1444 static SumType!HasOpAssign test() 1445 { 1446 SumType!HasOpAssign s; 1447 // Test both overloads 1448 s = HasOpAssign(); 1449 s = SumType!HasOpAssign(); 1450 return s; 1451 } 1452 1453 enum result = test(); 1454 } 1455 1456 /// True if `T` is an instance of the `SumType` template, otherwise false. 1457 private enum bool isSumTypeInstance(T) = is(T == SumType!Args, Args...); 1458 1459 @safe unittest { 1460 static struct Wrapper 1461 { 1462 SumType!int s; 1463 alias s this; 1464 } 1465 1466 assert(isSumTypeInstance!(SumType!int)); 1467 assert(!isSumTypeInstance!Wrapper); 1468 } 1469 1470 /// True if `T` is a [SumType] or implicitly converts to one, otherwise false. 1471 template isSumType(T) 1472 { 1473 static if (is(T : SumType!Args, Args...)) { 1474 enum isSumType = true; 1475 } else static if (is(T == struct) && __traits(getAliasThis, T).length > 0) { 1476 // Workaround for dlang issue 21975 1477 import std.traits: ReturnType; 1478 1479 alias AliasThisType = ReturnType!((T t) => 1480 __traits(getMember, t, __traits(getAliasThis, T)[0]) 1481 ); 1482 enum isSumType = .isSumType!AliasThisType; 1483 } else { 1484 enum isSumType = false; 1485 } 1486 } 1487 1488 /// 1489 @safe unittest { 1490 static struct ConvertsToSumType 1491 { 1492 SumType!int payload; 1493 alias payload this; 1494 } 1495 1496 static struct ContainsSumType 1497 { 1498 SumType!int payload; 1499 } 1500 1501 assert(isSumType!(SumType!int)); 1502 assert(isSumType!ConvertsToSumType); 1503 assert(!isSumType!ContainsSumType); 1504 } 1505 1506 @safe unittest { 1507 static struct AliasThisVar(T) 1508 { 1509 SumType!T payload; 1510 alias payload this; 1511 } 1512 1513 static struct AliasThisFunc(T) 1514 { 1515 SumType!T payload; 1516 ref get() { return payload; } 1517 alias get this; 1518 } 1519 1520 static assert(isSumType!(AliasThisVar!int)); 1521 static assert(isSumType!(AliasThisFunc!int)); 1522 } 1523 1524 /** 1525 * Calls a type-appropriate function with the value held in a [SumType]. 1526 * 1527 * For each possible type the [SumType] can hold, the given handlers are 1528 * checked, in order, to see whether they accept a single argument of that type. 1529 * The first one that does is chosen as the match for that type. (Note that the 1530 * first match may not always be the most exact match. 1531 * See [#avoiding-unintentional-matches|"Avoiding unintentional matches"] for 1532 * one common pitfall.) 1533 * 1534 * Every type must have a matching handler, and every handler must match at 1535 * least one type. This is enforced at compile time. 1536 * 1537 * Handlers may be functions, delegates, or objects with `opCall` overloads. If 1538 * a function with more than one overload is given as a handler, all of the 1539 * overloads are considered as potential matches. 1540 * 1541 * Templated handlers are also accepted, and will match any type for which they 1542 * can be [implicitly instantiated](https://dlang.org/glossary.html#ifti). See 1543 * [sumtype#introspection-based-matching|"Introspection-based matching"] for an 1544 * example of templated handler usage. 1545 * 1546 * If multiple [SumType]s are passed to `match`, their values are passed to the 1547 * handlers as separate arguments, and matching is done for each possible 1548 * combination of value types. See [#multiple-dispatch|"Multiple dispatch"] for 1549 * an example. 1550 * 1551 * Returns: 1552 * The value returned from the handler that matches the currently-held type. 1553 * 1554 * See_Also: `std.variant.visit` 1555 */ 1556 template match(handlers...) 1557 { 1558 import std.typecons: Yes; 1559 1560 /** 1561 * The actual `match` function. 1562 * 1563 * Params: 1564 * args = One or more [SumType] objects. 1565 */ 1566 auto ref match(SumTypes...)(auto ref SumTypes args) 1567 if (allSatisfy!(isSumType, SumTypes) && args.length > 0) 1568 { 1569 return matchImpl!(Yes.exhaustive, handlers)(args); 1570 } 1571 } 1572 1573 /** $(H3 Avoiding unintentional matches) 1574 * 1575 * Sometimes, implicit conversions may cause a handler to match more types than 1576 * intended. The example below shows two solutions to this problem. 1577 */ 1578 @safe unittest { 1579 alias Number = SumType!(double, int); 1580 1581 Number x; 1582 1583 // Problem: because int implicitly converts to double, the double 1584 // handler is used for both types, and the int handler never matches. 1585 assert(!__traits(compiles, 1586 x.match!( 1587 (double d) => "got double", 1588 (int n) => "got int" 1589 ) 1590 )); 1591 1592 // Solution 1: put the handler for the "more specialized" type (in this 1593 // case, int) before the handler for the type it converts to. 1594 assert(__traits(compiles, 1595 x.match!( 1596 (int n) => "got int", 1597 (double d) => "got double" 1598 ) 1599 )); 1600 1601 // Solution 2: use a template that only accepts the exact type it's 1602 // supposed to match, instead of any type that implicitly converts to it. 1603 alias exactly(T, alias fun) = function (arg) { 1604 static assert(is(typeof(arg) == T)); 1605 return fun(arg); 1606 }; 1607 1608 // Now, even if we put the double handler first, it will only be used for 1609 // doubles, not ints. 1610 assert(__traits(compiles, 1611 x.match!( 1612 exactly!(double, d => "got double"), 1613 exactly!(int, n => "got int") 1614 ) 1615 )); 1616 } 1617 1618 /** $(H3 Multiple dispatch) 1619 * 1620 * Pattern matching can be performed on multiple `SumType`s at once by passing 1621 * handlers with multiple arguments. This usually leads to more concise code 1622 * than using nested calls to `match`, as show below. 1623 */ 1624 @safe unittest { 1625 struct Point2D { double x, y; } 1626 struct Point3D { double x, y, z; } 1627 1628 alias Point = SumType!(Point2D, Point3D); 1629 1630 version (none) { 1631 // This function works, but the code is ugly and repetitive. 1632 // It uses three separate calls to match! 1633 @safe pure nothrow @nogc 1634 bool sameDimensions(Point p1, Point p2) 1635 { 1636 return p1.match!( 1637 (Point2D _) => p2.match!( 1638 (Point2D _) => true, 1639 _ => false 1640 ), 1641 (Point3D _) => p2.match!( 1642 (Point3D _) => true, 1643 _ => false 1644 ) 1645 ); 1646 } 1647 } 1648 1649 // This version is much nicer. 1650 @safe pure nothrow @nogc 1651 bool sameDimensions(Point p1, Point p2) 1652 { 1653 alias doMatch = match!( 1654 (Point2D _1, Point2D _2) => true, 1655 (Point3D _1, Point3D _2) => true, 1656 (_1, _2) => false 1657 ); 1658 1659 return doMatch(p1, p2); 1660 } 1661 1662 Point a = Point2D(1, 2); 1663 Point b = Point2D(3, 4); 1664 Point c = Point3D(5, 6, 7); 1665 Point d = Point3D(8, 9, 0); 1666 1667 assert( sameDimensions(a, b)); 1668 assert( sameDimensions(c, d)); 1669 assert(!sameDimensions(a, c)); 1670 assert(!sameDimensions(d, b)); 1671 } 1672 1673 version (D_Exceptions) 1674 /** 1675 * Attempts to call a type-appropriate function with the value held in a 1676 * [SumType], and throws on failure. 1677 * 1678 * Matches are chosen using the same rules as [match], but are not required to 1679 * be exhaustive—in other words, a type (or combination of types) is allowed to 1680 * have no matching handler. If a type without a handler is encountered at 1681 * runtime, a [MatchException] is thrown. 1682 * 1683 * Not available when compiled with `-betterC`. 1684 * 1685 * Returns: 1686 * The value returned from the handler that matches the currently-held type, 1687 * if a handler was given for that type. 1688 * 1689 * Throws: 1690 * [MatchException], if the currently-held type has no matching handler. 1691 * 1692 * See_Also: `std.variant.tryVisit` 1693 */ 1694 template tryMatch(handlers...) 1695 { 1696 import std.typecons: No; 1697 1698 /** 1699 * The actual `tryMatch` function. 1700 * 1701 * Params: 1702 * args = One or more [SumType] objects. 1703 */ 1704 auto ref tryMatch(SumTypes...)(auto ref SumTypes args) 1705 if (allSatisfy!(isSumType, SumTypes) && args.length > 0) 1706 { 1707 return matchImpl!(No.exhaustive, handlers)(args); 1708 } 1709 } 1710 1711 version (D_Exceptions) 1712 /** 1713 * Thrown by [tryMatch] when an unhandled type is encountered. 1714 * 1715 * Not available when compiled with `-betterC`. 1716 */ 1717 class MatchException : Exception 1718 { 1719 /// 1720 pure @safe @nogc nothrow 1721 this(string msg, string file = __FILE__, size_t line = __LINE__) 1722 { 1723 super(msg, file, line); 1724 } 1725 } 1726 1727 /** 1728 * True if `handler` is a potential match for `Ts`, otherwise false. 1729 * 1730 * See the documentation for [match] for a full explanation of how matches are 1731 * chosen. 1732 */ 1733 template canMatch(alias handler, Ts...) 1734 if (Ts.length > 0) 1735 { 1736 enum canMatch = is(typeof((ref Ts args) => handler(args))); 1737 } 1738 1739 /// 1740 @safe unittest { 1741 alias handleInt = (int i) => "got an int"; 1742 1743 assert( canMatch!(handleInt, int)); 1744 assert(!canMatch!(handleInt, string)); 1745 } 1746 1747 // Includes all overloads of the given handler 1748 @safe unittest { 1749 static struct OverloadSet 1750 { 1751 static void fun(int n) {} 1752 static void fun(double d) {} 1753 } 1754 1755 assert(canMatch!(OverloadSet.fun, int)); 1756 assert(canMatch!(OverloadSet.fun, double)); 1757 } 1758 1759 // Like aliasSeqOf!(iota(n)), but works in BetterC 1760 private template Iota(size_t n) 1761 { 1762 static if (n == 0) { 1763 alias Iota = AliasSeq!(); 1764 } else { 1765 alias Iota = AliasSeq!(Iota!(n - 1), n - 1); 1766 } 1767 } 1768 1769 @safe unittest { 1770 assert(is(Iota!0 == AliasSeq!())); 1771 assert(Iota!1 == AliasSeq!(0)); 1772 assert(Iota!3 == AliasSeq!(0, 1, 2)); 1773 } 1774 1775 private template matchImpl(Flag!"exhaustive" exhaustive, handlers...) 1776 { 1777 auto ref matchImpl(SumTypes...)(auto ref SumTypes args) 1778 if (allSatisfy!(isSumType, SumTypes) && args.length > 0) 1779 { 1780 // Single dispatch (fast path) 1781 static if (args.length == 1) { 1782 /* When there's only one argument, the caseId is just that 1783 * argument's tag, so there's no need to use TagTuple. 1784 */ 1785 enum handlerArgs(size_t caseId) = 1786 "args[0].get!(SumTypes[0].Types[" ~ toCtString!caseId ~ "])()"; 1787 1788 alias valueTypes(size_t caseId) = 1789 typeof(args[0].get!(SumTypes[0].Types[caseId])()); 1790 1791 enum numCases = SumTypes[0].Types.length; 1792 // Multiple dispatch (slow path) 1793 } else { 1794 alias typeCounts = Map!(typeCount, SumTypes); 1795 alias stride(size_t i) = .stride!(i, typeCounts); 1796 alias TagTuple = .TagTuple!typeCounts; 1797 1798 alias handlerArgs(size_t caseId) = .handlerArgs!(caseId, typeCounts); 1799 1800 /* An AliasSeq of the types of the member values in the argument 1801 * list returned by `handlerArgs!caseId`. 1802 * 1803 * Note that these are the actual (that is, qualified) types of the 1804 * member values, which may not be the same as the types listed in 1805 * the arguments' `.Types` properties. 1806 */ 1807 template valueTypes(size_t caseId) 1808 { 1809 enum tags = TagTuple.fromCaseId(caseId); 1810 1811 template getType(size_t i) 1812 { 1813 enum tid = tags[i]; 1814 alias T = SumTypes[i].Types[tid]; 1815 alias getType = typeof(args[i].get!T()); 1816 } 1817 1818 alias valueTypes = Map!(getType, Iota!(tags.length)); 1819 } 1820 1821 /* The total number of cases is 1822 * 1823 * Π SumTypes[i].Types.length for 0 ≤ i < SumTypes.length 1824 * 1825 * Conveniently, this is equal to stride!(SumTypes.length), so we 1826 * can use that function to compute it. 1827 */ 1828 enum numCases = stride!(SumTypes.length); 1829 } 1830 1831 /* Guaranteed to never be a valid handler index, since 1832 * handlers.length <= size_t.max. 1833 */ 1834 enum noMatch = size_t.max; 1835 1836 // An array that maps caseIds to handler indices ("hids"). 1837 enum matches = () { 1838 size_t[numCases] matches; 1839 1840 // Workaround for dlang issue 19561 1841 foreach (ref match; matches) { 1842 match = noMatch; 1843 } 1844 1845 static foreach (caseId; 0 .. numCases) { 1846 static foreach (hid, handler; handlers) { 1847 static if (canMatch!(handler, valueTypes!caseId)) { 1848 if (matches[caseId] == noMatch) { 1849 matches[caseId] = hid; 1850 } 1851 } 1852 } 1853 } 1854 1855 return matches; 1856 }(); 1857 1858 import std.algorithm.searching: canFind; 1859 1860 // Check for unreachable handlers 1861 static foreach (hid, handler; handlers) { 1862 static assert(matches[].canFind(hid), 1863 "`handlers[" ~ toCtString!hid ~ "]` " ~ 1864 "of type `" ~ ( __traits(isTemplate, handler) 1865 ? "template" 1866 : typeof(handler).stringof 1867 ) ~ "` " ~ 1868 "never matches. Perhaps the handler failed to compile" 1869 ); 1870 } 1871 1872 // Workaround for dlang issue 19993 1873 enum handlerName(size_t hid) = "handler" ~ toCtString!hid; 1874 1875 static foreach (size_t hid, handler; handlers) { 1876 mixin("alias ", handlerName!hid, " = handler;"); 1877 } 1878 1879 // Single dispatch (fast path) 1880 static if (args.length == 1) { 1881 immutable argsId = args[0].tag; 1882 // Multiple dispatch (slow path) 1883 } else { 1884 immutable argsId = TagTuple(args).toCaseId; 1885 } 1886 1887 final switch (argsId) { 1888 static foreach (caseId; 0 .. numCases) { 1889 case caseId: 1890 static if (matches[caseId] != noMatch) { 1891 return mixin(handlerName!(matches[caseId]), "(", handlerArgs!caseId, ")"); 1892 } else { 1893 static if(exhaustive) { 1894 static assert(false, 1895 "No matching handler for types `" ~ valueTypes!caseId.stringof ~ "`"); 1896 } else { 1897 throw new MatchException( 1898 "No matching handler for types `" ~ valueTypes!caseId.stringof ~ "`"); 1899 } 1900 } 1901 } 1902 } 1903 1904 assert(false, "unreachable"); 1905 } 1906 } 1907 1908 /* A TagTuple represents a single possible set of tags that the arguments to 1909 * `matchImpl` could have at runtime. 1910 * 1911 * Because D does not allow a struct to be the controlling expression 1912 * of a switch statement, we cannot dispatch on the TagTuple directly. 1913 * Instead, we must map each TagTuple to a unique integer and generate 1914 * a case label for each of those integers. 1915 * 1916 * This mapping is implemented in `fromCaseId` and `toCaseId`. It uses 1917 * the same technique that's used to map index tuples to memory offsets 1918 * in a multidimensional static array. 1919 * 1920 * For example, when `args` consists of two SumTypes with two member 1921 * types each, the TagTuples corresponding to each case label are: 1922 * 1923 * case 0: TagTuple([0, 0]) 1924 * case 1: TagTuple([1, 0]) 1925 * case 2: TagTuple([0, 1]) 1926 * case 3: TagTuple([1, 1]) 1927 * 1928 * When there is only one argument, the caseId is equal to that 1929 * argument's tag. 1930 */ 1931 private struct TagTuple(typeCounts...) 1932 { 1933 size_t[typeCounts.length] tags; 1934 alias tags this; 1935 1936 alias stride(size_t i) = .stride!(i, typeCounts); 1937 1938 invariant { 1939 static foreach (i; 0 .. tags.length) { 1940 assert(tags[i] < typeCounts[i]); 1941 } 1942 } 1943 1944 this(SumTypes...)(ref const SumTypes args) 1945 if (allSatisfy!(isSumType, SumTypes) && args.length == typeCounts.length) 1946 { 1947 static foreach (i; 0 .. tags.length) { 1948 tags[i] = args[i].tag; 1949 } 1950 } 1951 1952 static TagTuple fromCaseId(size_t caseId) 1953 { 1954 TagTuple result; 1955 1956 // Most-significant to least-significant 1957 static foreach_reverse (i; 0 .. result.length) { 1958 result[i] = caseId / stride!i; 1959 caseId %= stride!i; 1960 } 1961 1962 return result; 1963 } 1964 1965 size_t toCaseId() 1966 { 1967 size_t result; 1968 1969 static foreach (i; 0 .. tags.length) { 1970 result += tags[i] * stride!i; 1971 } 1972 1973 return result; 1974 } 1975 } 1976 1977 /* The number that the dim-th argument's tag is multiplied by when 1978 * converting TagTuples to and from case indices ("caseIds"). 1979 * 1980 * Named by analogy to the stride that the dim-th index into a 1981 * multidimensional static array is multiplied by to calculate the 1982 * offset of a specific element. 1983 */ 1984 private size_t stride(size_t dim, lengths...)() 1985 { 1986 import core.checkedint: mulu; 1987 1988 size_t result = 1; 1989 bool overflow = false; 1990 1991 static foreach (i; 0 .. dim) { 1992 result = mulu(result, lengths[i], overflow); 1993 } 1994 1995 /* The largest number matchImpl uses, numCases, is calculated with 1996 * stride!(SumTypes.length), so as long as this overflow check 1997 * passes, we don't need to check for overflow anywhere else. 1998 */ 1999 assert(!overflow, "Integer overflow"); 2000 return result; 2001 } 2002 2003 // Predicate for staticMap 2004 private enum typeCount(SumType) = SumType.Types.length; 2005 2006 /* A list of arguments to be passed to a handler needed for the case 2007 * labeled with `caseId`. 2008 */ 2009 template handlerArgs(size_t caseId, typeCounts...) 2010 { 2011 enum tags = TagTuple!typeCounts.fromCaseId(caseId); 2012 2013 alias handlerArgs = AliasSeq!(); 2014 2015 static foreach (i; 0 .. tags.length) { 2016 handlerArgs = AliasSeq!( 2017 handlerArgs, 2018 "args[" ~ toCtString!i ~ "].get!(SumTypes[" ~ toCtString!i ~ "]" ~ 2019 ".Types[" ~ toCtString!(tags[i]) ~ "])(), " 2020 ); 2021 } 2022 } 2023 2024 // Matching 2025 @safe unittest { 2026 alias MySum = SumType!(int, float); 2027 2028 MySum x = MySum(42); 2029 MySum y = MySum(3.14); 2030 2031 assert(x.match!((int v) => true, (float v) => false)); 2032 assert(y.match!((int v) => false, (float v) => true)); 2033 } 2034 2035 // Missing handlers 2036 @safe unittest { 2037 alias MySum = SumType!(int, float); 2038 2039 MySum x = MySum(42); 2040 2041 assert(!__traits(compiles, x.match!((int x) => true))); 2042 assert(!__traits(compiles, x.match!())); 2043 } 2044 2045 // Handlers with qualified parameters 2046 // Disabled in BetterC due to use of dynamic arrays 2047 version (D_BetterC) {} else 2048 @safe unittest { 2049 alias MySum = SumType!(int[], float[]); 2050 2051 MySum x = MySum([1, 2, 3]); 2052 MySum y = MySum([1.0, 2.0, 3.0]); 2053 2054 assert(x.match!((const(int[]) v) => true, (const(float[]) v) => false)); 2055 assert(y.match!((const(int[]) v) => false, (const(float[]) v) => true)); 2056 } 2057 2058 // Handlers for qualified types 2059 // Disabled in BetterC due to use of dynamic arrays 2060 version (D_BetterC) {} else 2061 @safe unittest { 2062 alias MySum = SumType!(immutable(int[]), immutable(float[])); 2063 2064 MySum x = MySum([1, 2, 3]); 2065 2066 assert(x.match!((immutable(int[]) v) => true, (immutable(float[]) v) => false)); 2067 assert(x.match!((const(int[]) v) => true, (const(float[]) v) => false)); 2068 // Tail-qualified parameters 2069 assert(x.match!((immutable(int)[] v) => true, (immutable(float)[] v) => false)); 2070 assert(x.match!((const(int)[] v) => true, (const(float)[] v) => false)); 2071 // Generic parameters 2072 assert(x.match!((immutable v) => true)); 2073 assert(x.match!((const v) => true)); 2074 // Unqualified parameters 2075 assert(!__traits(compiles, 2076 x.match!((int[] v) => true, (float[] v) => false) 2077 )); 2078 } 2079 2080 // Delegate handlers 2081 // Disabled in BetterC due to use of closures 2082 version (D_BetterC) {} else 2083 @safe unittest { 2084 alias MySum = SumType!(int, float); 2085 2086 int answer = 42; 2087 MySum x = MySum(42); 2088 MySum y = MySum(3.14); 2089 2090 assert(x.match!((int v) => v == answer, (float v) => v == answer)); 2091 assert(!y.match!((int v) => v == answer, (float v) => v == answer)); 2092 } 2093 2094 version (unittest) { 2095 version (D_BetterC) { 2096 // std.math.isClose depends on core.runtime.math, so use a 2097 // libc-based version for testing with -betterC 2098 @safe pure @nogc nothrow 2099 private bool isClose(double lhs, double rhs) 2100 { 2101 import core.stdc.math: fabs; 2102 2103 return fabs(lhs - rhs) < 1e-5; 2104 } 2105 } else { 2106 import std.math: isClose; 2107 } 2108 } 2109 2110 // Generic handler 2111 @safe unittest { 2112 alias MySum = SumType!(int, float); 2113 2114 MySum x = MySum(42); 2115 MySum y = MySum(3.14); 2116 2117 assert(x.match!(v => v*2) == 84); 2118 assert(y.match!(v => v*2).isClose(6.28)); 2119 } 2120 2121 // Fallback to generic handler 2122 // Disabled in BetterC due to use of std.conv.to 2123 version (D_BetterC) {} else 2124 @safe unittest { 2125 import std.conv: to; 2126 2127 alias MySum = SumType!(int, float, string); 2128 2129 MySum x = MySum(42); 2130 MySum y = MySum("42"); 2131 2132 assert(x.match!((string v) => v.to!int, v => v*2) == 84); 2133 assert(y.match!((string v) => v.to!int, v => v*2) == 42); 2134 } 2135 2136 // Multiple non-overlapping generic handlers 2137 @safe unittest { 2138 import std.array: staticArray; 2139 2140 alias MySum = SumType!(int, float, int[], char[]); 2141 2142 static ints = staticArray([1, 2, 3]); 2143 static chars = staticArray(['a', 'b', 'c']); 2144 2145 MySum x = MySum(42); 2146 MySum y = MySum(3.14); 2147 MySum z = MySum(ints[]); 2148 MySum w = MySum(chars[]); 2149 2150 assert(x.match!(v => v*2, v => v.length) == 84); 2151 assert(y.match!(v => v*2, v => v.length).isClose(6.28)); 2152 assert(w.match!(v => v*2, v => v.length) == 3); 2153 assert(z.match!(v => v*2, v => v.length) == 3); 2154 } 2155 2156 // Structural matching 2157 @safe unittest { 2158 static struct S1 { int x; } 2159 static struct S2 { int y; } 2160 alias MySum = SumType!(S1, S2); 2161 2162 MySum a = MySum(S1(0)); 2163 MySum b = MySum(S2(0)); 2164 2165 assert(a.match!(s1 => s1.x + 1, s2 => s2.y - 1) == 1); 2166 assert(b.match!(s1 => s1.x + 1, s2 => s2.y - 1) == -1); 2167 } 2168 2169 // Separate opCall handlers 2170 @safe unittest { 2171 static struct IntHandler 2172 { 2173 bool opCall(int arg) 2174 { 2175 return true; 2176 } 2177 } 2178 2179 static struct FloatHandler 2180 { 2181 bool opCall(float arg) 2182 { 2183 return false; 2184 } 2185 } 2186 2187 alias MySum = SumType!(int, float); 2188 2189 MySum x = MySum(42); 2190 MySum y = MySum(3.14); 2191 2192 assert(x.match!(IntHandler.init, FloatHandler.init)); 2193 assert(!y.match!(IntHandler.init, FloatHandler.init)); 2194 } 2195 2196 // Compound opCall handler 2197 @safe unittest { 2198 static struct CompoundHandler 2199 { 2200 bool opCall(int arg) 2201 { 2202 return true; 2203 } 2204 2205 bool opCall(float arg) 2206 { 2207 return false; 2208 } 2209 } 2210 2211 alias MySum = SumType!(int, float); 2212 2213 MySum x = MySum(42); 2214 MySum y = MySum(3.14); 2215 2216 assert(x.match!(CompoundHandler.init)); 2217 assert(!y.match!(CompoundHandler.init)); 2218 } 2219 2220 // Ordered matching 2221 @safe unittest { 2222 alias MySum = SumType!(int, float); 2223 2224 MySum x = MySum(42); 2225 2226 assert(x.match!((int v) => true, v => false)); 2227 } 2228 2229 // Non-exhaustive matching 2230 version (D_Exceptions) 2231 @system unittest { 2232 import std.exception: assertThrown, assertNotThrown; 2233 2234 alias MySum = SumType!(int, float); 2235 2236 MySum x = MySum(42); 2237 MySum y = MySum(3.14); 2238 2239 assertNotThrown!MatchException(x.tryMatch!((int n) => true)); 2240 assertThrown!MatchException(y.tryMatch!((int n) => true)); 2241 } 2242 2243 // Non-exhaustive matching in @safe code 2244 version (D_Exceptions) 2245 @safe unittest { 2246 SumType!(int, float) x; 2247 2248 assert(__traits(compiles, 2249 x.tryMatch!( 2250 (int n) => n + 1, 2251 ) 2252 )); 2253 2254 } 2255 2256 // Handlers with ref parameters 2257 @safe unittest { 2258 alias Value = SumType!(long, double); 2259 2260 auto value = Value(3.14); 2261 2262 value.match!( 2263 (long) {}, 2264 (ref double d) { d *= 2; } 2265 ); 2266 2267 assert(value.get!double.isClose(6.28)); 2268 } 2269 2270 // Handlers that return by ref 2271 @safe unittest { 2272 SumType!int x = 123; 2273 2274 x.match!(ref (ref int n) => n) = 456; 2275 2276 assert(x.match!((int n) => n == 456)); 2277 } 2278 2279 // Unreachable handlers 2280 @safe unittest { 2281 alias MySum = SumType!(int, string); 2282 2283 MySum s; 2284 2285 assert(!__traits(compiles, 2286 s.match!( 2287 (int _) => 0, 2288 (string _) => 1, 2289 (double _) => 2 2290 ) 2291 )); 2292 2293 assert(!__traits(compiles, 2294 s.match!( 2295 _ => 0, 2296 (int _) => 1 2297 ) 2298 )); 2299 } 2300 2301 // Unsafe handlers 2302 @system unittest { 2303 SumType!int x; 2304 alias unsafeHandler = (int x) @system { return; }; 2305 2306 assert(!__traits(compiles, () @safe { 2307 x.match!unsafeHandler; 2308 })); 2309 2310 assert(__traits(compiles, () @system { 2311 return x.match!unsafeHandler; 2312 })); 2313 } 2314 2315 // Overloaded handlers 2316 @safe unittest { 2317 static struct OverloadSet 2318 { 2319 static string fun(int i) { return "int"; } 2320 static string fun(double d) { return "double"; } 2321 } 2322 2323 alias MySum = SumType!(int, double); 2324 2325 MySum a = 42; 2326 MySum b = 3.14; 2327 2328 assert(a.match!(OverloadSet.fun) == "int"); 2329 assert(b.match!(OverloadSet.fun) == "double"); 2330 } 2331 2332 // Overload sets that include SumType arguments 2333 @safe unittest { 2334 alias Inner = SumType!(int, double); 2335 alias Outer = SumType!(Inner, string); 2336 2337 static struct OverloadSet 2338 { 2339 @safe: 2340 static string fun(int i) { return "int"; } 2341 static string fun(double d) { return "double"; } 2342 static string fun(string s) { return "string"; } 2343 static string fun(Inner i) { return i.match!fun; } 2344 static string fun(Outer o) { return o.match!fun; } 2345 } 2346 2347 Outer a = Inner(42); 2348 Outer b = Inner(3.14); 2349 Outer c = "foo"; 2350 2351 assert(OverloadSet.fun(a) == "int"); 2352 assert(OverloadSet.fun(b) == "double"); 2353 assert(OverloadSet.fun(c) == "string"); 2354 } 2355 2356 // Overload sets with ref arguments 2357 @safe unittest { 2358 static struct OverloadSet 2359 { 2360 static void fun(ref int i) { i = 42; } 2361 static void fun(ref double d) { d = 3.14; } 2362 } 2363 2364 alias MySum = SumType!(int, double); 2365 2366 MySum x = 0; 2367 MySum y = 0.0; 2368 2369 x.match!(OverloadSet.fun); 2370 y.match!(OverloadSet.fun); 2371 2372 assert(x.match!((value) => is(typeof(value) == int) && value == 42)); 2373 assert(y.match!((value) => is(typeof(value) == double) && value == 3.14)); 2374 } 2375 2376 // Overload sets with templates 2377 @safe unittest { 2378 import std.traits: isNumeric; 2379 2380 static struct OverloadSet 2381 { 2382 static string fun(string arg) 2383 { 2384 return "string"; 2385 } 2386 2387 static string fun(T)(T arg) 2388 if (isNumeric!T) 2389 { 2390 return "numeric"; 2391 } 2392 } 2393 2394 alias MySum = SumType!(int, string); 2395 2396 MySum x = 123; 2397 MySum y = "hello"; 2398 2399 assert(x.match!(OverloadSet.fun) == "numeric"); 2400 assert(y.match!(OverloadSet.fun) == "string"); 2401 } 2402 2403 // Github issue #24 2404 @safe unittest { 2405 assert(__traits(compiles, () @nogc { 2406 int acc = 0; 2407 SumType!int(1).match!((int x) => acc += x); 2408 })); 2409 } 2410 2411 // Github issue #31 2412 @safe unittest { 2413 assert(__traits(compiles, () @nogc { 2414 int acc = 0; 2415 2416 SumType!(int, string)(1).match!( 2417 (int x) => acc += x, 2418 (string _) => 0, 2419 ); 2420 })); 2421 } 2422 2423 // Types that `alias this` a SumType 2424 @safe unittest { 2425 static struct A {} 2426 static struct B {} 2427 static struct D { SumType!(A, B) value; alias value this; } 2428 2429 assert(__traits(compiles, D().match!(_ => true))); 2430 } 2431 2432 // Multiple dispatch 2433 @safe unittest { 2434 alias MySum = SumType!(int, string); 2435 2436 static int fun(MySum x, MySum y) 2437 { 2438 import std.meta: Args = AliasSeq; 2439 2440 return Args!(x, y).match!( 2441 (int xv, int yv) => 0, 2442 (string xv, int yv) => 1, 2443 (int xv, string yv) => 2, 2444 (string xv, string yv) => 3 2445 ); 2446 } 2447 2448 assert(fun(MySum(0), MySum(0)) == 0); 2449 assert(fun(MySum(""), MySum(0)) == 1); 2450 assert(fun(MySum(0), MySum("")) == 2); 2451 assert(fun(MySum(""), MySum("")) == 3); 2452 } 2453 2454 // inout SumTypes 2455 @safe unittest { 2456 assert(__traits(compiles, { 2457 inout(int[]) fun(inout(SumType!(int[])) x) 2458 { 2459 return x.match!((inout(int[]) a) => a); 2460 } 2461 })); 2462 } 2463 2464 // return ref 2465 // issue: https://issues.dlang.org/show_bug.cgi?id=23101 2466 // Only test on compiler versions >= 2.100, to avoid compiler bugs 2467 static if (haveDip1000 && __VERSION__ >= 2100) 2468 @safe unittest { 2469 assert(!__traits(compiles, () { 2470 SumType!(int, string) st; 2471 return st.match!( 2472 function int* (string x) => null, 2473 function int* (return ref int i) => &i, 2474 ); 2475 })); 2476 2477 SumType!(int, string) st; 2478 assert(__traits(compiles, () { 2479 return st.match!( 2480 function int* (string x) => null, 2481 function int* (return ref int i) => &i, 2482 ); 2483 })); 2484 } 2485 2486 private void destroyIfOwner(T)(ref T value) 2487 { 2488 static if (hasElaborateDestructor!T) { 2489 destroy(value); 2490 } 2491 } 2492 2493 static if (__traits(compiles, { import std.traits: DeducedParameterType; })) { 2494 import std.traits: DeducedParameterType; 2495 } else { 2496 /** 2497 * The parameter type deduced by IFTI when an expression of type T is passed as 2498 * an argument to a template function. 2499 * 2500 * For all types other than pointer and slice types, `DeducedParameterType!T` 2501 * is the same as `T`. For pointer and slice types, it is `T` with the 2502 * outer-most layer of qualifiers dropped. 2503 */ 2504 private template DeducedParameterType(T) 2505 { 2506 import std.traits: Unqual; 2507 2508 static if (is(T == U*, U) || is(T == U[], U)) 2509 alias DeducedParameterType = Unqual!T; 2510 else 2511 alias DeducedParameterType = T; 2512 } 2513 2514 @safe unittest 2515 { 2516 static assert(is(DeducedParameterType!(const(int)) == const(int))); 2517 static assert(is(DeducedParameterType!(const(int[2])) == const(int[2]))); 2518 2519 static assert(is(DeducedParameterType!(const(int*)) == const(int)*)); 2520 static assert(is(DeducedParameterType!(const(int[])) == const(int)[])); 2521 } 2522 2523 @safe unittest 2524 { 2525 static struct NoCopy 2526 { 2527 @disable this(this); 2528 } 2529 2530 static assert(is(DeducedParameterType!NoCopy == NoCopy)); 2531 } 2532 2533 @safe unittest 2534 { 2535 static assert(is(DeducedParameterType!(inout(int[])) == inout(int)[])); 2536 } 2537 }