1 /++
2 A sum type for modern D.
3 
4 [SumType] is an alternative to `std.variant.Algebraic` that features:
5 
6 $(LIST
7     * [match|Improved pattern-matching.]
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 )
13 
14 License: MIT
15 Authors: Paul Backus, Atila Neves
16 +/
17 module sumtype;
18 
19 static if (__VERSION__ < 2089L) {
20 template ReplaceTypeUnless(alias pred, From, To, T...)
21 {
22     import std.meta;
23     import std.typecons;
24 
25     static if (T.length == 1)
26     {
27         static if (pred!(T[0]))
28             alias ReplaceTypeUnless = T[0];
29         else static if (is(T[0] == From))
30             alias ReplaceTypeUnless = To;
31         else static if (is(T[0] == const(U), U))
32             alias ReplaceTypeUnless = const(ReplaceTypeUnless!(pred, From, To, U));
33         else static if (is(T[0] == immutable(U), U))
34             alias ReplaceTypeUnless = immutable(ReplaceTypeUnless!(pred, From, To, U));
35         else static if (is(T[0] == shared(U), U))
36             alias ReplaceTypeUnless = shared(ReplaceTypeUnless!(pred, From, To, U));
37         else static if (is(T[0] == U*, U))
38         {
39             static if (is(U == function))
40                 alias ReplaceTypeUnless = replaceTypeInFunctionTypeUnless!(pred, From, To, T[0]);
41             else
42                 alias ReplaceTypeUnless = ReplaceTypeUnless!(pred, From, To, U)*;
43         }
44         else static if (is(T[0] == delegate))
45         {
46             alias ReplaceTypeUnless = replaceTypeInFunctionTypeUnless!(pred, From, To, T[0]);
47         }
48         else static if (is(T[0] == function))
49         {
50             static assert(0, "Function types not supported," ~
51                 " use a function pointer type instead of " ~ T[0].stringof);
52         }
53         else static if (is(T[0] == U!V, alias U, V...))
54         {
55             template replaceTemplateArgs(T...)
56             {
57                 static if (is(typeof(T[0])))    // template argument is value or symbol
58                     enum replaceTemplateArgs = T[0];
59                 else
60                     alias replaceTemplateArgs = ReplaceTypeUnless!(pred, From, To, T[0]);
61             }
62             alias ReplaceTypeUnless = U!(staticMap!(replaceTemplateArgs, V));
63         }
64         else static if (is(T[0] == struct))
65             // don't match with alias this struct below (Issue 15168)
66             alias ReplaceTypeUnless = T[0];
67         else static if (is(T[0] == U[], U))
68             alias ReplaceTypeUnless = ReplaceTypeUnless!(pred, From, To, U)[];
69         else static if (is(T[0] == U[n], U, size_t n))
70             alias ReplaceTypeUnless = ReplaceTypeUnless!(pred, From, To, U)[n];
71         else static if (is(T[0] == U[V], U, V))
72             alias ReplaceTypeUnless =
73                 ReplaceTypeUnless!(pred, From, To, U)[ReplaceTypeUnless!(pred, From, To, V)];
74         else
75             alias ReplaceTypeUnless = T[0];
76     }
77     else static if (T.length > 1)
78     {
79         alias ReplaceTypeUnless = AliasSeq!(ReplaceTypeUnless!(pred, From, To, T[0]),
80             ReplaceTypeUnless!(pred, From, To, T[1 .. $]));
81     }
82     else
83     {
84         alias ReplaceTypeUnless = AliasSeq!();
85     }
86 }
87 } else {
88     import std.typecons: ReplaceTypeUnless;
89 }
90 
91 /// $(H3 Basic usage)
92 version (D_BetterC) {} else
93 @safe unittest {
94     import std.math: approxEqual;
95 
96     struct Fahrenheit { double degrees; }
97     struct Celsius { double degrees; }
98     struct Kelvin { double degrees; }
99 
100     alias Temperature = SumType!(Fahrenheit, Celsius, Kelvin);
101 
102     // Construct from any of the member types.
103     Temperature t1 = Fahrenheit(98.6);
104     Temperature t2 = Celsius(100);
105     Temperature t3 = Kelvin(273);
106 
107     // Use pattern matching to access the value.
108     pure @safe @nogc nothrow
109     Fahrenheit toFahrenheit(Temperature t)
110     {
111         return Fahrenheit(
112             t.match!(
113                 (Fahrenheit f) => f.degrees,
114                 (Celsius c) => c.degrees * 9.0/5 + 32,
115                 (Kelvin k) => k.degrees * 9.0/5 - 459.4
116             )
117         );
118     }
119 
120     assert(toFahrenheit(t1).degrees.approxEqual(98.6));
121     assert(toFahrenheit(t2).degrees.approxEqual(212));
122     assert(toFahrenheit(t3).degrees.approxEqual(32));
123 
124     // Use ref to modify the value in place.
125     pure @safe @nogc nothrow
126     void freeze(ref Temperature t)
127     {
128         t.match!(
129             (ref Fahrenheit f) => f.degrees = 32,
130             (ref Celsius c) => c.degrees = 0,
131             (ref Kelvin k) => k.degrees = 273
132         );
133     }
134 
135     freeze(t1);
136     assert(toFahrenheit(t1).degrees.approxEqual(32));
137 
138     // Use a catch-all handler to give a default result.
139     pure @safe @nogc nothrow
140     bool isFahrenheit(Temperature t)
141     {
142         return t.match!(
143             (Fahrenheit f) => true,
144             _ => false
145         );
146     }
147 
148     assert(isFahrenheit(t1));
149     assert(!isFahrenheit(t2));
150     assert(!isFahrenheit(t3));
151 }
152 
153 /** $(H3 Introspection-based matching)
154  *
155  * In the `length` and `horiz` functions below, the handlers for `match` do not
156  * specify the types of their arguments. Instead, matching is done based on how
157  * the argument is used in the body of the handler: any type with `x` and `y`
158  * properties will be matched by the `rect` handlers, and any type with `r` and
159  * `theta` properties will be matched by the `polar` handlers.
160  */
161 version (D_BetterC) {} else
162 @safe unittest {
163     import std.math: approxEqual, cos, PI, sqrt;
164 
165     struct Rectangular { double x, y; }
166     struct Polar { double r, theta; }
167     alias Vector = SumType!(Rectangular, Polar);
168 
169     pure @safe @nogc nothrow
170     double length(Vector v)
171     {
172         return v.match!(
173             rect => sqrt(rect.x^^2 + rect.y^^2),
174             polar => polar.r
175         );
176     }
177 
178     pure @safe @nogc nothrow
179     double horiz(Vector v)
180     {
181         return v.match!(
182             rect => rect.x,
183             polar => polar.r * cos(polar.theta)
184         );
185     }
186 
187     Vector u = Rectangular(1, 1);
188     Vector v = Polar(1, PI/4);
189 
190     assert(length(u).approxEqual(sqrt(2.0)));
191     assert(length(v).approxEqual(1));
192     assert(horiz(u).approxEqual(1));
193     assert(horiz(v).approxEqual(sqrt(0.5)));
194 }
195 
196 /** $(H3 Arithmetic expression evaluator)
197  *
198  * This example makes use of the special placeholder type `This` to define a
199  * [https://en.wikipedia.org/wiki/Recursive_data_type|recursive data type]: an
200  * [https://en.wikipedia.org/wiki/Abstract_syntax_tree|abstract syntax tree] for
201  * representing simple arithmetic expressions.
202  */
203 version (D_BetterC) {} else
204 @safe unittest {
205     import std.functional: partial;
206     import std.traits: EnumMembers;
207     import std.typecons: Tuple;
208 
209     enum Op : string
210     {
211         Plus  = "+",
212         Minus = "-",
213         Times = "*",
214         Div   = "/"
215     }
216 
217     // An expression is either
218     //  - a number,
219     //  - a variable, or
220     //  - a binary operation combining two sub-expressions.
221     alias Expr = SumType!(
222         double,
223         string,
224         Tuple!(Op, "op", This*, "lhs", This*, "rhs")
225     );
226 
227     // Shorthand for Tuple!(Op, "op", Expr*, "lhs", Expr*, "rhs"),
228     // the Tuple type above with Expr substituted for This.
229     alias BinOp = Expr.Types[2];
230 
231     // Factory function for number expressions
232     pure @safe
233     Expr* num(double value)
234     {
235         return new Expr(value);
236     }
237 
238     // Factory function for variable expressions
239     pure @safe
240     Expr* var(string name)
241     {
242         return new Expr(name);
243     }
244 
245     // Factory function for binary operation expressions
246     pure @safe
247     Expr* binOp(Op op, Expr* lhs, Expr* rhs)
248     {
249         return new Expr(BinOp(op, lhs, rhs));
250     }
251 
252     // Convenience wrappers for creating BinOp expressions
253     alias sum  = partial!(binOp, Op.Plus);
254     alias diff = partial!(binOp, Op.Minus);
255     alias prod = partial!(binOp, Op.Times);
256     alias quot = partial!(binOp, Op.Div);
257 
258     // Evaluate expr, looking up variables in env
259     pure @safe nothrow
260     double eval(Expr expr, double[string] env)
261     {
262         return expr.match!(
263             (double num) => num,
264             (string var) => env[var],
265             (BinOp bop) {
266                 double lhs = eval(*bop.lhs, env);
267                 double rhs = eval(*bop.rhs, env);
268                 final switch(bop.op) {
269                     static foreach(op; EnumMembers!Op) {
270                         case op:
271                             return mixin("lhs" ~ op ~ "rhs");
272                     }
273                 }
274             }
275         );
276     }
277 
278     // Return a "pretty-printed" representation of expr
279     @safe
280     string pprint(Expr expr)
281     {
282         import std.format;
283 
284         return expr.match!(
285             (double num) => "%g".format(num),
286             (string var) => var,
287             (BinOp bop) => "(%s %s %s)".format(
288                 pprint(*bop.lhs),
289                 cast(string) bop.op,
290                 pprint(*bop.rhs)
291             )
292         );
293     }
294 
295     Expr* myExpr = sum(var("a"), prod(num(2), var("b")));
296     double[string] myEnv = ["a":3, "b":4, "c":7];
297 
298     assert(eval(*myExpr, myEnv) == 11);
299     assert(pprint(*myExpr) == "(a + (2 * b))");
300 }
301 
302 // Converts an unsigned integer to a compile-time string constant.
303 private enum toCtString(ulong n) = n.stringof[0 .. $ - 2];
304 
305 @safe unittest {
306 	assert(toCtString!0 == "0");
307 	assert(toCtString!123456 == "123456");
308 }
309 
310 /// `This` placeholder, for use in self-referential types.
311 public import std.variant: This;
312 
313 import std.meta: NoDuplicates;
314 
315 /**
316  * A tagged union that can hold a single value from any of a specified set of
317  * types.
318  *
319  * The value in a `SumType` can be operated on using [match|pattern matching].
320  *
321  * To avoid ambiguity, duplicate types are not allowed (but see the
322  * [sumtype#basic-usage|"basic usage" example] for a workaround).
323  *
324  * The special type `This` can be used as a placeholder to create
325  * self-referential types, just like with `Algebraic`. See the
326  * [sumtype#arithmetic-expression-evaluator|"Arithmetic expression evaluator" example] for
327  * usage.
328  *
329  * A `SumType` is initialized by default to hold the `.init` value of its
330  * first member type, just like a regular union. The version identifier
331  * `SumTypeNoDefaultCtor` can be used to disable this behavior.
332  *
333  * Bugs:
334  *   Types with `@disable`d `opEquals` overloads cannot be members of a
335  *   `SumType`.
336  *
337  * See_Also: `std.variant.Algebraic`
338  */
339 struct SumType(TypeArgs...)
340 	if (is(NoDuplicates!TypeArgs == TypeArgs) && TypeArgs.length > 0)
341 {
342 	import std.meta: AliasSeq, Filter, staticIndexOf, staticMap;
343 	import std.meta: anySatisfy, allSatisfy;
344 	import std.traits: hasElaborateCopyConstructor, hasElaborateDestructor;
345 	import std.traits: isAssignable, isCopyable, isStaticArray;
346 	import std.traits: ConstOf, ImmutableOf;
347 
348 	/// The types a `SumType` can hold.
349 	alias Types = AliasSeq!(ReplaceTypeUnless!(isSumType, This, typeof(this), TypeArgs));
350 
351 private:
352 
353 	enum bool canHoldTag(T) = Types.length <= T.max;
354 	alias unsignedInts = AliasSeq!(ubyte, ushort, uint, ulong);
355 
356 	alias Tag = Filter!(canHoldTag, unsignedInts)[0];
357 
358 	union Storage
359 	{
360 		template memberName(T)
361 			if (staticIndexOf!(T, Types) >= 0)
362 		{
363 			enum tid = staticIndexOf!(T, Types);
364 			mixin("enum memberName = `values_", toCtString!tid, "`;");
365 		}
366 
367 		static foreach (T; Types) {
368 			mixin("T ", memberName!T, ";");
369 		}
370 	}
371 
372 	Storage storage;
373 	Tag tag;
374 
375 	@trusted
376 	ref inout(T) get(T)() inout
377 		if (staticIndexOf!(T, Types) >= 0)
378 	{
379 		enum tid = staticIndexOf!(T, Types);
380 		assert(tag == tid);
381 		return __traits(getMember, storage, Storage.memberName!T);
382 	}
383 
384 public:
385 
386 	static foreach (tid, T; Types) {
387 		/// Constructs a `SumType` holding a specific value.
388 		this()(auto ref T value)
389 		{
390 			import core.lifetime: forward;
391 
392 			static if (isCopyable!T) {
393 				mixin("Storage newStorage = { ", Storage.memberName!T, ": value };");
394 			} else {
395 				mixin("Storage newStorage = { ", Storage.memberName!T, " : forward!value };");
396 			}
397 
398 			storage = newStorage;
399 			tag = tid;
400 		}
401 
402 		static if (isCopyable!T) {
403 			/// ditto
404 			this()(auto ref const(T) value) const
405 			{
406 				mixin("const(Storage) newStorage = { ", Storage.memberName!T, ": value };");
407 				storage = newStorage;
408 				tag = tid;
409 			}
410 
411 			/// ditto
412 			this()(auto ref immutable(T) value) immutable
413 			{
414 				mixin("immutable(Storage) newStorage = { ", Storage.memberName!T, ": value };");
415 				storage = newStorage;
416 				tag = tid;
417 			}
418 		} else {
419 			@disable this(const(T) value) const;
420 			@disable this(immutable(T) value) immutable;
421 		}
422 	}
423 
424 	static if (allSatisfy!(isCopyable, Types)) {
425 		static if (anySatisfy!(hasElaborateCopyConstructor, Types)) {
426 			/// Constructs a `SumType` that's a copy of another `SumType`
427 			this(ref SumType other)
428 			{
429 				storage = other.match!((ref value) {
430 					alias T = typeof(value);
431 
432 					mixin("Storage newStorage = { ", Storage.memberName!T, ": value };");
433 					return newStorage;
434 				});
435 
436 				tag = other.tag;
437 			}
438 
439 			/// ditto
440 			this(ref const(SumType) other) const
441 			{
442 				storage = other.match!((ref value) {
443 					alias OtherTypes = staticMap!(ConstOf, Types);
444 					enum tid = staticIndexOf!(typeof(value), OtherTypes);
445 					alias T = Types[tid];
446 
447 					mixin("const(Storage) newStorage = { ", Storage.memberName!T, ": value };");
448 					return newStorage;
449 				});
450 
451 				tag = other.tag;
452 			}
453 
454 			/// ditto
455 			this(ref immutable(SumType) other) immutable
456 			{
457 				storage = other.match!((ref value) {
458 					alias OtherTypes = staticMap!(ImmutableOf, Types);
459 					enum tid = staticIndexOf!(typeof(value), OtherTypes);
460 					alias T = Types[tid];
461 
462 					mixin("immutable(Storage) newStorage = { ", Storage.memberName!T, ": value };");
463 					return newStorage;
464 				});
465 
466 				tag = other.tag;
467 			}
468 		}
469 	} else {
470 		/// `@disable`d if any member type is non-copyable.
471 		@disable this(this);
472 	}
473 
474 	version(SumTypeNoDefaultCtor) {
475 		@disable this();
476 	}
477 
478 	static foreach (tid, T; Types) {
479 		static if (isAssignable!T) {
480 			/**
481 			 * Assigns a value to a `SumType`.
482 			 *
483 			 * Assigning to a `SumType` is `@system` if any of the
484 			 * `SumType`'s members contain pointers or references, since
485 			 * those members may be reachable through external references,
486 			 * and overwriting them could therefore lead to memory
487 			 * corruption.
488 			 *
489 			 * An individual assignment can be `@trusted` if the caller can
490 			 * guarantee that there are no outstanding references to $(I any)
491 			 * of the `SumType`'s members when the assignment occurs.
492 			 */
493 			ref SumType opAssign()(auto ref T rhs)
494 			{
495 				import core.lifetime: forward;
496 				import std.traits: hasIndirections, hasNested;
497 				import std.meta: Or = templateOr;
498 
499 				enum mayContainPointers =
500 					anySatisfy!(Or!(hasIndirections, hasNested), Types);
501 
502 				static if (mayContainPointers) {
503 					cast(void) () @system {}();
504 				}
505 
506 				this.match!((ref value) {
507 					static if (hasElaborateDestructor!(typeof(value))) {
508 						destroy(value);
509 					}
510 				});
511 
512 				mixin("Storage newStorage = { ", Storage.memberName!T, ": forward!rhs };");
513 				storage = newStorage;
514 				tag = tid;
515 
516 				return this;
517 			}
518 		}
519 	}
520 
521 	static if (allSatisfy!(isAssignable, Types)) {
522 		static if (allSatisfy!(isCopyable, Types)) {
523 			/**
524 			 * Copies the value from another `SumType` into this one.
525 			 *
526 			 * See the value-assignment overload for details on `@safe`ty.
527 			 *
528 			 * Copy assignment is `@disable`d if any of `Types` is non-copyable.
529 			 */
530 			ref SumType opAssign(ref SumType rhs)
531 			{
532 				rhs.match!((ref value) { this = value; });
533 				return this;
534 			}
535 		} else {
536 			@disable ref SumType opAssign(ref SumType rhs);
537 		}
538 
539 		/**
540 		 * Moves the value from another `SumType` into this one.
541 		 *
542 		 * See the value-assignment overload for details on `@safe`ty.
543 		 */
544 		ref SumType opAssign(SumType rhs)
545 		{
546 			import core.lifetime: move;
547 
548 			rhs.match!((ref value) { this = move(value); });
549 			return this;
550 		}
551 	}
552 
553 	/**
554 	 * Compares two `SumType`s for equality.
555 	 *
556 	 * Two `SumType`s are equal if they are the same kind of `SumType`, they
557 	 * contain values of the same type, and those values are equal.
558 	 */
559 	bool opEquals()(auto ref const(SumType) rhs) const {
560 		return this.match!((ref value) {
561 			return rhs.match!((ref rhsValue) {
562 				static if (is(typeof(value) == typeof(rhsValue))) {
563 					return value == rhsValue;
564 				} else {
565 					return false;
566 				}
567 			});
568 		});
569 	}
570 
571 	// Workaround for dlang issue 19407
572 	static if (__traits(compiles, anySatisfy!(hasElaborateDestructor, Types))) {
573 		// If possible, include the destructor only when it's needed
574 		private enum includeDtor = anySatisfy!(hasElaborateDestructor, Types);
575 	} else {
576 		// If we can't tell, always include it, even when it does nothing
577 		private enum includeDtor = true;
578 	}
579 
580 	static if (includeDtor) {
581 		/// Calls the destructor of the `SumType`'s current value.
582 		~this()
583 		{
584 			this.match!((ref value) {
585 				static if (hasElaborateDestructor!(typeof(value))) {
586 					destroy(value);
587 				}
588 			});
589 		}
590 	}
591 
592 	invariant {
593 		this.match!((ref value) {
594 			static if (is(typeof(value) == class)) {
595 				if (value !is null) {
596 					assert(value);
597 				}
598 			} else static if (is(typeof(value) == struct)) {
599 				assert(&value);
600 			}
601 		});
602 	}
603 
604 	version (D_BetterC) {} else
605 	/**
606 	 * Returns a string representation of the `SumType`'s current value.
607 	 *
608 	 * Not available when compiled with `-betterC`.
609 	 */
610 	string toString(this T)()
611 	{
612 		import std.conv: to;
613 
614 		return this.match!(to!string);
615 	}
616 
617 	// toHash is required by the language spec to be nothrow and @safe
618 	private enum isHashable(T) = __traits(compiles,
619 		() nothrow @safe { hashOf(T.init); }
620 	);
621 
622 	static if (allSatisfy!(isHashable, staticMap!(ConstOf, Types))) {
623 		// Workaround for dlang issue 20095
624 		version (D_BetterC) {} else
625 		/**
626 		 * Returns the hash of the `SumType`'s current value.
627 		 *
628 		 * Not available when compiled with `-betterC`.
629 		 */
630 		size_t toHash() const
631 		{
632 			return this.match!hashOf;
633 		}
634 	}
635 }
636 
637 // Construction
638 @safe unittest {
639 	alias MySum = SumType!(int, float);
640 
641 	assert(__traits(compiles, MySum(42)));
642 	assert(__traits(compiles, MySum(3.14)));
643 }
644 
645 // Assignment
646 @safe unittest {
647 	alias MySum = SumType!(int, float);
648 
649 	MySum x = MySum(42);
650 
651 	assert(__traits(compiles, x = 3.14));
652 }
653 
654 // Self assignment
655 @safe unittest {
656 	alias MySum = SumType!(int, float);
657 
658 	MySum x = MySum(42);
659 	MySum y = MySum(3.14);
660 
661 	assert(__traits(compiles, y = x));
662 }
663 
664 // Equality
665 @safe unittest {
666 	alias MySum = SumType!(int, float);
667 
668 	MySum x = MySum(123);
669 	MySum y = MySum(123);
670 	MySum z = MySum(456);
671 	MySum w = MySum(123.0);
672 	MySum v = MySum(456.0);
673 
674 	assert(x == y);
675 	assert(x != z);
676 	assert(x != w);
677 	assert(x != v);
678 }
679 
680 // Imported types
681 @safe unittest {
682 	import std.typecons: Tuple;
683 
684 	assert(__traits(compiles, {
685 		alias MySum = SumType!(Tuple!(int, int));
686 	}));
687 }
688 
689 // const and immutable types
690 @safe unittest {
691 	assert(__traits(compiles, {
692 		alias MySum = SumType!(const(int[]), immutable(float[]));
693 	}));
694 }
695 
696 // Recursive types
697 @safe unittest {
698 	alias MySum = SumType!(This*);
699 	assert(is(MySum.Types[0] == MySum*));
700 }
701 
702 // Allowed types
703 @safe unittest {
704 	import std.meta: AliasSeq;
705 
706 	alias MySum = SumType!(int, float, This*);
707 
708 	assert(is(MySum.Types == AliasSeq!(int, float, MySum*)));
709 }
710 
711 // Works alongside Algebraic
712 version (D_BetterC) {} else
713 @safe unittest {
714 	import std.variant;
715 
716 	alias Bar = Algebraic!(This*);
717 
718 	assert(is(Bar.AllowedTypes[0] == Bar*));
719 }
720 
721 // Types with destructors and postblits
722 @system unittest {
723 	int copies;
724 
725 	static struct Test
726 	{
727 		bool initialized = false;
728 		int* copiesPtr;
729 
730 		this(this) { (*copiesPtr)++; }
731 		~this() { if (initialized) (*copiesPtr)--; }
732 	}
733 
734 	alias MySum = SumType!(int, Test);
735 
736 	Test t = Test(true, &copies);
737 
738 	{
739 		MySum x = t;
740 		assert(copies == 1);
741 	}
742 	assert(copies == 0);
743 
744 	{
745 		MySum x = 456;
746 		assert(copies == 0);
747 	}
748 	assert(copies == 0);
749 
750 	{
751 		MySum x = t;
752 		assert(copies == 1);
753 		x = 456;
754 		assert(copies == 0);
755 	}
756 
757 	{
758 		MySum x = 456;
759 		assert(copies == 0);
760 		x = t;
761 		assert(copies == 1);
762 	}
763 
764 	{
765 		MySum x = t;
766 		MySum y = x;
767 		assert(copies == 2);
768 	}
769 
770 	{
771 		MySum x = t;
772 		MySum y;
773 		y = x;
774 		assert(copies == 2);
775 	}
776 }
777 
778 // Doesn't destroy reference types
779 version (D_BetterC) {} else
780 @system unittest {
781 	bool destroyed;
782 
783 	class C
784 	{
785 		~this()
786 		{
787 			destroyed = true;
788 		}
789 	}
790 
791 	struct S
792 	{
793 		~this() {}
794 	}
795 
796 	alias MySum = SumType!(S, C);
797 
798 	C c = new C();
799 	{
800 		MySum x = c;
801 		destroyed = false;
802 	}
803 	assert(!destroyed);
804 
805 	{
806 		MySum x = c;
807 		destroyed = false;
808 		x = S();
809 		assert(!destroyed);
810 	}
811 }
812 
813 // Types with @disable this()
814 @safe unittest {
815 	static struct NoInit
816 	{
817 		@disable this();
818 	}
819 
820 	alias MySum = SumType!(NoInit, int);
821 
822 	assert(!__traits(compiles, MySum()));
823 	assert(__traits(compiles, MySum(42)));
824 }
825 
826 // const SumTypes
827 @safe unittest {
828 	assert(__traits(compiles,
829 		const(SumType!(int[]))([1, 2, 3])
830 	));
831 }
832 
833 // Equality of const SumTypes
834 @safe unittest {
835 	alias MySum = SumType!int;
836 
837 	assert(__traits(compiles,
838 		const(MySum)(123) == const(MySum)(456)
839 	));
840 }
841 
842 // Compares reference types using value equality
843 @safe unittest {
844 	import std.array: staticArray;
845 
846 	static struct Field {}
847 	static struct Struct { Field[] fields; }
848 	alias MySum = SumType!Struct;
849 
850 	static arr1 = staticArray([Field()]);
851 	static arr2 = staticArray([Field()]);
852 
853 	auto a = MySum(Struct(arr1[]));
854 	auto b = MySum(Struct(arr2[]));
855 
856 	assert(a == b);
857 }
858 
859 // toString
860 version (D_BetterC) {} else
861 @safe unittest {
862 	import std.conv: text;
863 
864 	static struct Int { int i; }
865 	static struct Double { double d; }
866 	alias Sum = SumType!(Int, Double);
867 
868 	assert(Sum(Int(42)).text == Int(42).text, Sum(Int(42)).text);
869 	assert(Sum(Double(33.3)).text == Double(33.3).text, Sum(Double(33.3)).text);
870 	assert((const(Sum)(Int(42))).text == (const(Int)(42)).text, (const(Sum)(Int(42))).text);
871 }
872 
873 // Github issue #16
874 version (D_BetterC) {} else
875 @safe unittest {
876 	alias Node = SumType!(This[], string);
877 
878 	// override inference of @system attribute for cyclic functions
879 	assert((() @trusted =>
880 		Node([Node([Node("x")])])
881 		==
882 		Node([Node([Node("x")])])
883 	)());
884 }
885 
886 // Github issue #16 with const
887 version (D_BetterC) {} else
888 @safe unittest {
889 	alias Node = SumType!(const(This)[], string);
890 
891 	// override inference of @system attribute for cyclic functions
892 	assert((() @trusted =>
893 		Node([Node([Node("x")])])
894 		==
895 		Node([Node([Node("x")])])
896 	)());
897 }
898 
899 // Stale pointers
900 version (D_BetterC) {} else
901 @system unittest {
902 	alias MySum = SumType!(ubyte, void*[2]);
903 
904 	MySum x = [null, cast(void*) 0x12345678];
905 	void** p = &x.get!(void*[2])[1];
906 	x = ubyte(123);
907 
908 	assert(*p != cast(void*) 0x12345678);
909 }
910 
911 // Exception-safe assignment
912 version (D_BetterC) {} else
913 @safe unittest {
914 	static struct A
915 	{
916 		int value = 123;
917 	}
918 
919 	static struct B
920 	{
921 		int value = 456;
922 		this(this) { throw new Exception("oops"); }
923 	}
924 
925 	alias MySum = SumType!(A, B);
926 
927 	MySum x;
928 	try {
929 		x = B();
930 	} catch (Exception e) {}
931 
932 	assert(
933 		(x.tag == 0 && x.get!A.value == 123) ||
934 		(x.tag == 1 && x.get!B.value == 456)
935 	);
936 }
937 
938 // Types with @disable this(this)
939 @safe unittest {
940 	import std.algorithm.mutation: move;
941 
942 	static struct NoCopy
943 	{
944 		@disable this(this);
945 	}
946 
947 	alias MySum = SumType!NoCopy;
948 
949 	NoCopy lval = NoCopy();
950 
951 	MySum x = NoCopy();
952 	MySum y = NoCopy();
953 
954 	assert(__traits(compiles, SumType!NoCopy(NoCopy())));
955 	assert(!__traits(compiles, SumType!NoCopy(lval)));
956 
957 	assert(__traits(compiles, y = NoCopy()));
958 	assert(__traits(compiles, y = move(x)));
959 	assert(!__traits(compiles, y = lval));
960 	assert(!__traits(compiles, y = x));
961 
962 	assert(__traits(compiles, x == y));
963 }
964 
965 // Github issue #22
966 version (D_BetterC) {} else
967 @safe unittest {
968 	import std.typecons;
969 	assert(__traits(compiles, {
970 		static struct A {
971 			SumType!(Nullable!int) a = Nullable!int.init;
972 		}
973 	}));
974 }
975 
976 // Static arrays of structs with postblits
977 version (D_BetterC) {} else
978 @safe unittest {
979 	static struct S
980 	{
981 		int n;
982 		this(this) { n++; }
983 	}
984 
985 	assert(__traits(compiles, SumType!(S[1])()));
986 
987 	SumType!(S[1]) x = [S(0)];
988 	SumType!(S[1]) y = x;
989 
990 	auto xval = x.get!(S[1])[0].n;
991 	auto yval = y.get!(S[1])[0].n;
992 
993 	assert(xval != yval);
994 }
995 
996 // Replacement does not happen inside SumType
997 version (D_BetterC) {} else
998 @safe unittest {
999 	import std.typecons : Tuple;
1000 	alias A = Tuple!(This*,SumType!(This*))[SumType!(This*,string)[This]];
1001 	alias TR = ReplaceTypeUnless!(isSumType, This, int, A);
1002 	static assert(is(TR == Tuple!(int*,SumType!(This*))[SumType!(This*, string)[int]]));
1003 }
1004 
1005 // Supports nested self-referential SumTypes
1006 @safe unittest {
1007 	import std.typecons : Tuple, Flag;
1008 	alias Nat = SumType!(Flag!"0", Tuple!(This*));
1009 	static assert(__traits(compiles, SumType!(Nat)));
1010 	static assert(__traits(compiles, SumType!(Nat*, Tuple!(This*, This*))));
1011 }
1012 
1013 // Doesn't call @system postblits in @safe code
1014 @safe unittest {
1015 	static struct SystemCopy { @system this(this) {} }
1016 	SystemCopy original;
1017 
1018 	assert(!__traits(compiles, () @safe {
1019 		SumType!SystemCopy copy = original;
1020 	}));
1021 
1022 	assert(!__traits(compiles, () @safe {
1023 		SumType!SystemCopy copy; copy = original;
1024 	}));
1025 }
1026 
1027 // Doesn't overwrite pointers in @safe code
1028 @safe unittest {
1029 	alias MySum = SumType!(int*, int);
1030 
1031 	MySum x;
1032 
1033 	assert(!__traits(compiles, () @safe {
1034 		x = 123;
1035 	}));
1036 
1037 	assert(!__traits(compiles, () @safe {
1038 		x = MySum(123);
1039 	}));
1040 }
1041 
1042 // Types with invariants
1043 version (D_BetterC) {} else
1044 @system unittest {
1045 	import std.exception: assertThrown;
1046 	import core.exception: AssertError;
1047 
1048 	struct S
1049 	{
1050 		int i;
1051 		invariant { assert(i >= 0); }
1052 	}
1053 
1054 	class C
1055 	{
1056 		int i;
1057 		invariant { assert(i >= 0); }
1058 	}
1059 
1060 	SumType!S x;
1061 	x.match!((ref v) { v.i = -1; });
1062 	assertThrown!AssertError(assert(&x));
1063 
1064 	SumType!C y = new C();
1065 	y.match!((ref v) { v.i = -1; });
1066 	assertThrown!AssertError(assert(&y));
1067 }
1068 
1069 // Calls value postblit on self-assignment
1070 @safe unittest {
1071 	static struct S
1072 	{
1073 		int n;
1074 		this(this) { n++; }
1075 	}
1076 
1077 	SumType!S x = S();
1078 	SumType!S y;
1079 	y = x;
1080 
1081 	auto xval = x.get!S.n;
1082 	auto yval = y.get!S.n;
1083 
1084 	assert(xval != yval);
1085 }
1086 
1087 // Github issue #29
1088 @safe unittest {
1089 	assert(__traits(compiles, () @safe {
1090 		alias A = SumType!string;
1091 
1092 		@safe A createA(string arg) {
1093 		return A(arg);
1094 		}
1095 
1096 		@safe void test() {
1097 		A a = createA("");
1098 		}
1099 	}));
1100 }
1101 
1102 // SumTypes as associative array keys
1103 version (D_BetterC) {} else
1104 @safe unittest {
1105 	assert(__traits(compiles, {
1106 		int[SumType!(int, string)] aa;
1107 	}));
1108 }
1109 
1110 // toString with non-copyable types
1111 version(D_BetterC) {} else
1112 @safe unittest {
1113 	struct NoCopy
1114 	{
1115 		@disable this(this);
1116 	}
1117 
1118 	SumType!NoCopy x;
1119 
1120 	assert(__traits(compiles, x.toString()));
1121 }
1122 
1123 // Can use the result of assignment
1124 @safe unittest {
1125 	alias MySum = SumType!(int, float);
1126 
1127 	MySum a = MySum(123);
1128 	MySum b = MySum(3.14);
1129 
1130 	assert((a = b) == b);
1131 	assert((a = MySum(123)) == MySum(123));
1132 	assert((a = 3.14) == MySum(3.14));
1133 	assert(((a = b) = MySum(123)) == MySum(123));
1134 }
1135 
1136 version(none) {
1137 	// Known bug; needs fix for dlang issue 19902
1138 	// Types with copy constructors
1139 	@safe unittest {
1140 		static struct S
1141 		{
1142 			int n;
1143 			this(ref return scope inout S other) inout { n++; }
1144 		}
1145 
1146 		SumType!S x = S();
1147 		SumType!S y = x;
1148 
1149 		auto xval = x.get!S.n;
1150 		auto yval = y.get!S.n;
1151 
1152 		assert(xval != yval);
1153 	}
1154 }
1155 
1156 version(none) {
1157 	// Known bug; needs fix for dlang issue 19458
1158 	// Types with disabled opEquals
1159 	@safe unittest {
1160 		static struct S
1161 		{
1162 			@disable bool opEquals(const S rhs) const;
1163 		}
1164 
1165 		assert(__traits(compiles, SumType!S(S())));
1166 	}
1167 }
1168 
1169 version(none) {
1170 	// Known bug; needs fix for dlang issue 19458
1171 	@safe unittest {
1172 		static struct S
1173 		{
1174 			int i;
1175 			bool opEquals(S rhs) { return i == rhs.i; }
1176 		}
1177 
1178 		assert(__traits(compiles, SumType!S(S(123))));
1179 	}
1180 }
1181 
1182 /// True if `T` is an instance of `SumType`, otherwise false.
1183 enum bool isSumType(T) = is(T == SumType!Args, Args...);
1184 
1185 unittest {
1186 	static struct Wrapper
1187 	{
1188 		SumType!int s;
1189 		alias s this;
1190 	}
1191 
1192 	assert(isSumType!(SumType!int));
1193 	assert(!isSumType!Wrapper);
1194 }
1195 
1196 /**
1197  * Calls a type-appropriate function with the value held in a [SumType].
1198  *
1199  * For each possible type the [SumType] can hold, the given handlers are
1200  * checked, in order, to see whether they accept a single argument of that type.
1201  * The first one that does is chosen as the match for that type. (Note that the
1202  * first match may not always be the most exact match.
1203  * See [#avoiding-unintentional-matches|"Avoiding unintentional matches"] for
1204  * one common pitfall.)
1205  *
1206  * Every type must have a matching handler, and every handler must match at
1207  * least one type. This is enforced at compile time.
1208  *
1209  * Handlers may be functions, delegates, or objects with opCall overloads. If a
1210  * function with more than one overload is given as a handler, all of the
1211  * overloads are considered as potential matches.
1212  *
1213  * Templated handlers are also accepted, and will match any type for which they
1214  * can be [implicitly instantiated](https://dlang.org/glossary.html#ifti). See
1215  * [sumtype#introspection-based-matching|"Introspection-based matching"] for an
1216  * example of templated handler usage.
1217  *
1218  * Returns:
1219  *   The value returned from the handler that matches the currently-held type.
1220  *
1221  * See_Also: `std.variant.visit`
1222  */
1223 template match(handlers...)
1224 {
1225 	import std.typecons: Yes;
1226 
1227 	/**
1228 	 * The actual `match` function.
1229 	 *
1230 	 * Params:
1231 	 *   self = A [SumType] object
1232 	 */
1233 	auto match(Self)(auto ref Self self)
1234 		if (is(Self : SumType!TypeArgs, TypeArgs...))
1235 	{
1236 		return self.matchImpl!(Yes.exhaustive, handlers);
1237 	}
1238 }
1239 
1240 /** $(H3 Avoiding unintentional matches)
1241  *
1242  * Sometimes, implicit conversions may cause a handler to match more types than
1243  * intended. The example below shows two solutions to this problem.
1244  */
1245 @safe unittest {
1246     alias Number = SumType!(double, int);
1247 
1248     Number x;
1249 
1250     // Problem: because int implicitly converts to double, the double
1251     // handler is used for both types, and the int handler never matches.
1252     assert(!__traits(compiles,
1253         x.match!(
1254             (double d) => "got double",
1255             (int n) => "got int"
1256         )
1257     ));
1258 
1259     // Solution 1: put the handler for the "more specialized" type (in this
1260     // case, int) before the handler for the type it converts to.
1261     assert(__traits(compiles,
1262         x.match!(
1263             (int n) => "got int",
1264             (double d) => "got double"
1265         )
1266     ));
1267 
1268     // Solution 2: use a template that only accepts the exact type it's
1269     // supposed to match, instead of any type that implicitly converts to it.
1270     alias exactly(T, alias fun) = function (arg) {
1271         static assert(is(typeof(arg) == T));
1272         return fun(arg);
1273     };
1274 
1275     // Now, even if we put the double handler first, it will only be used for
1276     // doubles, not ints.
1277     static if (__VERSION__ >= 2089L) {
1278     assert(__traits(compiles,
1279         x.match!(
1280             exactly!(double, d => "got double"),
1281             exactly!(int, n => "got int")
1282         )
1283     ));
1284     }
1285 }
1286 
1287 /**
1288  * Attempts to call a type-appropriate function with the value held in a
1289  * [SumType], and throws on failure.
1290  *
1291  * Matches are chosen using the same rules as [match], but are not required to
1292  * be exhaustive—in other words, a type is allowed to have no matching handler.
1293  * If a type without a handler is encountered at runtime, a [MatchException]
1294  * is thrown.
1295  *
1296  * Not available when compiled with `-betterC`.
1297  *
1298  * Returns:
1299  *   The value returned from the handler that matches the currently-held type,
1300  *   if a handler was given for that type.
1301  *
1302  * Throws:
1303  *   [MatchException], if the currently-held type has no matching handler.
1304  *
1305  * See_Also: `std.variant.tryVisit`
1306  */
1307 version (D_Exceptions)
1308 template tryMatch(handlers...)
1309 {
1310 	import std.typecons: No;
1311 
1312 	/**
1313 	 * The actual `tryMatch` function.
1314 	 *
1315 	 * Params:
1316 	 *   self = A [SumType] object
1317 	 */
1318 	auto tryMatch(Self)(auto ref Self self)
1319 		if (is(Self : SumType!TypeArgs, TypeArgs...))
1320 	{
1321 		return self.matchImpl!(No.exhaustive, handlers);
1322 	}
1323 }
1324 
1325 /**
1326  * Thrown by [tryMatch] when an unhandled type is encountered.
1327  *
1328  * Not available when compiled with `-betterC`.
1329  */
1330 version (D_Exceptions)
1331 class MatchException : Exception
1332 {
1333 	pure @safe @nogc nothrow
1334 	this(string msg, string file = __FILE__, size_t line = __LINE__)
1335 	{
1336 		super(msg, file, line);
1337 	}
1338 }
1339 
1340 /**
1341  * True if `handler` is a potential match for `T`, otherwise false.
1342  *
1343  * See the documentation for [match] for a full explanation of how matches are
1344  * chosen.
1345  */
1346 enum bool canMatch(alias handler, T) = is(typeof((T arg) => handler(arg)));
1347 
1348 // Includes all overloads of the given handler
1349 @safe unittest {
1350 	static struct OverloadSet
1351 	{
1352 		static void fun(int n) {}
1353 		static void fun(double d) {}
1354 	}
1355 
1356 	assert(canMatch!(OverloadSet.fun, int));
1357 	assert(canMatch!(OverloadSet.fun, double));
1358 }
1359 
1360 import std.typecons: Flag;
1361 
1362 private template matchImpl(Flag!"exhaustive" exhaustive, handlers...)
1363 {
1364 	auto matchImpl(Self)(auto ref Self self)
1365 		if (is(Self : SumType!TypeArgs, TypeArgs...))
1366 	{
1367 		alias Types = self.Types;
1368 		enum noMatch = size_t.max;
1369 
1370 		enum matches = () {
1371 			size_t[Types.length] matches;
1372 
1373 			// Workaround for dlang issue 19561
1374 			foreach (ref match; matches) {
1375 				match = noMatch;
1376 			}
1377 
1378 			static foreach (tid, T; Types) {
1379 				static foreach (hid, handler; handlers) {
1380 					static if (canMatch!(handler, typeof(self.get!T()))) {
1381 						if (matches[tid] == noMatch) {
1382 							matches[tid] = hid;
1383 						}
1384 					}
1385 				}
1386 			}
1387 
1388 			return matches;
1389 		}();
1390 
1391 		import std.algorithm.searching: canFind;
1392 
1393 		// Check for unreachable handlers
1394 		static foreach (hid, handler; handlers) {
1395 			static assert(matches[].canFind(hid),
1396 				"handler #" ~ toCtString!hid ~ " " ~
1397 				"of type `" ~ ( __traits(isTemplate, handler)
1398 					? "template"
1399 					: typeof(handler).stringof
1400 				) ~ "` " ~
1401 				"never matches"
1402 			);
1403 		}
1404 
1405 		// Workaround for dlang issue 19993
1406 		static foreach (size_t hid, handler; handlers) {
1407 			mixin("alias handler", toCtString!hid, " = handler;");
1408 		}
1409 
1410 		final switch (self.tag) {
1411 			static foreach (tid, T; Types) {
1412 				case tid:
1413 					static if (matches[tid] != noMatch) {
1414 						return mixin("handler", toCtString!(matches[tid]))(self.get!T);
1415 					} else {
1416 						static if(exhaustive) {
1417 							static assert(false,
1418 								"No matching handler for type `" ~ T.stringof ~ "`");
1419 						} else {
1420 							throw new MatchException(
1421 								"No matching handler for type `" ~ T.stringof ~ "`");
1422 						}
1423 					}
1424 			}
1425 		}
1426 
1427 		assert(false); // unreached
1428 	}
1429 }
1430 
1431 // Matching
1432 @safe unittest {
1433 	alias MySum = SumType!(int, float);
1434 
1435 	MySum x = MySum(42);
1436 	MySum y = MySum(3.14);
1437 
1438 	assert(x.match!((int v) => true, (float v) => false));
1439 	assert(y.match!((int v) => false, (float v) => true));
1440 }
1441 
1442 // Missing handlers
1443 @safe unittest {
1444 	alias MySum = SumType!(int, float);
1445 
1446 	MySum x = MySum(42);
1447 
1448 	assert(!__traits(compiles, x.match!((int x) => true)));
1449 	assert(!__traits(compiles, x.match!()));
1450 }
1451 
1452 // Handlers with qualified parameters
1453 version (D_BetterC) {} else
1454 @safe unittest {
1455 	alias MySum = SumType!(int[], float[]);
1456 
1457 	MySum x = MySum([1, 2, 3]);
1458 	MySum y = MySum([1.0, 2.0, 3.0]);
1459 
1460 	assert(x.match!((const(int[]) v) => true, (const(float[]) v) => false));
1461 	assert(y.match!((const(int[]) v) => false, (const(float[]) v) => true));
1462 }
1463 
1464 // Handlers for qualified types
1465 version (D_BetterC) {} else
1466 @safe unittest {
1467 	alias MySum = SumType!(immutable(int[]), immutable(float[]));
1468 
1469 	MySum x = MySum([1, 2, 3]);
1470 
1471 	assert(x.match!((immutable(int[]) v) => true, (immutable(float[]) v) => false));
1472 	assert(x.match!((const(int[]) v) => true, (const(float[]) v) => false));
1473 	// Tail-qualified parameters
1474 	assert(x.match!((immutable(int)[] v) => true, (immutable(float)[] v) => false));
1475 	assert(x.match!((const(int)[] v) => true, (const(float)[] v) => false));
1476 	// Generic parameters
1477 	assert(x.match!((immutable v) => true));
1478 	assert(x.match!((const v) => true));
1479 	// Unqualified parameters
1480 	assert(!__traits(compiles,
1481 		x.match!((int[] v) => true, (float[] v) => false)
1482 	));
1483 }
1484 
1485 // Delegate handlers
1486 version (D_BetterC) {} else
1487 @safe unittest {
1488 	alias MySum = SumType!(int, float);
1489 
1490 	int answer = 42;
1491 	MySum x = MySum(42);
1492 	MySum y = MySum(3.14);
1493 
1494 	assert(x.match!((int v) => v == answer, (float v) => v == answer));
1495 	assert(!y.match!((int v) => v == answer, (float v) => v == answer));
1496 }
1497 
1498 version(unittest) {
1499 	version(D_BetterC) {
1500 		// std.math.approxEqual depends on core.runtime.math, so use a
1501 		// libc-based version for testing with -betterC
1502 		@safe pure @nogc nothrow
1503 		private bool approxEqual(double lhs, double rhs)
1504 		{
1505 			import core.stdc.math: fabs;
1506 
1507 			return (lhs - rhs) < 1e-5;
1508 		}
1509 	} else {
1510 		import std.math: approxEqual;
1511 	}
1512 }
1513 
1514 // Generic handler
1515 @safe unittest {
1516 	alias MySum = SumType!(int, float);
1517 
1518 	MySum x = MySum(42);
1519 	MySum y = MySum(3.14);
1520 
1521 	assert(x.match!(v => v*2) == 84);
1522 	assert(y.match!(v => v*2).approxEqual(6.28));
1523 }
1524 
1525 // Fallback to generic handler
1526 version (D_BetterC) {} else
1527 @safe unittest {
1528 	import std.conv: to;
1529 
1530 	alias MySum = SumType!(int, float, string);
1531 
1532 	MySum x = MySum(42);
1533 	MySum y = MySum("42");
1534 
1535 	assert(x.match!((string v) => v.to!int, v => v*2) == 84);
1536 	assert(y.match!((string v) => v.to!int, v => v*2) == 42);
1537 }
1538 
1539 // Multiple non-overlapping generic handlers
1540 @safe unittest {
1541 	import std.array: staticArray;
1542 
1543 	alias MySum = SumType!(int, float, int[], char[]);
1544 
1545 	static ints = staticArray([1, 2, 3]);
1546 	static chars = staticArray(['a', 'b', 'c']);
1547 
1548 	MySum x = MySum(42);
1549 	MySum y = MySum(3.14);
1550 	MySum z = MySum(ints[]);
1551 	MySum w = MySum(chars[]);
1552 
1553 	assert(x.match!(v => v*2, v => v.length) == 84);
1554 	assert(y.match!(v => v*2, v => v.length).approxEqual(6.28));
1555 	assert(w.match!(v => v*2, v => v.length) == 3);
1556 	assert(z.match!(v => v*2, v => v.length) == 3);
1557 }
1558 
1559 // Structural matching
1560 @safe unittest {
1561 	static struct S1 { int x; }
1562 	static struct S2 { int y; }
1563 	alias MySum = SumType!(S1, S2);
1564 
1565 	MySum a = MySum(S1(0));
1566 	MySum b = MySum(S2(0));
1567 
1568 	assert(a.match!(s1 => s1.x + 1, s2 => s2.y - 1) == 1);
1569 	assert(b.match!(s1 => s1.x + 1, s2 => s2.y - 1) == -1);
1570 }
1571 
1572 // Separate opCall handlers
1573 @safe unittest {
1574 	static struct IntHandler
1575 	{
1576 		bool opCall(int arg)
1577 		{
1578 			return true;
1579 		}
1580 	}
1581 
1582 	static struct FloatHandler
1583 	{
1584 		bool opCall(float arg)
1585 		{
1586 			return false;
1587 		}
1588 	}
1589 
1590 	alias MySum = SumType!(int, float);
1591 
1592 	MySum x = MySum(42);
1593 	MySum y = MySum(3.14);
1594 
1595 	assert(x.match!(IntHandler.init, FloatHandler.init));
1596 	assert(!y.match!(IntHandler.init, FloatHandler.init));
1597 }
1598 
1599 // Compound opCall handler
1600 @safe unittest {
1601 	static struct CompoundHandler
1602 	{
1603 		bool opCall(int arg)
1604 		{
1605 			return true;
1606 		}
1607 
1608 		bool opCall(float arg)
1609 		{
1610 			return false;
1611 		}
1612 	}
1613 
1614 	alias MySum = SumType!(int, float);
1615 
1616 	MySum x = MySum(42);
1617 	MySum y = MySum(3.14);
1618 
1619 	assert(x.match!(CompoundHandler.init));
1620 	assert(!y.match!(CompoundHandler.init));
1621 }
1622 
1623 // Ordered matching
1624 @safe unittest {
1625 	alias MySum = SumType!(int, float);
1626 
1627 	MySum x = MySum(42);
1628 
1629 	assert(x.match!((int v) => true, v => false));
1630 }
1631 
1632 // Non-exhaustive matching
1633 version (D_Exceptions)
1634 @system unittest {
1635 	import std.exception: assertThrown, assertNotThrown;
1636 
1637 	alias MySum = SumType!(int, float);
1638 
1639 	MySum x = MySum(42);
1640 	MySum y = MySum(3.14);
1641 
1642 	assertNotThrown!MatchException(x.tryMatch!((int n) => true));
1643 	assertThrown!MatchException(y.tryMatch!((int n) => true));
1644 }
1645 
1646 // Non-exhaustive matching in @safe code
1647 version (D_Exceptions)
1648 @safe unittest {
1649 	SumType!(int, float) x;
1650 
1651 	assert(__traits(compiles,
1652 		x.tryMatch!(
1653 			(int n) => n + 1,
1654 		)
1655 	));
1656 
1657 }
1658 
1659 // Handlers with ref parameters
1660 @safe unittest {
1661 	import std.meta: staticIndexOf;
1662 
1663 	alias Value = SumType!(long, double);
1664 
1665 	auto value = Value(3.14);
1666 
1667 	value.match!(
1668 		(long) {},
1669 		(ref double d) { d *= 2; }
1670 	);
1671 
1672 	assert(value.get!double.approxEqual(6.28));
1673 }
1674 
1675 // Unreachable handlers
1676 @safe unittest {
1677 	alias MySum = SumType!(int, string);
1678 
1679 	MySum s;
1680 
1681 	assert(!__traits(compiles,
1682 		s.match!(
1683 			(int _) => 0,
1684 			(string _) => 1,
1685 			(double _) => 2
1686 		)
1687 	));
1688 
1689 	assert(!__traits(compiles,
1690 		s.match!(
1691 			_ => 0,
1692 			(int _) => 1
1693 		)
1694 	));
1695 }
1696 
1697 // Unsafe handlers
1698 unittest {
1699 	SumType!int x;
1700 	alias unsafeHandler = (int x) @system { return; };
1701 
1702 	assert(!__traits(compiles, () @safe {
1703 		x.match!unsafeHandler;
1704 	}));
1705 
1706 	assert(__traits(compiles, () @system {
1707 		return x.match!unsafeHandler;
1708 	}));
1709 }
1710 
1711 // Overloaded handlers
1712 @safe unittest {
1713 	static struct OverloadSet
1714 	{
1715 		static string fun(int i) { return "int"; }
1716 		static string fun(double d) { return "double"; }
1717 	}
1718 
1719 	alias MySum = SumType!(int, double);
1720 
1721 	MySum a = 42;
1722 	MySum b = 3.14;
1723 
1724 	assert(a.match!(OverloadSet.fun) == "int");
1725 	assert(b.match!(OverloadSet.fun) == "double");
1726 }
1727 
1728 // Overload sets that include SumType arguments
1729 @safe unittest {
1730 	alias Inner = SumType!(int, double);
1731 	alias Outer = SumType!(Inner, string);
1732 
1733 	static struct OverloadSet
1734 	{
1735 		@safe:
1736 		static string fun(int i) { return "int"; }
1737 		static string fun(double d) { return "double"; }
1738 		static string fun(string s) { return "string"; }
1739 		static string fun(Inner i) { return i.match!fun; }
1740 		static string fun(Outer o) { return o.match!fun; }
1741 	}
1742 
1743 	Outer a = Inner(42);
1744 	Outer b = Inner(3.14);
1745 	Outer c = "foo";
1746 
1747 	assert(OverloadSet.fun(a) == "int");
1748 	assert(OverloadSet.fun(b) == "double");
1749 	assert(OverloadSet.fun(c) == "string");
1750 }
1751 
1752 // Overload sets with ref arguments
1753 @safe unittest {
1754 	static struct OverloadSet
1755 	{
1756 		static void fun(ref int i) { i = 42; }
1757 		static void fun(ref double d) { d = 3.14; }
1758 	}
1759 
1760 	alias MySum = SumType!(int, double);
1761 
1762 	MySum x = 0;
1763 	MySum y = 0.0;
1764 
1765 	x.match!(OverloadSet.fun);
1766 	y.match!(OverloadSet.fun);
1767 
1768 	assert(x.match!((value) => is(typeof(value) == int) && value == 42));
1769 	assert(y.match!((value) => is(typeof(value) == double) && value == 3.14));
1770 }
1771 
1772 // Overload sets with templates
1773 @safe unittest {
1774 	import std.traits: isNumeric;
1775 
1776 	static struct OverloadSet
1777 	{
1778 		static string fun(string arg)
1779 		{
1780 			return "string";
1781 		}
1782 
1783 		static string fun(T)(T arg)
1784 			if (isNumeric!T)
1785 		{
1786 			return "numeric";
1787 		}
1788 	}
1789 
1790 	alias MySum = SumType!(int, string);
1791 
1792 	MySum x = 123;
1793 	MySum y = "hello";
1794 
1795 	assert(x.match!(OverloadSet.fun) == "numeric");
1796 	assert(y.match!(OverloadSet.fun) == "string");
1797 }
1798 
1799 // Github issue #24
1800 @safe unittest {
1801 	assert(__traits(compiles, () @nogc {
1802 		int acc = 0;
1803 		SumType!int(1).match!((int x) => acc += x);
1804 	}));
1805 }
1806 
1807 // Github issue #31
1808 @safe unittest {
1809 	assert(__traits(compiles, () @nogc {
1810 		int acc = 0;
1811 
1812 		SumType!(int, string)(1).match!(
1813 			(int x) => acc += x,
1814 			(string _) => 0,
1815 		);
1816 	}));
1817 }
1818 
1819 // Types that `alias this` a SumType
1820 @safe unittest {
1821 	static struct A {}
1822 	static struct B {}
1823 	static struct D { SumType!(A, B) value; alias value this; }
1824 
1825 	assert(__traits(compiles, D().match!(_ => true)));
1826 }
1827 
1828 version(SumTypeTestBetterC) {
1829 	version(D_BetterC) {}
1830 	else static assert(false, "Must compile with -betterC to run betterC tests");
1831 
1832 	version(unittest) {}
1833 	else static assert(false, "Must compile with -unittest to run betterC tests");
1834 
1835 	extern(C) int main()
1836 	{
1837 		import core.stdc.stdio: puts;
1838 		static foreach (test; __traits(getUnitTests, mixin(__MODULE__))) {
1839 			test();
1840 		}
1841 
1842 		puts("All unit tests have been run successfully.");
1843 		return 0;
1844 	}
1845 }