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 }