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