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