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 gfx.decl.sdlang.taggedalgebraic;
9 
10 import std.typetuple;
11 import std.traits : isInstanceOf, Unqual;
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, hasElaborateCopyConstructor, hasElaborateDestructor;
47 
48 	/// Alias of the type used for defining the possible storage types/kinds.
49 	alias Union = U;
50 
51 	private alias FieldTypes = FieldTypeTuple!U;
52 	private alias fieldNames = FieldNameTuple!U;
53 
54 	static assert(FieldTypes.length > 0, "The TaggedAlgebraic's union type must have at least one field.");
55 	static assert(FieldTypes.length == fieldNames.length);
56 
57 
58 	private {
59 		static if (is(FieldTypes[0] == typeof(null)) || is(FieldTypes[0] == Void) || __VERSION__ < 2072) {
60 			void[Largest!FieldTypes.sizeof] m_data;
61 		} else {
62 			union Dummy {
63 				FieldTypes[0] initField;
64 				void[Largest!FieldTypes.sizeof] data;
65 				alias data this;
66 			}
67 			Dummy m_data = { initField: FieldTypes[0].init };
68 		}
69 		Kind m_kind;
70 	}
71 
72 	/// A type enum that identifies the type of value currently stored.
73 	alias Kind = TypeEnum!U;
74 
75 	/// Compatibility alias
76 	deprecated("Use 'Kind' instead.") alias Type = Kind;
77 
78 	/// The type ID of the currently stored value.
79 	@property Kind kind() const { return m_kind; }
80 
81 	// Compatibility alias
82 	deprecated("Use 'kind' instead.")
83 	alias typeID = kind;
84 
85 	// constructors
86 	//pragma(msg, generateConstructors!U());
87 	mixin(generateConstructors!U);
88 
89 	this(TaggedAlgebraic other)
90 	{
91 		rawSwap(this, other);
92 	}
93 
94 	void opAssign(TaggedAlgebraic other)
95 	{
96 		rawSwap(this, other);
97 	}
98 
99 	// postblit constructor
100 	static if (anySatisfy!(hasElaborateCopyConstructor, FieldTypes))
101 	{
102 		this(this)
103 		{
104 			switch (m_kind) {
105 				default: break;
106 				foreach (i, tname; fieldNames) {
107 					alias T = typeof(__traits(getMember, U, tname));
108 					static if (hasElaborateCopyConstructor!T)
109 					{
110 						case __traits(getMember, Kind, tname):
111 							typeid(T).postblit(cast(void*)&trustedGet!tname());
112 							return;
113 					}
114 				}
115 			}
116 		}
117 	}
118 
119 	// destructor
120 	static if (anySatisfy!(hasElaborateDestructor, FieldTypes))
121 	{
122 		~this()
123 		{
124 			final switch (m_kind) {
125 				foreach (i, tname; fieldNames) {
126 					alias T = typeof(__traits(getMember, U, tname));
127 					case __traits(getMember, Kind, tname):
128 						static if (hasElaborateDestructor!T) {
129 							.destroy(trustedGet!tname);
130 						}
131 						return;
132 				}
133 			}
134 		}
135 	}
136 
137 	/// Enables conversion or extraction of the stored value.
138 	T opCast(T)()
139 	{
140 		import std.conv : to;
141 
142 		final switch (m_kind) {
143 			foreach (i, FT; FieldTypes) {
144 				case __traits(getMember, Kind, fieldNames[i]):
145 					static if (is(typeof(trustedGet!(fieldNames[i])) : T))
146 						return trustedGet!(fieldNames[i]);
147 					else static if (is(typeof(to!T(trustedGet!(fieldNames[i]))))) {
148 						return to!T(trustedGet!(fieldNames[i]));
149 					} else {
150 						assert(false, "Cannot cast a " ~ m_kind.to!string
151 								~ " value of type " ~ FT.stringof ~ " to " ~ T.stringof);
152 					}
153 			}
154 		}
155 		assert(false); // never reached
156 	}
157 	/// ditto
158 	T opCast(T)() const
159 	{
160 		// this method needs to be duplicated because inout doesn't work with to!()
161 		import std.conv : to;
162 
163 		final switch (m_kind) {
164 			foreach (i, FT; FieldTypes) {
165 				case __traits(getMember, Kind, fieldNames[i]):
166 					static if (is(typeof(trustedGet!(fieldNames[i])) : T))
167 						return trustedGet!(fieldNames[i]);
168 					else static if (is(typeof(to!T(trustedGet!(fieldNames[i]))))) {
169 						return to!T(trustedGet!(fieldNames[i]));
170 					} else {
171 						assert(false, "Cannot cast a " ~ m_kind.to!string
172 								~ " value of type" ~ FT.stringof ~ " to " ~ T.stringof);
173 					}
174 			}
175 		}
176 		assert(false); // never reached
177 	}
178 
179 	/// Uses `cast(string)`/`to!string` to return a string representation of the enclosed value.
180 	string toString() const { return cast(string)this; }
181 
182 	// NOTE: "this TA" is used here as the functional equivalent of inout,
183 	//       just that it generates one template instantiation per modifier
184 	//       combination, so that we can actually decide what to do for each
185 	//       case.
186 
187 	/// Enables the invocation of methods of the stored value.
188 	auto opDispatch(string name, this TA, ARGS...)(auto ref ARGS args) if (hasOp!(TA, OpKind.method, name, ARGS)) { return implementOp!(OpKind.method, name)(this, args); }
189 	/// Enables accessing properties/fields of the stored value.
190 	@property auto opDispatch(string name, this TA, ARGS...)(auto ref ARGS args) if (hasOp!(TA, OpKind.field, name, ARGS) && !hasOp!(TA, OpKind.method, name, ARGS)) { return implementOp!(OpKind.field, name)(this, args); }
191 	/// Enables equality comparison with the stored value.
192 	auto opEquals(T, this TA)(auto ref T other)
193 		if (is(Unqual!T == TaggedAlgebraic) || hasOp!(TA, OpKind.binary, "==", T))
194 	{
195 		static if (is(Unqual!T == TaggedAlgebraic)) {
196 			if (this.kind != other.kind) return false;
197 			final switch (this.kind)
198 				foreach (i, fname; fieldNames)
199 					case __traits(getMember, Kind, fname):
200 						return trustedGet!fname == other.trustedGet!fname;
201 			assert(false); // never reached
202 		} else return implementOp!(OpKind.binary, "==")(this, other);
203 	}
204 	/// Enables relational comparisons with the stored value.
205 	auto opCmp(T, this TA)(auto ref T other) if (hasOp!(TA, OpKind.binary, "<", T)) { assert(false, "TODO!"); }
206 	/// Enables the use of unary operators with the stored value.
207 	auto opUnary(string op, this TA)() if (hasOp!(TA, OpKind.unary, op)) { return implementOp!(OpKind.unary, op)(this); }
208 	/// Enables the use of binary operators with the stored value.
209 	auto opBinary(string op, T, this TA)(auto ref T other) if (hasOp!(TA, OpKind.binary, op, T)) { return implementOp!(OpKind.binary, op)(this, other); }
210 	/// Enables the use of binary operators with the stored value.
211 	auto opBinaryRight(string op, T, this TA)(auto ref T other) if (hasOp!(TA, OpKind.binaryRight, op, T)) { return implementOp!(OpKind.binaryRight, op)(this, other); }
212 	/// Enables operator assignments on the stored value.
213 	auto opOpAssign(string op, T, this TA)(auto ref T other) if (hasOp!(TA, OpKind.binary, op~"=", T)) { return implementOp!(OpKind.binary, op~"=")(this, other); }
214 	/// Enables indexing operations on the stored value.
215 	auto opIndex(this TA, ARGS...)(auto ref ARGS args) if (hasOp!(TA, OpKind.index, null, ARGS)) { return implementOp!(OpKind.index, null)(this, args); }
216 	/// Enables index assignments on the stored value.
217 	auto opIndexAssign(this TA, ARGS...)(auto ref ARGS args) if (hasOp!(TA, OpKind.indexAssign, null, ARGS)) { return implementOp!(OpKind.indexAssign, null)(this, args); }
218 	/// Enables call syntax operations on the stored value.
219 	auto opCall(this TA, ARGS...)(auto ref ARGS args) if (hasOp!(TA, OpKind.call, null, ARGS)) { return implementOp!(OpKind.call, null)(this, args); }
220 
221 	private @trusted @property ref inout(typeof(__traits(getMember, U, f))) trustedGet(string f)() inout { return trustedGet!(inout(typeof(__traits(getMember, U, f)))); }
222 	private @trusted @property ref inout(T) trustedGet(T)() inout { return *cast(inout(T)*)m_data.ptr; }
223 }
224 
225 ///
226 unittest
227 {
228 	import gfx.decl.sdlang.taggedalgebraic;
229 
230 	struct Foo {
231 		string name;
232 		void bar() {}
233 	}
234 
235 	union Base {
236 		int i;
237 		string str;
238 		Foo foo;
239 	}
240 
241 	alias Tagged = TaggedAlgebraic!Base;
242 
243 	// Instantiate
244 	Tagged taggedInt = 5;
245 	Tagged taggedString = "Hello";
246 	Tagged taggedFoo = Foo();
247 	Tagged taggedAny = taggedInt;
248 	taggedAny = taggedString;
249 	taggedAny = taggedFoo;
250 
251 	// Check type: Tagged.Kind is an enum
252 	assert(taggedInt.kind == Tagged.Kind.i);
253 	assert(taggedString.kind == Tagged.Kind.str);
254 	assert(taggedFoo.kind == Tagged.Kind.foo);
255 	assert(taggedAny.kind == Tagged.Kind.foo);
256 
257 	// In most cases, can simply use as-is
258 	auto num = 4 + taggedInt;
259 	auto msg = taggedString ~ " World!";
260 	taggedFoo.bar();
261 	if (taggedAny.kind == Tagged.Kind.foo) // Make sure to check type first!
262 		taggedAny.bar();
263 	//taggedString.bar(); // AssertError: Not a Foo!
264 
265 	// Convert back by casting
266 	auto i   = cast(int)    taggedInt;
267 	auto str = cast(string) taggedString;
268 	auto foo = cast(Foo)    taggedFoo;
269 	if (taggedAny.kind == Tagged.Kind.foo) // Make sure to check type first!
270 		auto foo2 = cast(Foo) taggedAny;
271 	//cast(Foo) taggedString; // AssertError!
272 
273 	// Kind is an enum, so final switch is supported:
274 	final switch (taggedAny.kind) {
275 		case Tagged.Kind.i:
276 			// It's "int i"
277 			break;
278 
279 		case Tagged.Kind.str:
280 			// It's "string str"
281 			break;
282 
283 		case Tagged.Kind.foo:
284 			// It's "Foo foo"
285 			break;
286 	}
287 }
288 
289 /** Operators and methods of the contained type can be used transparently.
290 */
291 @safe unittest {
292 	static struct S {
293 		int v;
294 		int test() { return v / 2; }
295 	}
296 
297 	static union Test {
298 		typeof(null) null_;
299 		int integer;
300 		string text;
301 		string[string] dictionary;
302 		S custom;
303 	}
304 
305 	alias TA = TaggedAlgebraic!Test;
306 
307 	TA ta;
308 	assert(ta.kind == TA.Kind.null_);
309 
310 	ta = 12;
311 	assert(ta.kind == TA.Kind.integer);
312 	assert(ta == 12);
313 	assert(cast(int)ta == 12);
314 	assert(cast(long)ta == 12);
315 	assert(cast(short)ta == 12);
316 
317 	ta += 12;
318 	assert(ta == 24);
319 	assert(ta - 10 == 14);
320 
321 	ta = ["foo" : "bar"];
322 	assert(ta.kind == TA.Kind.dictionary);
323 	assert(ta["foo"] == "bar");
324 
325 	ta["foo"] = "baz";
326 	assert(ta["foo"] == "baz");
327 
328 	ta = S(8);
329 	assert(ta.test() == 4);
330 }
331 
332 unittest { // std.conv integration
333 	import std.conv : to;
334 
335 	static struct S {
336 		int v;
337 		int test() { return v / 2; }
338 	}
339 
340 	static union Test {
341 		typeof(null) null_;
342 		int number;
343 		string text;
344 	}
345 
346 	alias TA = TaggedAlgebraic!Test;
347 
348 	TA ta;
349 	assert(ta.kind == TA.Kind.null_);
350 	ta = "34";
351 	assert(ta == "34");
352 	assert(to!int(ta) == 34, to!string(to!int(ta)));
353 	assert(to!string(ta) == "34", to!string(ta));
354 }
355 
356 /** Multiple fields are allowed to have the same type, in which case the type
357 	ID enum is used to disambiguate.
358 */
359 @safe unittest {
360 	static union Test {
361 		typeof(null) null_;
362 		int count;
363 		int difference;
364 	}
365 
366 	alias TA = TaggedAlgebraic!Test;
367 
368 	TA ta = TA(12, TA.Kind.count);
369 	assert(ta.kind == TA.Kind.count);
370 	assert(ta == 12);
371 
372 	ta = null;
373 	assert(ta.kind == TA.Kind.null_);
374 }
375 
376 @safe unittest { // comparison of whole TAs
377 	static union Test {
378 		typeof(null) a;
379 		typeof(null) b;
380 		Void c;
381 		Void d;
382 		int e;
383 		int f;
384 	}
385 	alias TA = TaggedAlgebraic!Test;
386 
387 	assert(TA(null, TA.Kind.a) == TA(null, TA.Kind.a));
388 	assert(TA(null, TA.Kind.a) != TA(null, TA.Kind.b));
389 	assert(TA(null, TA.Kind.a) != TA(Void.init, TA.Kind.c));
390 	assert(TA(null, TA.Kind.a) != TA(0, TA.Kind.e));
391 	assert(TA(Void.init, TA.Kind.c) == TA(Void.init, TA.Kind.c));
392 	assert(TA(Void.init, TA.Kind.c) != TA(Void.init, TA.Kind.d));
393 	assert(TA(1, TA.Kind.e) == TA(1, TA.Kind.e));
394 	assert(TA(1, TA.Kind.e) != TA(2, TA.Kind.e));
395 	assert(TA(1, TA.Kind.e) != TA(1, TA.Kind.f));
396 }
397 
398 unittest {
399 	// test proper type modifier support
400 	static struct  S {
401 		void test() {}
402 		void testI() immutable {}
403 		void testC() const {}
404 		void testS() shared {}
405 		void testSC() shared const {}
406 	}
407 	static union U {
408 		S s;
409 	}
410 
411 	auto u = TaggedAlgebraic!U(S.init);
412 	const uc = u;
413 	immutable ui = cast(immutable)u;
414 	//const shared usc = cast(shared)u;
415 	//shared us = cast(shared)u;
416 
417 	static assert( is(typeof(u.test())));
418 	static assert(!is(typeof(u.testI())));
419 	static assert( is(typeof(u.testC())));
420 	static assert(!is(typeof(u.testS())));
421 	static assert(!is(typeof(u.testSC())));
422 
423 	static assert(!is(typeof(uc.test())));
424 	static assert(!is(typeof(uc.testI())));
425 	static assert( is(typeof(uc.testC())));
426 	static assert(!is(typeof(uc.testS())));
427 	static assert(!is(typeof(uc.testSC())));
428 
429 	static assert(!is(typeof(ui.test())));
430 	static assert( is(typeof(ui.testI())));
431 	static assert( is(typeof(ui.testC())));
432 	static assert(!is(typeof(ui.testS())));
433 	static assert( is(typeof(ui.testSC())));
434 
435 	/*static assert(!is(typeof(us.test())));
436 	static assert(!is(typeof(us.testI())));
437 	static assert(!is(typeof(us.testC())));
438 	static assert( is(typeof(us.testS())));
439 	static assert( is(typeof(us.testSC())));
440 
441 	static assert(!is(typeof(usc.test())));
442 	static assert(!is(typeof(usc.testI())));
443 	static assert(!is(typeof(usc.testC())));
444 	static assert(!is(typeof(usc.testS())));
445 	static assert( is(typeof(usc.testSC())));*/
446 }
447 
448 unittest {
449 	// test attributes on contained values
450 	import std.typecons : Rebindable, rebindable;
451 
452 	class C {
453 		void test() {}
454 		void testC() const {}
455 		void testI() immutable {}
456 	}
457 	union U {
458 		Rebindable!(immutable(C)) c;
459 	}
460 
461 	auto ta = TaggedAlgebraic!U(rebindable(new immutable C));
462 	static assert(!is(typeof(ta.test())));
463 	static assert( is(typeof(ta.testC())));
464 	static assert( is(typeof(ta.testI())));
465 }
466 
467 // test recursive definition using a wrapper dummy struct
468 // (needed to avoid "no size yet for forward reference" errors)
469 unittest {
470 	static struct TA {
471 		union U {
472 			TA[] children;
473 			int value;
474 		}
475 		TaggedAlgebraic!U u;
476 		alias u this;
477 		this(ARGS...)(ARGS args) { u = TaggedAlgebraic!U(args); }
478 	}
479 
480 	auto ta = TA(null);
481 	ta ~= TA(0);
482 	ta ~= TA(1);
483 	ta ~= TA([TA(2)]);
484 	assert(ta[0] == 0);
485 	assert(ta[1] == 1);
486 	assert(ta[2][0] == 2);
487 }
488 
489 unittest { // postblit/destructor test
490 	static struct S {
491 		static int i = 0;
492 		bool initialized = false;
493 		this(bool) { initialized = true; i++; }
494 		this(this) { if (initialized) i++; }
495 		~this() { if (initialized) i--; }
496 	}
497 
498 	static struct U {
499 		S s;
500 		int t;
501 	}
502 	alias TA = TaggedAlgebraic!U;
503 	{
504 		assert(S.i == 0);
505 		auto ta = TA(S(true));
506 		assert(S.i == 1);
507 		{
508 			auto tb = ta;
509 			assert(S.i == 2);
510 			ta = tb;
511 			assert(S.i == 2);
512 			ta = 1;
513 			assert(S.i == 1);
514 			ta = S(true);
515 			assert(S.i == 2);
516 		}
517 		assert(S.i == 1);
518 	}
519 	assert(S.i == 0);
520 
521 	static struct U2 {
522 		S a;
523 		S b;
524 	}
525 	alias TA2 = TaggedAlgebraic!U2;
526 	{
527 		auto ta2 = TA2(S(true), TA2.Kind.a);
528 		assert(S.i == 1);
529 	}
530 	assert(S.i == 0);
531 }
532 
533 unittest {
534 	static struct S {
535 		union U {
536 			int i;
537 			string s;
538 			U[] a;
539 		}
540 		alias TA = TaggedAlgebraic!U;
541 		TA p;
542 		alias p this;
543 	}
544 	S s = S(S.TA("hello"));
545 	assert(cast(string)s == "hello");
546 }
547 
548 unittest { // multiple operator choices
549 	union U {
550 		int i;
551 		double d;
552 	}
553 	alias TA = TaggedAlgebraic!U;
554 	TA ta = 12;
555 	static assert(is(typeof(ta + 10) == TA)); // ambiguous, could be int or double
556 	assert((ta + 10).kind == TA.Kind.i);
557 	assert(ta + 10 == 22);
558 	static assert(is(typeof(ta + 10.5) == double));
559 	assert(ta + 10.5 == 22.5);
560 }
561 
562 unittest { // Binary op between two TaggedAlgebraic values
563 	union U { int i; }
564 	alias TA = TaggedAlgebraic!U;
565 
566 	TA a = 1, b = 2;
567 	static assert(is(typeof(a + b) == int));
568 	assert(a + b == 3);
569 }
570 
571 unittest { // Ambiguous binary op between two TaggedAlgebraic values
572 	union U { int i; double d; }
573 	alias TA = TaggedAlgebraic!U;
574 
575 	TA a = 1, b = 2;
576 	static assert(is(typeof(a + b) == TA));
577 	assert((a + b).kind == TA.Kind.i);
578 	assert(a + b == 3);
579 }
580 
581 unittest {
582 	struct S {
583 		union U {
584 			@disableIndex string str;
585 			S[] array;
586 			S[string] object;
587 		}
588 		alias TA = TaggedAlgebraic!U;
589 		TA payload;
590 		alias payload this;
591 	}
592 
593 	S a = S(S.TA("hello"));
594 	S b = S(S.TA(["foo": a]));
595 	S c = S(S.TA([a]));
596 	assert(b["foo"] == a);
597 	assert(b["foo"] == "hello");
598 	assert(c[0] == a);
599 	assert(c[0] == "hello");
600 }
601 
602 static if (__VERSION__ >= 2072) unittest { // default initialization
603 	struct S {
604 		int i = 42;
605 	}
606 
607 	union U { S s; int j; }
608 
609 	TaggedAlgebraic!U ta;
610 	assert(ta.i == 42);
611 }
612 
613 unittest
614 {
615 	import std.meta : AliasSeq;
616 
617 	union U { int[int] a; }
618 
619 	foreach (TA; AliasSeq!(TaggedAlgebraic!U, const(TaggedAlgebraic!U)))
620 	{
621 		TA ta = [1 : 2];
622 		assert(cast(int[int])ta == [1 : 2]);
623 	}
624 }
625 
626 static if (__VERSION__ >= 2072) {
627 	unittest { // issue #8
628 		static struct Result(T,E)
629 		{
630 			static union U
631 			{
632 				T ok;
633 				E err;
634 			}
635 			alias TA = TaggedAlgebraic!U;
636 			TA payload;
637 			alias payload this;
638 
639 			this(T ok) { payload = ok; }
640 			this(E err) { payload = err; }
641 		}
642 
643 		static struct Option(T)
644 		{
645 			static union U
646 			{
647 				T some;
648 				typeof(null) none;
649 			}
650 			alias TA = TaggedAlgebraic!U;
651 			TA payload;
652 			alias payload this;
653 
654 			this(T some) { payload = some; }
655 			this(typeof(null) none) { payload = null; }
656 		}
657 
658 		Result!(Option!size_t, int) foo()
659 		{
660 			return Result!(Option!size_t, int)(42);
661 		}
662 
663 		assert(foo() == 42);
664 	}
665 }
666 
667 unittest { // issue #13
668 	struct S1 { Void dummy; int foo; }
669 	struct S {
670 		struct T { TaggedAlgebraic!S1 foo() { return TaggedAlgebraic!S1(42); } }
671 		struct U { string foo() { return "foo"; } }
672 		Void dummy;
673 		T t;
674 		U u;
675 	}
676 	alias TA = TaggedAlgebraic!S;
677 	auto ta = TA(S.T.init);
678 	assert(ta.foo().get!(TaggedAlgebraic!S1) == 42);
679 
680 	ta = TA(S.U.init);
681 	assert(ta.foo() == "foo");
682 }
683 
684 
685 /** Tests if the algebraic type stores a value of a certain data type.
686 */
687 bool hasType(T, U)(in ref TaggedAlgebraic!U ta)
688 {
689 	alias Fields = Filter!(fieldMatchesType!(U, T), ta.fieldNames);
690 	static assert(Fields.length > 0, "Type "~T.stringof~" cannot be stored in a "~(TaggedAlgebraic!U).stringof~".");
691 
692 	switch (ta.kind) {
693 		default: return false;
694 		foreach (i, fname; Fields)
695 			case __traits(getMember, ta.Kind, fname):
696 				return true;
697 	}
698 	assert(false); // never reached
699 }
700 /// ditto
701 bool hasType(T, U)(in TaggedAlgebraic!U ta)
702 {
703 	return hasType!(T, U)(ta);
704 }
705 
706 ///
707 unittest {
708 	union Fields {
709 		int number;
710 		string text;
711 	}
712 
713 	TaggedAlgebraic!Fields ta = "test";
714 
715 	assert(ta.hasType!string);
716 	assert(!ta.hasType!int);
717 
718 	ta = 42;
719 	assert(ta.hasType!int);
720 	assert(!ta.hasType!string);
721 }
722 
723 unittest { // issue #1
724 	union U {
725 		int a;
726 		int b;
727 	}
728 	alias TA = TaggedAlgebraic!U;
729 
730 	TA ta = TA(0, TA.Kind.b);
731 	static assert(!is(typeof(ta.hasType!double)));
732 	assert(ta.hasType!int);
733 }
734 
735 unittest {
736 	union U {
737 		int a;
738 		float b;
739 	}
740 	alias TA = TaggedAlgebraic!U;
741 
742 	const(TA) test() { return TA(12); }
743 	assert(test().hasType!int);
744 }
745 
746 
747 static if (__VERSION__ >= 2072) {
748 	/** Maps a kind enumeration value to the corresponding field type.
749 
750 		`kind` must be a value of the `TaggedAlgebraic!T.Kind` enumeration.
751 	*/
752 	template TypeOf(alias kind)
753 		if (isInstanceOf!(TypeEnum, typeof(kind)))
754 	{
755 		import std.traits : FieldTypeTuple, TemplateArgsOf;
756 		alias U = TemplateArgsOf!(typeof(kind));
757 		alias TypeOf = FieldTypeTuple!U[kind];
758 	}
759 
760 	///
761 	unittest {
762 		static struct S {
763 			int a;
764 			string b;
765 			string c;
766 		}
767 		alias TA = TaggedAlgebraic!S;
768 
769 		static assert(is(TypeOf!(TA.Kind.a) == int));
770 		static assert(is(TypeOf!(TA.Kind.b) == string));
771 		static assert(is(TypeOf!(TA.Kind.c) == string));
772 	}
773 }
774 
775 
776 /** Gets the value stored in an algebraic type based on its data type.
777 */
778 ref inout(T) get(T, U)(ref inout(TaggedAlgebraic!U) ta)
779 {
780 	import std.format : format;
781 	assert(hasType!(T, U)(ta), () { scope (failure) assert(false); return format("Trying to get %s but have %s.", T.stringof, ta.kind); } ());
782 	return ta.trustedGet!T;
783 }
784 /// ditto
785 inout(T) get(T, U)(inout(TaggedAlgebraic!U) ta)
786 {
787 	import std.format : format;
788 	assert(hasType!(T, U)(ta), () { scope (failure) assert(false); return format("Trying to get %s but have %s.", T.stringof, ta.kind); } ());
789 	return ta.trustedGet!T;
790 }
791 
792 
793 /** Calls a the given callback with the static type of the contained value.
794 
795 	The `handler` callback must be a lambda or a single-argument template
796 	function that accepts all possible types that the given `TaggedAlgebraic`
797 	can hold.
798 
799 	Returns:
800 		If `handler` has a non-void return value, its return value gets
801 		forwarded to the caller.
802 */
803 auto apply(alias handler, TA)(TA ta)
804 	if (isInstanceOf!(TaggedAlgebraic, TA))
805 {
806 	final switch (ta.kind) {
807 		foreach (i, fn; TA.fieldNames) {
808 			case __traits(getMember, ta.Kind, fn):
809 				return handler(get!(TA.FieldTypes[i])(ta));
810 		}
811 	}
812 	static if (__VERSION__ <= 2068) assert(false);
813 }
814 /// ditto
815 auto apply(alias handler, T)(T value)
816 	if (!isInstanceOf!(TaggedAlgebraic, T))
817 {
818 	return handler(value);
819 }
820 
821 ///
822 unittest {
823 	union U {
824 		int i;
825 		string s;
826 	}
827 	alias TA = TaggedAlgebraic!U;
828 
829 	assert(TA(12).apply!((v) {
830 		static if (is(typeof(v) == int)) {
831 			assert(v == 12);
832 			return 1;
833 		} else {
834 			return 0;
835 		}
836 	}) == 1);
837 
838 	assert(TA("foo").apply!((v) {
839 		static if (is(typeof(v) == string)) {
840 			assert(v == "foo");
841 			return 2;
842 		} else {
843 			return 0;
844 		}
845 	}) == 2);
846 
847 	"baz".apply!((v) {
848 		assert(v == "baz");
849 	});
850 }
851 
852 
853 /// Convenience type that can be used for union fields that have no value (`void` is not allowed).
854 struct Void {}
855 
856 /// User-defined attibute to disable `opIndex` forwarding for a particular tagged union member.
857 @property auto disableIndex() { assert(__ctfe, "disableIndex must only be used as an attribute."); return DisableOpAttribute(OpKind.index, null); }
858 
859 private struct DisableOpAttribute {
860 	OpKind kind;
861 	string name;
862 }
863 
864 
865 private template hasOp(TA, OpKind kind, string name, ARGS...)
866 {
867 	import std.traits : CopyTypeQualifiers;
868 	alias UQ = CopyTypeQualifiers!(TA, TA.Union);
869 	enum hasOp = TypeTuple!(OpInfo!(UQ, kind, name, ARGS).fields).length > 0;
870 }
871 
872 unittest {
873 	static struct S {
874 		void m(int i) {}
875 		bool opEquals(int i) { return true; }
876 		bool opEquals(S s) { return true; }
877 	}
878 
879 	static union U { int i; string s; S st; }
880 	alias TA = TaggedAlgebraic!U;
881 
882 	static assert(hasOp!(TA, OpKind.binary, "+", int));
883 	static assert(hasOp!(TA, OpKind.binary, "~", string));
884 	static assert(hasOp!(TA, OpKind.binary, "==", int));
885 	static assert(hasOp!(TA, OpKind.binary, "==", string));
886 	static assert(hasOp!(TA, OpKind.binary, "==", int));
887 	static assert(hasOp!(TA, OpKind.binary, "==", S));
888 	static assert(hasOp!(TA, OpKind.method, "m", int));
889 	static assert(hasOp!(TA, OpKind.binary, "+=", int));
890 	static assert(!hasOp!(TA, OpKind.binary, "~", int));
891 	static assert(!hasOp!(TA, OpKind.binary, "~", int));
892 	static assert(!hasOp!(TA, OpKind.method, "m", string));
893 	static assert(!hasOp!(TA, OpKind.method, "m"));
894 	static assert(!hasOp!(const(TA), OpKind.binary, "+=", int));
895 	static assert(!hasOp!(const(TA), OpKind.method, "m", int));
896 }
897 
898 unittest {
899 	struct S {
900 		union U {
901 			string s;
902 			S[] arr;
903 			S[string] obj;
904 		}
905 		alias TA = TaggedAlgebraic!(S.U);
906 		TA payload;
907 		alias payload this;
908 	}
909 	static assert(hasOp!(S.TA, OpKind.index, null, size_t));
910 	static assert(hasOp!(S.TA, OpKind.index, null, int));
911 	static assert(hasOp!(S.TA, OpKind.index, null, string));
912 	static assert(hasOp!(S.TA, OpKind.field, "length"));
913 }
914 
915 unittest { // "in" operator
916 	union U {
917 		string[string] dict;
918 	}
919 	alias TA = TaggedAlgebraic!U;
920 	auto ta = TA(["foo": "bar"]);
921 	assert("foo" in ta);
922 	assert(*("foo" in ta) == "bar");
923 }
924 
925 private static auto implementOp(OpKind kind, string name, T, ARGS...)(ref T self, auto ref ARGS args)
926 {
927 	import std.array : join;
928 	import std.traits : CopyTypeQualifiers;
929 	import std.variant : Algebraic, Variant;
930 	alias UQ = CopyTypeQualifiers!(T, T.Union);
931 
932 	alias info = OpInfo!(UQ, kind, name, ARGS);
933 
934 	static assert(hasOp!(T, kind, name, ARGS));
935 
936 	static assert(info.fields.length > 0, "Implementing operator that has no valid implementation for any supported type.");
937 
938 	//pragma(msg, "Fields for "~kind.stringof~" "~name~", "~T.stringof~": "~info.fields.stringof);
939 	//pragma(msg, "Return types for "~kind.stringof~" "~name~", "~T.stringof~": "~info.ReturnTypes.stringof);
940 	//pragma(msg, typeof(T.Union.tupleof));
941 	//import std.meta : staticMap; pragma(msg, staticMap!(isMatchingUniqueType!(T.Union), info.ReturnTypes));
942 
943 	switch (self.m_kind) {
944 		default: assert(false, "Operator "~name~" ("~kind.stringof~") can only be used on values of the following types: "~[info.fields].join(", "));
945 		foreach (i, f; info.fields) {
946 			alias FT = typeof(__traits(getMember, T.Union, f));
947 			case __traits(getMember, T.Kind, f):
948 				static if (NoDuplicates!(info.ReturnTypes).length == 1)
949 					return info.perform(self.trustedGet!FT, args);
950 				else static if (allSatisfy!(isMatchingUniqueType!(T.Union), info.ReturnTypes))
951 					return TaggedAlgebraic!(T.Union)(info.perform(self.trustedGet!FT, args));
952 				else static if (allSatisfy!(isNoVariant, info.ReturnTypes)) {
953 					alias Alg = Algebraic!(NoDuplicates!(info.ReturnTypes));
954 					info.ReturnTypes[i] ret = info.perform(self.trustedGet!FT, args);
955 					import std.traits : isInstanceOf;
956 					return Alg(ret);
957 				}
958 				else static if (is(FT == Variant))
959 					return info.perform(self.trustedGet!FT, args);
960 				else
961 					return Variant(info.perform(self.trustedGet!FT, args));
962 		}
963 	}
964 
965 	assert(false); // never reached
966 }
967 
968 unittest { // opIndex on recursive TA with closed return value set
969 	static struct S {
970 		union U {
971 			char ch;
972 			string str;
973 			S[] arr;
974 		}
975 		alias TA = TaggedAlgebraic!U;
976 		TA payload;
977 		alias payload this;
978 
979 		this(T)(T t) { this.payload = t; }
980 	}
981 	S a = S("foo");
982 	S s = S([a]);
983 
984 	assert(implementOp!(OpKind.field, "length")(s.payload) == 1);
985 	static assert(is(typeof(implementOp!(OpKind.index, null)(s.payload, 0)) == S.TA));
986 	assert(implementOp!(OpKind.index, null)(s.payload, 0) == "foo");
987 }
988 
989 unittest { // opIndex on recursive TA with closed return value set using @disableIndex
990 	static struct S {
991 		union U {
992 			@disableIndex string str;
993 			S[] arr;
994 		}
995 		alias TA = TaggedAlgebraic!U;
996 		TA payload;
997 		alias payload this;
998 
999 		this(T)(T t) { this.payload = t; }
1000 	}
1001 	S a = S("foo");
1002 	S s = S([a]);
1003 
1004 	assert(implementOp!(OpKind.field, "length")(s.payload) == 1);
1005 	static assert(is(typeof(implementOp!(OpKind.index, null)(s.payload, 0)) == S));
1006 	assert(implementOp!(OpKind.index, null)(s.payload, 0) == "foo");
1007 }
1008 
1009 
1010 private auto performOpRaw(U, OpKind kind, string name, T, ARGS...)(ref T value, /*auto ref*/ ARGS args)
1011 {
1012 	static if (kind == OpKind.binary) return mixin("value "~name~" args[0]");
1013 	else static if (kind == OpKind.binaryRight) return mixin("args[0] "~name~" value");
1014 	else static if (kind == OpKind.unary) return mixin("name "~value);
1015 	else static if (kind == OpKind.method) return __traits(getMember, value, name)(args);
1016 	else static if (kind == OpKind.field) return __traits(getMember, value, name);
1017 	else static if (kind == OpKind.index) return value[args];
1018 	else static if (kind == OpKind.indexAssign) return value[args[1 .. $]] = args[0];
1019 	else static if (kind == OpKind.call) return value(args);
1020 	else static assert(false, "Unsupported kind of operator: "~kind.stringof);
1021 }
1022 
1023 unittest {
1024 	union U { int i; string s; }
1025 
1026 	{ int v = 1; assert(performOpRaw!(U, OpKind.binary, "+")(v, 3) == 4); }
1027 	{ string v = "foo"; assert(performOpRaw!(U, OpKind.binary, "~")(v, "bar") == "foobar"); }
1028 }
1029 
1030 
1031 private auto performOp(U, OpKind kind, string name, T, ARGS...)(ref T value, /*auto ref*/ ARGS args)
1032 {
1033 	import std.traits : isInstanceOf;
1034 	static if (ARGS.length > 0 && isInstanceOf!(TaggedAlgebraic, ARGS[0])) {
1035 		static if (is(typeof(performOpRaw!(U, kind, name, T, ARGS)(value, args)))) {
1036 			return performOpRaw!(U, kind, name, T, ARGS)(value, args);
1037 		} else {
1038 			alias TA = ARGS[0];
1039 			template MTypesImpl(size_t i) {
1040 				static if (i < TA.FieldTypes.length) {
1041 					alias FT = TA.FieldTypes[i];
1042 					static if (is(typeof(&performOpRaw!(U, kind, name, T, FT, ARGS[1 .. $]))))
1043 						alias MTypesImpl = TypeTuple!(FT, MTypesImpl!(i+1));
1044 					else alias MTypesImpl = TypeTuple!(MTypesImpl!(i+1));
1045 				} else alias MTypesImpl = TypeTuple!();
1046 			}
1047 			alias MTypes = NoDuplicates!(MTypesImpl!0);
1048 			static assert(MTypes.length > 0, "No type of the TaggedAlgebraic parameter matches any function declaration.");
1049 			static if (MTypes.length == 1) {
1050 				if (args[0].hasType!(MTypes[0]))
1051 					return performOpRaw!(U, kind, name)(value, args[0].get!(MTypes[0]), args[1 .. $]);
1052 			} else {
1053 				// TODO: allow all return types (fall back to Algebraic or Variant)
1054 				foreach (FT; MTypes) {
1055 					if (args[0].hasType!FT)
1056 						return ARGS[0](performOpRaw!(U, kind, name)(value, args[0].get!FT, args[1 .. $]));
1057 				}
1058 			}
1059 			throw new /*InvalidAgument*/Exception("Algebraic parameter type mismatch");
1060 		}
1061 	} else return performOpRaw!(U, kind, name, T, ARGS)(value, args);
1062 }
1063 
1064 unittest {
1065 	union U { int i; double d; string s; }
1066 
1067 	{ int v = 1; assert(performOp!(U, OpKind.binary, "+")(v, 3) == 4); }
1068 	{ string v = "foo"; assert(performOp!(U, OpKind.binary, "~")(v, "bar") == "foobar"); }
1069 	{ string v = "foo"; assert(performOp!(U, OpKind.binary, "~")(v, TaggedAlgebraic!U("bar")) == "foobar"); }
1070 	{ int v = 1; assert(performOp!(U, OpKind.binary, "+")(v, TaggedAlgebraic!U(3)) == 4); }
1071 }
1072 
1073 
1074 private template OpInfo(U, OpKind kind, string name, ARGS...)
1075 {
1076 	import std.traits : CopyTypeQualifiers, FieldTypeTuple, FieldNameTuple, ReturnType;
1077 
1078 	private alias FieldTypes = FieldTypeTuple!U;
1079 	private alias fieldNames = FieldNameTuple!U;
1080 
1081 	private template isOpEnabled(string field)
1082 	{
1083 		alias attribs = TypeTuple!(__traits(getAttributes, __traits(getMember, U, field)));
1084 		template impl(size_t i) {
1085 			static if (i < attribs.length) {
1086 				static if (is(typeof(attribs[i]) == DisableOpAttribute)) {
1087 					static if (kind == attribs[i].kind && name == attribs[i].name)
1088 						enum impl = false;
1089 					else enum impl = impl!(i+1);
1090 				} else enum impl = impl!(i+1);
1091 			} else enum impl = true;
1092 		}
1093 		enum isOpEnabled = impl!0;
1094 	}
1095 
1096 	template fieldsImpl(size_t i)
1097 	{
1098 		static if (i < FieldTypes.length) {
1099 			static if (isOpEnabled!(fieldNames[i]) && is(typeof(&performOp!(U, kind, name, FieldTypes[i], ARGS)))) {
1100 				alias fieldsImpl = TypeTuple!(fieldNames[i], fieldsImpl!(i+1));
1101 			} else alias fieldsImpl = fieldsImpl!(i+1);
1102 		} else alias fieldsImpl = TypeTuple!();
1103 	}
1104 	alias fields = fieldsImpl!0;
1105 
1106 	template ReturnTypesImpl(size_t i) {
1107 		static if (i < fields.length) {
1108 			alias FT = CopyTypeQualifiers!(U, typeof(__traits(getMember, U, fields[i])));
1109 			alias ReturnTypesImpl = TypeTuple!(ReturnType!(performOp!(U, kind, name, FT, ARGS)), ReturnTypesImpl!(i+1));
1110 		} else alias ReturnTypesImpl = TypeTuple!();
1111 	}
1112 	alias ReturnTypes = ReturnTypesImpl!0;
1113 
1114 	static auto perform(T)(ref T value, auto ref ARGS args) { return performOp!(U, kind, name)(value, args); }
1115 }
1116 
1117 private template ImplicitUnqual(T) {
1118 	import std.traits : Unqual, hasAliasing;
1119 	static if (is(T == void)) alias ImplicitUnqual = void;
1120 	else {
1121 		private static struct S { T t; }
1122 		static if (hasAliasing!S) alias ImplicitUnqual = T;
1123 		else alias ImplicitUnqual = Unqual!T;
1124 	}
1125 }
1126 
1127 private enum OpKind {
1128 	binary,
1129 	binaryRight,
1130 	unary,
1131 	method,
1132 	field,
1133 	index,
1134 	indexAssign,
1135 	call
1136 }
1137 
1138 private template TypeEnum(U)
1139 {
1140 	import std.array : join;
1141 	import std.traits : FieldNameTuple;
1142 	mixin("enum TypeEnum { " ~ [FieldNameTuple!U].join(", ") ~ " }");
1143 }
1144 
1145 private string generateConstructors(U)()
1146 {
1147 	import std.algorithm : map;
1148 	import std.array : join;
1149 	import std.string : format;
1150 	import std.traits : FieldTypeTuple;
1151 
1152 	string ret;
1153 
1154 	static if (__VERSION__ < 2072) {
1155 		// disable default construction if first type is not a null/Void type
1156 		static if (!is(FieldTypeTuple!U[0] == typeof(null)) && !is(FieldTypeTuple!U[0] == Void))
1157 		{
1158 			ret ~= q{
1159 				@disable this();
1160 			};
1161 		}
1162 	}
1163 
1164 	// normal type constructors
1165 	foreach (tname; UniqueTypeFields!U)
1166 		ret ~= q{
1167 			this(typeof(U.%s) value)
1168 			{
1169 				m_data.rawEmplace(value);
1170 				m_kind = Kind.%s;
1171 			}
1172 
1173 			void opAssign(typeof(U.%s) value)
1174 			{
1175 				if (m_kind != Kind.%s) {
1176 					// NOTE: destroy(this) doesn't work for some opDispatch-related reason
1177 					static if (is(typeof(&this.__xdtor)))
1178 						this.__xdtor();
1179 					m_data.rawEmplace(value);
1180 				} else {
1181 					trustedGet!"%s" = value;
1182 				}
1183 				m_kind = Kind.%s;
1184 			}
1185 		}.format(tname, tname, tname, tname, tname, tname);
1186 
1187 	// type constructors with explicit type tag
1188 	foreach (tname; TypeTuple!(UniqueTypeFields!U, AmbiguousTypeFields!U))
1189 		ret ~= q{
1190 			this(typeof(U.%s) value, Kind type)
1191 			{
1192 				assert(type.among!(%s), format("Invalid type ID for type %%s: %%s", typeof(U.%s).stringof, type));
1193 				m_data.rawEmplace(value);
1194 				m_kind = type;
1195 			}
1196 		}.format(tname, [SameTypeFields!(U, tname)].map!(f => "Kind."~f).join(", "), tname);
1197 
1198 	return ret;
1199 }
1200 
1201 private template UniqueTypeFields(U) {
1202 	import std.traits : FieldTypeTuple, FieldNameTuple;
1203 
1204 	alias Types = FieldTypeTuple!U;
1205 
1206 	template impl(size_t i) {
1207 		static if (i < Types.length) {
1208 			enum name = FieldNameTuple!U[i];
1209 			alias T = Types[i];
1210 			static if (staticIndexOf!(T, Types) == i && staticIndexOf!(T, Types[i+1 .. $]) < 0)
1211 				alias impl = TypeTuple!(name, impl!(i+1));
1212 			else alias impl = TypeTuple!(impl!(i+1));
1213 		} else alias impl = TypeTuple!();
1214 	}
1215 	alias UniqueTypeFields = impl!0;
1216 }
1217 
1218 private template AmbiguousTypeFields(U) {
1219 	import std.traits : FieldTypeTuple, FieldNameTuple;
1220 
1221 	alias Types = FieldTypeTuple!U;
1222 
1223 	template impl(size_t i) {
1224 		static if (i < Types.length) {
1225 			enum name = FieldNameTuple!U[i];
1226 			alias T = Types[i];
1227 			static if (staticIndexOf!(T, Types) == i && staticIndexOf!(T, Types[i+1 .. $]) >= 0)
1228 				alias impl = TypeTuple!(name, impl!(i+1));
1229 			else alias impl = impl!(i+1);
1230 		} else alias impl = TypeTuple!();
1231 	}
1232 	alias AmbiguousTypeFields = impl!0;
1233 }
1234 
1235 unittest {
1236 	union U {
1237 		int a;
1238 		string b;
1239 		int c;
1240 		double d;
1241 	}
1242 	static assert([UniqueTypeFields!U] == ["b", "d"]);
1243 	static assert([AmbiguousTypeFields!U] == ["a"]);
1244 }
1245 
1246 private template SameTypeFields(U, string field) {
1247 	import std.traits : FieldTypeTuple, FieldNameTuple;
1248 
1249 	alias Types = FieldTypeTuple!U;
1250 
1251 	alias T = typeof(__traits(getMember, U, field));
1252 	template impl(size_t i) {
1253 		static if (i < Types.length) {
1254 			enum name = FieldNameTuple!U[i];
1255 			static if (is(Types[i] == T))
1256 				alias impl = TypeTuple!(name, impl!(i+1));
1257 			else alias impl = TypeTuple!(impl!(i+1));
1258 		} else alias impl = TypeTuple!();
1259 	}
1260 	alias SameTypeFields = impl!0;
1261 }
1262 
1263 private template MemberType(U) {
1264 	template MemberType(string name) {
1265 		alias MemberType = typeof(__traits(getMember, U, name));
1266 	}
1267 }
1268 
1269 private template isMatchingType(U) {
1270 	import std.traits : FieldTypeTuple;
1271 	enum isMatchingType(T) = staticIndexOf!(T, FieldTypeTuple!U) >= 0;
1272 }
1273 
1274 private template isMatchingUniqueType(U) {
1275 	import std.traits : staticMap;
1276 	alias UniqueTypes = staticMap!(FieldTypeOf!U, UniqueTypeFields!U);
1277 	template isMatchingUniqueType(T) {
1278 		static if (is(T : TaggedAlgebraic!U)) enum isMatchingUniqueType = true;
1279 		else enum isMatchingUniqueType = staticIndexOfImplicit!(T, UniqueTypes) >= 0;
1280 	}
1281 }
1282 
1283 private template fieldMatchesType(U, T)
1284 {
1285 	enum fieldMatchesType(string field) = is(typeof(__traits(getMember, U, field)) == T);
1286 }
1287 
1288 private template FieldTypeOf(U) {
1289 	template FieldTypeOf(string name) {
1290 		alias FieldTypeOf = typeof(__traits(getMember, U, name));
1291 	}
1292 }
1293 
1294 private template staticIndexOfImplicit(T, Types...) {
1295 	template impl(size_t i) {
1296 		static if (i < Types.length) {
1297 			static if (is(T : Types[i])) enum impl = i;
1298 			else enum impl = impl!(i+1);
1299 		} else enum impl = -1;
1300 	}
1301 	enum staticIndexOfImplicit = impl!0;
1302 }
1303 
1304 unittest {
1305 	static assert(staticIndexOfImplicit!(immutable(char), char) == 0);
1306 	static assert(staticIndexOfImplicit!(int, long) == 0);
1307 	static assert(staticIndexOfImplicit!(long, int) < 0);
1308 	static assert(staticIndexOfImplicit!(int, int, double) == 0);
1309 	static assert(staticIndexOfImplicit!(double, int, double) == 1);
1310 }
1311 
1312 
1313 private template isNoVariant(T) {
1314 	import std.variant : Variant;
1315 	enum isNoVariant = !is(T == Variant);
1316 }
1317 
1318 private void rawEmplace(T)(void[] dst, ref T src)
1319 {
1320 	T[] tdst = () @trusted { return cast(T[])dst[0 .. T.sizeof]; } ();
1321 	static if (is(T == class)) {
1322 		tdst[0] = src;
1323 	} else {
1324 		import std.conv : emplace;
1325 		emplace!T(&tdst[0]);
1326 		tdst[0] = src;
1327 	}
1328 }
1329 
1330 // std.algorithm.mutation.swap sometimes fails to compile due to
1331 // internal errors in hasElaborateAssign!T/isAssignable!T. This is probably
1332 // caused by cyclic dependencies. However, there is no reason to do these
1333 // checks in this context, so we just directly move the raw memory.
1334 private void rawSwap(T)(ref T a, ref T b)
1335 @trusted {
1336 	void[T.sizeof] tmp = void;
1337 	void[] ab = (cast(void*)&a)[0 .. T.sizeof];
1338 	void[] bb = (cast(void*)&b)[0 .. T.sizeof];
1339 	tmp[] = ab[];
1340 	ab[] = bb[];
1341 	bb[] = tmp[];
1342 }
1343 
1344 
1345 unittest {
1346 	struct TU { int i; }
1347 	alias TA = TaggedAlgebraic!TU;
1348 
1349 	auto ta = TA(12);
1350 	static assert(!is(typeof(ta.put(12))));
1351 }