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