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