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