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