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