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