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