1 /++ 2 A sum type for modern D. 3 4 [SumType] is an alternative to `std.variant.Algebraic` that features: 5 6 $(LIST 7 * [match|improved pattern-matching] 8 * full attribute correctness (`pure`, `@safe`, `@nogc`, and `nothrow` are 9 inferred whenever possible) 10 * a type-safe and memory-safe API compatible with DIP 1000 (`scope`) 11 * no dependency on runtime type information (`TypeInfo`) 12 ) 13 14 License: MIT 15 Authors: Paul Backus, Atila Neves 16 +/ 17 module sumtype; 18 19 /// $(H3 Basic usage) 20 version (D_BetterC) { 21 } else 22 @safe unittest { 23 import std.math : approxEqual; 24 25 struct Fahrenheit { 26 double degrees; 27 } 28 29 struct Celsius { 30 double degrees; 31 } 32 33 struct Kelvin { 34 double degrees; 35 } 36 37 alias Temperature = SumType!(Fahrenheit, Celsius, Kelvin); 38 39 // Construct from any of the member types. 40 Temperature t1 = Fahrenheit(98.6); 41 Temperature t2 = Celsius(100); 42 Temperature t3 = Kelvin(273); 43 44 // Use pattern matching to access the value. 45 pure @safe @nogc nothrow Fahrenheit toFahrenheit(Temperature t) { 46 return Fahrenheit(t.match!((Fahrenheit f) => f.degrees, 47 (Celsius c) => c.degrees * 9.0 / 5 + 32, (Kelvin k) => k.degrees * 9.0 / 5 - 459.4)); 48 } 49 50 assert(toFahrenheit(t1).degrees.approxEqual(98.6)); 51 assert(toFahrenheit(t2).degrees.approxEqual(212)); 52 assert(toFahrenheit(t3).degrees.approxEqual(32)); 53 54 // Use ref to modify the value in place. 55 pure @safe @nogc nothrow void freeze(ref Temperature t) { 56 t.match!((ref Fahrenheit f) => f.degrees = 32, 57 (ref Celsius c) => c.degrees = 0, (ref Kelvin k) => k.degrees = 273); 58 } 59 60 freeze(t1); 61 assert(toFahrenheit(t1).degrees.approxEqual(32)); 62 63 // Use a catch-all handler to give a default result. 64 pure @safe @nogc nothrow bool isFahrenheit(Temperature t) { 65 return t.match!((Fahrenheit f) => true, _ => false); 66 } 67 68 assert(isFahrenheit(t1)); 69 assert(!isFahrenheit(t2)); 70 assert(!isFahrenheit(t3)); 71 } 72 73 /** $(H3 Introspection-based matching) 74 * 75 * In the `length` and `horiz` functions below, the handlers for `match` do not 76 * specify the types of their arguments. Instead, matching is done based on how 77 * the argument is used in the body of the handler: any type with `x` and `y` 78 * properties will be matched by the `rect` handlers, and any type with `r` and 79 * `theta` properties will be matched by the `polar` handlers. 80 */ 81 version (D_BetterC) { 82 } else 83 @safe unittest { 84 import std.math : approxEqual, cos, PI, sqrt; 85 86 struct Rectangular { 87 double x, y; 88 } 89 90 struct Polar { 91 double r, theta; 92 } 93 94 alias Vector = SumType!(Rectangular, Polar); 95 96 pure @safe @nogc nothrow double length(Vector v) { 97 return v.match!(rect => sqrt(rect.x ^^ 2 + rect.y ^^ 2), polar => polar.r); 98 } 99 100 pure @safe @nogc nothrow double horiz(Vector v) { 101 return v.match!(rect => rect.x, polar => polar.r * cos(polar.theta)); 102 } 103 104 Vector u = Rectangular(1, 1); 105 Vector v = Polar(1, PI / 4); 106 107 assert(length(u).approxEqual(sqrt(2.0))); 108 assert(length(v).approxEqual(1)); 109 assert(horiz(u).approxEqual(1)); 110 assert(horiz(v).approxEqual(sqrt(0.5))); 111 } 112 113 /** $(H3 Arithmetic expression evaluator) 114 * 115 * This example makes use of the special placeholder type `This` to define a 116 * [https://en.wikipedia.org/wiki/Recursive_data_type|recursive data type]: an 117 * [https://en.wikipedia.org/wiki/Abstract_syntax_tree|abstract syntax tree] for 118 * representing simple arithmetic expressions. 119 */ 120 version (D_BetterC) { 121 } else 122 @safe unittest { 123 import std.functional : partial; 124 import std.traits : EnumMembers; 125 import std.typecons : Tuple; 126 127 enum Op : string { 128 Plus = "+", 129 Minus = "-", 130 Times = "*", 131 Div = "/" 132 } 133 134 // An expression is either 135 // - a number, 136 // - a variable, or 137 // - a binary operation combining two sub-expressions. 138 alias Expr = SumType!(double, string, Tuple!(Op, "op", This*, "lhs", This*, "rhs")); 139 140 // Shorthand for Tuple!(Op, "op", Expr*, "lhs", Expr*, "rhs"), 141 // the Tuple type above with Expr substituted for This. 142 alias BinOp = Expr.Types[2]; 143 144 // Factory function for number expressions 145 pure @safe Expr* num(double value) { 146 return new Expr(value); 147 } 148 149 // Factory function for variable expressions 150 pure @safe Expr* var(string name) { 151 return new Expr(name); 152 } 153 154 // Factory function for binary operation expressions 155 pure @safe Expr* binOp(Op op, Expr* lhs, Expr* rhs) { 156 return new Expr(BinOp(op, lhs, rhs)); 157 } 158 159 // Convenience wrappers for creating BinOp expressions 160 alias sum = partial!(binOp, Op.Plus); 161 alias diff = partial!(binOp, Op.Minus); 162 alias prod = partial!(binOp, Op.Times); 163 alias quot = partial!(binOp, Op.Div); 164 165 // Evaluate expr, looking up variables in env 166 pure @safe nothrow double eval(Expr expr, double[string] env) { 167 return expr.match!((double num) => num, (string var) => env[var], (BinOp bop) { 168 double lhs = eval(*bop.lhs, env); 169 double rhs = eval(*bop.rhs, env); 170 final switch (bop.op) { 171 static foreach (op; EnumMembers!Op) { 172 case op: 173 return mixin("lhs" ~ op ~ "rhs"); 174 } 175 } 176 }); 177 } 178 179 // Return a "pretty-printed" representation of expr 180 @safe string pprint(Expr expr) { 181 import std.format; 182 183 return expr.match!((double num) => "%g".format(num), (string var) => var, 184 (BinOp bop) => "(%s %s %s)".format(pprint(*bop.lhs), bop.op, pprint(*bop.rhs))); 185 } 186 187 Expr* myExpr = sum(var("a"), prod(num(2), var("b"))); 188 double[string] myEnv = ["a" : 3, "b" : 4, "c" : 7]; 189 190 assert(eval(*myExpr, myEnv) == 11); 191 assert(pprint(*myExpr) == "(a + (2 * b))"); 192 } 193 194 /// `This` placeholder, for use in self-referential types. 195 public import std.variant : This; 196 197 import std.meta : NoDuplicates; 198 199 /** 200 * A tagged union that can hold a single value from any of a specified set of 201 * types. 202 * 203 * The value in a `SumType` can be operated on using [match|pattern matching]. 204 * 205 * To avoid ambiguity, duplicate types are not allowed (but see the 206 * [sumtype#basic-usage|"basic usage" example] for a workaround). 207 * 208 * The special type `This` can be used as a placeholder to create 209 * self-referential types, just like with `Algebraic`. See the 210 * [sumtype#arithmetic-expression-evaluator|"Arithmetic expression evaluator" example] for 211 * usage. 212 * 213 * A `SumType` is initialized by default to hold the `.init` value of its 214 * first member type, just like a regular union. The version identifier 215 * `SumTypeNoDefaultCtor` can be used to disable this behavior. 216 * 217 * Bugs: 218 * Types with `@disable`d `opEquals` overloads cannot be members of a 219 * `SumType`. 220 * 221 * See_Also: `std.variant.Algebraic` 222 */ 223 struct SumType(TypeArgs...) 224 if (is(NoDuplicates!TypeArgs == TypeArgs) && TypeArgs.length > 0) { 225 import std.meta : AliasSeq, Filter, anySatisfy, allSatisfy, staticIndexOf; 226 import std.traits : hasElaborateCopyConstructor, hasElaborateDestructor; 227 import std.traits : isAssignable, isCopyable, isStaticArray; 228 229 /// The types a `SumType` can hold. 230 alias Types = AliasSeq!(ReplaceTypeUnless!(isSumType, This, typeof(this), TypeArgs)); 231 232 private: 233 234 enum bool canHoldTag(T) = Types.length <= T.max; 235 alias unsignedInts = AliasSeq!(ubyte, ushort, uint, ulong); 236 237 alias Tag = Filter!(canHoldTag, unsignedInts)[0]; 238 239 union Storage { 240 template memberName(T) if (staticIndexOf!(T, Types) >= 0) { 241 mixin("enum memberName = `values_", staticIndexOf!(T, Types), "`;"); 242 } 243 244 static foreach (tid, T; Types) { 245 mixin("Types[tid] ", memberName!T, ";"); 246 } 247 } 248 249 Tag tag; 250 Storage storage; 251 252 @trusted ref inout(T) get(T)() inout if (staticIndexOf!(T, Types) >= 0) { 253 enum tid = staticIndexOf!(T, Types); 254 assert(tag == tid); 255 return __traits(getMember, storage, Storage.memberName!T); 256 } 257 258 public: 259 260 static foreach (tid, T; Types) { 261 /// Constructs a `SumType` holding a specific value. 262 this()(auto ref T value) { 263 import core.lifetime : forward; 264 265 static if (isCopyable!T) { 266 mixin("Storage newStorage = { ", Storage.memberName!T, ": value };"); 267 } else { 268 mixin("Storage newStorage = { ", Storage.memberName!T, " : forward!value };"); 269 } 270 271 storage = newStorage; 272 tag = tid; 273 } 274 275 static if (isCopyable!T) { 276 /// ditto 277 this()(auto ref const(T) value) const { 278 mixin("const(Storage) newStorage = { ", Storage.memberName!T, ": value };"); 279 storage = newStorage; 280 tag = tid; 281 } 282 283 /// ditto 284 this()(auto ref immutable(T) value) immutable { 285 mixin("immutable(Storage) newStorage = { ", Storage.memberName!T, ": value };"); 286 storage = newStorage; 287 tag = tid; 288 } 289 } else { 290 @disable this(const(T) value) const; 291 @disable this(immutable(T) value) immutable; 292 } 293 } 294 295 static if (allSatisfy!(isCopyable, Types)) { 296 static if (anySatisfy!(hasElaborateCopyConstructor, Types)) { 297 /// Constructs a `SumType` that's a copy of another `SumType` 298 this(ref SumType other) { 299 storage = other.match!((ref value) { 300 alias T = typeof(value); 301 302 mixin("Storage newStorage = { ", Storage.memberName!T, ": value };"); 303 return newStorage; 304 }); 305 306 tag = other.tag; 307 } 308 309 /// ditto 310 this(ref const(SumType) other) const { 311 import std.meta : staticMap; 312 import std.traits : ConstOf; 313 314 storage = other.match!((ref value) { 315 alias OtherTypes = staticMap!(ConstOf, Types); 316 enum tid = staticIndexOf!(typeof(value), OtherTypes); 317 alias T = Types[tid]; 318 319 mixin("const(Storage) newStorage = { ", Storage.memberName!T, ": value };"); 320 return newStorage; 321 }); 322 323 tag = other.tag; 324 } 325 326 /// ditto 327 this(ref immutable(SumType) other) immutable { 328 import std.meta : staticMap; 329 import std.traits : ImmutableOf; 330 331 storage = other.match!((ref value) { 332 alias OtherTypes = staticMap!(ImmutableOf, Types); 333 enum tid = staticIndexOf!(typeof(value), OtherTypes); 334 alias T = Types[tid]; 335 336 mixin("immutable(Storage) newStorage = { ", Storage.memberName!T, ": value };"); 337 return newStorage; 338 }); 339 340 tag = other.tag; 341 } 342 } 343 } else { 344 /// `@disable`d if any member type is non-copyable. 345 @disable this(this); 346 } 347 348 version (SumTypeNoDefaultCtor) { 349 @disable this(); 350 } 351 352 static foreach (tid, T; Types) { 353 static if (isAssignable!T) { 354 /** 355 * Assigns a value to a `SumType`. 356 * 357 * Assigning to a `SumType` is `@system` if any of the 358 * `SumType`'s members contain pointers or references, since 359 * those members may be reachable through external references, 360 * and overwriting them could therefore lead to memory 361 * corruption. 362 * 363 * An individual assignment can be `@trusted` if the caller can 364 * guarantee that there are no outstanding references to $(I any) 365 * of the `SumType`'s members when the assignment occurs. 366 */ 367 void opAssign()(auto ref T rhs) { 368 import core.lifetime : forward; 369 import std.traits : hasIndirections, hasNested; 370 import std.meta : Or = templateOr; 371 372 enum mayContainPointers = anySatisfy!(Or!(hasIndirections, hasNested), Types); 373 374 static if (mayContainPointers) { 375 cast(void)() @system {}(); 376 } 377 378 this.match!((ref value) { 379 static if (hasElaborateDestructor!(typeof(value))) { 380 destroy(value); 381 } 382 }); 383 384 mixin("Storage newStorage = { ", Storage.memberName!T, ": forward!rhs };"); 385 storage = newStorage; 386 tag = tid; 387 } 388 } 389 } 390 391 static if (allSatisfy!(isAssignable, Types)) { 392 static if (allSatisfy!(isCopyable, Types)) { 393 /** 394 * Copies the value from another `SumType` into this one. 395 * 396 * See the value-assignment overload for details on `@safe`ty. 397 * 398 * Copy assignment is `@disable`d if any of `Types` is non-copyable. 399 */ 400 void opAssign(ref SumType rhs) { 401 rhs.match!((ref value) { this = value; }); 402 } 403 } else { 404 @disable void opAssign(ref SumType rhs); 405 } 406 407 /** 408 * Moves the value from another `SumType` into this one. 409 * 410 * See the value-assignment overload for details on `@safe`ty. 411 */ 412 void opAssign(SumType rhs) { 413 import core.lifetime : move; 414 415 rhs.match!((ref value) { this = move(value); }); 416 } 417 } 418 419 /** 420 * Compares two `SumType`s for equality. 421 * 422 * Two `SumType`s are equal if they are the same kind of `SumType`, they 423 * contain values of the same type, and those values are equal. 424 */ 425 bool opEquals(const SumType rhs) const { 426 return this.match!((ref value) { 427 return rhs.match!((ref rhsValue) { 428 static if (is(typeof(value) == typeof(rhsValue))) { 429 return value == rhsValue; 430 } else { 431 return false; 432 } 433 }); 434 }); 435 } 436 437 // Workaround for dlang issue 19407 438 static if (__traits(compiles, anySatisfy!(hasElaborateDestructor, Types))) { 439 // If possible, include the destructor only when it's needed 440 private enum includeDtor = anySatisfy!(hasElaborateDestructor, Types); 441 } else { 442 // If we can't tell, always include it, even when it does nothing 443 private enum includeDtor = true; 444 } 445 446 static if (includeDtor) { 447 /// Calls the destructor of the `SumType`'s current value. 448 ~this() { 449 this.match!((ref value) { 450 static if (hasElaborateDestructor!(typeof(value))) { 451 destroy(value); 452 } 453 }); 454 } 455 } 456 457 invariant { 458 this.match!((ref value) { 459 static if (is(typeof(value) == class)) { 460 if (value !is null) { 461 assert(value); 462 } 463 } else static if (is(typeof(value) == struct)) { 464 assert(&value); 465 } 466 }); 467 } 468 469 static if (allSatisfy!(isCopyable, Types)) { 470 /** 471 * Returns a string representation of a `SumType`'s value. 472 * 473 * Not available when compiled with `-betterC`. 474 */ 475 version (D_BetterC) { 476 } else 477 string toString(this T)() { 478 import std.conv : text; 479 480 return this.match!((auto ref value) { return value.text; }); 481 } 482 } 483 } 484 485 // Construction 486 @safe unittest { 487 alias MySum = SumType!(int, float); 488 489 assert(__traits(compiles, MySum(42))); 490 assert(__traits(compiles, MySum(3.14))); 491 } 492 493 // Assignment 494 @safe unittest { 495 alias MySum = SumType!(int, float); 496 497 MySum x = MySum(42); 498 499 assert(__traits(compiles, x = 3.14)); 500 } 501 502 // Self assignment 503 @safe unittest { 504 alias MySum = SumType!(int, float); 505 506 MySum x = MySum(42); 507 MySum y = MySum(3.14); 508 509 assert(__traits(compiles, y = x)); 510 } 511 512 // Equality 513 @safe unittest { 514 alias MySum = SumType!(int, float); 515 516 MySum x = MySum(123); 517 MySum y = MySum(123); 518 MySum z = MySum(456); 519 MySum w = MySum(123.0); 520 MySum v = MySum(456.0); 521 522 assert(x == y); 523 assert(x != z); 524 assert(x != w); 525 assert(x != v); 526 } 527 528 // Imported types 529 @safe unittest { 530 import std.typecons : Tuple; 531 532 assert(__traits(compiles, { alias MySum = SumType!(Tuple!(int, int)); })); 533 } 534 535 // const and immutable types 536 @safe unittest { 537 assert(__traits(compiles, { 538 alias MySum = SumType!(const(int[]), immutable(float[])); 539 })); 540 } 541 542 // Recursive types 543 @safe unittest { 544 alias MySum = SumType!(This*); 545 assert(is(MySum.Types[0] == MySum*)); 546 } 547 548 // Allowed types 549 @safe unittest { 550 import std.meta : AliasSeq; 551 552 alias MySum = SumType!(int, float, This*); 553 554 assert(is(MySum.Types == AliasSeq!(int, float, MySum*))); 555 } 556 557 // Works alongside Algebraic 558 version (D_BetterC) { 559 } else 560 @safe unittest { 561 import std.variant; 562 563 alias Bar = Algebraic!(This*); 564 565 assert(is(Bar.AllowedTypes[0] == Bar*)); 566 } 567 568 // Types with destructors and postblits 569 @system unittest { 570 int copies; 571 572 static struct Test { 573 bool initialized = false; 574 int* copiesPtr; 575 576 this(this) { 577 (*copiesPtr)++; 578 } 579 580 ~this() { 581 if (initialized) 582 (*copiesPtr)--; 583 } 584 } 585 586 alias MySum = SumType!(int, Test); 587 588 Test t = Test(true, &copies); 589 590 { 591 MySum x = t; 592 assert(copies == 1); 593 } 594 assert(copies == 0); 595 596 { 597 MySum x = 456; 598 assert(copies == 0); 599 } 600 assert(copies == 0); 601 602 { 603 MySum x = t; 604 assert(copies == 1); 605 x = 456; 606 assert(copies == 0); 607 } 608 609 { 610 MySum x = 456; 611 assert(copies == 0); 612 x = t; 613 assert(copies == 1); 614 } 615 616 { 617 MySum x = t; 618 MySum y = x; 619 assert(copies == 2); 620 } 621 622 { 623 MySum x = t; 624 MySum y; 625 y = x; 626 assert(copies == 2); 627 } 628 } 629 630 // Doesn't destroy reference types 631 version (D_BetterC) { 632 } else 633 @system unittest { 634 bool destroyed; 635 636 class C { 637 ~this() { 638 destroyed = true; 639 } 640 } 641 642 struct S { 643 ~this() { 644 } 645 } 646 647 alias MySum = SumType!(S, C); 648 649 C c = new C(); 650 { 651 MySum x = c; 652 destroyed = false; 653 } 654 assert(!destroyed); 655 656 { 657 MySum x = c; 658 destroyed = false; 659 x = S(); 660 assert(!destroyed); 661 } 662 } 663 664 // Types with @disable this() 665 @safe unittest { 666 static struct NoInit { 667 @disable this(); 668 } 669 670 alias MySum = SumType!(NoInit, int); 671 672 assert(!__traits(compiles, MySum())); 673 assert(__traits(compiles, MySum(42))); 674 } 675 676 // const SumTypes 677 @safe unittest { 678 assert(__traits(compiles, const(SumType!(int[]))([1, 2, 3]))); 679 } 680 681 // Equality of const SumTypes 682 @safe unittest { 683 alias MySum = SumType!int; 684 685 assert(__traits(compiles, const(MySum)(123) == const(MySum)(456))); 686 } 687 688 // Compares reference types using value equality 689 @safe unittest { 690 import std.array : staticArray; 691 692 static struct Field { 693 } 694 695 static struct Struct { 696 Field[] fields; 697 } 698 699 alias MySum = SumType!Struct; 700 701 static arr1 = staticArray([Field()]); 702 static arr2 = staticArray([Field()]); 703 704 auto a = MySum(Struct(arr1[])); 705 auto b = MySum(Struct(arr2[])); 706 707 assert(a == b); 708 } 709 710 // toString 711 version (D_BetterC) { 712 } else 713 @safe unittest { 714 import std.conv : text; 715 716 static struct Int { 717 int i; 718 } 719 720 static struct Double { 721 double d; 722 } 723 724 alias Sum = SumType!(Int, Double); 725 726 assert(Sum(Int(42)).text == Int(42).text, Sum(Int(42)).text); 727 assert(Sum(Double(33.3)).text == Double(33.3).text, Sum(Double(33.3)).text); 728 assert((const(Sum)(Int(42))).text == (const(Int)(42)).text, (const(Sum)(Int(42))).text); 729 } 730 731 // Github issue #16 732 version (D_BetterC) { 733 } else 734 @safe unittest { 735 alias Node = SumType!(This[], string); 736 737 // override inference of @system attribute for cyclic functions 738 assert((() @trusted => Node([Node([Node("x")])]) == Node([Node([Node("x")])]))()); 739 } 740 741 // Github issue #16 with const 742 version (D_BetterC) { 743 } else 744 @safe unittest { 745 alias Node = SumType!(const(This)[], string); 746 747 // override inference of @system attribute for cyclic functions 748 assert((() @trusted => Node([Node([Node("x")])]) == Node([Node([Node("x")])]))()); 749 } 750 751 // Stale pointers 752 version (D_BetterC) { 753 } else 754 @system unittest { 755 alias MySum = SumType!(ubyte, void*[2]); 756 757 MySum x = [null, cast(void*) 0x12345678]; 758 void** p = &x.get!(void*[2])[1]; 759 x = ubyte(123); 760 761 assert(*p != cast(void*) 0x12345678); 762 } 763 764 // Exception-safe assignment 765 version (D_BetterC) { 766 } else 767 @safe unittest { 768 static struct A { 769 int value = 123; 770 } 771 772 static struct B { 773 int value = 456; 774 this(this) { 775 throw new Exception("oops"); 776 } 777 } 778 779 alias MySum = SumType!(A, B); 780 781 MySum x; 782 try { 783 x = B(); 784 } catch (Exception e) { 785 } 786 787 assert((x.tag == 0 && x.get!A.value == 123) || (x.tag == 1 && x.get!B.value == 456)); 788 } 789 790 // Types with @disable this(this) 791 @safe unittest { 792 import std.algorithm.mutation : move; 793 794 static struct NoCopy { 795 @disable this(this); 796 } 797 798 alias MySum = SumType!NoCopy; 799 800 NoCopy lval = NoCopy(); 801 802 MySum x = NoCopy(); 803 MySum y = NoCopy(); 804 805 assert(__traits(compiles, SumType!NoCopy(NoCopy()))); 806 assert(!__traits(compiles, SumType!NoCopy(lval))); 807 808 assert(__traits(compiles, y = NoCopy())); 809 assert(__traits(compiles, y = move(x))); 810 assert(!__traits(compiles, y = lval)); 811 assert(!__traits(compiles, y = x)); 812 } 813 814 // Github issue #22 815 version (D_BetterC) { 816 } else 817 @safe unittest { 818 import std.typecons; 819 820 assert(__traits(compiles, { 821 static struct A { 822 SumType!(Nullable!int) a = Nullable!int.init; 823 } 824 })); 825 } 826 827 // Static arrays of structs with postblits 828 version (D_BetterC) { 829 } else 830 @safe unittest { 831 static struct S { 832 int n; 833 this(this) { 834 n++; 835 } 836 } 837 838 assert(__traits(compiles, SumType!(S[1])())); 839 840 SumType!(S[1]) x = [S(0)]; 841 SumType!(S[1]) y = x; 842 843 auto xval = x.get!(S[1])[0].n; 844 auto yval = y.get!(S[1])[0].n; 845 846 assert(xval != yval); 847 } 848 849 // Replacement does not happen inside SumType 850 version (D_BetterC) { 851 } else 852 @safe unittest { 853 import std.typecons : Tuple; 854 855 alias A = Tuple!(This*, SumType!(This*))[SumType!(This*, string)[This]]; 856 alias TR = ReplaceTypeUnless!(isSumType, This, int, A); 857 static assert(is(TR == Tuple!(int*, SumType!(This*))[SumType!(This * , string)[int]])); 858 } 859 860 // Supports nested self-referential SumTypes 861 @safe unittest { 862 import std.typecons : Tuple, Flag; 863 864 alias Nat = SumType!(Flag!"0", Tuple!(This*)); 865 static assert(__traits(compiles, SumType!(Nat))); 866 static assert(__traits(compiles, SumType!(Nat*, Tuple!(This*, This*)))); 867 } 868 869 // Doesn't call @system postblits in @safe code 870 @safe unittest { 871 static struct SystemCopy { 872 @system this(this) { 873 } 874 } 875 876 SystemCopy original; 877 878 assert(!__traits(compiles, () @safe { SumType!SystemCopy copy = original; })); 879 880 assert(!__traits(compiles, () @safe { 881 SumType!SystemCopy copy; 882 copy = original; 883 })); 884 } 885 886 // Doesn't overwrite pointers in @safe code 887 @safe unittest { 888 alias MySum = SumType!(int*, int); 889 890 MySum x; 891 892 assert(!__traits(compiles, () @safe { x = 123; })); 893 894 assert(!__traits(compiles, () @safe { x = MySum(123); })); 895 } 896 897 // Types with invariants 898 version (D_BetterC) { 899 } else 900 @system unittest { 901 import std.exception : assertThrown; 902 import core.exception : AssertError; 903 904 struct S { 905 int i; 906 invariant { 907 assert(i >= 0); 908 } 909 } 910 911 class C { 912 int i; 913 invariant { 914 assert(i >= 0); 915 } 916 } 917 918 SumType!S x; 919 x.match!((ref v) { v.i = -1; }); 920 assertThrown!AssertError(assert(&x)); 921 922 SumType!C y = new C(); 923 y.match!((ref v) { v.i = -1; }); 924 assertThrown!AssertError(assert(&y)); 925 } 926 927 // Calls value postblit on self-assignment 928 @safe unittest { 929 static struct S { 930 int n; 931 this(this) { 932 n++; 933 } 934 } 935 936 SumType!S x = S(); 937 SumType!S y; 938 y = x; 939 940 auto xval = x.get!S.n; 941 auto yval = y.get!S.n; 942 943 assert(xval != yval); 944 } 945 946 // Github issue #29 947 @safe unittest { 948 assert(__traits(compiles, () @safe { 949 alias A = SumType!string; 950 951 @safe A createA(string arg) { 952 return A(arg); 953 } 954 955 @safe void test() { 956 A a = createA(""); 957 } 958 })); 959 } 960 961 version (none) { 962 // Known bug; needs fix for dlang issue 19902 963 // Types with copy constructors 964 @safe unittest { 965 static struct S { 966 int n; 967 this(ref return scope inout S other) inout { 968 n++; 969 } 970 } 971 972 SumType!S x = S(); 973 SumType!S y = x; 974 975 auto xval = x.get!S.n; 976 auto yval = y.get!S.n; 977 978 assert(xval != yval); 979 } 980 } 981 982 version (none) { 983 // Known bug; needs fix for dlang issue 19458 984 // Types with disabled opEquals 985 @safe unittest { 986 static struct S { 987 @disable bool opEquals(const S rhs) const; 988 } 989 990 assert(__traits(compiles, SumType!S(S()))); 991 } 992 } 993 994 version (none) { 995 // Known bug; needs fix for dlang issue 19458 996 @safe unittest { 997 static struct S { 998 int i; 999 bool opEquals(S rhs) { 1000 return i == rhs.i; 1001 } 1002 } 1003 1004 assert(__traits(compiles, SumType!S(S(123)))); 1005 } 1006 } 1007 1008 /// True if `T` is an instance of `SumType`, otherwise false. 1009 enum isSumType(T) = is(T == SumType!Args, Args...); 1010 1011 unittest { 1012 static struct Wrapper { 1013 SumType!int s; 1014 alias s this; 1015 } 1016 1017 assert(isSumType!(SumType!int)); 1018 assert(!isSumType!Wrapper); 1019 } 1020 1021 /** 1022 * Calls a type-appropriate function with the value held in a [SumType]. 1023 * 1024 * For each possible type the [SumType] can hold, the given handlers are 1025 * checked, in order, to see whether they accept a single argument of that type. 1026 * The first one that does is chosen as the match for that type. 1027 * 1028 * Implicit conversions are not taken into account, except between 1029 * differently-qualified versions of the same type. For example, a handler that 1030 * accepts a `long` will not match the type `int`, but a handler that accepts a 1031 * `const(int)[]` will match the type `immutable(int)[]`. 1032 * 1033 * Every type must have a matching handler, and every handler must match at 1034 * least one type. This is enforced at compile time. 1035 * 1036 * Handlers may be functions, delegates, or objects with opCall overloads. If a 1037 * function with more than one overload is given as a handler, all of the 1038 * overloads are considered as potential matches. 1039 * 1040 * Templated handlers are also accepted, and will match any type for which they 1041 * can be [implicitly instantiated](https://dlang.org/glossary.html#ifti). See 1042 * [sumtype#introspection-based-matching|"Introspection-based matching"] for an 1043 * example of templated handler usage. 1044 * 1045 * Returns: 1046 * The value returned from the handler that matches the currently-held type. 1047 * 1048 * See_Also: `std.variant.visit` 1049 */ 1050 template match(handlers...) { 1051 import std.typecons : Yes; 1052 1053 /** 1054 * The actual `match` function. 1055 * 1056 * Params: 1057 * self = A [SumType] object 1058 */ 1059 auto match(Self)(auto ref Self self) 1060 if (is(Self : SumType!TypeArgs, TypeArgs...)) { 1061 return self.matchImpl!(Yes.exhaustive, handlers); 1062 } 1063 } 1064 1065 /** 1066 * Attempts to call a type-appropriate function with the value held in a 1067 * [SumType], and throws on failure. 1068 * 1069 * Matches are chosen using the same rules as [match], but are not required to 1070 * be exhaustive—in other words, a type is allowed to have no matching handler. 1071 * If a type without a handler is encountered at runtime, a [MatchException] 1072 * is thrown. 1073 * 1074 * Not available when compiled with `-betterC`. 1075 * 1076 * Returns: 1077 * The value returned from the handler that matches the currently-held type, 1078 * if a handler was given for that type. 1079 * 1080 * Throws: 1081 * [MatchException], if the currently-held type has no matching handler. 1082 * 1083 * See_Also: `std.variant.tryVisit` 1084 */ 1085 version (D_Exceptions) template tryMatch(handlers...) { 1086 import std.typecons : No; 1087 1088 /** 1089 * The actual `tryMatch` function. 1090 * 1091 * Params: 1092 * self = A [SumType] object 1093 */ 1094 auto tryMatch(Self)(auto ref Self self) 1095 if (is(Self : SumType!TypeArgs, TypeArgs...)) { 1096 return self.matchImpl!(No.exhaustive, handlers); 1097 } 1098 } 1099 1100 /** 1101 * Thrown by [tryMatch] when an unhandled type is encountered. 1102 * 1103 * Not available when compiled with `-betterC`. 1104 */ 1105 version (D_Exceptions) class MatchException : Exception { 1106 pure @safe @nogc nothrow this(string msg, string file = __FILE__, size_t line = __LINE__) { 1107 super(msg, file, line); 1108 } 1109 } 1110 1111 /** 1112 * Checks whether a handler can match a given type. 1113 * 1114 * See the documentation for [match] for a full explanation of how matches are 1115 * chosen. 1116 */ 1117 template canMatch(alias handler, T) { 1118 private bool canMatchImpl() { 1119 import std.traits : hasMember, isCallable, isSomeFunction, Parameters; 1120 1121 // Include overloads even when called from outside of matchImpl 1122 alias realHandler = handlerWithOverloads!handler; 1123 1124 // immutable recursively overrides all other qualifiers, so the 1125 // right-hand side is true if and only if the two types are the 1126 // same when qualifiers are ignored. 1127 enum sameUpToQuals(T, U) = is(immutable(T) == immutable(U)); 1128 1129 alias opCallOverloads(T) = __traits(getOverloads, T, "opCall"); 1130 1131 bool result = false; 1132 1133 static if (is(typeof((T arg) { realHandler(arg); }(T.init)))) { 1134 // Regular handlers 1135 static if (isCallable!realHandler) { 1136 // Functions and delegates 1137 static if (isSomeFunction!realHandler) { 1138 static if (sameUpToQuals!(T, Parameters!realHandler[0])) { 1139 result = true; 1140 } 1141 // Objects with overloaded opCall 1142 } else static if (hasMember!(typeof(realHandler), "opCall")) { 1143 static foreach (overload; opCallOverloads!(typeof(realHandler))) { 1144 static if (sameUpToQuals!(T, Parameters!overload[0])) { 1145 result = true; 1146 } 1147 } 1148 } 1149 // Generic handlers 1150 } else { 1151 result = true; 1152 } 1153 } 1154 1155 return result; 1156 } 1157 1158 /// True if `handler` is a potential match for `T`, otherwise false. 1159 enum bool canMatch = canMatchImpl; 1160 } 1161 1162 // Includes all overloads of the given handler 1163 @safe unittest { 1164 static struct OverloadSet { 1165 static void fun(int n) { 1166 } 1167 1168 static void fun(double d) { 1169 } 1170 } 1171 1172 assert(canMatch!(OverloadSet.fun, int)); 1173 assert(canMatch!(OverloadSet.fun, double)); 1174 } 1175 1176 import std.traits : isFunction; 1177 1178 // An AliasSeq of a function's overloads 1179 private template FunctionOverloads(alias fun) if (isFunction!fun) { 1180 import std.meta : AliasSeq; 1181 1182 alias FunctionOverloads = AliasSeq!(__traits(getOverloads, __traits(parent, 1183 fun), __traits(identifier, fun), true // include template overloads 1184 )); 1185 } 1186 1187 // A function that dispatches to the overloads of `fun` 1188 private template overloadDispatcher(alias fun) if (isFunction!fun) { 1189 import std.traits : Parameters, ReturnType; 1190 1191 // Merge overloads into a local overload set 1192 static foreach (overload; FunctionOverloads!fun) { 1193 alias overloadSet = overload; 1194 } 1195 1196 alias overloadDispatcher = (ref arg) => overloadSet(arg); 1197 } 1198 1199 // A handler that includes all overloads of the original handler, if applicable 1200 private template handlerWithOverloads(alias handler) { 1201 // Delegates and function pointers can't have overloads 1202 static if (isFunction!handler && FunctionOverloads!handler.length > 1) { 1203 alias handlerWithOverloads = overloadDispatcher!handler; 1204 } else { 1205 alias handlerWithOverloads = handler; 1206 } 1207 } 1208 1209 import std.typecons : Flag; 1210 1211 private template matchImpl(Flag!"exhaustive" exhaustive, handlers...) { 1212 auto matchImpl(Self)(auto ref Self self) 1213 if (is(Self : SumType!TypeArgs, TypeArgs...)) { 1214 import std.meta : staticMap; 1215 1216 alias Types = self.Types; 1217 enum noMatch = size_t.max; 1218 1219 alias realHandlers = staticMap!(handlerWithOverloads, handlers); 1220 1221 pure size_t[Types.length] getHandlerIndices() { 1222 size_t[Types.length] indices; 1223 1224 version (D_BetterC) { 1225 // Workaround for dlang issue 19561 1226 foreach (ref index; indices) { 1227 index = noMatch; 1228 } 1229 } else { 1230 indices[] = noMatch; 1231 } 1232 1233 static foreach (tid, T; Types) { 1234 static foreach (hid, handler; realHandlers) { 1235 static if (canMatch!(handler, typeof(self.get!T()))) { 1236 if (indices[tid] == noMatch) { 1237 indices[tid] = hid; 1238 } 1239 } 1240 } 1241 } 1242 1243 return indices; 1244 } 1245 1246 enum handlerIndices = getHandlerIndices; 1247 1248 import std.algorithm.searching : canFind; 1249 1250 // Check for unreachable handlers 1251 static foreach (hid, handler; realHandlers) { 1252 static assert(handlerIndices[].canFind(hid), "handler `" ~ __traits(identifier, 1253 handler) ~ "` " ~ "of type `" ~ (__traits(isTemplate, handler) 1254 ? "template" : typeof(handler).stringof) ~ "` " ~ "never matches"); 1255 } 1256 1257 final switch (self.tag) { 1258 static foreach (tid, T; Types) { 1259 case tid: 1260 static if (handlerIndices[tid] != noMatch) { 1261 return realHandlers[handlerIndices[tid]](self.get!T); 1262 } else { 1263 static if (exhaustive) { 1264 static assert(false, "No matching handler for type `" ~ T.stringof ~ "`"); 1265 } else { 1266 throw new MatchException("No matching handler for type `" ~ T.stringof ~ "`"); 1267 } 1268 } 1269 } 1270 } 1271 1272 assert(false); // unreached 1273 } 1274 } 1275 1276 // Matching 1277 @safe unittest { 1278 alias MySum = SumType!(int, float); 1279 1280 MySum x = MySum(42); 1281 MySum y = MySum(3.14); 1282 1283 assert(x.match!((int v) => true, (float v) => false)); 1284 assert(y.match!((int v) => false, (float v) => true)); 1285 } 1286 1287 // Missing handlers 1288 @safe unittest { 1289 alias MySum = SumType!(int, float); 1290 1291 MySum x = MySum(42); 1292 1293 assert(!__traits(compiles, x.match!((int x) => true))); 1294 assert(!__traits(compiles, x.match!())); 1295 } 1296 1297 // No implicit converstion 1298 @safe unittest { 1299 alias MySum = SumType!(int, float); 1300 1301 MySum x = MySum(42); 1302 1303 assert(!__traits(compiles, x.match!((long v) => true, (float v) => false))); 1304 } 1305 1306 // Handlers with qualified parameters 1307 version (D_BetterC) { 1308 } else 1309 @safe unittest { 1310 alias MySum = SumType!(int[], float[]); 1311 1312 MySum x = MySum([1, 2, 3]); 1313 MySum y = MySum([1.0, 2.0, 3.0]); 1314 1315 assert(x.match!((const(int[]) v) => true, (const(float[]) v) => false)); 1316 assert(y.match!((const(int[]) v) => false, (const(float[]) v) => true)); 1317 } 1318 1319 // Handlers for qualified types 1320 version (D_BetterC) { 1321 } else 1322 @safe unittest { 1323 alias MySum = SumType!(immutable(int[]), immutable(float[])); 1324 1325 MySum x = MySum([1, 2, 3]); 1326 1327 assert(x.match!((immutable(int[]) v) => true, (immutable(float[]) v) => false)); 1328 assert(x.match!((const(int[]) v) => true, (const(float[]) v) => false)); 1329 // Tail-qualified parameters 1330 assert(x.match!((immutable(int)[] v) => true, (immutable(float)[] v) => false)); 1331 assert(x.match!((const(int)[] v) => true, (const(float)[] v) => false)); 1332 // Generic parameters 1333 assert(x.match!((immutable v) => true)); 1334 assert(x.match!((const v) => true)); 1335 // Unqualified parameters 1336 assert(!__traits(compiles, x.match!((int[] v) => true, (float[] v) => false))); 1337 } 1338 1339 // Delegate handlers 1340 version (D_BetterC) { 1341 } else 1342 @safe unittest { 1343 alias MySum = SumType!(int, float); 1344 1345 int answer = 42; 1346 MySum x = MySum(42); 1347 MySum y = MySum(3.14); 1348 1349 assert(x.match!((int v) => v == answer, (float v) => v == answer)); 1350 assert(!y.match!((int v) => v == answer, (float v) => v == answer)); 1351 } 1352 1353 version (unittest) { 1354 version (D_BetterC) { 1355 // std.math.approxEqual depends on core.runtime.math, so use a 1356 // libc-based version for testing with -betterC 1357 @safe pure @nogc nothrow private bool approxEqual(double lhs, double rhs) { 1358 import core.stdc.math : fabs; 1359 1360 return (lhs - rhs) < 1e-5; 1361 } 1362 } else { 1363 import std.math : approxEqual; 1364 } 1365 } 1366 1367 // Generic handler 1368 @safe unittest { 1369 alias MySum = SumType!(int, float); 1370 1371 MySum x = MySum(42); 1372 MySum y = MySum(3.14); 1373 1374 assert(x.match!(v => v * 2) == 84); 1375 assert(y.match!(v => v * 2).approxEqual(6.28)); 1376 } 1377 1378 // Fallback to generic handler 1379 version (D_BetterC) { 1380 } else 1381 @safe unittest { 1382 import std.conv : to; 1383 1384 alias MySum = SumType!(int, float, string); 1385 1386 MySum x = MySum(42); 1387 MySum y = MySum("42"); 1388 1389 assert(x.match!((string v) => v.to!int, v => v * 2) == 84); 1390 assert(y.match!((string v) => v.to!int, v => v * 2) == 42); 1391 } 1392 1393 // Multiple non-overlapping generic handlers 1394 @safe unittest { 1395 import std.array : staticArray; 1396 1397 alias MySum = SumType!(int, float, int[], char[]); 1398 1399 static ints = staticArray([1, 2, 3]); 1400 static chars = staticArray(['a', 'b', 'c']); 1401 1402 MySum x = MySum(42); 1403 MySum y = MySum(3.14); 1404 MySum z = MySum(ints[]); 1405 MySum w = MySum(chars[]); 1406 1407 assert(x.match!(v => v * 2, v => v.length) == 84); 1408 assert(y.match!(v => v * 2, v => v.length).approxEqual(6.28)); 1409 assert(w.match!(v => v * 2, v => v.length) == 3); 1410 assert(z.match!(v => v * 2, v => v.length) == 3); 1411 } 1412 1413 // Structural matching 1414 @safe unittest { 1415 static struct S1 { 1416 int x; 1417 } 1418 1419 static struct S2 { 1420 int y; 1421 } 1422 1423 alias MySum = SumType!(S1, S2); 1424 1425 MySum a = MySum(S1(0)); 1426 MySum b = MySum(S2(0)); 1427 1428 assert(a.match!(s1 => s1.x + 1, s2 => s2.y - 1) == 1); 1429 assert(b.match!(s1 => s1.x + 1, s2 => s2.y - 1) == -1); 1430 } 1431 1432 // Separate opCall handlers 1433 @safe unittest { 1434 static struct IntHandler { 1435 bool opCall(int arg) { 1436 return true; 1437 } 1438 } 1439 1440 static struct FloatHandler { 1441 bool opCall(float arg) { 1442 return false; 1443 } 1444 } 1445 1446 alias MySum = SumType!(int, float); 1447 1448 MySum x = MySum(42); 1449 MySum y = MySum(3.14); 1450 1451 assert(x.match!(IntHandler.init, FloatHandler.init)); 1452 assert(!y.match!(IntHandler.init, FloatHandler.init)); 1453 } 1454 1455 // Compound opCall handler 1456 @safe unittest { 1457 static struct CompoundHandler { 1458 bool opCall(int arg) { 1459 return true; 1460 } 1461 1462 bool opCall(float arg) { 1463 return false; 1464 } 1465 } 1466 1467 alias MySum = SumType!(int, float); 1468 1469 MySum x = MySum(42); 1470 MySum y = MySum(3.14); 1471 1472 assert(x.match!(CompoundHandler.init)); 1473 assert(!y.match!(CompoundHandler.init)); 1474 } 1475 1476 // Ordered matching 1477 @safe unittest { 1478 alias MySum = SumType!(int, float); 1479 1480 MySum x = MySum(42); 1481 1482 assert(x.match!((int v) => true, v => false)); 1483 } 1484 1485 // Non-exhaustive matching 1486 version (D_Exceptions) @system unittest { 1487 import std.exception : assertThrown, assertNotThrown; 1488 1489 alias MySum = SumType!(int, float); 1490 1491 MySum x = MySum(42); 1492 MySum y = MySum(3.14); 1493 1494 assertNotThrown!MatchException(x.tryMatch!((int n) => true)); 1495 assertThrown!MatchException(y.tryMatch!((int n) => true)); 1496 } 1497 1498 // Non-exhaustive matching in @safe code 1499 version (D_Exceptions) @safe unittest { 1500 SumType!(int, float) x; 1501 1502 assert(__traits(compiles, x.tryMatch!((int n) => n + 1,))); 1503 1504 } 1505 1506 // Handlers with ref parameters 1507 @safe unittest { 1508 import std.meta : staticIndexOf; 1509 1510 alias Value = SumType!(long, double); 1511 1512 auto value = Value(3.14); 1513 1514 value.match!((long) {}, (ref double d) { d *= 2; }); 1515 1516 assert(value.get!double.approxEqual(6.28)); 1517 } 1518 1519 // Unreachable handlers 1520 @safe unittest { 1521 alias MySum = SumType!(int, string); 1522 1523 MySum s; 1524 1525 assert(!__traits(compiles, s.match!((int _) => 0, (string _) => 1, (double _) => 2))); 1526 1527 assert(!__traits(compiles, s.match!(_ => 0, (int _) => 1))); 1528 } 1529 1530 // Unsafe handlers 1531 unittest { 1532 SumType!int x; 1533 alias unsafeHandler = (int x) @system { return; }; 1534 1535 assert(!__traits(compiles, () @safe { x.match!unsafeHandler; })); 1536 1537 assert(__traits(compiles, () @system { return x.match!unsafeHandler; })); 1538 } 1539 1540 // Overloaded handlers 1541 @safe unittest { 1542 static struct OverloadSet { 1543 static string fun(int i) { 1544 return "int"; 1545 } 1546 1547 static string fun(double d) { 1548 return "double"; 1549 } 1550 } 1551 1552 alias MySum = SumType!(int, double); 1553 1554 MySum a = 42; 1555 MySum b = 3.14; 1556 1557 assert(a.match!(OverloadSet.fun) == "int"); 1558 assert(b.match!(OverloadSet.fun) == "double"); 1559 } 1560 1561 // Overload sets that include SumType arguments 1562 @safe unittest { 1563 alias Inner = SumType!(int, double); 1564 alias Outer = SumType!(Inner, string); 1565 1566 static struct OverloadSet { 1567 @safe: 1568 static string fun(int i) { 1569 return "int"; 1570 } 1571 1572 static string fun(double d) { 1573 return "double"; 1574 } 1575 1576 static string fun(string s) { 1577 return "string"; 1578 } 1579 1580 static string fun(Inner i) { 1581 return i.match!fun; 1582 } 1583 1584 static string fun(Outer o) { 1585 return o.match!fun; 1586 } 1587 } 1588 1589 Outer a = Inner(42); 1590 Outer b = Inner(3.14); 1591 Outer c = "foo"; 1592 1593 assert(OverloadSet.fun(a) == "int"); 1594 assert(OverloadSet.fun(b) == "double"); 1595 assert(OverloadSet.fun(c) == "string"); 1596 } 1597 1598 // Overload sets with ref arguments 1599 @safe unittest { 1600 static struct OverloadSet { 1601 static void fun(ref int i) { 1602 i = 42; 1603 } 1604 1605 static void fun(ref double d) { 1606 d = 3.14; 1607 } 1608 } 1609 1610 alias MySum = SumType!(int, double); 1611 1612 MySum x = 0; 1613 MySum y = 0.0; 1614 1615 x.match!(OverloadSet.fun); 1616 y.match!(OverloadSet.fun); 1617 1618 assert(x.match!((value) => is(typeof(value) == int) && value == 42)); 1619 assert(y.match!((value) => is(typeof(value) == double) && value == 3.14)); 1620 } 1621 1622 // Overload sets with templates 1623 @safe unittest { 1624 import std.traits : isNumeric; 1625 1626 static struct OverloadSet { 1627 static string fun(string arg) { 1628 return "string"; 1629 } 1630 1631 static string fun(T)(T arg) if (isNumeric!T) { 1632 return "numeric"; 1633 } 1634 } 1635 1636 alias MySum = SumType!(int, string); 1637 1638 MySum x = 123; 1639 MySum y = "hello"; 1640 1641 assert(x.match!(OverloadSet.fun) == "numeric"); 1642 assert(y.match!(OverloadSet.fun) == "string"); 1643 } 1644 1645 // Github issue #24 1646 @safe unittest { 1647 assert(__traits(compiles, () @nogc { 1648 int acc = 0; 1649 SumType!int(1).match!((int x) => acc += x); 1650 })); 1651 } 1652 1653 // Types that `alias this` a SumType 1654 @safe unittest { 1655 static struct A { 1656 } 1657 1658 static struct B { 1659 } 1660 1661 static struct D { 1662 SumType!(A, B) value; 1663 alias value this; 1664 } 1665 1666 assert(__traits(compiles, D().match!(_ => true))); 1667 } 1668 1669 version (SumTypeTestBetterC) { 1670 version (D_BetterC) { 1671 } else 1672 static assert(false, "Must compile with -betterC to run betterC tests"); 1673 1674 version (unittest) { 1675 } else 1676 static assert(false, "Must compile with -unittest to run betterC tests"); 1677 1678 extern (C) int main() { 1679 import core.stdc.stdio : puts; 1680 1681 static foreach (test; __traits(getUnitTests, mixin(__MODULE__))) { 1682 test(); 1683 } 1684 1685 puts("All unit tests have been run successfully."); 1686 return 0; 1687 } 1688 } 1689 1690 static if (__traits(compiles, { import std.typecons : ReplaceTypeUnless; })) { 1691 import std.typecons : ReplaceTypeUnless; 1692 } else { 1693 /** 1694 * Replaces all occurrences of `From` into `To`, in one or more types `T` 1695 * whenever the predicate applied to `T` evaluates to false. For example, $(D 1696 * ReplaceTypeUnless!(isBoolean, int, uint, Tuple!(int, float)[string])) yields 1697 * $(D Tuple!(uint, float)[string]) while $(D ReplaceTypeUnless!(isTuple, int, 1698 * string, Tuple!(int, bool)[int])) yields $(D Tuple!(int, bool)[string]). The 1699 * types in which replacement is performed may be arbitrarily complex, 1700 * including qualifiers, built-in type constructors (pointers, arrays, 1701 * associative arrays, functions, and delegates), and template instantiations; 1702 * replacement proceeds transitively through the type definition. However, 1703 * member types in `struct`s or `class`es are not replaced because there are no 1704 * ways to express the types resulting after replacement. 1705 * 1706 * This is an advanced type manipulation necessary e.g. for replacing the 1707 * placeholder type `This` in $(REF SumType). 1708 * 1709 * This template is a generalised version of the one in 1710 * https://github.com/dlang/phobos/blob/d1c8fb0b69dc12669554d5cb96d3045753549619/std/typecons.d 1711 * 1712 * Returns: `ReplaceTypeUnless` aliases itself to the type(s) that result after 1713 * replacement. 1714 */ 1715 private template ReplaceTypeUnless(alias Pred, From, To, T...) { 1716 import std.meta; 1717 1718 static if (T.length == 1) { 1719 static if (Pred!(T[0])) 1720 alias ReplaceTypeUnless = T[0]; 1721 else static if (is(T[0] == From)) 1722 alias ReplaceTypeUnless = To; 1723 else static if (is(T[0] == const(U), U)) 1724 alias ReplaceTypeUnless = const(ReplaceTypeUnless!(Pred, From, To, U)); 1725 else static if (is(T[0] == immutable(U), U)) 1726 alias ReplaceTypeUnless = immutable(ReplaceTypeUnless!(Pred, From, To, U)); 1727 else static if (is(T[0] == shared(U), U)) 1728 alias ReplaceTypeUnless = shared(ReplaceTypeUnless!(Pred, From, To, U)); 1729 else static if (is(T[0] == U*, U)) { 1730 static if (is(U == function)) 1731 alias ReplaceTypeUnless = replaceTypeInFunctionTypeUnless!(Pred, From, To, T[0]); 1732 else 1733 alias ReplaceTypeUnless = ReplaceTypeUnless!(Pred, From, To, U)*; 1734 } else static if (is(T[0] == delegate)) { 1735 alias ReplaceTypeUnless = replaceTypeInFunctionTypeUnless!(Pred, From, To, T[0]); 1736 } else static if (is(T[0] == function)) { 1737 static assert(0, 1738 "Function types not supported," 1739 ~ " use a function pointer type instead of " ~ T[0].stringof); 1740 } else static if (is(T[0] == U!V, alias U, V...)) { 1741 template replaceTemplateArgs(T...) { 1742 static if (is(typeof(T[0]))) // template argument is value or symbol 1743 enum replaceTemplateArgs = T[0]; 1744 else 1745 alias replaceTemplateArgs = ReplaceTypeUnless!(Pred, From, To, T[0]); 1746 } 1747 1748 alias ReplaceTypeUnless = U!(staticMap!(replaceTemplateArgs, V)); 1749 } else static if (is(T[0] == struct)) // don't match with alias this struct below (Issue 15168) 1750 alias ReplaceTypeUnless = T[0]; 1751 else static if (is(T[0] == U[], U)) 1752 alias ReplaceTypeUnless = ReplaceTypeUnless!(Pred, From, To, U)[]; 1753 else static if (is(T[0] == U[n], U, size_t n)) 1754 alias ReplaceTypeUnless = ReplaceTypeUnless!(Pred, From, To, U)[n]; 1755 else static if (is(T[0] == U[V], U, V)) 1756 alias ReplaceTypeUnless = ReplaceTypeUnless!(Pred, From, To, U)[ReplaceTypeUnless!(Pred, 1757 From, To, V)]; 1758 else 1759 alias ReplaceTypeUnless = T[0]; 1760 } else static if (T.length > 1) { 1761 alias ReplaceTypeUnless = AliasSeq!(ReplaceTypeUnless!(Pred, From, 1762 To, T[0]), ReplaceTypeUnless!(Pred, From, To, T[1 .. $])); 1763 } else { 1764 alias ReplaceTypeUnless = AliasSeq!(); 1765 } 1766 } 1767 1768 private template replaceTypeInFunctionTypeUnless(alias Pred, From, To, fun) { 1769 import std.traits; 1770 import std.meta : AliasSeq; 1771 1772 alias RX = ReplaceTypeUnless!(Pred, From, To, ReturnType!fun); 1773 alias PX = AliasSeq!(ReplaceTypeUnless!(Pred, From, To, Parameters!fun)); 1774 // Wrapping with AliasSeq is neccesary because ReplaceType doesn't return 1775 // tuple if Parameters!fun.length == 1 1776 1777 string gen() { 1778 enum linkage = functionLinkage!fun; 1779 alias attributes = functionAttributes!fun; 1780 enum variadicStyle = variadicFunctionStyle!fun; 1781 alias storageClasses = ParameterStorageClassTuple!fun; 1782 1783 string result; 1784 1785 result ~= "extern(" ~ linkage ~ ") "; 1786 static if (attributes & FunctionAttribute.ref_) { 1787 result ~= "ref "; 1788 } 1789 1790 result ~= "RX"; 1791 static if (is(fun == delegate)) 1792 result ~= " delegate"; 1793 else 1794 result ~= " function"; 1795 1796 result ~= "("; 1797 static foreach (i; 0 .. PX.length) { 1798 if (i) 1799 result ~= ", "; 1800 if (storageClasses[i] & ParameterStorageClass.scope_) 1801 result ~= "scope "; 1802 if (storageClasses[i] & ParameterStorageClass.out_) 1803 result ~= "out "; 1804 if (storageClasses[i] & ParameterStorageClass.ref_) 1805 result ~= "ref "; 1806 if (storageClasses[i] & ParameterStorageClass.lazy_) 1807 result ~= "lazy "; 1808 if (storageClasses[i] & ParameterStorageClass.return_) 1809 result ~= "return "; 1810 1811 result ~= "PX[" ~ i.stringof ~ "]"; 1812 } 1813 static if (variadicStyle == Variadic.typesafe) 1814 result ~= " ..."; 1815 else static if (variadicStyle != Variadic.no) 1816 result ~= ", ..."; 1817 result ~= ")"; 1818 1819 static if (attributes & FunctionAttribute.pure_) 1820 result ~= " pure"; 1821 static if (attributes & FunctionAttribute.nothrow_) 1822 result ~= " nothrow"; 1823 static if (attributes & FunctionAttribute.property) 1824 result ~= " @property"; 1825 static if (attributes & FunctionAttribute.trusted) 1826 result ~= " @trusted"; 1827 static if (attributes & FunctionAttribute.safe) 1828 result ~= " @safe"; 1829 static if (attributes & FunctionAttribute.nogc) 1830 result ~= " @nogc"; 1831 static if (attributes & FunctionAttribute.system) 1832 result ~= " @system"; 1833 static if (attributes & FunctionAttribute.const_) 1834 result ~= " const"; 1835 static if (attributes & FunctionAttribute.immutable_) 1836 result ~= " immutable"; 1837 static if (attributes & FunctionAttribute.inout_) 1838 result ~= " inout"; 1839 static if (attributes & FunctionAttribute.shared_) 1840 result ~= " shared"; 1841 static if (attributes & FunctionAttribute.return_) 1842 result ~= " return"; 1843 1844 return result; 1845 } 1846 1847 mixin("alias replaceTypeInFunctionTypeUnless = " ~ gen() ~ ";"); 1848 } 1849 1850 // Adapted from: 1851 // https://github.com/dlang/phobos/blob/d1c8fb0b69dc12669554d5cb96d3045753549619/std/typecons.d 1852 @safe unittest { 1853 import std.typecons : Tuple; 1854 1855 enum False(T) = false; 1856 static assert(is(ReplaceTypeUnless!(False, int, string, 1857 int[]) == string[]) && is(ReplaceTypeUnless!(False, int, string, 1858 int[int]) == string[string]) && is(ReplaceTypeUnless!(False, int, 1859 string, const(int)[]) == const(string)[]) && is(ReplaceTypeUnless!(False, 1860 int, string, Tuple!(int[], float)) == Tuple!(string[], float))); 1861 } 1862 1863 // Adapted from: 1864 // https://github.com/dlang/phobos/blob/d1c8fb0b69dc12669554d5cb96d3045753549619/std/typecons.d 1865 version (D_BetterC) { 1866 } else 1867 @safe unittest { 1868 import std.typecons; 1869 1870 enum False(T) = false; 1871 template Test(Ts...) { 1872 static if (Ts.length) { 1873 static assert(is(ReplaceTypeUnless!(False, Ts[0], Ts[1], 1874 Ts[2]) == Ts[3]), "ReplaceTypeUnless!(False, " ~ Ts[0].stringof ~ ", " 1875 ~ Ts[1].stringof ~ ", " ~ Ts[2].stringof ~ ") == " ~ ReplaceTypeUnless!(False, 1876 Ts[0], Ts[1], Ts[2]).stringof); 1877 alias Test = Test!(Ts[4 .. $]); 1878 } else 1879 alias Test = void; 1880 } 1881 1882 import core.stdc.stdio; 1883 1884 alias RefFun1 = ref int function(float, long); 1885 alias RefFun2 = ref float function(float, long); 1886 extern (C) int printf(const char*, ...) nothrow @nogc @system; 1887 extern (C) float floatPrintf(const char*, ...) nothrow @nogc @system; 1888 int func(float); 1889 1890 int x; 1891 struct S1 { 1892 void foo() { 1893 x = 1; 1894 } 1895 } 1896 1897 struct S2 { 1898 void bar() { 1899 x = 2; 1900 } 1901 } 1902 1903 alias Pass = Test!(int, float, typeof(&func), float delegate(float), 1904 int, float, typeof(&printf), typeof(&floatPrintf), int, 1905 float, int function(out long, ...), float function(out long, 1906 ...), int, float, int function(ref float, long), 1907 float function(ref float, long), int, float, int function(ref int, long), 1908 float function(ref float, long), int, float, int function(out int, long), 1909 float function(out float, long), int, float, int function(lazy int, long), 1910 float function(lazy float, long), int, float, int function(out long, 1911 ref const int), float function(out long, ref const float), int, 1912 int, int, int, int, float, int, float, int, float, const int, 1913 const float, int, float, immutable int, immutable float, int, 1914 float, shared int, shared float, int, float, int*, float*, int, 1915 float, const(int)*, const(float)*, int, float, const(int*), 1916 const(float*), const(int)*, float, const(int*), const(float), 1917 int*, float, const(int)*, const(int)*, int, float, int[], 1918 float[], int, float, int[42], float[42], int, float, 1919 const(int)[42], const(float)[42], int, float, const(int[42]), 1920 const(float[42]), int, float, int[int], float[float], int, 1921 float, int[double], float[double], int, float, double[int], 1922 double[float], int, float, int function(float, long), 1923 float function(float, long), int, float, int function(float), 1924 float function(float), int, float, int function(float, int), 1925 float function(float, float), int, float, int delegate(float, long), 1926 float delegate(float, long), int, float, int delegate(float), 1927 float delegate(float), int, float, int delegate(float, int), 1928 float delegate(float, float), int, float, Unique!int, Unique!float, 1929 int, float, Tuple!(float, int), Tuple!(float, float), int, 1930 float, RefFun1, RefFun2, S1, S2, S1[1][][S1]* function(), 1931 S2[1][][S2]* function(), int, string, int[3]function(int[] arr, 1932 int[2]...) pure @trusted, string[3]function(string[] arr, 1933 string[2]...) pure @trusted,); 1934 1935 // Dlang Bugzilla 15168 1936 static struct T1 { 1937 string s; 1938 alias s this; 1939 } 1940 1941 static struct T2 { 1942 char[10] s; 1943 alias s this; 1944 } 1945 1946 static struct T3 { 1947 string[string] s; 1948 alias s this; 1949 } 1950 1951 alias Pass2 = Test!(ubyte, ubyte, T1, T1, ubyte, ubyte, T2, T2, ubyte, ubyte, T3, T3,); 1952 } 1953 1954 version (D_BetterC) { 1955 } else 1956 @safe unittest // Dlang Bugzilla 17116 1957 { 1958 enum False(T) = false; 1959 alias ConstDg = void delegate(float) const; 1960 alias B = void delegate(int) const; 1961 alias A = ReplaceTypeUnless!(False, float, int, ConstDg); 1962 static assert(is(B == A)); 1963 } 1964 1965 // Github issue #27 1966 @safe unittest { 1967 enum False(T) = false; 1968 struct A(T) { 1969 } 1970 1971 struct B { 1972 A!int a; 1973 alias a this; 1974 } 1975 1976 static assert(is(ReplaceTypeUnless!(False, void, void, B) == B)); 1977 } 1978 }