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