1 /**
2  * Algebraic data type implementation based on a tagged union.
3  *
4  * Copyright: Copyright 2015-2016, Sönke Ludwig.
5  * License:   $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
6  * Authors:   Sönke Ludwig
7 */
8 module taggedalgebraic;
9 
10 import std.typetuple;
11 import std.traits : Unqual, isInstanceOf;
12 
13 // TODO:
14 //  - distinguish between @property and non@-property methods.
15 //  - verify that static methods are handled properly
16 
17 /** Implements a generic algebraic type using an enum to identify the stored type.
18 
19 	This struct takes a `union` or `struct` declaration as an input and builds
20 	an algebraic data type from its fields, using an automatically generated
21 	`Kind` enumeration to identify which field of the union is currently used.
22 	Multiple fields with the same value are supported.
23 
24 	All operators and methods are transparently forwarded to the contained
25 	value. The caller has to make sure that the contained value supports the
26 	requested operation. Failure to do so will result in an assertion failure.
27 
28 	The return value of forwarded operations is determined as follows:
29 	$(UL
30 		$(LI If the type can be uniquely determined, it is used as the return
31 			value)
32 		$(LI If there are multiple possible return values and all of them match
33 			the unique types defined in the `TaggedAlgebraic`, a
34 			`TaggedAlgebraic` is returned.)
35 		$(LI If there are multiple return values and none of them is a
36 			`Variant`, an `Algebraic` of the set of possible return types is
37 			returned.)
38 		$(LI If any of the possible operations returns a `Variant`, this is used
39 			as the return value.)
40 	)
41 */
42 struct TaggedAlgebraic(U) if (is(U == union) || is(U == struct))
43 {
44 	import std.algorithm : among;
45 	import std.string : format;
46 	import std.traits : FieldTypeTuple, FieldNameTuple, Largest,
47 		hasElaborateCopyConstructor, hasElaborateDestructor;
48 
49 	/// Alias of the type used for defining the possible storage types/kinds.
50 	alias Union = U;
51 
52 	private alias FieldTypes = FieldTypeTuple!U;
53 	private alias fieldNames = FieldNameTuple!U;
54 
55 	static assert(FieldTypes.length > 0,
56 			"The TaggedAlgebraic's union type must have at least one field.");
57 	static assert(FieldTypes.length == fieldNames.length);
58 
59 	private
60 	{
61 		static if (is(FieldTypes[0] == typeof(null)) || is(FieldTypes[0] == Void)
62 				|| __VERSION__ < 2072)
63 		{
64 			void[Largest!FieldTypes.sizeof] m_data;
65 		}
66 		else
67 		{
68 			union Dummy
69 			{
70 				FieldTypes[0] initField;
71 				void[Largest!FieldTypes.sizeof] data;
72 				alias data this;
73 			}
74 
75 			Dummy m_data = {initField:
76 			FieldTypes[0].init};
77 		}
78 		Kind m_kind;
79 	}
80 
81 	/// A type enum that identifies the type of value currently stored.
82 	alias Kind = TypeEnum!U;
83 
84 	/// Compatibility alias
85 	deprecated("Use 'Kind' instead.") alias Type = Kind;
86 
87 	/// The type ID of the currently stored value.
88 	@property Kind kind() const
89 	{
90 		return m_kind;
91 	}
92 
93 	// Compatibility alias
94 	deprecated("Use 'kind' instead.") alias typeID = kind;
95 
96 	// constructors
97 	//pragma(msg, generateConstructors!U());
98 	mixin(generateConstructors!U);
99 
100 	this(TaggedAlgebraic other)
101 	{
102 		rawSwap(this, other);
103 	}
104 
105 	void opAssign(TaggedAlgebraic other)
106 	{
107 		rawSwap(this, other);
108 	}
109 
110 	// postblit constructor
111 	static if (anySatisfy!(hasElaborateCopyConstructor, FieldTypes))
112 	{
113 		this(this)
114 		{
115 			switch (m_kind)
116 			{
117 			default:
118 				break;
119 				foreach (i, tname; fieldNames)
120 				{
121 					alias T = typeof(__traits(getMember, U, tname));
122 					static if (hasElaborateCopyConstructor!T)
123 					{
124 			case __traits(getMember, Kind, tname):
125 						typeid(T).postblit(cast(void*)&trustedGet!tname());
126 						return;
127 					}
128 				}
129 			}
130 		}
131 	}
132 
133 	// destructor
134 	static if (anySatisfy!(hasElaborateDestructor, FieldTypes))
135 	{
136 		~this()
137 		{
138 			final switch (m_kind)
139 			{
140 				foreach (i, tname; fieldNames)
141 				{
142 					alias T = typeof(__traits(getMember, U, tname));
143 			case __traits(getMember, Kind, tname):
144 					static if (hasElaborateDestructor!T)
145 					{
146 						.destroy(trustedGet!tname);
147 					}
148 					return;
149 				}
150 			}
151 		}
152 	}
153 
154 	/// Enables conversion or extraction of the stored value.
155 	T opCast(T)()
156 	{
157 		import std.conv : to;
158 
159 		final switch (m_kind)
160 		{
161 			foreach (i, FT; FieldTypes)
162 			{
163 		case __traits(getMember, Kind, fieldNames[i]):
164 				static if (is(typeof(trustedGet!(fieldNames[i])) : T))
165 					return trustedGet!(fieldNames[i]);
166 				else static if (is(typeof(to!T(trustedGet!(fieldNames[i])))))
167 				{
168 					return to!T(trustedGet!(fieldNames[i]));
169 				}
170 				else
171 				{
172 					assert(false,
173 							"Cannot cast a " ~ fieldNames[i] ~ " value of type "
174 							~ FT.stringof ~ " to " ~ T.stringof);
175 				}
176 			}
177 		}
178 		assert(false); // never reached
179 	}
180 	/// ditto
181 	T opCast(T)() const
182 	{
183 		// this method needs to be duplicated because inout doesn't work with to!()
184 		import std.conv : to;
185 
186 		final switch (m_kind)
187 		{
188 			foreach (i, FT; FieldTypes)
189 			{
190 		case __traits(getMember, Kind, fieldNames[i]):
191 				static if (is(typeof(trustedGet!(fieldNames[i])) : T))
192 					return trustedGet!(fieldNames[i]);
193 				else static if (is(typeof(to!T(trustedGet!(fieldNames[i])))))
194 				{
195 					return to!T(trustedGet!(fieldNames[i]));
196 				}
197 				else
198 				{
199 					assert(false,
200 							"Cannot cast a " ~ fieldNames[i] ~ " value of type"
201 							~ FT.stringof ~ " to " ~ T.stringof);
202 				}
203 			}
204 		}
205 		assert(false); // never reached
206 	}
207 
208 	/// Uses `cast(string)`/`to!string` to return a string representation of the enclosed value.
209 	string toString() const
210 	{
211 		return cast(string) this;
212 	}
213 
214 	// NOTE: "this TA" is used here as the functional equivalent of inout,
215 	//       just that it generates one template instantiation per modifier
216 	//       combination, so that we can actually decide what to do for each
217 	//       case.
218 
219 	/// Enables the invocation of methods of the stored value.
220 	auto opDispatch(string name, this TA, ARGS...)(auto ref ARGS args)
221 			if (hasOp!(TA, OpKind.method, name, ARGS))
222 	{
223 		return implementOp!(OpKind.method, name)(this, args);
224 	}
225 	/// Enables accessing properties/fields of the stored value.
226 	@property auto opDispatch(string name, this TA, ARGS...)(auto ref ARGS args)
227 			if (hasOp!(TA, OpKind.field, name, ARGS) && !hasOp!(TA, OpKind.method, name, ARGS))
228 	{
229 		return implementOp!(OpKind.field, name)(this, args);
230 	}
231 	/// Enables equality comparison with the stored value.
232 	auto opEquals(T, this TA)(auto ref T other)
233 			if (is(Unqual!T == TaggedAlgebraic) || hasOp!(TA, OpKind.binary, "==", T))
234 	{
235 		static if (is(Unqual!T == TaggedAlgebraic))
236 		{
237 			if (this.kind != other.kind)
238 				return false;
239 			final switch (this.kind) foreach (i, fname; fieldNames)
240 			case __traits(getMember, Kind, fname):
241 					return trustedGet!fname == other.trustedGet!fname;
242 			assert(false); // never reached
243 		}
244 		else
245 			return implementOp!(OpKind.binary, "==")(this, other);
246 	}
247 	/// Enables relational comparisons with the stored value.
248 	auto opCmp(T, this TA)(auto ref T other) if (hasOp!(TA, OpKind.binary, "<", T))
249 	{
250 		assert(false, "TODO!");
251 	}
252 	/// Enables the use of unary operators with the stored value.
253 	auto opUnary(string op, this TA)() if (hasOp!(TA, OpKind.unary, op))
254 	{
255 		return implementOp!(OpKind.unary, op)(this);
256 	}
257 	/// Enables the use of binary operators with the stored value.
258 	auto opBinary(string op, T, this TA)(auto ref T other)
259 			if (hasOp!(TA, OpKind.binary, op, T))
260 	{
261 		return implementOp!(OpKind.binary, op)(this, other);
262 	}
263 	/// Enables the use of binary operators with the stored value.
264 	auto opBinaryRight(string op, T, this TA)(auto ref T other)
265 			if (hasOp!(TA, OpKind.binaryRight, op, T))
266 	{
267 		return implementOp!(OpKind.binaryRight, op)(this, other);
268 	}
269 	/// Enables operator assignments on the stored value.
270 	auto opOpAssign(string op, T, this TA)(auto ref T other)
271 			if (hasOp!(TA, OpKind.binary, op ~ "=", T))
272 	{
273 		return implementOp!(OpKind.binary, op ~ "=")(this, other);
274 	}
275 	/// Enables indexing operations on the stored value.
276 	auto opIndex(this TA, ARGS...)(auto ref ARGS args)
277 			if (hasOp!(TA, OpKind.index, null, ARGS))
278 	{
279 		return implementOp!(OpKind.index, null)(this, args);
280 	}
281 	/// Enables index assignments on the stored value.
282 	auto opIndexAssign(this TA, ARGS...)(auto ref ARGS args)
283 			if (hasOp!(TA, OpKind.indexAssign, null, ARGS))
284 	{
285 		return implementOp!(OpKind.indexAssign, null)(this, args);
286 	}
287 	/// Enables call syntax operations on the stored value.
288 	auto opCall(this TA, ARGS...)(auto ref ARGS args)
289 			if (hasOp!(TA, OpKind.call, null, ARGS))
290 	{
291 		return implementOp!(OpKind.call, null)(this, args);
292 	}
293 
294 	private @trusted @property ref inout(typeof(__traits(getMember, U, f))) trustedGet(string f)() inout
295 	{
296 		return trustedGet!(inout(typeof(__traits(getMember, U, f))));
297 	}
298 
299 	private @trusted @property ref inout(T) trustedGet(T)() inout
300 	{
301 		return *cast(inout(T)*) m_data.ptr;
302 	}
303 }
304 
305 ///
306 @safe unittest
307 {
308 	import taggedalgebraic;
309 
310 	struct Foo
311 	{
312 		string name;
313 		void bar() @safe
314 		{
315 		}
316 	}
317 
318 	union Base
319 	{
320 		int i;
321 		string str;
322 		Foo foo;
323 	}
324 
325 	alias Tagged = TaggedAlgebraic!Base;
326 
327 	// Instantiate
328 	Tagged taggedInt = 5;
329 	Tagged taggedString = "Hello";
330 	Tagged taggedFoo = Foo();
331 	Tagged taggedAny = taggedInt;
332 	taggedAny = taggedString;
333 	taggedAny = taggedFoo;
334 
335 	// Check type: Tagged.Kind is an enum
336 	assert(taggedInt.kind == Tagged.Kind.i);
337 	assert(taggedString.kind == Tagged.Kind.str);
338 	assert(taggedFoo.kind == Tagged.Kind.foo);
339 	assert(taggedAny.kind == Tagged.Kind.foo);
340 
341 	// In most cases, can simply use as-is
342 	auto num = 4 + taggedInt;
343 	auto msg = taggedString ~ " World!";
344 	taggedFoo.bar();
345 	if (taggedAny.kind == Tagged.Kind.foo) // Make sure to check type first!
346 		taggedAny.bar();
347 	//taggedString.bar(); // AssertError: Not a Foo!
348 
349 	// Convert back by casting
350 	auto i = cast(int) taggedInt;
351 	auto str = cast(string) taggedString;
352 	auto foo = cast(Foo) taggedFoo;
353 	if (taggedAny.kind == Tagged.Kind.foo) // Make sure to check type first!
354 		auto foo2 = cast(Foo) taggedAny;
355 	//cast(Foo) taggedString; // AssertError!
356 
357 	// Kind is an enum, so final switch is supported:
358 	final switch (taggedAny.kind)
359 	{
360 	case Tagged.Kind.i:
361 		// It's "int i"
362 		break;
363 
364 	case Tagged.Kind.str:
365 		// It's "string str"
366 		break;
367 
368 	case Tagged.Kind.foo:
369 		// It's "Foo foo"
370 		break;
371 	}
372 }
373 
374 /** Operators and methods of the contained type can be used transparently.
375 */
376 @safe unittest
377 {
378 	static struct S
379 	{
380 		int v;
381 		int test()
382 		{
383 			return v / 2;
384 		}
385 	}
386 
387 	static union Test
388 	{
389 		typeof(null) null_;
390 		int integer;
391 		string text;
392 		string[string] dictionary;
393 		S custom;
394 	}
395 
396 	alias TA = TaggedAlgebraic!Test;
397 
398 	TA ta;
399 	assert(ta.kind == TA.Kind.null_);
400 
401 	ta = 12;
402 	assert(ta.kind == TA.Kind.integer);
403 	assert(ta == 12);
404 	assert(cast(int) ta == 12);
405 	assert(cast(long) ta == 12);
406 	assert(cast(short) ta == 12);
407 
408 	ta += 12;
409 	assert(ta == 24);
410 	assert(ta - 10 == 14);
411 
412 	ta = ["foo" : "bar"];
413 	assert(ta.kind == TA.Kind.dictionary);
414 	assert(ta["foo"] == "bar");
415 
416 	ta["foo"] = "baz";
417 	assert(ta["foo"] == "baz");
418 
419 	ta = S(8);
420 	assert(ta.test() == 4);
421 }
422 
423 unittest
424 { // std.conv integration
425 	import std.conv : to;
426 
427 	static struct S
428 	{
429 		int v;
430 		int test()
431 		{
432 			return v / 2;
433 		}
434 	}
435 
436 	static union Test
437 	{
438 		typeof(null) null_;
439 		int number;
440 		string text;
441 	}
442 
443 	alias TA = TaggedAlgebraic!Test;
444 
445 	TA ta;
446 	assert(ta.kind == TA.Kind.null_);
447 	ta = "34";
448 	assert(ta == "34");
449 	assert(to!int(ta) == 34, to!string(to!int(ta)));
450 	assert(to!string(ta) == "34", to!string(ta));
451 }
452 
453 /** Multiple fields are allowed to have the same type, in which case the type
454 	ID enum is used to disambiguate.
455 */
456 @safe unittest
457 {
458 	static union Test
459 	{
460 		typeof(null) null_;
461 		int count;
462 		int difference;
463 	}
464 
465 	alias TA = TaggedAlgebraic!Test;
466 
467 	TA ta = TA(12, TA.Kind.count);
468 	assert(ta.kind == TA.Kind.count);
469 	assert(ta == 12);
470 
471 	ta = null;
472 	assert(ta.kind == TA.Kind.null_);
473 }
474 
475 @safe unittest
476 { // comparison of whole TAs
477 	static union Test
478 	{
479 		typeof(null) a;
480 		typeof(null) b;
481 		Void c;
482 		Void d;
483 		int e;
484 		int f;
485 	}
486 
487 	alias TA = TaggedAlgebraic!Test;
488 
489 	assert(TA(null, TA.Kind.a) == TA(null, TA.Kind.a));
490 	assert(TA(null, TA.Kind.a) != TA(null, TA.Kind.b));
491 	assert(TA(null, TA.Kind.a) != TA(Void.init, TA.Kind.c));
492 	assert(TA(null, TA.Kind.a) != TA(0, TA.Kind.e));
493 	assert(TA(Void.init, TA.Kind.c) == TA(Void.init, TA.Kind.c));
494 	assert(TA(Void.init, TA.Kind.c) != TA(Void.init, TA.Kind.d));
495 	assert(TA(1, TA.Kind.e) == TA(1, TA.Kind.e));
496 	assert(TA(1, TA.Kind.e) != TA(2, TA.Kind.e));
497 	assert(TA(1, TA.Kind.e) != TA(1, TA.Kind.f));
498 }
499 
500 unittest
501 {
502 	// test proper type modifier support
503 	static struct S
504 	{
505 		void test()
506 		{
507 		}
508 
509 		void testI() immutable
510 		{
511 		}
512 
513 		void testC() const
514 		{
515 		}
516 
517 		void testS() shared
518 		{
519 		}
520 
521 		void testSC() shared const
522 		{
523 		}
524 	}
525 
526 	static union U
527 	{
528 		S s;
529 	}
530 
531 	auto u = TaggedAlgebraic!U(S.init);
532 	const uc = u;
533 	immutable ui = cast(immutable) u;
534 	//const shared usc = cast(shared)u;
535 	//shared us = cast(shared)u;
536 
537 	static assert(is(typeof(u.test())));
538 	static assert(!is(typeof(u.testI())));
539 	static assert(is(typeof(u.testC())));
540 	static assert(!is(typeof(u.testS())));
541 	static assert(!is(typeof(u.testSC())));
542 
543 	static assert(!is(typeof(uc.test())));
544 	static assert(!is(typeof(uc.testI())));
545 	static assert(is(typeof(uc.testC())));
546 	static assert(!is(typeof(uc.testS())));
547 	static assert(!is(typeof(uc.testSC())));
548 
549 	static assert(!is(typeof(ui.test())));
550 	static assert(is(typeof(ui.testI())));
551 	static assert(is(typeof(ui.testC())));
552 	static assert(!is(typeof(ui.testS())));
553 	static assert(is(typeof(ui.testSC())));
554 
555 	/*static assert(!is(typeof(us.test())));
556 	static assert(!is(typeof(us.testI())));
557 	static assert(!is(typeof(us.testC())));
558 	static assert( is(typeof(us.testS())));
559 	static assert( is(typeof(us.testSC())));
560 
561 	static assert(!is(typeof(usc.test())));
562 	static assert(!is(typeof(usc.testI())));
563 	static assert(!is(typeof(usc.testC())));
564 	static assert(!is(typeof(usc.testS())));
565 	static assert( is(typeof(usc.testSC())));*/
566 }
567 
568 unittest
569 {
570 	// test attributes on contained values
571 	import std.typecons : Rebindable, rebindable;
572 
573 	class C
574 	{
575 		void test()
576 		{
577 		}
578 
579 		void testC() const
580 		{
581 		}
582 
583 		void testI() immutable
584 		{
585 		}
586 	}
587 
588 	union U
589 	{
590 		Rebindable!(immutable(C)) c;
591 	}
592 
593 	auto ta = TaggedAlgebraic!U(rebindable(new immutable C));
594 	static assert(!is(typeof(ta.test())));
595 	static assert(is(typeof(ta.testC())));
596 	static assert(is(typeof(ta.testI())));
597 }
598 
599 // test recursive definition using a wrapper dummy struct
600 // (needed to avoid "no size yet for forward reference" errors)
601 unittest
602 {
603 	static struct TA
604 	{
605 		union U
606 		{
607 			TA[] children;
608 			int value;
609 		}
610 
611 		TaggedAlgebraic!U u;
612 		alias u this;
613 		this(ARGS...)(ARGS args)
614 		{
615 			u = TaggedAlgebraic!U(args);
616 		}
617 	}
618 
619 	auto ta = TA(null);
620 	ta ~= TA(0);
621 	ta ~= TA(1);
622 	ta ~= TA([TA(2)]);
623 	assert(ta[0] == 0);
624 	assert(ta[1] == 1);
625 	assert(ta[2][0] == 2);
626 }
627 
628 unittest
629 { // postblit/destructor test
630 	static struct S
631 	{
632 		static int i = 0;
633 		bool initialized = false;
634 		this(bool)
635 		{
636 			initialized = true;
637 			i++;
638 		}
639 
640 		this(this)
641 		{
642 			if (initialized)
643 				i++;
644 		}
645 
646 		~this()
647 		{
648 			if (initialized)
649 				i--;
650 		}
651 	}
652 
653 	static struct U
654 	{
655 		S s;
656 		int t;
657 	}
658 
659 	alias TA = TaggedAlgebraic!U;
660 	{
661 		assert(S.i == 0);
662 		auto ta = TA(S(true));
663 		assert(S.i == 1);
664 		{
665 			auto tb = ta;
666 			assert(S.i == 2);
667 			ta = tb;
668 			assert(S.i == 2);
669 			ta = 1;
670 			assert(S.i == 1);
671 			ta = S(true);
672 			assert(S.i == 2);
673 		}
674 		assert(S.i == 1);
675 	}
676 	assert(S.i == 0);
677 
678 	static struct U2
679 	{
680 		S a;
681 		S b;
682 	}
683 
684 	alias TA2 = TaggedAlgebraic!U2;
685 	{
686 		auto ta2 = TA2(S(true), TA2.Kind.a);
687 		assert(S.i == 1);
688 	}
689 	assert(S.i == 0);
690 }
691 
692 unittest
693 {
694 	static struct S
695 	{
696 		union U
697 		{
698 			int i;
699 			string s;
700 			U[] a;
701 		}
702 
703 		alias TA = TaggedAlgebraic!U;
704 		TA p;
705 		alias p this;
706 	}
707 
708 	S s = S(S.TA("hello"));
709 	assert(cast(string) s == "hello");
710 }
711 
712 unittest
713 { // multiple operator choices
714 	union U
715 	{
716 		int i;
717 		double d;
718 	}
719 
720 	alias TA = TaggedAlgebraic!U;
721 	TA ta = 12;
722 	static assert(is(typeof(ta + 10) == TA)); // ambiguous, could be int or double
723 	assert((ta + 10).kind == TA.Kind.i);
724 	assert(ta + 10 == 22);
725 	static assert(is(typeof(ta + 10.5) == double));
726 	assert(ta + 10.5 == 22.5);
727 }
728 
729 unittest
730 { // Binary op between two TaggedAlgebraic values
731 	union U
732 	{
733 		int i;
734 	}
735 
736 	alias TA = TaggedAlgebraic!U;
737 
738 	TA a = 1, b = 2;
739 	static assert(is(typeof(a + b) == int));
740 	assert(a + b == 3);
741 }
742 
743 unittest
744 { // Ambiguous binary op between two TaggedAlgebraic values
745 	union U
746 	{
747 		int i;
748 		double d;
749 	}
750 
751 	alias TA = TaggedAlgebraic!U;
752 
753 	TA a = 1, b = 2;
754 	static assert(is(typeof(a + b) == TA));
755 	assert((a + b).kind == TA.Kind.i);
756 	assert(a + b == 3);
757 }
758 
759 unittest
760 {
761 	struct S
762 	{
763 		union U
764 		{
765 			@disableIndex string str;
766 			S[] array;
767 			S[string] object;
768 		}
769 
770 		alias TA = TaggedAlgebraic!U;
771 		TA payload;
772 		alias payload this;
773 	}
774 
775 	S a = S(S.TA("hello"));
776 	S b = S(S.TA(["foo" : a]));
777 	S c = S(S.TA([a]));
778 	assert(b["foo"] == a);
779 	assert(b["foo"] == "hello");
780 	assert(c[0] == a);
781 	assert(c[0] == "hello");
782 }
783 
784 static if (__VERSION__ >= 2072)
785 	unittest
786 	{ // default initialization
787 		struct S
788 		{
789 			int i = 42;
790 		}
791 
792 		union U
793 		{
794 			S s;
795 			int j;
796 		}
797 
798 		TaggedAlgebraic!U ta;
799 		assert(ta.i == 42);
800 	}
801 
802 unittest
803 {
804 	import std.meta : AliasSeq;
805 
806 	union U
807 	{
808 		int[int] a;
809 	}
810 
811 	foreach (TA; AliasSeq!(TaggedAlgebraic!U, const(TaggedAlgebraic!U)))
812 	{
813 		TA ta = [1 : 2];
814 		assert(cast(int[int]) ta == [1 : 2]);
815 	}
816 }
817 
818 static if (__VERSION__ >= 2072)
819 {
820 	unittest
821 	{ // issue #8
822 		static struct Result(T, E)
823 		{
824 			static union U
825 			{
826 				T ok;
827 				E err;
828 			}
829 
830 			alias TA = TaggedAlgebraic!U;
831 			TA payload;
832 			alias payload this;
833 
834 			this(T ok)
835 			{
836 				payload = ok;
837 			}
838 
839 			this(E err)
840 			{
841 				payload = err;
842 			}
843 		}
844 
845 		static struct Option(T)
846 		{
847 			static union U
848 			{
849 				T some;
850 				typeof(null) none;
851 			}
852 
853 			alias TA = TaggedAlgebraic!U;
854 			TA payload;
855 			alias payload this;
856 
857 			this(T some)
858 			{
859 				payload = some;
860 			}
861 
862 			this(typeof(null) none)
863 			{
864 				payload = null;
865 			}
866 		}
867 
868 		Result!(Option!size_t, int) foo()
869 		{
870 			return Result!(Option!size_t, int)(42);
871 		}
872 
873 		assert(foo() == 42);
874 	}
875 }
876 
877 unittest
878 { // issue #13
879 	struct S1
880 	{
881 		Void dummy;
882 		int foo;
883 	}
884 
885 	struct S
886 	{
887 		struct T
888 		{
889 			TaggedAlgebraic!S1 foo()
890 			{
891 				return TaggedAlgebraic!S1(42);
892 			}
893 		}
894 
895 		struct U
896 		{
897 			string foo()
898 			{
899 				return "foo";
900 			}
901 		}
902 
903 		Void dummy;
904 		T t;
905 		U u;
906 	}
907 
908 	alias TA = TaggedAlgebraic!S;
909 	auto ta = TA(S.T.init);
910 	assert(ta.foo().get!(TaggedAlgebraic!S1) == 42);
911 
912 	ta = TA(S.U.init);
913 	assert(ta.foo() == "foo");
914 }
915 
916 /** Tests if the algebraic type stores a value of a certain data type.
917 */
918 bool hasType(T, U)(in ref TaggedAlgebraic!U ta)
919 {
920 	alias Fields = Filter!(fieldMatchesType!(U, T), ta.fieldNames);
921 	static assert(Fields.length > 0,
922 			"Type " ~ T.stringof ~ " cannot be stored in a " ~ (TaggedAlgebraic!U).stringof ~ ".");
923 
924 	switch (ta.kind)
925 	{
926 	default:
927 		return false;
928 		foreach (i, fname; Fields)
929 	case __traits(getMember, ta.Kind, fname):
930 			return true;
931 	}
932 	assert(false); // never reached
933 }
934 /// ditto
935 bool hasType(T, U)(in TaggedAlgebraic!U ta)
936 {
937 	return hasType!(T, U)(ta);
938 }
939 
940 ///
941 unittest
942 {
943 	union Fields
944 	{
945 		int number;
946 		string text;
947 	}
948 
949 	TaggedAlgebraic!Fields ta = "test";
950 
951 	assert(ta.hasType!string);
952 	assert(!ta.hasType!int);
953 
954 	ta = 42;
955 	assert(ta.hasType!int);
956 	assert(!ta.hasType!string);
957 }
958 
959 unittest
960 { // issue #1
961 	union U
962 	{
963 		int a;
964 		int b;
965 	}
966 
967 	alias TA = TaggedAlgebraic!U;
968 
969 	TA ta = TA(0, TA.Kind.b);
970 	static assert(!is(typeof(ta.hasType!double)));
971 	assert(ta.hasType!int);
972 }
973 
974 unittest
975 {
976 	union U
977 	{
978 		int a;
979 		float b;
980 	}
981 
982 	alias TA = TaggedAlgebraic!U;
983 
984 	const(TA) test()
985 	{
986 		return TA(12);
987 	}
988 
989 	assert(test().hasType!int);
990 }
991 
992 static if (__VERSION__ >= 2072)
993 {
994 	/** Maps a kind enumeration value to the corresponding field type.
995 
996 		`kind` must be a value of the `TaggedAlgebraic!T.Kind` enumeration.
997 	*/
998 	template TypeOf(alias kind) if (isInstanceOf!(TypeEnum, typeof(kind)))
999 	{
1000 		import std.traits : FieldTypeTuple, TemplateArgsOf;
1001 
1002 		alias U = TemplateArgsOf!(typeof(kind));
1003 		alias TypeOf = FieldTypeTuple!U[kind];
1004 	}
1005 
1006 	///
1007 	unittest
1008 	{
1009 		static struct S
1010 		{
1011 			int a;
1012 			string b;
1013 			string c;
1014 		}
1015 
1016 		alias TA = TaggedAlgebraic!S;
1017 
1018 		static assert(is(TypeOf!(TA.Kind.a) == int));
1019 		static assert(is(TypeOf!(TA.Kind.b) == string));
1020 		static assert(is(TypeOf!(TA.Kind.c) == string));
1021 	}
1022 }
1023 
1024 /** Gets the value stored in an algebraic type based on its data type.
1025 */
1026 ref inout(T) get(T, U)(ref inout(TaggedAlgebraic!U) ta)
1027 {
1028 	import std.format : format;
1029 
1030 	assert(hasType!(T, U)(ta), "Type mismatch!");
1031 	return ta.trustedGet!T;
1032 }
1033 /// ditto
1034 inout(T) get(T, U)(inout(TaggedAlgebraic!U) ta)
1035 {
1036 	import std.format : format;
1037 
1038 	assert(hasType!(T, U)(ta), "Type mismatch!");
1039 	return ta.trustedGet!T;
1040 }
1041 
1042 @nogc @safe nothrow unittest
1043 {
1044 	struct Fields
1045 	{
1046 		int a;
1047 		float b;
1048 	}
1049 
1050 	alias TA = TaggedAlgebraic!Fields;
1051 	auto ta = TA(1);
1052 	assert(ta.get!int == 1);
1053 	ta.get!int = 2;
1054 	assert(ta.get!int == 2);
1055 	ta = TA(1.0);
1056 	assert(ta.get!float == 1.0);
1057 }
1058 
1059 /** Calls a the given callback with the static type of the contained value.
1060 
1061 	The `handler` callback must be a lambda or a single-argument template
1062 	function that accepts all possible types that the given `TaggedAlgebraic`
1063 	can hold.
1064 
1065 	Returns:
1066 		If `handler` has a non-void return value, its return value gets
1067 		forwarded to the caller.
1068 */
1069 auto apply(alias handler, TA)(TA ta) if (isInstanceOf!(TaggedAlgebraic, TA))
1070 {
1071 	final switch (ta.kind)
1072 	{
1073 		foreach (i, fn; TA.fieldNames)
1074 		{
1075 	case __traits(getMember, ta.Kind, fn):
1076 			return handler(get!(TA.FieldTypes[i])(ta));
1077 		}
1078 	}
1079 	static if (__VERSION__ <= 2068)
1080 		assert(false);
1081 }
1082 /// ditto
1083 auto apply(alias handler, T)(T value) if (!isInstanceOf!(TaggedAlgebraic, T))
1084 {
1085 	return handler(value);
1086 }
1087 
1088 ///
1089 unittest
1090 {
1091 	union U
1092 	{
1093 		int i;
1094 		string s;
1095 	}
1096 
1097 	alias TA = TaggedAlgebraic!U;
1098 
1099 	assert(TA(12).apply!((v) {
1100 			static if (is(typeof(v) == int))
1101 			{
1102 				assert(v == 12);
1103 				return 1;
1104 			}
1105 			else
1106 			{
1107 				return 0;
1108 			}
1109 		}) == 1);
1110 
1111 	assert(TA("foo").apply!((v) {
1112 			static if (is(typeof(v) == string))
1113 			{
1114 				assert(v == "foo");
1115 				return 2;
1116 			}
1117 			else
1118 			{
1119 				return 0;
1120 			}
1121 		}) == 2);
1122 
1123 	"baz".apply!((v) { assert(v == "baz"); });
1124 }
1125 
1126 /// Convenience type that can be used for union fields that have no value (`void` is not allowed).
1127 struct Void
1128 {
1129 }
1130 
1131 /// User-defined attibute to disable `opIndex` forwarding for a particular tagged union member.
1132 @property auto disableIndex()
1133 {
1134 	assert(__ctfe, "disableIndex must only be used as an attribute.");
1135 	return DisableOpAttribute(OpKind.index, null);
1136 }
1137 
1138 private struct DisableOpAttribute
1139 {
1140 	OpKind kind;
1141 	string name;
1142 }
1143 
1144 private template hasOp(TA, OpKind kind, string name, ARGS...)
1145 {
1146 	import std.traits : CopyTypeQualifiers;
1147 
1148 	alias UQ = CopyTypeQualifiers!(TA, TA.Union);
1149 	enum hasOp = TypeTuple!(OpInfo!(UQ, kind, name, ARGS).fields).length > 0;
1150 }
1151 
1152 unittest
1153 {
1154 	static struct S
1155 	{
1156 		void m(int i)
1157 		{
1158 		}
1159 
1160 		bool opEquals(int i)
1161 		{
1162 			return true;
1163 		}
1164 
1165 		bool opEquals(S s)
1166 		{
1167 			return true;
1168 		}
1169 	}
1170 
1171 	static union U
1172 	{
1173 		int i;
1174 		string s;
1175 		S st;
1176 	}
1177 
1178 	alias TA = TaggedAlgebraic!U;
1179 
1180 	static assert(hasOp!(TA, OpKind.binary, "+", int));
1181 	static assert(hasOp!(TA, OpKind.binary, "~", string));
1182 	static assert(hasOp!(TA, OpKind.binary, "==", int));
1183 	static assert(hasOp!(TA, OpKind.binary, "==", string));
1184 	static assert(hasOp!(TA, OpKind.binary, "==", int));
1185 	static assert(hasOp!(TA, OpKind.binary, "==", S));
1186 	static assert(hasOp!(TA, OpKind.method, "m", int));
1187 	static assert(hasOp!(TA, OpKind.binary, "+=", int));
1188 	static assert(!hasOp!(TA, OpKind.binary, "~", int));
1189 	static assert(!hasOp!(TA, OpKind.binary, "~", int));
1190 	static assert(!hasOp!(TA, OpKind.method, "m", string));
1191 	static assert(!hasOp!(TA, OpKind.method, "m"));
1192 	static assert(!hasOp!(const(TA), OpKind.binary, "+=", int));
1193 	static assert(!hasOp!(const(TA), OpKind.method, "m", int));
1194 }
1195 
1196 unittest
1197 {
1198 	struct S
1199 	{
1200 		union U
1201 		{
1202 			string s;
1203 			S[] arr;
1204 			S[string] obj;
1205 		}
1206 
1207 		alias TA = TaggedAlgebraic!(S.U);
1208 		TA payload;
1209 		alias payload this;
1210 	}
1211 
1212 	static assert(hasOp!(S.TA, OpKind.index, null, size_t));
1213 	static assert(hasOp!(S.TA, OpKind.index, null, int));
1214 	static assert(hasOp!(S.TA, OpKind.index, null, string));
1215 	static assert(hasOp!(S.TA, OpKind.field, "length"));
1216 }
1217 
1218 unittest
1219 { // "in" operator
1220 	union U
1221 	{
1222 		string[string] dict;
1223 	}
1224 
1225 	alias TA = TaggedAlgebraic!U;
1226 	auto ta = TA(["foo" : "bar"]);
1227 	assert("foo" in ta);
1228 	assert(*("foo" in ta) == "bar");
1229 }
1230 
1231 private static auto implementOp(OpKind kind, string name, T, ARGS...)(ref T self, auto ref ARGS args)
1232 {
1233 	import std.array : join;
1234 	import std.traits : CopyTypeQualifiers;
1235 	import std.variant : Algebraic, Variant;
1236 
1237 	alias UQ = CopyTypeQualifiers!(T, T.Union);
1238 
1239 	alias info = OpInfo!(UQ, kind, name, ARGS);
1240 
1241 	static assert(hasOp!(T, kind, name, ARGS));
1242 
1243 	static assert(info.fields.length > 0,
1244 			"Implementing operator that has no valid implementation for any supported type.");
1245 
1246 	//pragma(msg, "Fields for "~kind.stringof~" "~name~", "~T.stringof~": "~info.fields.stringof);
1247 	//pragma(msg, "Return types for "~kind.stringof~" "~name~", "~T.stringof~": "~info.ReturnTypes.stringof);
1248 	//pragma(msg, typeof(T.Union.tupleof));
1249 	//import std.meta : staticMap; pragma(msg, staticMap!(isMatchingUniqueType!(T.Union), info.ReturnTypes));
1250 
1251 	switch (self.m_kind)
1252 	{
1253 		enum assert_msg = "Operator " ~ name ~ " (" ~ kind.stringof
1254 			~ ") can only be used on values of the following types: " ~ [info.fields].join(", ");
1255 	default:
1256 		assert(false, assert_msg);
1257 		foreach (i, f; info.fields)
1258 		{
1259 			alias FT = typeof(__traits(getMember, T.Union, f));
1260 	case __traits(getMember, T.Kind, f):
1261 			static if (NoDuplicates!(info.ReturnTypes).length == 1)
1262 				return info.perform(self.trustedGet!FT, args);
1263 			else static if (allSatisfy!(isMatchingUniqueType!(T.Union), info.ReturnTypes))
1264 				return TaggedAlgebraic!(T.Union)(info.perform(self.trustedGet!FT, args));
1265 			else static if (allSatisfy!(isNoVariant, info.ReturnTypes))
1266 			{
1267 				alias Alg = Algebraic!(NoDuplicates!(info.ReturnTypes));
1268 				info.ReturnTypes[i] ret = info.perform(self.trustedGet!FT, args);
1269 				import std.traits : isInstanceOf;
1270 
1271 				return Alg(ret);
1272 			}
1273 			else static if (is(FT == Variant))
1274 				return info.perform(self.trustedGet!FT, args);
1275 			else
1276 				return Variant(info.perform(self.trustedGet!FT, args));
1277 		}
1278 	}
1279 
1280 	assert(false); // never reached
1281 }
1282 
1283 unittest
1284 { // opIndex on recursive TA with closed return value set
1285 	static struct S
1286 	{
1287 		union U
1288 		{
1289 			char ch;
1290 			string str;
1291 			S[] arr;
1292 		}
1293 
1294 		alias TA = TaggedAlgebraic!U;
1295 		TA payload;
1296 		alias payload this;
1297 
1298 		this(T)(T t)
1299 		{
1300 			this.payload = t;
1301 		}
1302 	}
1303 
1304 	S a = S("foo");
1305 	S s = S([a]);
1306 
1307 	assert(implementOp!(OpKind.field, "length")(s.payload) == 1);
1308 	static assert(is(typeof(implementOp!(OpKind.index, null)(s.payload, 0)) == S.TA));
1309 	assert(implementOp!(OpKind.index, null)(s.payload, 0) == "foo");
1310 }
1311 
1312 unittest
1313 { // opIndex on recursive TA with closed return value set using @disableIndex
1314 	static struct S
1315 	{
1316 		union U
1317 		{
1318 			@disableIndex string str;
1319 			S[] arr;
1320 		}
1321 
1322 		alias TA = TaggedAlgebraic!U;
1323 		TA payload;
1324 		alias payload this;
1325 
1326 		this(T)(T t)
1327 		{
1328 			this.payload = t;
1329 		}
1330 	}
1331 
1332 	S a = S("foo");
1333 	S s = S([a]);
1334 
1335 	assert(implementOp!(OpKind.field, "length")(s.payload) == 1);
1336 	static assert(is(typeof(implementOp!(OpKind.index, null)(s.payload, 0)) == S));
1337 	assert(implementOp!(OpKind.index, null)(s.payload, 0) == "foo");
1338 }
1339 
1340 private auto performOpRaw(U, OpKind kind, string name, T, ARGS...)(ref T value, /*auto ref*/ ARGS args)
1341 {
1342 	static if (kind == OpKind.binary)
1343 		return mixin("value " ~ name ~ " args[0]");
1344 	else static if (kind == OpKind.binaryRight)
1345 		return mixin("args[0] " ~ name ~ " value");
1346 	else static if (kind == OpKind.unary)
1347 		return mixin("name " ~ value);
1348 	else static if (kind == OpKind.method)
1349 		return __traits(getMember, value, name)(args);
1350 	else static if (kind == OpKind.field)
1351 		return __traits(getMember, value, name);
1352 	else static if (kind == OpKind.index)
1353 		return value[args];
1354 	else static if (kind == OpKind.indexAssign)
1355 		return value[args[1 .. $]] = args[0];
1356 	else static if (kind == OpKind.call)
1357 		return value(args);
1358 	else
1359 		static assert(false, "Unsupported kind of operator: " ~ kind.stringof);
1360 }
1361 
1362 unittest
1363 {
1364 	union U
1365 	{
1366 		int i;
1367 		string s;
1368 	}
1369 
1370 	{
1371 		int v = 1;
1372 		assert(performOpRaw!(U, OpKind.binary, "+")(v, 3) == 4);
1373 	}
1374 	{
1375 		string v = "foo";
1376 		assert(performOpRaw!(U, OpKind.binary, "~")(v, "bar") == "foobar");
1377 	}
1378 }
1379 
1380 private auto performOp(U, OpKind kind, string name, T, ARGS...)(ref T value, /*auto ref*/ ARGS args)
1381 {
1382 	import std.traits : isInstanceOf;
1383 
1384 	static if (ARGS.length > 0 && isInstanceOf!(TaggedAlgebraic, ARGS[0]))
1385 	{
1386 		static if (is(typeof(performOpRaw!(U, kind, name, T, ARGS)(value, args))))
1387 		{
1388 			return performOpRaw!(U, kind, name, T, ARGS)(value, args);
1389 		}
1390 		else
1391 		{
1392 			alias TA = ARGS[0];
1393 			template MTypesImpl(size_t i)
1394 			{
1395 				static if (i < TA.FieldTypes.length)
1396 				{
1397 					alias FT = TA.FieldTypes[i];
1398 					static if (is(typeof(&performOpRaw!(U, kind, name, T, FT, ARGS[1 .. $]))))
1399 						alias MTypesImpl = TypeTuple!(FT, MTypesImpl!(i + 1));
1400 					else
1401 						alias MTypesImpl = TypeTuple!(MTypesImpl!(i + 1));
1402 				}
1403 				else
1404 					alias MTypesImpl = TypeTuple!();
1405 			}
1406 
1407 			alias MTypes = NoDuplicates!(MTypesImpl!0);
1408 			static assert(MTypes.length > 0,
1409 					"No type of the TaggedAlgebraic parameter matches any function declaration.");
1410 			static if (MTypes.length == 1)
1411 			{
1412 				if (args[0].hasType!(MTypes[0]))
1413 					return performOpRaw!(U, kind, name)(value,
1414 							args[0].get!(MTypes[0]), args[1 .. $]);
1415 			}
1416 			else
1417 			{
1418 				// TODO: allow all return types (fall back to Algebraic or Variant)
1419 				foreach (FT; MTypes)
1420 				{
1421 					if (args[0].hasType!FT)
1422 						return ARGS[0](performOpRaw!(U, kind, name)(value,
1423 								args[0].get!FT, args[1 .. $]));
1424 				}
1425 			}
1426 			throw new  /*InvalidAgument*/ Exception("Algebraic parameter type mismatch");
1427 		}
1428 	}
1429 	else
1430 		return performOpRaw!(U, kind, name, T, ARGS)(value, args);
1431 }
1432 
1433 unittest
1434 {
1435 	union U
1436 	{
1437 		int i;
1438 		double d;
1439 		string s;
1440 	}
1441 
1442 	{
1443 		int v = 1;
1444 		assert(performOp!(U, OpKind.binary, "+")(v, 3) == 4);
1445 	}
1446 	{
1447 		string v = "foo";
1448 		assert(performOp!(U, OpKind.binary, "~")(v, "bar") == "foobar");
1449 	}
1450 	{
1451 		string v = "foo";
1452 		assert(performOp!(U, OpKind.binary, "~")(v, TaggedAlgebraic!U("bar")) == "foobar");
1453 	}
1454 	{
1455 		int v = 1;
1456 		assert(performOp!(U, OpKind.binary, "+")(v, TaggedAlgebraic!U(3)) == 4);
1457 	}
1458 }
1459 
1460 private template OpInfo(U, OpKind kind, string name, ARGS...)
1461 {
1462 	import std.traits : CopyTypeQualifiers, FieldTypeTuple, FieldNameTuple, ReturnType;
1463 
1464 	private alias FieldTypes = FieldTypeTuple!U;
1465 	private alias fieldNames = FieldNameTuple!U;
1466 
1467 	private template isOpEnabled(string field)
1468 	{
1469 		alias attribs = TypeTuple!(__traits(getAttributes, __traits(getMember, U, field)));
1470 		template impl(size_t i)
1471 		{
1472 			static if (i < attribs.length)
1473 			{
1474 				static if (is(typeof(attribs[i]) == DisableOpAttribute))
1475 				{
1476 					static if (kind == attribs[i].kind && name == attribs[i].name)
1477 						enum impl = false;
1478 					else
1479 						enum impl = impl!(i + 1);
1480 				}
1481 				else
1482 					enum impl = impl!(i + 1);
1483 			}
1484 			else
1485 				enum impl = true;
1486 		}
1487 
1488 		enum isOpEnabled = impl!0;
1489 	}
1490 
1491 	template fieldsImpl(size_t i)
1492 	{
1493 		static if (i < FieldTypes.length)
1494 		{
1495 			static if (isOpEnabled!(fieldNames[i])
1496 					&& is(typeof(&performOp!(U, kind, name, FieldTypes[i], ARGS))))
1497 			{
1498 				alias fieldsImpl = TypeTuple!(fieldNames[i], fieldsImpl!(i + 1));
1499 			}
1500 			else
1501 				alias fieldsImpl = fieldsImpl!(i + 1);
1502 		}
1503 		else
1504 			alias fieldsImpl = TypeTuple!();
1505 	}
1506 
1507 	alias fields = fieldsImpl!0;
1508 
1509 	template ReturnTypesImpl(size_t i)
1510 	{
1511 		static if (i < fields.length)
1512 		{
1513 			alias FT = CopyTypeQualifiers!(U, typeof(__traits(getMember, U, fields[i])));
1514 			alias ReturnTypesImpl = TypeTuple!(ReturnType!(performOp!(U,
1515 					kind, name, FT, ARGS)), ReturnTypesImpl!(i + 1));
1516 		}
1517 		else
1518 			alias ReturnTypesImpl = TypeTuple!();
1519 	}
1520 
1521 	alias ReturnTypes = ReturnTypesImpl!0;
1522 
1523 	static auto perform(T)(ref T value, auto ref ARGS args)
1524 	{
1525 		return performOp!(U, kind, name)(value, args);
1526 	}
1527 }
1528 
1529 private template ImplicitUnqual(T)
1530 {
1531 	import std.traits : Unqual, hasAliasing;
1532 
1533 	static if (is(T == void))
1534 		alias ImplicitUnqual = void;
1535 	else
1536 	{
1537 		private static struct S
1538 		{
1539 			T t;
1540 		}
1541 
1542 		static if (hasAliasing!S)
1543 			alias ImplicitUnqual = T;
1544 		else
1545 			alias ImplicitUnqual = Unqual!T;
1546 	}
1547 }
1548 
1549 private enum OpKind
1550 {
1551 	binary,
1552 	binaryRight,
1553 	unary,
1554 	method,
1555 	field,
1556 	index,
1557 	indexAssign,
1558 	call
1559 }
1560 
1561 private template TypeEnum(U)
1562 {
1563 	import std.array : join;
1564 	import std.traits : FieldNameTuple;
1565 
1566 	mixin("enum TypeEnum { " ~ [FieldNameTuple!U].join(", ") ~ " }");
1567 }
1568 
1569 private string generateConstructors(U)()
1570 {
1571 	import std.algorithm : map;
1572 	import std.array : join;
1573 	import std.string : format;
1574 	import std.traits : FieldTypeTuple;
1575 
1576 	string ret;
1577 
1578 	static if (__VERSION__ < 2072)
1579 	{
1580 		// disable default construction if first type is not a null/Void type
1581 		static if (!is(FieldTypeTuple!U[0] == typeof(null)) && !is(FieldTypeTuple!U[0] == Void))
1582 		{
1583 			ret ~= q{
1584 				@disable this();
1585 			};
1586 		}
1587 	}
1588 
1589 	// normal type constructors
1590 	foreach (tname; UniqueTypeFields!U)
1591 		ret ~= q{
1592 			this(typeof(U.%s) value)
1593 			{
1594 				m_data.rawEmplace(value);
1595 				m_kind = Kind.%s;
1596 			}
1597 
1598 			void opAssign(typeof(U.%s) value)
1599 			{
1600 				if (m_kind != Kind.%s) {
1601 					// NOTE: destroy(this) doesn't work for some opDispatch-related reason
1602 					static if (is(typeof(&this.__xdtor)))
1603 						this.__xdtor();
1604 					m_data.rawEmplace(value);
1605 				} else {
1606 					trustedGet!"%s" = value;
1607 				}
1608 				m_kind = Kind.%s;
1609 			}
1610 		}.format(tname, tname, tname, tname, tname, tname);
1611 
1612 	// type constructors with explicit type tag
1613 	foreach (tname; TypeTuple!(UniqueTypeFields!U, AmbiguousTypeFields!U))
1614 		ret ~= q{
1615 			this(typeof(U.%s) value, Kind type)
1616 			{
1617 				assert(type.among!(%s), format("Invalid type ID for type %%s: %%s", typeof(U.%s).stringof, type));
1618 				m_data.rawEmplace(value);
1619 				m_kind = type;
1620 			}
1621 		}.format(tname, [SameTypeFields!(U,
1622 				tname)].map!(f => "Kind." ~ f).join(", "), tname);
1623 
1624 	return ret;
1625 }
1626 
1627 private template UniqueTypeFields(U)
1628 {
1629 	import std.traits : FieldTypeTuple, FieldNameTuple;
1630 
1631 	alias Types = FieldTypeTuple!U;
1632 
1633 	template impl(size_t i)
1634 	{
1635 		static if (i < Types.length)
1636 		{
1637 			enum name = FieldNameTuple!U[i];
1638 			alias T = Types[i];
1639 			static if (staticIndexOf!(T, Types) == i && staticIndexOf!(T, Types[i + 1 .. $]) < 0)
1640 				alias impl = TypeTuple!(name, impl!(i + 1));
1641 			else
1642 				alias impl = TypeTuple!(impl!(i + 1));
1643 		}
1644 		else
1645 			alias impl = TypeTuple!();
1646 	}
1647 
1648 	alias UniqueTypeFields = impl!0;
1649 }
1650 
1651 private template AmbiguousTypeFields(U)
1652 {
1653 	import std.traits : FieldTypeTuple, FieldNameTuple;
1654 
1655 	alias Types = FieldTypeTuple!U;
1656 
1657 	template impl(size_t i)
1658 	{
1659 		static if (i < Types.length)
1660 		{
1661 			enum name = FieldNameTuple!U[i];
1662 			alias T = Types[i];
1663 			static if (staticIndexOf!(T, Types) == i && staticIndexOf!(T, Types[i + 1 .. $]) >= 0)
1664 				alias impl = TypeTuple!(name, impl!(i + 1));
1665 			else
1666 				alias impl = impl!(i + 1);
1667 		}
1668 		else
1669 			alias impl = TypeTuple!();
1670 	}
1671 
1672 	alias AmbiguousTypeFields = impl!0;
1673 }
1674 
1675 unittest
1676 {
1677 	union U
1678 	{
1679 		int a;
1680 		string b;
1681 		int c;
1682 		double d;
1683 	}
1684 
1685 	static assert([UniqueTypeFields!U] == ["b", "d"]);
1686 	static assert([AmbiguousTypeFields!U] == ["a"]);
1687 }
1688 
1689 private template SameTypeFields(U, string field)
1690 {
1691 	import std.traits : FieldTypeTuple, FieldNameTuple;
1692 
1693 	alias Types = FieldTypeTuple!U;
1694 
1695 	alias T = typeof(__traits(getMember, U, field));
1696 	template impl(size_t i)
1697 	{
1698 		static if (i < Types.length)
1699 		{
1700 			enum name = FieldNameTuple!U[i];
1701 			static if (is(Types[i] == T))
1702 				alias impl = TypeTuple!(name, impl!(i + 1));
1703 			else
1704 				alias impl = TypeTuple!(impl!(i + 1));
1705 		}
1706 		else
1707 			alias impl = TypeTuple!();
1708 	}
1709 
1710 	alias SameTypeFields = impl!0;
1711 }
1712 
1713 private template MemberType(U)
1714 {
1715 	template MemberType(string name)
1716 	{
1717 		alias MemberType = typeof(__traits(getMember, U, name));
1718 	}
1719 }
1720 
1721 private template isMatchingType(U)
1722 {
1723 	import std.traits : FieldTypeTuple;
1724 
1725 	enum isMatchingType(T) = staticIndexOf!(T, FieldTypeTuple!U) >= 0;
1726 }
1727 
1728 private template isMatchingUniqueType(U)
1729 {
1730 	import std.traits : staticMap;
1731 
1732 	alias UniqueTypes = staticMap!(FieldTypeOf!U, UniqueTypeFields!U);
1733 	template isMatchingUniqueType(T)
1734 	{
1735 		static if (is(T : TaggedAlgebraic!U))
1736 			enum isMatchingUniqueType = true;
1737 		else
1738 			enum isMatchingUniqueType = staticIndexOfImplicit!(T, UniqueTypes) >= 0;
1739 	}
1740 }
1741 
1742 private template fieldMatchesType(U, T)
1743 {
1744 	enum fieldMatchesType(string field) = is(typeof(__traits(getMember, U, field)) == T);
1745 }
1746 
1747 private template FieldTypeOf(U)
1748 {
1749 	template FieldTypeOf(string name)
1750 	{
1751 		alias FieldTypeOf = typeof(__traits(getMember, U, name));
1752 	}
1753 }
1754 
1755 private template staticIndexOfImplicit(T, Types...)
1756 {
1757 	template impl(size_t i)
1758 	{
1759 		static if (i < Types.length)
1760 		{
1761 			static if (is(T : Types[i]))
1762 				enum impl = i;
1763 			else
1764 				enum impl = impl!(i + 1);
1765 		}
1766 		else
1767 			enum impl = -1;
1768 	}
1769 
1770 	enum staticIndexOfImplicit = impl!0;
1771 }
1772 
1773 unittest
1774 {
1775 	static assert(staticIndexOfImplicit!(immutable(char), char) == 0);
1776 	static assert(staticIndexOfImplicit!(int, long) == 0);
1777 	static assert(staticIndexOfImplicit!(long, int) < 0);
1778 	static assert(staticIndexOfImplicit!(int, int, double) == 0);
1779 	static assert(staticIndexOfImplicit!(double, int, double) == 1);
1780 }
1781 
1782 private template isNoVariant(T)
1783 {
1784 	import std.variant : Variant;
1785 
1786 	enum isNoVariant = !is(T == Variant);
1787 }
1788 
1789 private void rawEmplace(T)(void[] dst, ref T src)
1790 {
1791 	T[] tdst = () @trusted { return cast(T[]) dst[0 .. T.sizeof]; }();
1792 	static if (is(T == class))
1793 	{
1794 		tdst[0] = src;
1795 	}
1796 	else
1797 	{
1798 		import std.conv : emplace;
1799 
1800 		emplace!T(&tdst[0]);
1801 		tdst[0] = src;
1802 	}
1803 }
1804 
1805 // std.algorithm.mutation.swap sometimes fails to compile due to
1806 // internal errors in hasElaborateAssign!T/isAssignable!T. This is probably
1807 // caused by cyclic dependencies. However, there is no reason to do these
1808 // checks in this context, so we just directly move the raw memory.
1809 private void rawSwap(T)(ref T a, ref T b) @trusted
1810 {
1811 	void[T.sizeof] tmp = void;
1812 	void[] ab = (cast(void*)&a)[0 .. T.sizeof];
1813 	void[] bb = (cast(void*)&b)[0 .. T.sizeof];
1814 	tmp[] = ab[];
1815 	ab[] = bb[];
1816 	bb[] = tmp[];
1817 }
1818 
1819 unittest
1820 {
1821 	struct TU
1822 	{
1823 		int i;
1824 	}
1825 
1826 	alias TA = TaggedAlgebraic!TU;
1827 
1828 	auto ta = TA(12);
1829 	static assert(!is(typeof(ta.put(12))));
1830 }