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 // Construction
848 @safe unittest
849 {
850     alias MySum = SumType!(int, float);
851 
852     MySum x = MySum(42);
853     MySum y = MySum(3.14);
854 }
855 
856 // Assignment
857 @safe unittest
858 {
859     alias MySum = SumType!(int, float);
860 
861     MySum x = MySum(42);
862     x = 3.14;
863 }
864 
865 // Self assignment
866 @safe unittest
867 {
868     alias MySum = SumType!(int, float);
869 
870     MySum x = MySum(42);
871     MySum y = MySum(3.14);
872     y = x;
873 }
874 
875 // Equality
876 @safe unittest
877 {
878     alias MySum = SumType!(int, float);
879 
880     assert(MySum(123) == MySum(123));
881     assert(MySum(123) != MySum(456));
882     assert(MySum(123) != MySum(123.0));
883     assert(MySum(123) != MySum(456.0));
884 
885 }
886 
887 // Equality of differently-qualified SumTypes
888 // Disabled in BetterC due to use of dynamic arrays
889 version (D_BetterC) {} else
890 @safe unittest
891 {
892     alias SumA = SumType!(int, float);
893     alias SumB = SumType!(const(int[]), int[]);
894     alias SumC = SumType!(int[], const(int[]));
895 
896     int[] ma = [1, 2, 3];
897     const(int[]) ca = [1, 2, 3];
898 
899     assert(const(SumA)(123) == SumA(123));
900     assert(const(SumB)(ma[]) == SumB(ca[]));
901     assert(const(SumC)(ma[]) == SumC(ca[]));
902 }
903 
904 // Imported types
905 @safe unittest
906 {
907     import std.typecons : Tuple;
908 
909     alias MySum = SumType!(Tuple!(int, int));
910 }
911 
912 // const and immutable types
913 @safe unittest
914 {
915     alias MySum = SumType!(const(int[]), immutable(float[]));
916 }
917 
918 // Recursive types
919 @safe unittest
920 {
921     alias MySum = SumType!(This*);
922     assert(is(MySum.Types[0] == MySum*));
923 }
924 
925 // Allowed types
926 @safe unittest
927 {
928     import std.meta : AliasSeq;
929 
930     alias MySum = SumType!(int, float, This*);
931 
932     assert(is(MySum.Types == AliasSeq!(int, float, MySum*)));
933 }
934 
935 // Types with destructors and postblits
936 @system unittest
937 {
938     int copies;
939 
940     static struct Test
941     {
942         bool initialized = false;
943         int* copiesPtr;
944 
945         this(this) { (*copiesPtr)++; }
946         ~this() { if (initialized) (*copiesPtr)--; }
947     }
948 
949     alias MySum = SumType!(int, Test);
950 
951     Test t = Test(true, &copies);
952 
953     {
954         MySum x = t;
955         assert(copies == 1);
956     }
957     assert(copies == 0);
958 
959     {
960         MySum x = 456;
961         assert(copies == 0);
962     }
963     assert(copies == 0);
964 
965     {
966         MySum x = t;
967         assert(copies == 1);
968         x = 456;
969         assert(copies == 0);
970     }
971 
972     {
973         MySum x = 456;
974         assert(copies == 0);
975         x = t;
976         assert(copies == 1);
977     }
978 
979     {
980         MySum x = t;
981         MySum y = x;
982         assert(copies == 2);
983     }
984 
985     {
986         MySum x = t;
987         MySum y;
988         y = x;
989         assert(copies == 2);
990     }
991 }
992 
993 // Doesn't destroy reference types
994 // Disabled in BetterC due to use of classes
995 version (D_BetterC) {} else
996 @system unittest
997 {
998     bool destroyed;
999 
1000     class C
1001     {
1002         ~this()
1003         {
1004             destroyed = true;
1005         }
1006     }
1007 
1008     struct S
1009     {
1010         ~this() {}
1011     }
1012 
1013     alias MySum = SumType!(S, C);
1014 
1015     C c = new C();
1016     {
1017         MySum x = c;
1018         destroyed = false;
1019     }
1020     assert(!destroyed);
1021 
1022     {
1023         MySum x = c;
1024         destroyed = false;
1025         x = S();
1026         assert(!destroyed);
1027     }
1028 }
1029 
1030 // Types with @disable this()
1031 @safe unittest
1032 {
1033     static struct NoInit
1034     {
1035         @disable this();
1036     }
1037 
1038     alias MySum = SumType!(NoInit, int);
1039 
1040     assert(!__traits(compiles, MySum()));
1041     auto _ = MySum(42);
1042 }
1043 
1044 // const SumTypes
1045 version (D_BetterC) {} else // not @nogc, https://issues.dlang.org/show_bug.cgi?id=22117
1046 @safe unittest
1047 {
1048     auto _ = const(SumType!(int[]))([1, 2, 3]);
1049 }
1050 
1051 // Equality of const SumTypes
1052 @safe unittest
1053 {
1054     alias MySum = SumType!int;
1055 
1056     auto _ = const(MySum)(123) == const(MySum)(456);
1057 }
1058 
1059 // Compares reference types using value equality
1060 @safe unittest
1061 {
1062     import std.array : staticArray;
1063 
1064     static struct Field {}
1065     static struct Struct { Field[] fields; }
1066     alias MySum = SumType!Struct;
1067 
1068     static arr1 = staticArray([Field()]);
1069     static arr2 = staticArray([Field()]);
1070 
1071     auto a = MySum(Struct(arr1[]));
1072     auto b = MySum(Struct(arr2[]));
1073 
1074     assert(a == b);
1075 }
1076 
1077 // toString
1078 // Disabled in BetterC due to use of std.conv.text
1079 version (D_BetterC) {} else
1080 @safe unittest
1081 {
1082     import std.conv : text;
1083 
1084     static struct Int { int i; }
1085     static struct Double { double d; }
1086     alias Sum = SumType!(Int, Double);
1087 
1088     assert(Sum(Int(42)).text == Int(42).text, Sum(Int(42)).text);
1089     assert(Sum(Double(33.3)).text == Double(33.3).text, Sum(Double(33.3)).text);
1090     assert((const(Sum)(Int(42))).text == (const(Int)(42)).text, (const(Sum)(Int(42))).text);
1091 }
1092 
1093 // string formatting
1094 // Disabled in BetterC due to use of std.format.format
1095 version (D_BetterC) {} else
1096 @safe unittest
1097 {
1098     import std.format : format;
1099 
1100     SumType!int x = 123;
1101 
1102     assert(format!"%s"(x) == format!"%s"(123));
1103     assert(format!"%x"(x) == format!"%x"(123));
1104 }
1105 
1106 // string formatting of qualified SumTypes
1107 // Disabled in BetterC due to use of std.format.format and dynamic arrays
1108 version (D_BetterC) {} else
1109 @safe unittest
1110 {
1111     import std.format : format;
1112 
1113     int[] a = [1, 2, 3];
1114     const(SumType!(int[])) x = a;
1115 
1116     assert(format!"%(%d, %)"(x) == format!"%(%s, %)"(a));
1117 }
1118 
1119 // Github issue #16
1120 // Disabled in BetterC due to use of dynamic arrays
1121 version (D_BetterC) {} else
1122 @safe unittest
1123 {
1124     alias Node = SumType!(This[], string);
1125 
1126     // override inference of @system attribute for cyclic functions
1127     assert((() @trusted =>
1128         Node([Node([Node("x")])])
1129         ==
1130         Node([Node([Node("x")])])
1131     )());
1132 }
1133 
1134 // Github issue #16 with const
1135 // Disabled in BetterC due to use of dynamic arrays
1136 version (D_BetterC) {} else
1137 @safe unittest
1138 {
1139     alias Node = SumType!(const(This)[], string);
1140 
1141     // override inference of @system attribute for cyclic functions
1142     assert((() @trusted =>
1143         Node([Node([Node("x")])])
1144         ==
1145         Node([Node([Node("x")])])
1146     )());
1147 }
1148 
1149 // Stale pointers
1150 // Disabled in BetterC due to use of dynamic arrays
1151 version (D_BetterC) {} else
1152 @system unittest
1153 {
1154     alias MySum = SumType!(ubyte, void*[2]);
1155 
1156     MySum x = [null, cast(void*) 0x12345678];
1157     void** p = &x.getByIndex!1[1];
1158     x = ubyte(123);
1159 
1160     assert(*p != cast(void*) 0x12345678);
1161 }
1162 
1163 // Exception-safe assignment
1164 // Disabled in BetterC due to use of exceptions
1165 version (D_BetterC) {} else
1166 @safe unittest
1167 {
1168     static struct A
1169     {
1170         int value = 123;
1171     }
1172 
1173     static struct B
1174     {
1175         int value = 456;
1176         this(this) { throw new Exception("oops"); }
1177     }
1178 
1179     alias MySum = SumType!(A, B);
1180 
1181     MySum x;
1182     try
1183     {
1184         x = B();
1185     }
1186     catch (Exception e) {}
1187 
1188     assert(
1189         (x.tag == 0 && x.getByIndex!0.value == 123) ||
1190         (x.tag == 1 && x.getByIndex!1.value == 456)
1191     );
1192 }
1193 
1194 // Types with @disable this(this)
1195 @safe unittest
1196 {
1197     import core.lifetime : move;
1198 
1199     static struct NoCopy
1200     {
1201         @disable this(this);
1202     }
1203 
1204     alias MySum = SumType!NoCopy;
1205 
1206     NoCopy lval = NoCopy();
1207 
1208     MySum x = NoCopy();
1209     MySum y = NoCopy();
1210 
1211 
1212     assert(!__traits(compiles, SumType!NoCopy(lval)));
1213 
1214     y = NoCopy();
1215     y = move(x);
1216     assert(!__traits(compiles, y = lval));
1217     assert(!__traits(compiles, y = x));
1218 
1219     bool b = x == y;
1220 }
1221 
1222 // Github issue #22
1223 // Disabled in BetterC due to use of std.typecons.Nullable
1224 version (D_BetterC) {} else
1225 @safe unittest
1226 {
1227     import std.typecons;
1228 
1229     static struct A
1230     {
1231         SumType!(Nullable!int) a = Nullable!int.init;
1232     }
1233 }
1234 
1235 // Static arrays of structs with postblits
1236 // Disabled in BetterC due to use of dynamic arrays
1237 version (D_BetterC) {} else
1238 @safe unittest
1239 {
1240     static struct S
1241     {
1242         int n;
1243         this(this) { n++; }
1244     }
1245 
1246     SumType!(S[1]) x = [S(0)];
1247     SumType!(S[1]) y = x;
1248 
1249     auto xval = x.getByIndex!0[0].n;
1250     auto yval = y.getByIndex!0[0].n;
1251 
1252     assert(xval != yval);
1253 }
1254 
1255 // Replacement does not happen inside SumType
1256 // Disabled in BetterC due to use of associative arrays
1257 version (D_BetterC) {} else
1258 @safe unittest
1259 {
1260     import std.typecons : Tuple, ReplaceTypeUnless;
1261     alias A = Tuple!(This*,SumType!(This*))[SumType!(This*,string)[This]];
1262     alias TR = ReplaceTypeUnless!(isSumTypeInstance, This, int, A);
1263     static assert(is(TR == Tuple!(int*,SumType!(This*))[SumType!(This*, string)[int]]));
1264 }
1265 
1266 // Supports nested self-referential SumTypes
1267 @safe unittest
1268 {
1269     import std.typecons : Tuple, Flag;
1270     alias Nat = SumType!(Flag!"0", Tuple!(This*));
1271     alias Inner = SumType!Nat;
1272     alias Outer = SumType!(Nat*, Tuple!(This*, This*));
1273 }
1274 
1275 // Self-referential SumTypes inside Algebraic
1276 // Disabled in BetterC due to use of std.variant.Algebraic
1277 version (D_BetterC) {} else
1278 @safe unittest
1279 {
1280     import std.variant : Algebraic;
1281 
1282     alias T = Algebraic!(SumType!(This*));
1283 
1284     assert(is(T.AllowedTypes[0].Types[0] == T.AllowedTypes[0]*));
1285 }
1286 
1287 // Doesn't call @system postblits in @safe code
1288 @safe unittest
1289 {
1290     static struct SystemCopy { @system this(this) {} }
1291     SystemCopy original;
1292 
1293     assert(!__traits(compiles, () @safe
1294             {
1295         SumType!SystemCopy copy = original;
1296     }));
1297 
1298     assert(!__traits(compiles, () @safe
1299             {
1300         SumType!SystemCopy copy; copy = original;
1301     }));
1302 }
1303 
1304 // Doesn't overwrite pointers in @safe code
1305 @safe unittest
1306 {
1307     alias MySum = SumType!(int*, int);
1308 
1309     MySum x;
1310 
1311     assert(!__traits(compiles, () @safe
1312             {
1313         x = 123;
1314     }));
1315 
1316     assert(!__traits(compiles, () @safe
1317             {
1318         x = MySum(123);
1319     }));
1320 }
1321 
1322 // Calls value postblit on self-assignment
1323 @safe unittest
1324 {
1325     static struct S
1326     {
1327         int n;
1328         this(this) { n++; }
1329     }
1330 
1331     SumType!S x = S();
1332     SumType!S y;
1333     y = x;
1334 
1335     auto xval = x.getByIndex!0.n;
1336     auto yval = y.getByIndex!0.n;
1337 
1338     assert(xval != yval);
1339 }
1340 
1341 // Github issue #29
1342 @safe unittest
1343 {
1344     alias A = SumType!string;
1345 
1346     @safe A createA(string arg)
1347     {
1348         return A(arg);
1349     }
1350 
1351     @safe void test()
1352     {
1353         A a = createA("");
1354     }
1355 }
1356 
1357 // SumTypes as associative array keys
1358 // Disabled in BetterC due to use of associative arrays
1359 version (D_BetterC) {} else
1360 @safe unittest
1361 {
1362     int[SumType!(int, string)] aa;
1363 }
1364 
1365 // toString with non-copyable types
1366 // Disabled in BetterC due to use of std.conv.to (in toString)
1367 version (D_BetterC) {} else
1368 @safe unittest
1369 {
1370     struct NoCopy
1371     {
1372         @disable this(this);
1373     }
1374 
1375     SumType!NoCopy x;
1376 
1377     auto _ = x.toString();
1378 }
1379 
1380 // Can use the result of assignment
1381 @safe unittest
1382 {
1383     alias MySum = SumType!(int, float);
1384 
1385     MySum a = MySum(123);
1386     MySum b = MySum(3.14);
1387 
1388     assert((a = b) == b);
1389     assert((a = MySum(123)) == MySum(123));
1390     assert((a = 3.14) == MySum(3.14));
1391     assert(((a = b) = MySum(123)) == MySum(123));
1392 }
1393 
1394 // Types with copy constructors
1395 @safe unittest
1396 {
1397     static struct S
1398     {
1399         int n;
1400 
1401         this(ref return scope inout S other) inout
1402         {
1403             n = other.n + 1;
1404         }
1405     }
1406 
1407     SumType!S x = S();
1408     SumType!S y = x;
1409 
1410     auto xval = x.getByIndex!0.n;
1411     auto yval = y.getByIndex!0.n;
1412 
1413     assert(xval != yval);
1414 }
1415 
1416 // Copyable by generated copy constructors
1417 @safe unittest
1418 {
1419     static struct Inner
1420     {
1421         ref this(ref inout Inner other) {}
1422     }
1423 
1424     static struct Outer
1425     {
1426         SumType!Inner inner;
1427     }
1428 
1429     Outer x;
1430     Outer y = x;
1431 }
1432 
1433 // Types with qualified copy constructors
1434 @safe unittest
1435 {
1436     static struct ConstCopy
1437     {
1438         int n;
1439         this(inout int n) inout { this.n = n; }
1440         this(ref const typeof(this) other) const { this.n = other.n; }
1441     }
1442 
1443     static struct ImmutableCopy
1444     {
1445         int n;
1446         this(inout int n) inout { this.n = n; }
1447         this(ref immutable typeof(this) other) immutable { this.n = other.n; }
1448     }
1449 
1450     const SumType!ConstCopy x = const(ConstCopy)(1);
1451     immutable SumType!ImmutableCopy y = immutable(ImmutableCopy)(1);
1452 }
1453 
1454 // Types with disabled opEquals
1455 @safe unittest
1456 {
1457     static struct S
1458     {
1459         @disable bool opEquals(const S rhs) const;
1460     }
1461 
1462     auto _ = SumType!S(S());
1463 }
1464 
1465 // Types with non-const opEquals
1466 @safe unittest
1467 {
1468     static struct S
1469     {
1470         int i;
1471         bool opEquals(S rhs) { return i == rhs.i; }
1472     }
1473 
1474     auto _ = SumType!S(S(123));
1475 }
1476 
1477 // Incomparability of different SumTypes
1478 @safe unittest
1479 {
1480     SumType!(int, string) x = 123;
1481     SumType!(string, int) y = 123;
1482 
1483     assert(!__traits(compiles, x != y));
1484 }
1485 
1486 // Self-reference in return/parameter type of function pointer member
1487 // Disabled in BetterC due to use of delegates
1488 version (D_BetterC) {} else
1489 @safe unittest
1490 {
1491     alias T = SumType!(int, This delegate(This));
1492 }
1493 
1494 // Construction and assignment from implicitly-convertible lvalue
1495 @safe unittest
1496 {
1497     alias MySum = SumType!bool;
1498 
1499     const(bool) b = true;
1500 
1501     MySum x = b;
1502     MySum y; y = b;
1503 }
1504 
1505 // @safe assignment to the only pointer type in a SumType
1506 @safe unittest
1507 {
1508     SumType!(string, int) sm = 123;
1509     sm = "this should be @safe";
1510 }
1511 
1512 // Pointers to local variables
1513 // https://issues.dlang.org/show_bug.cgi?id=22117
1514 @safe unittest
1515 {
1516     int n = 123;
1517     immutable int ni = 456;
1518 
1519     SumType!(int*) s = &n;
1520     const SumType!(int*) sc = &n;
1521     immutable SumType!(int*) si = &ni;
1522 }
1523 
1524 // Immutable member type with copy constructor
1525 // https://issues.dlang.org/show_bug.cgi?id=22572
1526 @safe unittest
1527 {
1528     static struct CopyConstruct
1529     {
1530         this(ref inout CopyConstruct other) inout {}
1531     }
1532 
1533     static immutable struct Value
1534     {
1535         CopyConstruct c;
1536     }
1537 
1538     SumType!Value s;
1539 }
1540 
1541 // Construction of inout-qualified SumTypes
1542 // https://issues.dlang.org/show_bug.cgi?id=22901
1543 @safe unittest
1544 {
1545     static inout(SumType!(int[])) example(inout(int[]) arr)
1546     {
1547         return inout(SumType!(int[]))(arr);
1548     }
1549 }
1550 
1551 // Assignment of struct with overloaded opAssign in CTFE
1552 // https://issues.dlang.org/show_bug.cgi?id=23182
1553 @safe unittest
1554 {
1555     static struct HasOpAssign
1556     {
1557         void opAssign(HasOpAssign rhs) {}
1558     }
1559 
1560     static SumType!HasOpAssign test()
1561     {
1562         SumType!HasOpAssign s;
1563         // Test both overloads
1564         s = HasOpAssign();
1565         s = SumType!HasOpAssign();
1566         return s;
1567     }
1568 
1569     // Force CTFE
1570     enum result = test();
1571 }
1572 
1573 // https://github.com/dlang/phobos/issues/10563
1574 // Do not waste space for tag if sumtype has only single type
1575 @safe unittest
1576 {
1577     static assert(SumType!int.sizeof == int.sizeof);
1578 }
1579 
1580 /// True if `T` is an instance of the `SumType` template, otherwise false.
1581 private enum bool isSumTypeInstance(T) = is(T == SumType!Args, Args...);
1582 
1583 @safe unittest
1584 {
1585     static struct Wrapper
1586     {
1587         SumType!int s;
1588         alias s this;
1589     }
1590 
1591     assert(isSumTypeInstance!(SumType!int));
1592     assert(!isSumTypeInstance!Wrapper);
1593 }
1594 
1595 /// True if `T` is a [SumType] or implicitly converts to one, otherwise false.
1596 enum bool isSumType(T) = is(T : SumType!Args, Args...);
1597 
1598 ///
1599 @safe unittest
1600 {
1601     static struct ConvertsToSumType
1602     {
1603         SumType!int payload;
1604         alias payload this;
1605     }
1606 
1607     static struct ContainsSumType
1608     {
1609         SumType!int payload;
1610     }
1611 
1612     assert(isSumType!(SumType!int));
1613     assert(isSumType!ConvertsToSumType);
1614     assert(!isSumType!ContainsSumType);
1615 }
1616 
1617 /**
1618  * Calls a type-appropriate function with the value held in a [SumType].
1619  *
1620  * For each possible type the [SumType] can hold, the given handlers are
1621  * checked, in order, to see whether they accept a single argument of that type.
1622  * The first one that does is chosen as the match for that type. (Note that the
1623  * first match may not always be the most exact match.
1624  * See ["Avoiding unintentional matches"](#avoiding-unintentional-matches) for
1625  * one common pitfall.)
1626  *
1627  * Every type must have a matching handler, and every handler must match at
1628  * least one type. This is enforced at compile time.
1629  *
1630  * Handlers may be functions, delegates, or objects with `opCall` overloads. If
1631  * a function with more than one overload is given as a handler, all of the
1632  * overloads are considered as potential matches.
1633  *
1634  * Templated handlers are also accepted, and will match any type for which they
1635  * can be [implicitly instantiated](https://dlang.org/glossary.html#ifti).
1636  * (Remember that a $(DDSUBLINK spec/expression,function_literals, function literal)
1637  * without an explicit argument type is considered a template.)
1638  *
1639  * If multiple [SumType]s are passed to match, their values are passed to the
1640  * handlers as separate arguments, and matching is done for each possible
1641  * combination of value types. See ["Multiple dispatch"](#multiple-dispatch) for
1642  * an example.
1643  *
1644  * Returns:
1645  *   The value returned from the handler that matches the currently-held type.
1646  *
1647  * See_Also: $(REF visit, std,variant)
1648  */
1649 template match(handlers...)
1650 {
1651     import std.typecons : Yes;
1652 
1653     /**
1654      * The actual `match` function.
1655      *
1656      * Params:
1657      *   args = One or more [SumType] objects.
1658      */
1659     auto ref match(SumTypes...)(auto ref SumTypes args)
1660     if (allSatisfy!(isSumType, SumTypes) && args.length > 0)
1661     {
1662         return matchImpl!(Yes.exhaustive, handlers)(args);
1663     }
1664 }
1665 
1666 /** $(DIVID avoiding-unintentional-matches, $(H3 Avoiding unintentional matches))
1667  *
1668  * Sometimes, implicit conversions may cause a handler to match more types than
1669  * intended. The example below shows two solutions to this problem.
1670  */
1671 @safe unittest
1672 {
1673     alias Number = SumType!(double, int);
1674 
1675     Number x;
1676 
1677     // Problem: because int implicitly converts to double, the double
1678     // handler is used for both types, and the int handler never matches.
1679     assert(!__traits(compiles,
1680         x.match!(
1681             (double d) => "got double",
1682             (int n) => "got int"
1683         )
1684     ));
1685 
1686     // Solution 1: put the handler for the "more specialized" type (in this
1687     // case, int) before the handler for the type it converts to.
1688     assert(__traits(compiles,
1689         x.match!(
1690             (int n) => "got int",
1691             (double d) => "got double"
1692         )
1693     ));
1694 
1695     // Solution 2: use a template that only accepts the exact type it's
1696     // supposed to match, instead of any type that implicitly converts to it.
1697     alias exactly(T, alias fun) = function (arg)
1698     {
1699         static assert(is(typeof(arg) == T));
1700         return fun(arg);
1701     };
1702 
1703     // Now, even if we put the double handler first, it will only be used for
1704     // doubles, not ints.
1705     assert(__traits(compiles,
1706         x.match!(
1707             exactly!(double, d => "got double"),
1708             exactly!(int, n => "got int")
1709         )
1710     ));
1711 }
1712 
1713 /** $(DIVID multiple-dispatch, $(H3 Multiple dispatch))
1714  *
1715  * Pattern matching can be performed on multiple `SumType`s at once by passing
1716  * handlers with multiple arguments. This usually leads to more concise code
1717  * than using nested calls to `match`, as show below.
1718  */
1719 @safe unittest
1720 {
1721     struct Point2D { double x, y; }
1722     struct Point3D { double x, y, z; }
1723 
1724     alias Point = SumType!(Point2D, Point3D);
1725 
1726     version (none)
1727     {
1728         // This function works, but the code is ugly and repetitive.
1729         // It uses three separate calls to match!
1730         @safe pure nothrow @nogc
1731         bool sameDimensions(Point p1, Point p2)
1732         {
1733             return p1.match!(
1734                 (Point2D _) => p2.match!(
1735                     (Point2D _) => true,
1736                     _ => false
1737                 ),
1738                 (Point3D _) => p2.match!(
1739                     (Point3D _) => true,
1740                     _ => false
1741                 )
1742             );
1743         }
1744     }
1745 
1746     // This version is much nicer.
1747     @safe pure nothrow @nogc
1748     bool sameDimensions(Point p1, Point p2)
1749     {
1750         alias doMatch = match!(
1751             (Point2D _1, Point2D _2) => true,
1752             (Point3D _1, Point3D _2) => true,
1753             (_1, _2) => false
1754         );
1755 
1756         return doMatch(p1, p2);
1757     }
1758 
1759     Point a = Point2D(1, 2);
1760     Point b = Point2D(3, 4);
1761     Point c = Point3D(5, 6, 7);
1762     Point d = Point3D(8, 9, 0);
1763 
1764     assert( sameDimensions(a, b));
1765     assert( sameDimensions(c, d));
1766     assert(!sameDimensions(a, c));
1767     assert(!sameDimensions(d, b));
1768 }
1769 
1770 /**
1771  * Attempts to call a type-appropriate function with the value held in a
1772  * [SumType], and throws on failure.
1773  *
1774  * Matches are chosen using the same rules as [match], but are not required to
1775  * be exhaustive—in other words, a type (or combination of types) is allowed to
1776  * have no matching handler. If a type without a handler is encountered at
1777  * runtime, a [MatchException] is thrown.
1778  *
1779  * Not available when compiled with `-betterC`.
1780  *
1781  * Returns:
1782  *   The value returned from the handler that matches the currently-held type,
1783  *   if a handler was given for that type.
1784  *
1785  * Throws:
1786  *   [MatchException], if the currently-held type has no matching handler.
1787  *
1788  * See_Also: $(REF tryVisit, std,variant)
1789  */
1790 version (D_Exceptions)
1791 template tryMatch(handlers...)
1792 {
1793     import std.typecons : No;
1794 
1795     /**
1796      * The actual `tryMatch` function.
1797      *
1798      * Params:
1799      *   args = One or more [SumType] objects.
1800      */
1801     auto ref tryMatch(SumTypes...)(auto ref SumTypes args)
1802     if (allSatisfy!(isSumType, SumTypes) && args.length > 0)
1803     {
1804         return matchImpl!(No.exhaustive, handlers)(args);
1805     }
1806 }
1807 
1808 /**
1809  * Thrown by [tryMatch] when an unhandled type is encountered.
1810  *
1811  * Not available when compiled with `-betterC`.
1812  */
1813 version (D_Exceptions)
1814 class MatchException : Exception
1815 {
1816     ///
1817     pure @safe @nogc nothrow
1818     this(string msg, string file = __FILE__, size_t line = __LINE__)
1819     {
1820         super(msg, file, line);
1821     }
1822 }
1823 
1824 /**
1825  * True if `handler` is a potential match for `Ts`, otherwise false.
1826  *
1827  * See the documentation for [match] for a full explanation of how matches are
1828  * chosen.
1829  */
1830 template canMatch(alias handler, Ts...)
1831 if (Ts.length > 0)
1832 {
1833     enum canMatch = is(typeof(auto ref (ref Ts args) => handler(args)));
1834 }
1835 
1836 ///
1837 @safe unittest
1838 {
1839     alias handleInt = (int i) => "got an int";
1840 
1841     assert( canMatch!(handleInt, int));
1842     assert(!canMatch!(handleInt, string));
1843 }
1844 
1845 // Includes all overloads of the given handler
1846 @safe unittest
1847 {
1848     static struct OverloadSet
1849     {
1850         static void fun(int n) {}
1851         static void fun(double d) {}
1852     }
1853 
1854     assert(canMatch!(OverloadSet.fun, int));
1855     assert(canMatch!(OverloadSet.fun, double));
1856 }
1857 
1858 // Allows returning non-copyable types by ref
1859 // https://github.com/dlang/phobos/issues/10647
1860 @safe unittest
1861 {
1862     static struct NoCopy
1863     {
1864         @disable this(this);
1865     }
1866 
1867     static NoCopy lvalue;
1868     static ref handler(int _) => lvalue;
1869 
1870     assert(canMatch!(handler, int));
1871 }
1872 
1873 // Like aliasSeqOf!(iota(n)), but works in BetterC
1874 private template Iota(size_t n)
1875 {
1876     static if (n == 0)
1877     {
1878         alias Iota = AliasSeq!();
1879     }
1880     else
1881     {
1882         alias Iota = AliasSeq!(Iota!(n - 1), n - 1);
1883     }
1884 }
1885 
1886 @safe unittest
1887 {
1888     assert(is(Iota!0 == AliasSeq!()));
1889     assert(Iota!1 == AliasSeq!(0));
1890     assert(Iota!3 == AliasSeq!(0, 1, 2));
1891 }
1892 
1893 private template matchImpl(Flag!"exhaustive" exhaustive, handlers...)
1894 {
1895     auto ref matchImpl(SumTypes...)(auto ref SumTypes args)
1896     if (allSatisfy!(isSumType, SumTypes) && args.length > 0)
1897     {
1898         // Single dispatch (fast path)
1899         static if (args.length == 1)
1900         {
1901             /* When there's only one argument, the caseId is just that
1902              * argument's tag, so there's no need for TagTuple.
1903              */
1904             enum handlerArgs(size_t caseId) =
1905                 "args[0].getByIndex!(" ~ toCtString!caseId ~ ")()";
1906 
1907             alias valueTypes(size_t caseId) =
1908                 typeof(args[0].getByIndex!(caseId)());
1909 
1910             enum numCases = SumTypes[0].Types.length;
1911         }
1912         // Multiple dispatch (slow path)
1913         else
1914         {
1915             alias typeCounts = Map!(typeCount, SumTypes);
1916             alias stride(size_t i) = .stride!(i, typeCounts);
1917             alias TagTuple = .TagTuple!typeCounts;
1918 
1919             alias handlerArgs(size_t caseId) = .handlerArgs!(caseId, typeCounts);
1920 
1921             /* An AliasSeq of the types of the member values in the argument list
1922              * returned by `handlerArgs!caseId`.
1923              *
1924              * Note that these are the actual (that is, qualified) types of the
1925              * member values, which may not be the same as the types listed in
1926              * the arguments' `.Types` properties.
1927              */
1928             template valueTypes(size_t caseId)
1929             {
1930                 enum tags = TagTuple.fromCaseId(caseId);
1931 
1932                 template getType(size_t i)
1933                 {
1934                     alias getType = typeof(args[i].getByIndex!(tags[i])());
1935                 }
1936 
1937                 alias valueTypes = Map!(getType, Iota!(tags.length));
1938             }
1939 
1940             /* The total number of cases is
1941              *
1942              *   Π SumTypes[i].Types.length for 0 ≤ i < SumTypes.length
1943              *
1944              * Conveniently, this is equal to stride!(SumTypes.length), so we can
1945              * use that function to compute it.
1946              */
1947             enum numCases = stride!(SumTypes.length);
1948         }
1949 
1950         /* Guaranteed to never be a valid handler index, since
1951          * handlers.length <= size_t.max.
1952          */
1953         enum noMatch = size_t.max;
1954 
1955         // An array that maps caseIds to handler indices ("hids").
1956         enum matches = ()
1957         {
1958             size_t[numCases] result;
1959 
1960             // Workaround for https://issues.dlang.org/show_bug.cgi?id=19561
1961             foreach (ref match; result)
1962             {
1963                 match = noMatch;
1964             }
1965 
1966             static foreach (caseId; 0 .. numCases)
1967             {
1968                 static foreach (hid, handler; handlers)
1969                 {
1970                     static if (canMatch!(handler, valueTypes!caseId))
1971                     {
1972                         if (result[caseId] == noMatch)
1973                         {
1974                             result[caseId] = hid;
1975                         }
1976                     }
1977                 }
1978             }
1979 
1980             return result;
1981         }();
1982 
1983         import std.algorithm.searching : canFind;
1984 
1985         // Check for unreachable handlers
1986         static foreach (hid, handler; handlers)
1987         {
1988             static assert(matches[].canFind(hid),
1989                 "`handlers[" ~ toCtString!hid ~ "]` " ~
1990                 "of type `" ~ ( __traits(isTemplate, handler)
1991                     ? "template"
1992                     : typeof(handler).stringof
1993                 ) ~ "` " ~
1994                 "never matches. Perhaps the handler failed to compile"
1995             );
1996         }
1997 
1998         // Workaround for https://issues.dlang.org/show_bug.cgi?id=19993
1999         enum handlerName(size_t hid) = "handler" ~ toCtString!hid;
2000 
2001         static foreach (size_t hid, handler; handlers)
2002         {
2003             mixin("alias ", handlerName!hid, " = handler;");
2004         }
2005 
2006         // Single dispatch (fast path)
2007         static if (args.length == 1)
2008             immutable argsId = args[0].tag;
2009         // Multiple dispatch (slow path)
2010         else
2011             immutable argsId = TagTuple(args).toCaseId;
2012 
2013         final switch (argsId)
2014         {
2015             static foreach (caseId; 0 .. numCases)
2016             {
2017                 case caseId:
2018                     static if (matches[caseId] != noMatch)
2019                     {
2020                         return mixin(handlerName!(matches[caseId]), "(", handlerArgs!caseId, ")");
2021                     }
2022                     else
2023                     {
2024                         static if (exhaustive)
2025                         {
2026                             static assert(false,
2027                                 "No matching handler for types `" ~ valueTypes!caseId.stringof ~ "`");
2028                         }
2029                         else
2030                         {
2031                             throw new MatchException(
2032                                 "No matching handler for types `" ~ valueTypes!caseId.stringof ~ "`");
2033                         }
2034                     }
2035             }
2036         }
2037 
2038         assert(false, "unreachable");
2039     }
2040 }
2041 
2042 // Predicate for staticMap
2043 private enum typeCount(SumType) = SumType.Types.length;
2044 
2045 /* A TagTuple represents a single possible set of tags that the arguments to
2046  * `matchImpl` could have at runtime.
2047  *
2048  * Because D does not allow a struct to be the controlling expression
2049  * of a switch statement, we cannot dispatch on the TagTuple directly.
2050  * Instead, we must map each TagTuple to a unique integer and generate
2051  * a case label for each of those integers.
2052  *
2053  * This mapping is implemented in `fromCaseId` and `toCaseId`. It uses
2054  * the same technique that's used to map index tuples to memory offsets
2055  * in a multidimensional static array.
2056  *
2057  * For example, when `args` consists of two SumTypes with two member
2058  * types each, the TagTuples corresponding to each case label are:
2059  *
2060  *   case 0:  TagTuple([0, 0])
2061  *   case 1:  TagTuple([1, 0])
2062  *   case 2:  TagTuple([0, 1])
2063  *   case 3:  TagTuple([1, 1])
2064  *
2065  * When there is only one argument, the caseId is equal to that
2066  * argument's tag.
2067  */
2068 private struct TagTuple(typeCounts...)
2069 {
2070     size_t[typeCounts.length] tags;
2071     alias tags this;
2072 
2073     alias stride(size_t i) = .stride!(i, typeCounts);
2074 
2075     invariant
2076     {
2077         static foreach (i; 0 .. tags.length)
2078         {
2079             assert(tags[i] < typeCounts[i], "Invalid tag");
2080         }
2081     }
2082 
2083     this(SumTypes...)(ref const SumTypes args)
2084     if (allSatisfy!(isSumType, SumTypes) && args.length == typeCounts.length)
2085     {
2086         static foreach (i; 0 .. tags.length)
2087         {
2088             tags[i] = args[i].tag;
2089         }
2090     }
2091 
2092     static TagTuple fromCaseId(size_t caseId)
2093     {
2094         TagTuple result;
2095 
2096         // Most-significant to least-significant
2097         static foreach_reverse (i; 0 .. result.length)
2098         {
2099             result[i] = caseId / stride!i;
2100             caseId %= stride!i;
2101         }
2102 
2103         return result;
2104     }
2105 
2106     size_t toCaseId()
2107     {
2108         size_t result;
2109 
2110         static foreach (i; 0 .. tags.length)
2111         {
2112             result += tags[i] * stride!i;
2113         }
2114 
2115         return result;
2116     }
2117 }
2118 
2119 /* The number that the dim-th argument's tag is multiplied by when
2120  * converting TagTuples to and from case indices ("caseIds").
2121  *
2122  * Named by analogy to the stride that the dim-th index into a
2123  * multidimensional static array is multiplied by to calculate the
2124  * offset of a specific element.
2125  */
2126 private size_t stride(size_t dim, lengths...)()
2127 {
2128     import core.checkedint : mulu;
2129 
2130     size_t result = 1;
2131     bool overflow = false;
2132 
2133     static foreach (i; 0 .. dim)
2134     {
2135         result = mulu(result, lengths[i], overflow);
2136     }
2137 
2138     /* The largest number matchImpl uses, numCases, is calculated with
2139      * stride!(SumTypes.length), so as long as this overflow check
2140      * passes, we don't need to check for overflow anywhere else.
2141      */
2142     assert(!overflow, "Integer overflow");
2143     return result;
2144 }
2145 
2146 /* A list of arguments to be passed to a handler needed for the case
2147  * labeled with `caseId`.
2148  */
2149 private template handlerArgs(size_t caseId, typeCounts...)
2150 {
2151     enum tags = TagTuple!typeCounts.fromCaseId(caseId);
2152 
2153     alias handlerArgs = AliasSeq!();
2154 
2155     static foreach (i; 0 .. tags.length)
2156     {
2157         handlerArgs = AliasSeq!(
2158             handlerArgs,
2159             "args[" ~ toCtString!i ~ "].getByIndex!(" ~ toCtString!(tags[i]) ~ ")(), "
2160         );
2161     }
2162 }
2163 
2164 // Matching
2165 @safe unittest
2166 {
2167     alias MySum = SumType!(int, float);
2168 
2169     MySum x = MySum(42);
2170     MySum y = MySum(3.14);
2171 
2172     assert(x.match!((int v) => true, (float v) => false));
2173     assert(y.match!((int v) => false, (float v) => true));
2174 }
2175 
2176 // Missing handlers
2177 @safe unittest
2178 {
2179     alias MySum = SumType!(int, float);
2180 
2181     MySum x = MySum(42);
2182 
2183     assert(!__traits(compiles, x.match!((int x) => true)));
2184     assert(!__traits(compiles, x.match!()));
2185 }
2186 
2187 // Handlers with qualified parameters
2188 // Disabled in BetterC due to use of dynamic arrays
2189 version (D_BetterC) {} else
2190 @safe unittest
2191 {
2192     alias MySum = SumType!(int[], float[]);
2193 
2194     MySum x = MySum([1, 2, 3]);
2195     MySum y = MySum([1.0, 2.0, 3.0]);
2196 
2197     assert(x.match!((const(int[]) v) => true, (const(float[]) v) => false));
2198     assert(y.match!((const(int[]) v) => false, (const(float[]) v) => true));
2199 }
2200 
2201 // Handlers for qualified types
2202 // Disabled in BetterC due to use of dynamic arrays
2203 version (D_BetterC) {} else
2204 @safe unittest
2205 {
2206     alias MySum = SumType!(immutable(int[]), immutable(float[]));
2207 
2208     MySum x = MySum([1, 2, 3]);
2209 
2210     assert(x.match!((immutable(int[]) v) => true, (immutable(float[]) v) => false));
2211     assert(x.match!((const(int[]) v) => true, (const(float[]) v) => false));
2212     // Tail-qualified parameters
2213     assert(x.match!((immutable(int)[] v) => true, (immutable(float)[] v) => false));
2214     assert(x.match!((const(int)[] v) => true, (const(float)[] v) => false));
2215     // Generic parameters
2216     assert(x.match!((immutable v) => true));
2217     assert(x.match!((const v) => true));
2218     // Unqualified parameters
2219     assert(!__traits(compiles,
2220         x.match!((int[] v) => true, (float[] v) => false)
2221     ));
2222 }
2223 
2224 // Delegate handlers
2225 // Disabled in BetterC due to use of closures
2226 version (D_BetterC) {} else
2227 @safe unittest
2228 {
2229     alias MySum = SumType!(int, float);
2230 
2231     int answer = 42;
2232     MySum x = MySum(42);
2233     MySum y = MySum(3.14);
2234 
2235     assert(x.match!((int v) => v == answer, (float v) => v == answer));
2236     assert(!y.match!((int v) => v == answer, (float v) => v == answer));
2237 }
2238 
2239 version (unittest)
2240 {
2241     version (D_BetterC)
2242     {
2243         // std.math.isClose depends on core.runtime.math, so use a
2244         // libc-based version for testing with -betterC
2245         @safe pure @nogc nothrow
2246         private bool isClose(double lhs, double rhs)
2247         {
2248             import core.stdc.math : fabs;
2249 
2250             return fabs(lhs - rhs) < 1e-5;
2251         }
2252     }
2253     else
2254     {
2255         import std.math.operations : isClose;
2256     }
2257 }
2258 
2259 // Generic handler
2260 @safe unittest
2261 {
2262     alias MySum = SumType!(int, float);
2263 
2264     MySum x = MySum(42);
2265     MySum y = MySum(3.14);
2266 
2267     assert(x.match!(v => v*2) == 84);
2268     assert(y.match!(v => v*2).isClose(6.28));
2269 }
2270 
2271 // Fallback to generic handler
2272 // Disabled in BetterC due to use of std.conv.to
2273 version (D_BetterC) {} else
2274 @safe unittest
2275 {
2276     import std.conv : to;
2277 
2278     alias MySum = SumType!(int, float, string);
2279 
2280     MySum x = MySum(42);
2281     MySum y = MySum("42");
2282 
2283     assert(x.match!((string v) => v.to!int, v => v*2) == 84);
2284     assert(y.match!((string v) => v.to!int, v => v*2) == 42);
2285 }
2286 
2287 // Multiple non-overlapping generic handlers
2288 @safe unittest
2289 {
2290     import std.array : staticArray;
2291 
2292     alias MySum = SumType!(int, float, int[], char[]);
2293 
2294     static ints = staticArray([1, 2, 3]);
2295     static chars = staticArray(['a', 'b', 'c']);
2296 
2297     MySum x = MySum(42);
2298     MySum y = MySum(3.14);
2299     MySum z = MySum(ints[]);
2300     MySum w = MySum(chars[]);
2301 
2302     assert(x.match!(v => v*2, v => v.length) == 84);
2303     assert(y.match!(v => v*2, v => v.length).isClose(6.28));
2304     assert(w.match!(v => v*2, v => v.length) == 3);
2305     assert(z.match!(v => v*2, v => v.length) == 3);
2306 }
2307 
2308 // Structural matching
2309 @safe unittest
2310 {
2311     static struct S1 { int x; }
2312     static struct S2 { int y; }
2313     alias MySum = SumType!(S1, S2);
2314 
2315     MySum a = MySum(S1(0));
2316     MySum b = MySum(S2(0));
2317 
2318     assert(a.match!(s1 => s1.x + 1, s2 => s2.y - 1) == 1);
2319     assert(b.match!(s1 => s1.x + 1, s2 => s2.y - 1) == -1);
2320 }
2321 
2322 // Separate opCall handlers
2323 @safe unittest
2324 {
2325     static struct IntHandler
2326     {
2327         bool opCall(int arg)
2328         {
2329             return true;
2330         }
2331     }
2332 
2333     static struct FloatHandler
2334     {
2335         bool opCall(float arg)
2336         {
2337             return false;
2338         }
2339     }
2340 
2341     alias MySum = SumType!(int, float);
2342 
2343     MySum x = MySum(42);
2344     MySum y = MySum(3.14);
2345 
2346     assert(x.match!(IntHandler.init, FloatHandler.init));
2347     assert(!y.match!(IntHandler.init, FloatHandler.init));
2348 }
2349 
2350 // Compound opCall handler
2351 @safe unittest
2352 {
2353     static struct CompoundHandler
2354     {
2355         bool opCall(int arg)
2356         {
2357             return true;
2358         }
2359 
2360         bool opCall(float arg)
2361         {
2362             return false;
2363         }
2364     }
2365 
2366     alias MySum = SumType!(int, float);
2367 
2368     MySum x = MySum(42);
2369     MySum y = MySum(3.14);
2370 
2371     assert(x.match!(CompoundHandler.init));
2372     assert(!y.match!(CompoundHandler.init));
2373 }
2374 
2375 // Ordered matching
2376 @safe unittest
2377 {
2378     alias MySum = SumType!(int, float);
2379 
2380     MySum x = MySum(42);
2381 
2382     assert(x.match!((int v) => true, v => false));
2383 }
2384 
2385 // Non-exhaustive matching
2386 version (D_Exceptions)
2387 @system unittest
2388 {
2389     import std.exception : assertThrown, assertNotThrown;
2390 
2391     alias MySum = SumType!(int, float);
2392 
2393     MySum x = MySum(42);
2394     MySum y = MySum(3.14);
2395 
2396     assertNotThrown!MatchException(x.tryMatch!((int n) => true));
2397     assertThrown!MatchException(y.tryMatch!((int n) => true));
2398 }
2399 
2400 // Non-exhaustive matching in @safe code
2401 version (D_Exceptions)
2402 @safe unittest
2403 {
2404     SumType!(int, float) x;
2405 
2406     auto _ = x.tryMatch!(
2407         (int n) => n + 1,
2408     );
2409 }
2410 
2411 // Handlers with ref parameters
2412 @safe unittest
2413 {
2414     alias Value = SumType!(long, double);
2415 
2416     auto value = Value(3.14);
2417 
2418     value.match!(
2419         (long) {},
2420         (ref double d) { d *= 2; }
2421     );
2422 
2423     assert(value.getByIndex!1.isClose(6.28));
2424 }
2425 
2426 // Unreachable handlers
2427 @safe unittest
2428 {
2429     alias MySum = SumType!(int, string);
2430 
2431     MySum s;
2432 
2433     assert(!__traits(compiles,
2434         s.match!(
2435             (int _) => 0,
2436             (string _) => 1,
2437             (double _) => 2
2438         )
2439     ));
2440 
2441     assert(!__traits(compiles,
2442         s.match!(
2443             _ => 0,
2444             (int _) => 1
2445         )
2446     ));
2447 }
2448 
2449 // Unsafe handlers
2450 @system unittest
2451 {
2452     SumType!int x;
2453     alias unsafeHandler = (int x) @system { return; };
2454 
2455     assert(!__traits(compiles, () @safe
2456             {
2457         x.match!unsafeHandler;
2458     }));
2459 
2460     auto test() @system
2461     {
2462         return x.match!unsafeHandler;
2463     }
2464 }
2465 
2466 // Overloaded handlers
2467 @safe unittest
2468 {
2469     static struct OverloadSet
2470     {
2471         static string fun(int i) { return "int"; }
2472         static string fun(double d) { return "double"; }
2473     }
2474 
2475     alias MySum = SumType!(int, double);
2476 
2477     MySum a = 42;
2478     MySum b = 3.14;
2479 
2480     assert(a.match!(OverloadSet.fun) == "int");
2481     assert(b.match!(OverloadSet.fun) == "double");
2482 }
2483 
2484 // Overload sets that include SumType arguments
2485 @safe unittest
2486 {
2487     alias Inner = SumType!(int, double);
2488     alias Outer = SumType!(Inner, string);
2489 
2490     static struct OverloadSet
2491     {
2492         @safe:
2493         static string fun(int i) { return "int"; }
2494         static string fun(double d) { return "double"; }
2495         static string fun(string s) { return "string"; }
2496         static string fun(Inner i) { return i.match!fun; }
2497         static string fun(Outer o) { return o.match!fun; }
2498     }
2499 
2500     Outer a = Inner(42);
2501     Outer b = Inner(3.14);
2502     Outer c = "foo";
2503 
2504     assert(OverloadSet.fun(a) == "int");
2505     assert(OverloadSet.fun(b) == "double");
2506     assert(OverloadSet.fun(c) == "string");
2507 }
2508 
2509 // Overload sets with ref arguments
2510 @safe unittest
2511 {
2512     static struct OverloadSet
2513     {
2514         static void fun(ref int i) { i = 42; }
2515         static void fun(ref double d) { d = 3.14; }
2516     }
2517 
2518     alias MySum = SumType!(int, double);
2519 
2520     MySum x = 0;
2521     MySum y = 0.0;
2522 
2523     x.match!(OverloadSet.fun);
2524     y.match!(OverloadSet.fun);
2525 
2526     assert(x.match!((value) => is(typeof(value) == int) && value == 42));
2527     assert(y.match!((value) => is(typeof(value) == double) && value == 3.14));
2528 }
2529 
2530 // Overload sets with templates
2531 @safe unittest
2532 {
2533     import std.traits : isNumeric;
2534 
2535     static struct OverloadSet
2536     {
2537         static string fun(string arg)
2538         {
2539             return "string";
2540         }
2541 
2542         static string fun(T)(T arg)
2543         if (isNumeric!T)
2544         {
2545             return "numeric";
2546         }
2547     }
2548 
2549     alias MySum = SumType!(int, string);
2550 
2551     MySum x = 123;
2552     MySum y = "hello";
2553 
2554     assert(x.match!(OverloadSet.fun) == "numeric");
2555     assert(y.match!(OverloadSet.fun) == "string");
2556 }
2557 
2558 // Github issue #24
2559 @safe unittest
2560 {
2561     void test() @nogc
2562     {
2563         int acc = 0;
2564         SumType!int(1).match!((int x) => acc += x);
2565     }
2566 }
2567 
2568 // Github issue #31
2569 @safe unittest
2570 {
2571     void test() @nogc
2572     {
2573         int acc = 0;
2574 
2575         SumType!(int, string)(1).match!(
2576             (int x) => acc += x,
2577             (string _) => 0,
2578         );
2579     }
2580 }
2581 
2582 // Types that `alias this` a SumType
2583 @safe unittest
2584 {
2585     static struct A {}
2586     static struct B {}
2587     static struct D { SumType!(A, B) value; alias value this; }
2588 
2589     auto _ = D().match!(_ => true);
2590 }
2591 
2592 // Multiple dispatch
2593 @safe unittest
2594 {
2595     alias MySum = SumType!(int, string);
2596 
2597     static int fun(MySum x, MySum y)
2598     {
2599         import std.meta : Args = AliasSeq;
2600 
2601         return Args!(x, y).match!(
2602             (int    xv, int    yv) => 0,
2603             (string xv, int    yv) => 1,
2604             (int    xv, string yv) => 2,
2605             (string xv, string yv) => 3
2606         );
2607     }
2608 
2609     assert(fun(MySum(0),  MySum(0))  == 0);
2610     assert(fun(MySum(""), MySum(0))  == 1);
2611     assert(fun(MySum(0),  MySum("")) == 2);
2612     assert(fun(MySum(""), MySum("")) == 3);
2613 }
2614 
2615 // inout SumTypes
2616 @safe unittest
2617 {
2618     inout(int[]) fun(inout(SumType!(int[])) x)
2619     {
2620         return x.match!((inout(int[]) a) => a);
2621     }
2622 }
2623 
2624 // return ref
2625 // issue: https://issues.dlang.org/show_bug.cgi?id=23101
2626 @safe unittest
2627 {
2628     static assert(!__traits(compiles, () {
2629         SumType!(int, string) st;
2630         return st.match!(
2631             function int* (string x) => assert(0),
2632             function int* (return ref int i) => &i,
2633         );
2634     }));
2635 
2636     SumType!(int, string) st;
2637     static assert(__traits(compiles, () {
2638         return st.match!(
2639             function int* (string x) => null,
2640             function int* (return ref int i) => &i,
2641         );
2642     }));
2643 }
2644 
2645 /**
2646  * Checks whether a `SumType` contains a value of a given type.
2647  *
2648  * The types must match exactly, without implicit conversions.
2649  *
2650  * Params:
2651  *   T = the type to check for.
2652  */
2653 template has(T)
2654 {
2655     /**
2656      * The actual `has` function.
2657      *
2658      * Params:
2659      *   self = the `SumType` to check.
2660      *
2661      * Returns: true if `self` contains a `T`, otherwise false.
2662      */
2663     bool has(Self)(auto ref Self self)
2664     if (isSumType!Self)
2665     {
2666         return self.match!checkType;
2667     }
2668 
2669     // Helper to avoid redundant template instantiations
2670     private bool checkType(Value)(ref Value value)
2671     {
2672         return is(Value == T);
2673     }
2674 }
2675 
2676 /// Basic usage
2677 @safe unittest
2678 {
2679     SumType!(string, double) example = "hello";
2680 
2681     assert( example.has!string);
2682     assert(!example.has!double);
2683 
2684     // If T isn't part of the SumType, has!T will always return false.
2685     assert(!example.has!int);
2686 }
2687 
2688 /// With type qualifiers
2689 @safe unittest
2690 {
2691     alias Example = SumType!(string, double);
2692 
2693     Example m = "mutable";
2694     const Example c = "const";
2695     immutable Example i = "immutable";
2696 
2697     assert( m.has!string);
2698     assert(!m.has!(const(string)));
2699     assert(!m.has!(immutable(string)));
2700 
2701     assert(!c.has!string);
2702     assert( c.has!(const(string)));
2703     assert(!c.has!(immutable(string)));
2704 
2705     assert(!i.has!string);
2706     assert(!i.has!(const(string)));
2707     assert( i.has!(immutable(string)));
2708 }
2709 
2710 /// As a predicate
2711 version (D_BetterC) {} else
2712 @safe unittest
2713 {
2714     import std.algorithm.iteration : filter;
2715     import std.algorithm.comparison : equal;
2716 
2717     alias Example = SumType!(string, double);
2718 
2719     auto arr = [
2720         Example("foo"),
2721         Example(0),
2722         Example("bar"),
2723         Example(1),
2724         Example(2),
2725         Example("baz")
2726     ];
2727 
2728     auto strings = arr.filter!(has!string);
2729     auto nums = arr.filter!(has!double);
2730 
2731     assert(strings.equal([Example("foo"), Example("bar"), Example("baz")]));
2732     assert(nums.equal([Example(0), Example(1), Example(2)]));
2733 }
2734 
2735 // Non-copyable types
2736 @safe unittest
2737 {
2738     static struct NoCopy
2739     {
2740         @disable this(this);
2741     }
2742 
2743     SumType!NoCopy x;
2744 
2745     assert(x.has!NoCopy);
2746 }
2747 
2748 /**
2749  * Accesses a `SumType`'s value.
2750  *
2751  * The value must be of the specified type. Use [has] to check.
2752  *
2753  * Params:
2754  *   T = the type of the value being accessed.
2755  */
2756 template get(T)
2757 {
2758     /**
2759      * The actual `get` function.
2760      *
2761      * Params:
2762      *   self = the `SumType` whose value is being accessed.
2763      *
2764      * Returns: the `SumType`'s value.
2765      */
2766     auto ref T get(Self)(auto ref Self self)
2767     if (isSumType!Self)
2768     {
2769         import std.typecons : No;
2770 
2771         static if (__traits(isRef, self))
2772             return self.match!(getLvalue!(No.try_, T));
2773         else
2774             return self.match!(getRvalue!(No.try_, T));
2775     }
2776 }
2777 
2778 /// Basic usage
2779 @safe unittest
2780 {
2781     SumType!(string, double) example1 = "hello";
2782     SumType!(string, double) example2 = 3.14;
2783 
2784     assert(example1.get!string == "hello");
2785     assert(example2.get!double == 3.14);
2786 }
2787 
2788 /// With type qualifiers
2789 @safe unittest
2790 {
2791     alias Example = SumType!(string, double);
2792 
2793     Example m = "mutable";
2794     const(Example) c = "const";
2795     immutable(Example) i = "immutable";
2796 
2797     assert(m.get!string == "mutable");
2798     assert(c.get!(const(string)) == "const");
2799     assert(i.get!(immutable(string)) == "immutable");
2800 }
2801 
2802 /// As a predicate
2803 version (D_BetterC) {} else
2804 @safe unittest
2805 {
2806     import std.algorithm.iteration : map;
2807     import std.algorithm.comparison : equal;
2808 
2809     alias Example = SumType!(string, double);
2810 
2811     auto arr = [Example(0), Example(1), Example(2)];
2812     auto values = arr.map!(get!double);
2813 
2814     assert(values.equal([0, 1, 2]));
2815 }
2816 
2817 // Non-copyable types
2818 @safe unittest
2819 {
2820     static struct NoCopy
2821     {
2822         @disable this(this);
2823     }
2824 
2825     SumType!NoCopy lvalue;
2826     auto rvalue() => SumType!NoCopy();
2827 
2828     assert(lvalue.get!NoCopy == NoCopy());
2829     assert(rvalue.get!NoCopy == NoCopy());
2830 }
2831 
2832 // Immovable rvalues
2833 @safe unittest
2834 {
2835     auto rvalue() => const(SumType!string)("hello");
2836 
2837     assert(rvalue.get!(const(string)) == "hello");
2838 }
2839 
2840 // Nontrivial rvalues at compile time
2841 @safe unittest
2842 {
2843     static struct ElaborateCopy
2844     {
2845         this(this) {}
2846     }
2847 
2848     enum rvalue = SumType!ElaborateCopy();
2849     enum ctResult = rvalue.get!ElaborateCopy;
2850 
2851     assert(ctResult == ElaborateCopy());
2852 }
2853 
2854 /**
2855  * Attempt to access a `SumType`'s value.
2856  *
2857  * If the `SumType` does not contain a value of the specified type, an
2858  * exception is thrown.
2859  *
2860  * Params:
2861  *   T = the type of the value being accessed.
2862  */
2863 version (D_Exceptions)
2864 template tryGet(T)
2865 {
2866     /**
2867      * The actual `tryGet` function.
2868      *
2869      * Params:
2870      *   self = the `SumType` whose value is being accessed.
2871      *
2872      * Throws: `MatchException` if the value does not have the expected type.
2873      *
2874      * Returns: the `SumType`'s value.
2875      */
2876     auto ref T tryGet(Self)(auto ref Self self)
2877     if (isSumType!Self)
2878     {
2879         import std.typecons : Yes;
2880 
2881         static if (__traits(isRef, self))
2882             return self.match!(getLvalue!(Yes.try_, T));
2883         else
2884             return self.match!(getRvalue!(Yes.try_, T));
2885     }
2886 }
2887 
2888 /// Basic usage
2889 version (D_Exceptions)
2890 @safe unittest
2891 {
2892     SumType!(string, double) example = "hello";
2893 
2894     assert(example.tryGet!string == "hello");
2895 
2896     double result = double.nan;
2897     try
2898         result = example.tryGet!double;
2899     catch (MatchException e)
2900         result = 0;
2901 
2902     // Exception was thrown
2903     assert(result == 0);
2904 }
2905 
2906 /// With type qualifiers
2907 version (D_Exceptions)
2908 @safe unittest
2909 {
2910     import std.exception : assertThrown;
2911 
2912     const(SumType!(string, double)) example = "const";
2913 
2914     // Qualifier mismatch; throws exception
2915     assertThrown!MatchException(example.tryGet!string);
2916     // Qualifier matches; no exception
2917     assert(example.tryGet!(const(string)) == "const");
2918 }
2919 
2920 /// As a predicate
2921 version (D_BetterC) {} else
2922 @safe unittest
2923 {
2924     import std.algorithm.iteration : map, sum;
2925     import std.functional : pipe;
2926     import std.exception : assertThrown;
2927 
2928     alias Example = SumType!(string, double);
2929 
2930     auto arr1 = [Example(0), Example(1), Example(2)];
2931     auto arr2 = [Example("foo"), Example("bar"), Example("baz")];
2932 
2933     alias trySum = pipe!(map!(tryGet!double), sum);
2934 
2935     assert(trySum(arr1) == 0 + 1 + 2);
2936     assertThrown!MatchException(trySum(arr2));
2937 }
2938 
2939 // Throws if requested type is impossible
2940 version (D_Exceptions)
2941 @safe unittest
2942 {
2943     import std.exception : assertThrown;
2944 
2945     SumType!int x;
2946 
2947     assertThrown!MatchException(x.tryGet!string);
2948 }
2949 
2950 // Non-copyable types
2951 version (D_Exceptions)
2952 @safe unittest
2953 {
2954     static struct NoCopy
2955     {
2956         @disable this(this);
2957     }
2958 
2959     SumType!NoCopy lvalue;
2960     auto rvalue() => SumType!NoCopy();
2961 
2962     assert(lvalue.tryGet!NoCopy == NoCopy());
2963     assert(rvalue.tryGet!NoCopy == NoCopy());
2964 }
2965 
2966 // Immovable rvalues
2967 version (D_Exceptions)
2968 @safe unittest
2969 {
2970     auto rvalue() => const(SumType!string)("hello");
2971 
2972     assert(rvalue.tryGet!(const(string)) == "hello");
2973 }
2974 
2975 // Nontrivial rvalues at compile time
2976 version (D_Exceptions)
2977 @safe unittest
2978 {
2979     static struct ElaborateCopy
2980     {
2981         this(this) {}
2982     }
2983 
2984     enum rvalue = SumType!ElaborateCopy();
2985     enum ctResult = rvalue.tryGet!ElaborateCopy;
2986 
2987     assert(ctResult == ElaborateCopy());
2988 }
2989 
2990 private template failedGetMessage(Expected, Actual)
2991 {
2992     static if (Expected.stringof == Actual.stringof)
2993     {
2994         enum expectedStr = __traits(fullyQualifiedName, Expected);
2995         enum actualStr = __traits(fullyQualifiedName, Actual);
2996     }
2997     else
2998     {
2999         enum expectedStr = Expected.stringof;
3000         enum actualStr = Actual.stringof;
3001     }
3002 
3003     enum failedGetMessage =
3004         "Tried to get `" ~ expectedStr ~ "`" ~
3005         " but found `" ~ actualStr ~ "`";
3006 }
3007 
3008 private template getLvalue(Flag!"try_" try_, T)
3009 {
3010     ref T getLvalue(Value)(ref Value value)
3011     {
3012         static if (is(Value == T))
3013         {
3014             return value;
3015         }
3016         else
3017         {
3018             static if (try_)
3019                 throw new MatchException(failedGetMessage!(T, Value));
3020             else
3021                 assert(false, failedGetMessage!(T, Value));
3022         }
3023     }
3024 }
3025 
3026 private template getRvalue(Flag!"try_" try_, T)
3027 {
3028     T getRvalue(Value)(ref Value value)
3029     {
3030         static if (is(Value == T))
3031         {
3032             import core.lifetime : move;
3033 
3034             // Move if possible; otherwise fall back to copy
3035             static if (is(typeof(move(value))))
3036             {
3037                 static if (isCopyable!Value)
3038                     // Workaround for https://issues.dlang.org/show_bug.cgi?id=21542
3039                     return __ctfe ? value : move(value);
3040                 else
3041                     return move(value);
3042             }
3043             else
3044                 return value;
3045         }
3046         else
3047         {
3048             static if (try_)
3049                 throw new MatchException(failedGetMessage!(T, Value));
3050             else
3051                 assert(false, failedGetMessage!(T, Value));
3052         }
3053     }
3054 }
3055 
3056 private void destroyIfOwner(T)(ref T value)
3057 {
3058     static if (hasElaborateDestructor!T)
3059     {
3060         destroy(value);
3061     }
3062 }