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