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