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