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