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