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),
185                     cast(string) bop.op, pprint(*bop.rhs)));
186     }
187 
188     Expr* myExpr = sum(var("a"), prod(num(2), var("b")));
189     double[string] myEnv = ["a" : 3, "b" : 4, "c" : 7];
190 
191     assert(eval(*myExpr, myEnv) == 11);
192     assert(pprint(*myExpr) == "(a + (2 * b))");
193 }
194 
195 // Converts an unsigned integer to a compile-time string constant.
196 private enum toCtString(ulong n) = n.stringof[0 .. $ - 2];
197 
198 @safe unittest {
199     assert(toCtString!0 == "0");
200     assert(toCtString!123456 == "123456");
201 }
202 
203 /// `This` placeholder, for use in self-referential types.
204 public import std.variant : This;
205 
206 import std.meta : NoDuplicates;
207 
208 /**
209  * A tagged union that can hold a single value from any of a specified set of
210  * types.
211  *
212  * The value in a `SumType` can be operated on using [match|pattern matching].
213  *
214  * To avoid ambiguity, duplicate types are not allowed (but see the
215  * [sumtype#basic-usage|"basic usage" example] for a workaround).
216  *
217  * The special type `This` can be used as a placeholder to create
218  * self-referential types, just like with `Algebraic`. See the
219  * [sumtype#arithmetic-expression-evaluator|"Arithmetic expression evaluator" example] for
220  * usage.
221  *
222  * A `SumType` is initialized by default to hold the `.init` value of its
223  * first member type, just like a regular union. The version identifier
224  * `SumTypeNoDefaultCtor` can be used to disable this behavior.
225  *
226  * Bugs:
227  *   Types with `@disable`d `opEquals` overloads cannot be members of a
228  *   `SumType`.
229  *
230  * See_Also: `std.variant.Algebraic`
231  */
232 struct SumType(TypeArgs...)
233         if (is(NoDuplicates!TypeArgs == TypeArgs) && TypeArgs.length > 0) {
234     import std.meta : AliasSeq, Filter, staticIndexOf, staticMap;
235     import std.meta : anySatisfy, allSatisfy;
236     import std.traits : hasElaborateCopyConstructor, hasElaborateDestructor;
237     import std.traits : isAssignable, isCopyable, isStaticArray;
238     import std.traits : ConstOf, ImmutableOf;
239     import std.typecons : ReplaceTypeUnless;
240 
241     /// The types a `SumType` can hold.
242     alias Types = AliasSeq!(ReplaceTypeUnless!(isSumType, This, typeof(this), TypeArgs));
243 
244 private:
245 
246     enum bool canHoldTag(T) = Types.length <= T.max;
247     alias unsignedInts = AliasSeq!(ubyte, ushort, uint, ulong);
248 
249     alias Tag = Filter!(canHoldTag, unsignedInts)[0];
250 
251     union Storage {
252         template memberName(T) if (staticIndexOf!(T, Types) >= 0) {
253             enum tid = staticIndexOf!(T, Types);
254             mixin("enum memberName = `values_", toCtString!tid, "`;");
255         }
256 
257         static foreach (T; Types) {
258             mixin("T ", memberName!T, ";");
259         }
260     }
261 
262     Storage storage;
263     Tag tag;
264 
265     @system ref inout(T) get(T)() inout if (staticIndexOf!(T, Types) >= 0) {
266         enum tid = staticIndexOf!(T, Types);
267         assert(tag == tid);
268         return __traits(getMember, storage, Storage.memberName!T);
269     }
270 
271 public:
272 
273     static foreach (tid, T; Types) {
274         /// Constructs a `SumType` holding a specific value.
275         this()(auto ref T value) {
276             import core.lifetime : forward;
277 
278             static if (isCopyable!T) {
279                 mixin("Storage newStorage = { ", Storage.memberName!T, ": value", " };");
280             } else {
281                 mixin("Storage newStorage = { ", Storage.memberName!T, " : forward!value", " };");
282             }
283 
284             storage = newStorage;
285             tag = tid;
286         }
287 
288         static if (isCopyable!T) {
289             /// ditto
290             this()(auto ref const(T) value) const {
291                 mixin("const(Storage) newStorage = { ", Storage.memberName!T, ": value", " };");
292 
293                 storage = newStorage;
294                 tag = tid;
295             }
296 
297             /// ditto
298             this()(auto ref immutable(T) value) immutable {
299                 mixin("immutable(Storage) newStorage = { ", Storage.memberName!T, ": value", " };");
300 
301                 storage = newStorage;
302                 tag = tid;
303             }
304         } else {
305             @disable this(const(T) value) const;
306             @disable this(immutable(T) value) immutable;
307         }
308     }
309 
310     static if (allSatisfy!(isCopyable, Types)) {
311         static if (anySatisfy!(hasElaborateCopyConstructor, Types)) {
312             /// Constructs a `SumType` that's a copy of another `SumType`
313             this(ref SumType other) {
314                 storage = other.match!((ref value) {
315                     alias T = typeof(value);
316 
317                     mixin("Storage newStorage = { ", Storage.memberName!T, ": value", " };");
318 
319                     return newStorage;
320                 });
321 
322                 tag = other.tag;
323             }
324 
325             /// ditto
326             this(ref const(SumType) other) const {
327                 storage = other.match!((ref value) {
328                     alias OtherTypes = staticMap!(ConstOf, Types);
329                     enum tid = staticIndexOf!(typeof(value), OtherTypes);
330                     alias T = Types[tid];
331 
332                     mixin("const(Storage) newStorage = { ", Storage.memberName!T, ": value", " };");
333 
334                     return newStorage;
335                 });
336 
337                 tag = other.tag;
338             }
339 
340             /// ditto
341             this(ref immutable(SumType) other) immutable {
342                 storage = other.match!((ref value) {
343                     alias OtherTypes = staticMap!(ImmutableOf, Types);
344                     enum tid = staticIndexOf!(typeof(value), OtherTypes);
345                     alias T = Types[tid];
346 
347                     mixin("immutable(Storage) newStorage = { ",
348                         Storage.memberName!T, ": value", " };");
349 
350                     return newStorage;
351                 });
352 
353                 tag = other.tag;
354             }
355         }
356     } else {
357         /// `@disable`d if any member type is non-copyable.
358         @disable this(this);
359     }
360 
361     version (SumTypeNoDefaultCtor) {
362         @disable this();
363     }
364 
365     static foreach (tid, T; Types) {
366         static if (isAssignable!T) {
367             /**
368 			 * Assigns a value to a `SumType`.
369 			 *
370 			 * Assigning to a `SumType` is `@system` if any of the
371 			 * `SumType`'s members contain pointers or references, since
372 			 * those members may be reachable through external references,
373 			 * and overwriting them could therefore lead to memory
374 			 * corruption.
375 			 *
376 			 * An individual assignment can be `@trusted` if the caller can
377 			 * guarantee that there are no outstanding references to $(I any)
378 			 * of the `SumType`'s members when the assignment occurs.
379 			 */
380             ref SumType opAssign()(auto ref T rhs) {
381                 import core.lifetime : forward;
382                 import std.traits : hasIndirections, hasNested;
383                 import std.meta : Or = templateOr;
384 
385                 enum mayContainPointers = anySatisfy!(Or!(hasIndirections, hasNested), Types);
386 
387                 static if (mayContainPointers) {
388                     cast(void)() @system {}();
389                 }
390 
391                 this.match!((ref value) {
392                     static if (hasElaborateDestructor!(typeof(value))) {
393                         destroy(value);
394                     }
395                 });
396 
397                 mixin("Storage newStorage = { ", Storage.memberName!T, ": forward!rhs", " };");
398 
399                 storage = newStorage;
400                 tag = tid;
401 
402                 return this;
403             }
404         }
405     }
406 
407     static if (allSatisfy!(isAssignable, Types)) {
408         static if (allSatisfy!(isCopyable, Types)) {
409             /**
410 			 * Copies the value from another `SumType` into this one.
411 			 *
412 			 * See the value-assignment overload for details on `@safe`ty.
413 			 *
414 			 * Copy assignment is `@disable`d if any of `Types` is non-copyable.
415 			 */
416             ref SumType opAssign(ref SumType rhs) {
417                 rhs.match!((ref value) { this = value; });
418                 return this;
419             }
420         } else {
421             @disable ref SumType opAssign(ref SumType rhs);
422         }
423 
424         /**
425 		 * Moves the value from another `SumType` into this one.
426 		 *
427 		 * See the value-assignment overload for details on `@safe`ty.
428 		 */
429         ref SumType opAssign(SumType rhs) {
430             import core.lifetime : move;
431 
432             rhs.match!((ref value) { this = move(value); });
433             return this;
434         }
435     }
436 
437     /**
438 	 * Compares two `SumType`s for equality.
439 	 *
440 	 * Two `SumType`s are equal if they are the same kind of `SumType`, they
441 	 * contain values of the same type, and those values are equal.
442 	 */
443     bool opEquals()(auto ref const(SumType) rhs) const {
444         return this.match!((ref value) {
445             return rhs.match!((ref rhsValue) {
446                 static if (is(typeof(value) == typeof(rhsValue))) {
447                     return value == rhsValue;
448                 } else {
449                     return false;
450                 }
451             });
452         });
453     }
454 
455     // Workaround for dlang issue 19407
456     static if (__traits(compiles, anySatisfy!(hasElaborateDestructor, Types))) {
457         // If possible, include the destructor only when it's needed
458         private enum includeDtor = anySatisfy!(hasElaborateDestructor, Types);
459     } else {
460         // If we can't tell, always include it, even when it does nothing
461         private enum includeDtor = true;
462     }
463 
464     static if (includeDtor) {
465         /// Calls the destructor of the `SumType`'s current value.
466         ~this() {
467             this.match!((ref value) {
468                 static if (hasElaborateDestructor!(typeof(value))) {
469                     destroy(value);
470                 }
471             });
472         }
473     }
474 
475     invariant {
476         this.match!((ref value) {
477             static if (is(typeof(value) == class)) {
478                 if (value !is null) {
479                     assert(value);
480                 }
481             } else static if (is(typeof(value) == struct)) {
482                 assert(&value);
483             }
484         });
485     }
486 
487     version (D_BetterC) {
488     } else /**
489 	 * Returns a string representation of the `SumType`'s current value.
490 	 *
491 	 * Not available when compiled with `-betterC`.
492 	 */
493         string toString(this T)() {
494         import std.conv : to;
495 
496         return this.match!(to!string);
497     }
498 
499     // toHash is required by the language spec to be nothrow and @safe
500     private enum isHashable(T) = __traits(compiles, () nothrow @safe {
501             hashOf(T.init);
502         });
503 
504     static if (allSatisfy!(isHashable, staticMap!(ConstOf, Types))) {
505         // Workaround for dlang issue 20095
506         version (D_BetterC) {
507         } else /**
508 		 * Returns the hash of the `SumType`'s current value.
509 		 *
510 		 * Not available when compiled with `-betterC`.
511 		 */
512             size_t toHash() const {
513             return this.match!hashOf;
514         }
515     }
516 }
517 
518 // Construction
519 @safe unittest {
520     alias MySum = SumType!(int, float);
521 
522     assert(__traits(compiles, MySum(42)));
523     assert(__traits(compiles, MySum(3.14)));
524 }
525 
526 // Assignment
527 @safe unittest {
528     alias MySum = SumType!(int, float);
529 
530     MySum x = MySum(42);
531 
532     assert(__traits(compiles, x = 3.14));
533 }
534 
535 // Self assignment
536 @safe unittest {
537     alias MySum = SumType!(int, float);
538 
539     MySum x = MySum(42);
540     MySum y = MySum(3.14);
541 
542     assert(__traits(compiles, y = x));
543 }
544 
545 // Equality
546 @safe unittest {
547     alias MySum = SumType!(int, float);
548 
549     MySum x = MySum(123);
550     MySum y = MySum(123);
551     MySum z = MySum(456);
552     MySum w = MySum(123.0);
553     MySum v = MySum(456.0);
554 
555     assert(x == y);
556     assert(x != z);
557     assert(x != w);
558     assert(x != v);
559 }
560 
561 // Imported types
562 @safe unittest {
563     import std.typecons : Tuple;
564 
565     assert(__traits(compiles, { alias MySum = SumType!(Tuple!(int, int)); }));
566 }
567 
568 // const and immutable types
569 @safe unittest {
570     assert(__traits(compiles, {
571             alias MySum = SumType!(const(int[]), immutable(float[]));
572         }));
573 }
574 
575 // Recursive types
576 @safe unittest {
577     alias MySum = SumType!(This*);
578     assert(is(MySum.Types[0] == MySum*));
579 }
580 
581 // Allowed types
582 @safe unittest {
583     import std.meta : AliasSeq;
584 
585     alias MySum = SumType!(int, float, This*);
586 
587     assert(is(MySum.Types == AliasSeq!(int, float, MySum*)));
588 }
589 
590 // Works alongside Algebraic
591 version (D_BetterC) {
592 } else
593     @safe unittest {
594     import std.variant;
595 
596     alias Bar = Algebraic!(This*);
597 
598     assert(is(Bar.AllowedTypes[0] == Bar*));
599 }
600 
601 // Types with destructors and postblits
602 @system unittest {
603     int copies;
604 
605     static struct Test {
606         bool initialized = false;
607         int* copiesPtr;
608 
609         this(this) {
610             (*copiesPtr)++;
611         }
612 
613         ~this() {
614             if (initialized)
615                 (*copiesPtr)--;
616         }
617     }
618 
619     alias MySum = SumType!(int, Test);
620 
621     Test t = Test(true, &copies);
622 
623     {
624         MySum x = t;
625         assert(copies == 1);
626     }
627     assert(copies == 0);
628 
629     {
630         MySum x = 456;
631         assert(copies == 0);
632     }
633     assert(copies == 0);
634 
635     {
636         MySum x = t;
637         assert(copies == 1);
638         x = 456;
639         assert(copies == 0);
640     }
641 
642     {
643         MySum x = 456;
644         assert(copies == 0);
645         x = t;
646         assert(copies == 1);
647     }
648 
649     {
650         MySum x = t;
651         MySum y = x;
652         assert(copies == 2);
653     }
654 
655     {
656         MySum x = t;
657         MySum y;
658         y = x;
659         assert(copies == 2);
660     }
661 }
662 
663 // Doesn't destroy reference types
664 version (D_BetterC) {
665 } else
666     @system unittest {
667     bool destroyed;
668 
669     class C {
670         ~this() {
671             destroyed = true;
672         }
673     }
674 
675     struct S {
676         ~this() {
677         }
678     }
679 
680     alias MySum = SumType!(S, C);
681 
682     C c = new C();
683     {
684         MySum x = c;
685         destroyed = false;
686     }
687     assert(!destroyed);
688 
689     {
690         MySum x = c;
691         destroyed = false;
692         x = S();
693         assert(!destroyed);
694     }
695 }
696 
697 // Types with @disable this()
698 @safe unittest {
699     static struct NoInit {
700         @disable this();
701     }
702 
703     alias MySum = SumType!(NoInit, int);
704 
705     assert(!__traits(compiles, MySum()));
706     assert(__traits(compiles, MySum(42)));
707 }
708 
709 // const SumTypes
710 @safe unittest {
711     assert(__traits(compiles, const(SumType!(int[]))([1, 2, 3])));
712 }
713 
714 // Equality of const SumTypes
715 @safe unittest {
716     alias MySum = SumType!int;
717 
718     assert(__traits(compiles, const(MySum)(123) == const(MySum)(456)));
719 }
720 
721 // Compares reference types using value equality
722 @safe unittest {
723     import std.array : staticArray;
724 
725     static struct Field {
726     }
727 
728     static struct Struct {
729         Field[] fields;
730     }
731 
732     alias MySum = SumType!Struct;
733 
734     static arr1 = staticArray([Field()]);
735     static arr2 = staticArray([Field()]);
736 
737     auto a = MySum(Struct(arr1[]));
738     auto b = MySum(Struct(arr2[]));
739 
740     assert(a == b);
741 }
742 
743 // toString
744 version (D_BetterC) {
745 } else
746     @safe unittest {
747     import std.conv : text;
748 
749     static struct Int {
750         int i;
751     }
752 
753     static struct Double {
754         double d;
755     }
756 
757     alias Sum = SumType!(Int, Double);
758 
759     assert(Sum(Int(42)).text == Int(42).text, Sum(Int(42)).text);
760     assert(Sum(Double(33.3)).text == Double(33.3).text, Sum(Double(33.3)).text);
761     assert((const(Sum)(Int(42))).text == (const(Int)(42)).text, (const(Sum)(Int(42))).text);
762 }
763 
764 // Github issue #16
765 version (D_BetterC) {
766 } else
767     @safe unittest {
768     alias Node = SumType!(This[], string);
769 
770     // override inference of @system attribute for cyclic functions
771     assert((() @trusted => Node([Node([Node("x")])]) == Node([Node([Node("x")])]))());
772 }
773 
774 // Github issue #16 with const
775 version (D_BetterC) {
776 } else
777     @safe unittest {
778     alias Node = SumType!(const(This)[], string);
779 
780     // override inference of @system attribute for cyclic functions
781     assert((() @trusted => Node([Node([Node("x")])]) == Node([Node([Node("x")])]))());
782 }
783 
784 // Stale pointers
785 version (D_BetterC) {
786 } else
787     @system unittest {
788     alias MySum = SumType!(ubyte, void*[2]);
789 
790     MySum x = [null, cast(void*) 0x12345678];
791     void** p = &x.get!(void*[2])[1];
792     x = ubyte(123);
793 
794     assert(*p != cast(void*) 0x12345678);
795 }
796 
797 // Exception-safe assignment
798 version (D_BetterC) {
799 } else
800     @system unittest {
801     static struct A {
802         int value = 123;
803     }
804 
805     static struct B {
806         int value = 456;
807         this(this) {
808             throw new Exception("oops");
809         }
810     }
811 
812     alias MySum = SumType!(A, B);
813 
814     MySum x;
815     try {
816         x = B();
817     } catch (Exception e) {
818     }
819 
820     assert((x.tag == 0 && x.get!A.value == 123) || (x.tag == 1 && x.get!B.value == 456));
821 }
822 
823 // Types with @disable this(this)
824 @safe unittest {
825     import std.algorithm.mutation : move;
826 
827     static struct NoCopy {
828         @disable this(this);
829     }
830 
831     alias MySum = SumType!NoCopy;
832 
833     NoCopy lval = NoCopy();
834 
835     MySum x = NoCopy();
836     MySum y = NoCopy();
837 
838     assert(__traits(compiles, SumType!NoCopy(NoCopy())));
839     assert(!__traits(compiles, SumType!NoCopy(lval)));
840 
841     assert(__traits(compiles, y = NoCopy()));
842     assert(__traits(compiles, y = move(x)));
843     assert(!__traits(compiles, y = lval));
844     assert(!__traits(compiles, y = x));
845 
846     assert(__traits(compiles, x == y));
847 }
848 
849 // Github issue #22
850 version (D_BetterC) {
851 } else
852     @safe unittest {
853     import std.typecons;
854 
855     assert(__traits(compiles, {
856             static struct A {
857                 SumType!(Nullable!int) a = Nullable!int.init;
858             }
859         }));
860 }
861 
862 // Static arrays of structs with postblits
863 version (D_BetterC) {
864 } else
865     @system unittest {
866     static struct S {
867         int n;
868         this(this) {
869             n++;
870         }
871     }
872 
873     assert(__traits(compiles, SumType!(S[1])()));
874 
875     SumType!(S[1]) x = [S(0)];
876     SumType!(S[1]) y = x;
877 
878     auto xval = x.get!(S[1])[0].n;
879     auto yval = y.get!(S[1])[0].n;
880 
881     assert(xval != yval);
882 }
883 
884 // Replacement does not happen inside SumType
885 version (D_BetterC) {
886 } else
887     @safe unittest {
888     import std.typecons : Tuple, ReplaceTypeUnless;
889 
890     alias A = Tuple!(This*, SumType!(This*))[SumType!(This*, string)[This]];
891     alias TR = ReplaceTypeUnless!(isSumType, This, int, A);
892     static assert(is(TR == Tuple!(int*, SumType!(This*))[SumType!(This * , string)[int]]));
893 }
894 
895 // Supports nested self-referential SumTypes
896 @safe unittest {
897     import std.typecons : Tuple, Flag;
898 
899     alias Nat = SumType!(Flag!"0", Tuple!(This*));
900     static assert(__traits(compiles, SumType!(Nat)));
901     static assert(__traits(compiles, SumType!(Nat*, Tuple!(This*, This*))));
902 }
903 
904 // Doesn't call @system postblits in @safe code
905 @safe unittest {
906     static struct SystemCopy {
907         @system this(this) {
908         }
909     }
910 
911     SystemCopy original;
912 
913     assert(!__traits(compiles, () @safe { SumType!SystemCopy copy = original; }));
914 
915     assert(!__traits(compiles, () @safe {
916             SumType!SystemCopy copy;
917             copy = original;
918         }));
919 }
920 
921 // Doesn't overwrite pointers in @safe code
922 @safe unittest {
923     alias MySum = SumType!(int*, int);
924 
925     MySum x;
926 
927     assert(!__traits(compiles, () @safe { x = 123; }));
928 
929     assert(!__traits(compiles, () @safe { x = MySum(123); }));
930 }
931 
932 // Types with invariants
933 version (D_BetterC) {
934 } else
935     @system unittest {
936     import std.exception : assertThrown;
937     import core.exception : AssertError;
938 
939     struct S {
940         int i;
941         invariant {
942             assert(i >= 0);
943         }
944     }
945 
946     class C {
947         int i;
948         invariant {
949             assert(i >= 0);
950         }
951     }
952 
953     SumType!S x;
954     x.match!((ref v) { v.i = -1; });
955     assertThrown!AssertError(assert(&x));
956 
957     SumType!C y = new C();
958     y.match!((ref v) { v.i = -1; });
959     assertThrown!AssertError(assert(&y));
960 }
961 
962 // Calls value postblit on self-assignment
963 @system unittest {
964     static struct S {
965         int n;
966         this(this) {
967             n++;
968         }
969     }
970 
971     SumType!S x = S();
972     SumType!S y;
973     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 // Github issue #29
982 @safe unittest {
983     assert(__traits(compiles, () @safe {
984             alias A = SumType!string;
985 
986             @safe A createA(string arg) {
987                 return A(arg);
988             }
989 
990             @safe void test() {
991                 A a = createA("");
992             }
993         }));
994 }
995 
996 // SumTypes as associative array keys
997 version (D_BetterC) {
998 } else
999     @safe unittest {
1000     assert(__traits(compiles, { int[SumType!(int, string)] aa; }));
1001 }
1002 
1003 // toString with non-copyable types
1004 version (D_BetterC) {
1005 } else
1006     @safe unittest {
1007     struct NoCopy {
1008         @disable this(this);
1009     }
1010 
1011     SumType!NoCopy x;
1012 
1013     assert(__traits(compiles, x.toString()));
1014 }
1015 
1016 // Can use the result of assignment
1017 @safe unittest {
1018     alias MySum = SumType!(int, float);
1019 
1020     MySum a = MySum(123);
1021     MySum b = MySum(3.14);
1022 
1023     assert((a = b) == b);
1024     assert((a = MySum(123)) == MySum(123));
1025     assert((a = 3.14) == MySum(3.14));
1026     assert(((a = b) = MySum(123)) == MySum(123));
1027 }
1028 
1029 version (none) {
1030     // Known bug; needs fix for dlang issue 19902
1031     // Types with copy constructors
1032     @safe unittest {
1033         static struct S {
1034             int n;
1035             this(ref return scope inout S other) inout {
1036                 n++;
1037             }
1038         }
1039 
1040         SumType!S x = S();
1041         SumType!S y = x;
1042 
1043         auto xval = x.get!S.n;
1044         auto yval = y.get!S.n;
1045 
1046         assert(xval != yval);
1047     }
1048 }
1049 
1050 version (none) {
1051     // Known bug; needs fix for dlang issue 19458
1052     // Types with disabled opEquals
1053     @safe unittest {
1054         static struct S {
1055             @disable bool opEquals(const S rhs) const;
1056         }
1057 
1058         assert(__traits(compiles, SumType!S(S())));
1059     }
1060 }
1061 
1062 version (none) {
1063     // Known bug; needs fix for dlang issue 19458
1064     @safe unittest {
1065         static struct S {
1066             int i;
1067             bool opEquals(S rhs) {
1068                 return i == rhs.i;
1069             }
1070         }
1071 
1072         assert(__traits(compiles, SumType!S(S(123))));
1073     }
1074 }
1075 
1076 /// True if `T` is an instance of `SumType`, otherwise false.
1077 enum bool isSumType(T) = is(T == SumType!Args, Args...);
1078 
1079 unittest {
1080     static struct Wrapper {
1081         SumType!int s;
1082         alias s this;
1083     }
1084 
1085     assert(isSumType!(SumType!int));
1086     assert(!isSumType!Wrapper);
1087 }
1088 
1089 /**
1090  * Calls a type-appropriate function with the value held in a [SumType].
1091  *
1092  * For each possible type the [SumType] can hold, the given handlers are
1093  * checked, in order, to see whether they accept a single argument of that type.
1094  * The first one that does is chosen as the match for that type. (Note that the
1095  * first match may not always be the most exact match.
1096  * See [#avoiding-unintentional-matches|"Avoiding unintentional matches"] for
1097  * one common pitfall.)
1098  *
1099  * Every type must have a matching handler, and every handler must match at
1100  * least one type. This is enforced at compile time.
1101  *
1102  * Handlers may be functions, delegates, or objects with opCall overloads. If a
1103  * function with more than one overload is given as a handler, all of the
1104  * overloads are considered as potential matches.
1105  *
1106  * Templated handlers are also accepted, and will match any type for which they
1107  * can be [implicitly instantiated](https://dlang.org/glossary.html#ifti). See
1108  * [sumtype#introspection-based-matching|"Introspection-based matching"] for an
1109  * example of templated handler usage.
1110  *
1111  * Returns:
1112  *   The value returned from the handler that matches the currently-held type.
1113  *
1114  * See_Also: `std.variant.visit`
1115  */
1116 template match(handlers...) {
1117     import std.typecons : Yes;
1118 
1119     /**
1120 	 * The actual `match` function.
1121 	 *
1122 	 * Params:
1123 	 *   self = A [SumType] object
1124 	 */
1125     auto match(Self)(auto ref Self self)
1126             if (is(Self : SumType!TypeArgs, TypeArgs...)) {
1127         return self.matchImpl!(Yes.exhaustive, handlers);
1128     }
1129 }
1130 
1131 /** $(H3 Avoiding unintentional matches)
1132  *
1133  * Sometimes, implicit conversions may cause a handler to match more types than
1134  * intended. The example below shows two solutions to this problem.
1135  */
1136 @safe unittest {
1137     alias Number = SumType!(double, int);
1138 
1139     Number x;
1140 
1141     // Problem: because int implicitly converts to double, the double
1142     // handler is used for both types, and the int handler never matches.
1143     assert(!__traits(compiles, x.match!((double d) => "got double", (int n) => "got int")));
1144 
1145     // Solution 1: put the handler for the "more specialized" type (in this
1146     // case, int) before the handler for the type it converts to.
1147     assert(__traits(compiles, x.match!((int n) => "got int", (double d) => "got double")));
1148 
1149     // Solution 2: use a template that only accepts the exact type it's
1150     // supposed to match, instead of any type that implicitly converts to it.
1151     alias exactly(T, alias fun) = function(arg) {
1152         static assert(is(typeof(arg) == T));
1153         return fun(arg);
1154     };
1155 
1156     // Now, even if we put the double handler first, it will only be used for
1157     // doubles, not ints.
1158     assert(__traits(compiles, x.match!(exactly!(double, d => "got double"),
1159             exactly!(int, n => "got int"))));
1160 }
1161 
1162 /**
1163  * Attempts to call a type-appropriate function with the value held in a
1164  * [SumType], and throws on failure.
1165  *
1166  * Matches are chosen using the same rules as [match], but are not required to
1167  * be exhaustive—in other words, a type is allowed to have no matching handler.
1168  * If a type without a handler is encountered at runtime, a [MatchException]
1169  * is thrown.
1170  *
1171  * Not available when compiled with `-betterC`.
1172  *
1173  * Returns:
1174  *   The value returned from the handler that matches the currently-held type,
1175  *   if a handler was given for that type.
1176  *
1177  * Throws:
1178  *   [MatchException], if the currently-held type has no matching handler.
1179  *
1180  * See_Also: `std.variant.tryVisit`
1181  */
1182 version (D_Exceptions) template tryMatch(handlers...) {
1183     import std.typecons : No;
1184 
1185     /**
1186 	 * The actual `tryMatch` function.
1187 	 *
1188 	 * Params:
1189 	 *   self = A [SumType] object
1190 	 */
1191     auto tryMatch(Self)(auto ref Self self)
1192             if (is(Self : SumType!TypeArgs, TypeArgs...)) {
1193         return self.matchImpl!(No.exhaustive, handlers);
1194     }
1195 }
1196 
1197 /**
1198  * Thrown by [tryMatch] when an unhandled type is encountered.
1199  *
1200  * Not available when compiled with `-betterC`.
1201  */
1202 version (D_Exceptions) class MatchException : Exception {
1203     pure @safe @nogc nothrow this(string msg, string file = __FILE__, size_t line = __LINE__) {
1204         super(msg, file, line);
1205     }
1206 }
1207 
1208 /**
1209  * True if `handler` is a potential match for `T`, otherwise false.
1210  *
1211  * See the documentation for [match] for a full explanation of how matches are
1212  * chosen.
1213  */
1214 enum bool canMatch(alias handler, T) = is(typeof((T arg) => handler(arg)));
1215 
1216 // Includes all overloads of the given handler
1217 @safe unittest {
1218     static struct OverloadSet {
1219         static void fun(int n) {
1220         }
1221 
1222         static void fun(double d) {
1223         }
1224     }
1225 
1226     assert(canMatch!(OverloadSet.fun, int));
1227     assert(canMatch!(OverloadSet.fun, double));
1228 }
1229 
1230 import std.typecons : Flag;
1231 
1232 private template matchImpl(Flag!"exhaustive" exhaustive, handlers...) {
1233     auto matchImpl(Self)(auto ref Self self)
1234             if (is(Self : SumType!TypeArgs, TypeArgs...)) {
1235         alias Types = self.Types;
1236         enum noMatch = size_t.max;
1237 
1238         enum matches = () {
1239             size_t[Types.length] matches;
1240 
1241             // Workaround for dlang issue 19561
1242             foreach (ref match; matches) {
1243                 match = noMatch;
1244             }
1245 
1246             static foreach (tid, T; Types) {
1247                 static foreach (hid, handler; handlers) {
1248                     static if (canMatch!(handler, typeof(self.get!T()))) {
1249                         if (matches[tid] == noMatch) {
1250                             matches[tid] = hid;
1251                         }
1252                     }
1253                 }
1254             }
1255 
1256             return matches;
1257         }();
1258 
1259         import std.algorithm.searching : canFind;
1260 
1261         // Check for unreachable handlers
1262         static foreach (hid, handler; handlers) {
1263             static assert(matches[].canFind(hid),
1264                     "`handlers[" ~ toCtString!hid ~ "]` " ~ "of type `" ~ (__traits(isTemplate, handler)
1265                         ? "template" : typeof(handler).stringof) ~ "` " ~ "never matches");
1266         }
1267 
1268         // Workaround for dlang issue 19993
1269         static foreach (size_t hid, handler; handlers) {
1270             mixin("alias handler", toCtString!hid, " = handler;");
1271         }
1272 
1273         final switch (self.tag) {
1274             static foreach (tid, T; Types) {
1275         case tid: {
1276                     static if (matches[tid] != noMatch) {
1277                         mixin("alias handler = handler", toCtString!(matches[tid]), ";");
1278 
1279                         /* The call to `get` can be @trusted here because
1280 						 *
1281 						 *   - @safe's prohibition against taking the address
1282 						 *     of a function parameter makes it impossible for
1283 						 *     a reference to the accessed SumType member to
1284 						 *     escape from the handler.
1285 						 *
1286 						 *   - SumType.opAssign's forced-@system attribute
1287 						 *     for SumTypes that contain pointers makes it
1288 						 *     impossible for unsafe aliasing to occur in
1289 						 *     the body of the handler.
1290 						 */
1291                         return handler(ref() @trusted { return self.get!T; }());
1292                     } else {
1293                         static if (exhaustive) {
1294                             static assert(false, "No matching handler for type `" ~ T.stringof ~ "`");
1295                         } else {
1296                             throw new MatchException(
1297                                     "No matching handler for type `" ~ T.stringof ~ "`");
1298                         }
1299                     }
1300                 }
1301             }
1302         }
1303 
1304         assert(false); // unreached
1305     }
1306 }
1307 
1308 // Matching
1309 @safe unittest {
1310     alias MySum = SumType!(int, float);
1311 
1312     MySum x = MySum(42);
1313     MySum y = MySum(3.14);
1314 
1315     assert(x.match!((int v) => true, (float v) => false));
1316     assert(y.match!((int v) => false, (float v) => true));
1317 }
1318 
1319 // Missing handlers
1320 @safe unittest {
1321     alias MySum = SumType!(int, float);
1322 
1323     MySum x = MySum(42);
1324 
1325     assert(!__traits(compiles, x.match!((int x) => true)));
1326     assert(!__traits(compiles, x.match!()));
1327 }
1328 
1329 // Handlers with qualified parameters
1330 version (D_BetterC) {
1331 } else
1332     @safe unittest {
1333     alias MySum = SumType!(int[], float[]);
1334 
1335     MySum x = MySum([1, 2, 3]);
1336     MySum y = MySum([1.0, 2.0, 3.0]);
1337 
1338     assert(x.match!((const(int[]) v) => true, (const(float[]) v) => false));
1339     assert(y.match!((const(int[]) v) => false, (const(float[]) v) => true));
1340 }
1341 
1342 // Handlers for qualified types
1343 version (D_BetterC) {
1344 } else
1345     @safe unittest {
1346     alias MySum = SumType!(immutable(int[]), immutable(float[]));
1347 
1348     MySum x = MySum([1, 2, 3]);
1349 
1350     assert(x.match!((immutable(int[]) v) => true, (immutable(float[]) v) => false));
1351     assert(x.match!((const(int[]) v) => true, (const(float[]) v) => false));
1352     // Tail-qualified parameters
1353     assert(x.match!((immutable(int)[] v) => true, (immutable(float)[] v) => false));
1354     assert(x.match!((const(int)[] v) => true, (const(float)[] v) => false));
1355     // Generic parameters
1356     assert(x.match!((immutable v) => true));
1357     assert(x.match!((const v) => true));
1358     // Unqualified parameters
1359     assert(!__traits(compiles, x.match!((int[] v) => true, (float[] v) => false)));
1360 }
1361 
1362 // Delegate handlers
1363 version (D_BetterC) {
1364 } else
1365     @safe unittest {
1366     alias MySum = SumType!(int, float);
1367 
1368     int answer = 42;
1369     MySum x = MySum(42);
1370     MySum y = MySum(3.14);
1371 
1372     assert(x.match!((int v) => v == answer, (float v) => v == answer));
1373     assert(!y.match!((int v) => v == answer, (float v) => v == answer));
1374 }
1375 
1376 version (unittest) {
1377     version (D_BetterC) {
1378         // std.math.approxEqual depends on core.runtime.math, so use a
1379         // libc-based version for testing with -betterC
1380         @safe pure @nogc nothrow private bool approxEqual(double lhs, double rhs) {
1381             import core.stdc.math : fabs;
1382 
1383             return (lhs - rhs) < 1e-5;
1384         }
1385     } else {
1386         import std.math : approxEqual;
1387     }
1388 }
1389 
1390 // Generic handler
1391 @safe unittest {
1392     alias MySum = SumType!(int, float);
1393 
1394     MySum x = MySum(42);
1395     MySum y = MySum(3.14);
1396 
1397     assert(x.match!(v => v * 2) == 84);
1398     assert(y.match!(v => v * 2).approxEqual(6.28));
1399 }
1400 
1401 // Fallback to generic handler
1402 version (D_BetterC) {
1403 } else
1404     @safe unittest {
1405     import std.conv : to;
1406 
1407     alias MySum = SumType!(int, float, string);
1408 
1409     MySum x = MySum(42);
1410     MySum y = MySum("42");
1411 
1412     assert(x.match!((string v) => v.to!int, v => v * 2) == 84);
1413     assert(y.match!((string v) => v.to!int, v => v * 2) == 42);
1414 }
1415 
1416 // Multiple non-overlapping generic handlers
1417 @safe unittest {
1418     import std.array : staticArray;
1419 
1420     alias MySum = SumType!(int, float, int[], char[]);
1421 
1422     static ints = staticArray([1, 2, 3]);
1423     static chars = staticArray(['a', 'b', 'c']);
1424 
1425     MySum x = MySum(42);
1426     MySum y = MySum(3.14);
1427     MySum z = MySum(ints[]);
1428     MySum w = MySum(chars[]);
1429 
1430     assert(x.match!(v => v * 2, v => v.length) == 84);
1431     assert(y.match!(v => v * 2, v => v.length).approxEqual(6.28));
1432     assert(w.match!(v => v * 2, v => v.length) == 3);
1433     assert(z.match!(v => v * 2, v => v.length) == 3);
1434 }
1435 
1436 // Structural matching
1437 @safe unittest {
1438     static struct S1 {
1439         int x;
1440     }
1441 
1442     static struct S2 {
1443         int y;
1444     }
1445 
1446     alias MySum = SumType!(S1, S2);
1447 
1448     MySum a = MySum(S1(0));
1449     MySum b = MySum(S2(0));
1450 
1451     assert(a.match!(s1 => s1.x + 1, s2 => s2.y - 1) == 1);
1452     assert(b.match!(s1 => s1.x + 1, s2 => s2.y - 1) == -1);
1453 }
1454 
1455 // Separate opCall handlers
1456 @safe unittest {
1457     static struct IntHandler {
1458         bool opCall(int arg) {
1459             return true;
1460         }
1461     }
1462 
1463     static struct FloatHandler {
1464         bool opCall(float arg) {
1465             return false;
1466         }
1467     }
1468 
1469     alias MySum = SumType!(int, float);
1470 
1471     MySum x = MySum(42);
1472     MySum y = MySum(3.14);
1473 
1474     assert(x.match!(IntHandler.init, FloatHandler.init));
1475     assert(!y.match!(IntHandler.init, FloatHandler.init));
1476 }
1477 
1478 // Compound opCall handler
1479 @safe unittest {
1480     static struct CompoundHandler {
1481         bool opCall(int arg) {
1482             return true;
1483         }
1484 
1485         bool opCall(float arg) {
1486             return false;
1487         }
1488     }
1489 
1490     alias MySum = SumType!(int, float);
1491 
1492     MySum x = MySum(42);
1493     MySum y = MySum(3.14);
1494 
1495     assert(x.match!(CompoundHandler.init));
1496     assert(!y.match!(CompoundHandler.init));
1497 }
1498 
1499 // Ordered matching
1500 @safe unittest {
1501     alias MySum = SumType!(int, float);
1502 
1503     MySum x = MySum(42);
1504 
1505     assert(x.match!((int v) => true, v => false));
1506 }
1507 
1508 // Non-exhaustive matching
1509 version (D_Exceptions) @system unittest {
1510     import std.exception : assertThrown, assertNotThrown;
1511 
1512     alias MySum = SumType!(int, float);
1513 
1514     MySum x = MySum(42);
1515     MySum y = MySum(3.14);
1516 
1517     assertNotThrown!MatchException(x.tryMatch!((int n) => true));
1518     assertThrown!MatchException(y.tryMatch!((int n) => true));
1519 }
1520 
1521 // Non-exhaustive matching in @safe code
1522 version (D_Exceptions) @safe unittest {
1523     SumType!(int, float) x;
1524 
1525     assert(__traits(compiles, x.tryMatch!((int n) => n + 1,)));
1526 
1527 }
1528 
1529 // Handlers with ref parameters
1530 @system unittest {
1531     import std.meta : staticIndexOf;
1532 
1533     alias Value = SumType!(long, double);
1534 
1535     auto value = Value(3.14);
1536 
1537     value.match!((long) {}, (ref double d) { d *= 2; });
1538 
1539     assert(value.get!double.approxEqual(6.28));
1540 }
1541 
1542 // Unreachable handlers
1543 @safe unittest {
1544     alias MySum = SumType!(int, string);
1545 
1546     MySum s;
1547 
1548     assert(!__traits(compiles, s.match!((int _) => 0, (string _) => 1, (double _) => 2)));
1549 
1550     assert(!__traits(compiles, s.match!(_ => 0, (int _) => 1)));
1551 }
1552 
1553 // Unsafe handlers
1554 unittest {
1555     SumType!int x;
1556     alias unsafeHandler = (int x) @system { return; };
1557 
1558     assert(!__traits(compiles, () @safe { x.match!unsafeHandler; }));
1559 
1560     assert(__traits(compiles, () @system { return x.match!unsafeHandler; }));
1561 }
1562 
1563 // Overloaded handlers
1564 @safe unittest {
1565     static struct OverloadSet {
1566         static string fun(int i) {
1567             return "int";
1568         }
1569 
1570         static string fun(double d) {
1571             return "double";
1572         }
1573     }
1574 
1575     alias MySum = SumType!(int, double);
1576 
1577     MySum a = 42;
1578     MySum b = 3.14;
1579 
1580     assert(a.match!(OverloadSet.fun) == "int");
1581     assert(b.match!(OverloadSet.fun) == "double");
1582 }
1583 
1584 // Overload sets that include SumType arguments
1585 @safe unittest {
1586     alias Inner = SumType!(int, double);
1587     alias Outer = SumType!(Inner, string);
1588 
1589     static struct OverloadSet {
1590     @safe:
1591         static string fun(int i) {
1592             return "int";
1593         }
1594 
1595         static string fun(double d) {
1596             return "double";
1597         }
1598 
1599         static string fun(string s) {
1600             return "string";
1601         }
1602 
1603         static string fun(Inner i) {
1604             return i.match!fun;
1605         }
1606 
1607         static string fun(Outer o) {
1608             return o.match!fun;
1609         }
1610     }
1611 
1612     Outer a = Inner(42);
1613     Outer b = Inner(3.14);
1614     Outer c = "foo";
1615 
1616     assert(OverloadSet.fun(a) == "int");
1617     assert(OverloadSet.fun(b) == "double");
1618     assert(OverloadSet.fun(c) == "string");
1619 }
1620 
1621 // Overload sets with ref arguments
1622 @safe unittest {
1623     static struct OverloadSet {
1624         static void fun(ref int i) {
1625             i = 42;
1626         }
1627 
1628         static void fun(ref double d) {
1629             d = 3.14;
1630         }
1631     }
1632 
1633     alias MySum = SumType!(int, double);
1634 
1635     MySum x = 0;
1636     MySum y = 0.0;
1637 
1638     x.match!(OverloadSet.fun);
1639     y.match!(OverloadSet.fun);
1640 
1641     assert(x.match!((value) =>  is(typeof(value) == int) && value == 42));
1642     assert(y.match!((value) =>  is(typeof(value) == double) && value == 3.14));
1643 }
1644 
1645 // Overload sets with templates
1646 @safe unittest {
1647     import std.traits : isNumeric;
1648 
1649     static struct OverloadSet {
1650         static string fun(string arg) {
1651             return "string";
1652         }
1653 
1654         static string fun(T)(T arg) if (isNumeric!T) {
1655             return "numeric";
1656         }
1657     }
1658 
1659     alias MySum = SumType!(int, string);
1660 
1661     MySum x = 123;
1662     MySum y = "hello";
1663 
1664     assert(x.match!(OverloadSet.fun) == "numeric");
1665     assert(y.match!(OverloadSet.fun) == "string");
1666 }
1667 
1668 // Github issue #24
1669 @safe unittest {
1670     assert(__traits(compiles, () @nogc {
1671             int acc = 0;
1672             SumType!int(1).match!((int x) => acc += x);
1673         }));
1674 }
1675 
1676 // Github issue #31
1677 @safe unittest {
1678     assert(__traits(compiles, () @nogc {
1679             int acc = 0;
1680 
1681             SumType!(int, string)(1).match!((int x) => acc += x, (string _) => 0,);
1682         }));
1683 }
1684 
1685 // Types that `alias this` a SumType
1686 @safe unittest {
1687     static struct A {
1688     }
1689 
1690     static struct B {
1691     }
1692 
1693     static struct D {
1694         SumType!(A, B) value;
1695         alias value this;
1696     }
1697 
1698     assert(__traits(compiles, D().match!(_ => true)));
1699 }
1700 
1701 version (SumTypeTestBetterC) {
1702     version (D_BetterC) {
1703     } else
1704         static assert(false, "Must compile with -betterC to run betterC tests");
1705 
1706     version (unittest) {
1707     } else
1708         static assert(false, "Must compile with -unittest to run betterC tests");
1709 
1710     extern (C) int main() {
1711         import core.stdc.stdio : puts;
1712 
1713         static foreach (test; __traits(getUnitTests, mixin(__MODULE__))) {
1714             test();
1715         }
1716 
1717         puts("All unit tests have been run successfully.");
1718         return 0;
1719     }
1720 }