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