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