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