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