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