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