1 /**
2  * Generic tagged union and algebraic data type implementations.
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.taggedunion;
9 
10 // scheduled for deprecation
11 static import taggedalgebraic.visit;
12 
13 alias visit = taggedalgebraic.visit.visit;
14 
15 import std.algorithm.mutation : move, swap;
16 import std.meta;
17 import std.range : isOutputRange;
18 import std.traits : EnumMembers, FieldNameTuple, Unqual, hasUDA, isInstanceOf;
19 
20 /** Implements a generic tagged union 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 	For each field defined by `U` a number of convenience members are generated.
28 	For a given field "foo", these fields are:
29 
30 	$(UL
31 		$(LI `static foo(value)` - returns a new tagged union with the specified value)
32 		$(LI `isFoo` - equivalent to `kind == Kind.foo`)
33 		$(LI `setFoo(value)` - equivalent to `set!(Kind.foo)(value)`)
34 		$(LI `getFoo` - equivalent to `get!(Kind.foo)`)
35 	)
36 */
37 template TaggedUnion(U) if (is(U == union) || is(U == struct) || is(U == enum))
38 {
39 	align(commonAlignment!(UnionKindTypes!(UnionFieldEnum!U))) struct TaggedUnion
40 	{
41 		import std.traits : FieldTypeTuple, FieldNameTuple, Largest,
42 			hasElaborateCopyConstructor, hasElaborateDestructor, isCopyable;
43 		import std.meta : templateOr;
44 		import std.ascii : toUpper;
45 
46 		alias FieldDefinitionType = U;
47 
48 		/// A type enum that identifies the type of value currently stored.
49 		alias Kind = UnionFieldEnum!U;
50 
51 		alias FieldTypes = UnionKindTypes!Kind;
52 		alias fieldNames = UnionKindNames!Kind;
53 
54 		static assert(FieldTypes.length > 0,
55 				"The TaggedUnions's union type must have at least one field.");
56 		static assert(FieldTypes.length == fieldNames.length);
57 
58 		package alias FieldTypeByName(string name) = FieldTypes[__traits(getMember, Kind, name)];
59 
60 		private
61 		{
62 			static if (isUnitType!(FieldTypes[0]) || __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: FieldTypes[0].init};
76 			}
77 			Kind m_kind;
78 		}
79 
80 		this(TaggedUnion other)
81 		{
82 			rawSwap(this, other);
83 		}
84 
85 		void opAssign(TaggedUnion other)
86 		{
87 			rawSwap(this, other);
88 		}
89 
90 		static foreach (ti; UniqueTypes!FieldTypes)
91 			static if (!is(FieldTypes[ti] == void))
92 				{
93 				this(FieldTypes[ti] value)
94 				{
95 					static if (isUnitType!(FieldTypes[ti]))
96 						set!(cast(Kind) ti)();
97 					else
98 						set!(cast(Kind) ti)(.move(value));
99 				}
100 
101 				void opAssign(FieldTypes[ti] value)
102 				{
103 					static if (isUnitType!(FieldTypes[ti]))
104 						set!(cast(Kind) ti)();
105 					else
106 						set!(cast(Kind) ti)(.move(value));
107 				}
108 			}
109 
110 		// disable default construction if first type is not a null/Void type
111 		static if (!isUnitType!(FieldTypes[0]) && __VERSION__ < 2072)
112 		{
113 			@disable this();
114 		}
115 
116 		// postblit constructor
117 		static if (!allSatisfy!(templateOr!(isCopyable, isUnitType), FieldTypes))
118 		{
119 			@disable this(this);
120 		}
121 		else static if (anySatisfy!(hasElaborateCopyConstructor, FieldTypes))
122 		{
123 			this(this)
124 			{
125 				switch (m_kind)
126 				{
127 				default:
128 					break;
129 					foreach (i, tname; fieldNames)
130 					{
131 						alias T = FieldTypes[i];
132 						static if (hasElaborateCopyConstructor!T)
133 						{
134 				case __traits(getMember, Kind, tname):
135 							static if (hasUDA!(U, typeof(forceNothrowPostblit())))
136 							{
137 								try
138 									typeid(T).postblit(cast(void*)&trustedGet!T());
139 								catch (Exception e)
140 									assert(false, e.msg);
141 							}
142 							else
143 							{
144 								typeid(T).postblit(cast(void*)&trustedGet!T());
145 							}
146 							return;
147 						}
148 					}
149 				}
150 			}
151 		}
152 
153 		// destructor
154 		static if (anySatisfy!(hasElaborateDestructor, FieldTypes))
155 		{
156 			~this()
157 			{
158 				final switch (m_kind)
159 				{
160 					foreach (i, tname; fieldNames)
161 					{
162 						alias T = FieldTypes[i];
163 				case __traits(getMember, Kind, tname):
164 						static if (hasElaborateDestructor!T)
165 						{
166 							.destroy(trustedGet!T);
167 						}
168 						return;
169 					}
170 				}
171 			}
172 		}
173 
174 		/// Enables conversion or extraction of the stored value.
175 		T opCast(T)()
176 		{
177 			import std.conv : to;
178 
179 			final switch (m_kind)
180 			{
181 				foreach (i, FT; FieldTypes)
182 				{
183 			case __traits(getMember, Kind, fieldNames[i]):
184 					static if (is(typeof(trustedGet!FT) : T))
185 						return trustedGet!FT;
186 					else static if (is(typeof(to!T(trustedGet!FT))))
187 					{
188 						return to!T(trustedGet!FT);
189 					}
190 					else
191 					{
192 						assert(false,
193 								"Cannot cast a " ~ fieldNames[i] ~ " value of type "
194 								~ FT.stringof ~ " to " ~ T.stringof);
195 					}
196 				}
197 			}
198 			assert(false); // never reached
199 		}
200 		/// ditto
201 		T opCast(T)() const
202 		{
203 			// this method needs to be duplicated because inout doesn't work with to!()
204 			import std.conv : to;
205 
206 			final switch (m_kind)
207 			{
208 				foreach (i, FT; FieldTypes)
209 				{
210 			case __traits(getMember, Kind, fieldNames[i]):
211 					static if (is(typeof(trustedGet!FT) : T))
212 						return trustedGet!FT;
213 					else static if (is(typeof(to!T(trustedGet!FT))))
214 					{
215 						return to!T(trustedGet!FT);
216 					}
217 					else
218 					{
219 						assert(false,
220 								"Cannot cast a " ~ fieldNames[i] ~ " value of type"
221 								~ FT.stringof ~ " to " ~ T.stringof);
222 					}
223 				}
224 			}
225 			assert(false); // never reached
226 		}
227 
228 		/// Enables equality comparison with the stored value.
229 		bool opEquals()(auto ref inout(TaggedUnion) other) inout
230 		{
231 			if (this.kind != other.kind)
232 				return false;
233 
234 			final switch (this.kind)
235 			{
236 				foreach (i, fname; TaggedUnion!U.fieldNames)
237 			case __traits(getMember, Kind, fname):
238 					return trustedGet!(FieldTypes[i]) == other.trustedGet!(FieldTypes[i]);
239 			}
240 			assert(false); // never reached
241 		}
242 
243 		/// The type ID of the currently stored value.
244 		@property Kind kind() const
245 		{
246 			return m_kind;
247 		}
248 
249 		static foreach (i, name; fieldNames)
250 		{
251 			// NOTE: using getX/setX here because using just x would be prone to
252 			//       misuse (attempting to "get" a value for modification when
253 			//       a different kind is set instead of assigning a new value)
254 			mixin("alias set" ~ pascalCase(name) ~ " = set!(Kind." ~ name ~ ");");
255 			mixin("@property bool is" ~ pascalCase(
256 					name) ~ "() const { return m_kind == Kind." ~ name ~ "; }");
257 
258 			static if (name[$ - 1] == '_')
259 			{
260 				mixin("alias set" ~ pascalCase(name[0 .. $ - 1]) ~ " = set!(Kind." ~ name ~ ");");
261 				mixin("@property bool is" ~ pascalCase(
262 						name[0 .. $ - 1]) ~ "() const { return m_kind == Kind." ~ name ~ "; }");
263 			}
264 
265 			static if (!isUnitType!(FieldTypes[i]))
266 			{
267 				mixin("alias " ~ name ~ "Value = value!(Kind." ~ name ~ ");");
268 
269 				// remove trailing underscore from names like "int_"
270 				static if (name[$ - 1] == '_')
271 					mixin("alias " ~ name[0 .. $ - 1] ~ "Value = value!(Kind." ~ name ~ ");");
272 
273 				mixin("static TaggedUnion " ~ name ~ "(FieldTypes[" ~ i.stringof ~ "] value)"
274 						~ "{ TaggedUnion tu; tu.set!(Kind." ~ name ~ ")(.move(value)); return tu; }");
275 
276 				// TODO: define assignment operator for unique types
277 			}
278 			else
279 			{
280 				mixin(
281 						"static @property TaggedUnion " ~ name
282 						~ "() { TaggedUnion tu; tu.set!(Kind." ~ name ~ "); return tu; }");
283 			}
284 		}
285 
286 		/** Checks whether the currently stored value has a given type.
287 	*/
288 		@property bool hasType(T)() const
289 		{
290 			static assert(staticIndexOf!(T, FieldTypes) >= 0,
291 					"Type " ~ T.stringof ~ " not part of " ~ FieldTypes.stringof);
292 
293 			final switch (this.kind)
294 			{
295 				static foreach (i, n; fieldNames)
296 				{
297 			case __traits(getMember, Kind, n):
298 					return is(FieldTypes[i] == T);
299 				}
300 			}
301 		}
302 
303 		/** Accesses the contained value by reference.
304 
305 		The specified `kind` must equal the current value of the `this.kind`
306 		property. Setting a different type must be done with `set` or `opAssign`
307 		instead.
308 
309 		See_Also: `set`, `opAssign`
310 	*/
311 		@property ref inout(FieldTypes[kind]) value(Kind kind)() inout
312 		{
313 			if (this.kind != kind)
314 			{
315 				enum msg(.string k_is) = "Attempt to get kind " ~ kind.stringof
316 					~ " from tagged union with kind " ~ k_is;
317 				final switch (this.kind)
318 				{
319 					static foreach (i, n; fieldNames)
320 				case __traits(getMember, Kind, n):
321 						assert(false, msg!n);
322 				}
323 			}
324 			//return trustedGet!(FieldTypes[kind]);
325 			return *() @trusted {
326 				return cast(const(FieldTypes[kind])*) m_data.ptr;
327 			}();
328 		}
329 
330 		/** Accesses the contained value by reference.
331 
332 		The specified type `T` must equal the type of the currently set value.
333 		Setting a different type must be done with `set` or `opAssign` instead.
334 
335 		See_Also: `set`, `opAssign`
336 	*/
337 		@property ref inout(T) value(T)() inout
338 		{
339 			static assert(staticIndexOf!(T, FieldTypes) >= 0,
340 					"Type " ~ T.stringof ~ " not part of " ~ FieldTypes.stringof);
341 
342 			final switch (this.kind)
343 			{
344 				static foreach (i, n; fieldNames)
345 				{
346 			case __traits(getMember, Kind, n):
347 					static if (is(FieldTypes[i] == T))
348 						return trustedGet!T;
349 					else
350 						assert(false, "Attempting to get type " ~ T.stringof ~ " from a TaggedUnion with type "
351 								~ FieldTypes[__traits(getMember, Kind, n)].stringof);
352 				}
353 			}
354 		}
355 
356 		/** Sets a new value of the specified `kind`.
357 	*/
358 		ref FieldTypes[kind] set(Kind kind)(FieldTypes[kind] value)
359 				if (!isUnitType!(FieldTypes[kind]))
360 		{
361 			if (m_kind != kind)
362 			{
363 				destroy(this);
364 				m_data.rawEmplace(value);
365 			}
366 			else
367 			{
368 				rawSwap(trustedGet!(FieldTypes[kind]), value);
369 			}
370 			m_kind = kind;
371 
372 			return trustedGet!(FieldTypes[kind]);
373 		}
374 
375 		/** Sets a `void` value of the specified kind.
376 	*/
377 		void set(Kind kind)() if (isUnitType!(FieldTypes[kind]))
378 		{
379 			if (m_kind != kind)
380 			{
381 				destroy(this);
382 			}
383 			m_kind = kind;
384 		}
385 
386 		/** Converts the contained value to a string.
387 
388 		The format output by this method is "(kind: value)", where "kind" is
389 		the enum name of the currently stored type and "value" is the string
390 		representation of the stored value.
391 	*/
392 		void toString(W)(ref W w) const if (isOutputRange!(W, char))
393 		{
394 			import std.range.primitives : put;
395 			import std.conv : text;
396 			import std.format : FormatSpec, formatValue;
397 
398 			final switch (m_kind)
399 			{
400 				foreach (i, v; EnumMembers!Kind)
401 				{
402 			case v:
403 					enum vstr = text(v);
404 					static if (isUnitType!(FieldTypes[i]))
405 						put(w, vstr);
406 					else
407 					{
408 						// NOTE: using formatValue instead of formattedWrite
409 						//       because formattedWrite does not work for
410 						//       non-copyable types
411 						enum prefix = "(" ~ vstr ~ ": ";
412 						enum suffix = ")";
413 						put(w, prefix);
414 						FormatSpec!char spec;
415 						formatValue(w, value!v, spec);
416 						put(w, suffix);
417 					}
418 					break;
419 				}
420 			}
421 		}
422 
423 		package @trusted @property ref inout(T) trustedGet(T)() inout
424 		{
425 			return *cast(inout(T)*) m_data.ptr;
426 		}
427 	}
428 }
429 
430 ///
431 @safe nothrow unittest
432 {
433 	union Kinds
434 	{
435 		int count;
436 		string text;
437 	}
438 
439 	alias TU = TaggedUnion!Kinds;
440 
441 	// default initialized to the first field defined
442 	TU tu;
443 	assert(tu.kind == TU.Kind.count);
444 	assert(tu.isCount); // qequivalent to the line above
445 	assert(!tu.isText);
446 	assert(tu.value!(TU.Kind.count) == int.init);
447 
448 	// set to a specific count
449 	tu.setCount(42);
450 	assert(tu.isCount);
451 	assert(tu.countValue == 42);
452 	assert(tu.value!(TU.Kind.count) == 42);
453 	assert(tu.value!int == 42); // can also get by type
454 	assert(tu.countValue == 42);
455 
456 	// assign a new tagged algebraic value
457 	tu = TU.count(43);
458 
459 	// test equivalence with other tagged unions
460 	assert(tu == TU.count(43));
461 	assert(tu != TU.count(42));
462 	assert(tu != TU.text("hello"));
463 
464 	// modify by reference
465 	tu.countValue++;
466 	assert(tu.countValue == 44);
467 
468 	// set the second field
469 	tu.setText("hello");
470 	assert(!tu.isCount);
471 	assert(tu.isText);
472 	assert(tu.kind == TU.Kind.text);
473 	assert(tu.textValue == "hello");
474 
475 	// unique types can also be directly constructed
476 	tu = TU(12);
477 	assert(tu.countValue == 12);
478 	tu = TU("foo");
479 	assert(tu.textValue == "foo");
480 }
481 
482 ///
483 @safe nothrow unittest
484 {
485 	// Enum annotations supported since DMD 2.082.0. The mixin below is
486 	// necessary to keep the parser happy on older versions.
487 	static if (__VERSION__ >= 2082)
488 	{
489 		alias myint = int;
490 		// tagged unions can be defined in terms of an annotated enum
491 		mixin(q{enum E {
492 			none,
493 			@string text
494 		}});
495 
496 		alias TU = TaggedUnion!E;
497 		static assert(is(TU.Kind == E));
498 
499 		TU tu;
500 		assert(tu.isNone);
501 		assert(tu.kind == E.none);
502 
503 		tu.setText("foo");
504 		assert(tu.kind == E.text);
505 		assert(tu.textValue == "foo");
506 	}
507 }
508 
509 unittest
510 { // test for name clashes
511 	union U
512 	{
513 		.string string;
514 	}
515 
516 	alias TU = TaggedUnion!U;
517 	TU tu;
518 	tu = TU..string("foo");
519 	assert(tu.isString);
520 	assert(tu.stringValue == "foo");
521 }
522 
523 unittest
524 { // test woraround for Phobos issue 19696
525 	struct T
526 	{
527 		struct F
528 		{
529 			int num;
530 		}
531 
532 		alias Payload = TaggedUnion!F;
533 		Payload payload;
534 		alias payload this;
535 	}
536 
537 	struct U
538 	{
539 		T t;
540 	}
541 
542 	alias TU = TaggedUnion!U;
543 	static assert(is(TU.FieldTypes[0] == T));
544 }
545 
546 unittest
547 { // non-copyable types
548 	import std.traits : isCopyable;
549 
550 	struct S
551 	{
552 		@disable this(this);
553 	}
554 
555 	struct U
556 	{
557 		int i;
558 		S s;
559 	}
560 
561 	alias TU = TaggedUnion!U;
562 	static assert(!isCopyable!TU);
563 
564 	auto tu = TU(42);
565 	tu.setS(S.init);
566 }
567 
568 unittest
569 { // alignment
570 	union S1
571 	{
572 		int v;
573 	}
574 
575 	union S2
576 	{
577 		ulong v;
578 	}
579 
580 	union S3
581 	{
582 		void* v;
583 	}
584 
585 	// sanity check for the actual checks - this may differ on non-x86 architectures
586 	static assert(S1.alignof == 4);
587 	static assert(S2.alignof == 8);
588 	version (D_LP64)
589 		static assert(S3.alignof == 8);
590 	else
591 		static assert(S3.alignof == 4);
592 
593 	// test external struct alignment
594 	static assert(TaggedUnion!S1.alignof == 4);
595 	static assert(TaggedUnion!S2.alignof == 8);
596 	version (D_LP64)
597 		static assert(TaggedUnion!S3.alignof == 8);
598 	else
599 		static assert(TaggedUnion!S3.alignof == 4);
600 
601 	// test internal struct alignment
602 	TaggedUnion!S1 s1;
603 	assert((cast(ubyte*)&s1.vValue() - cast(ubyte*)&s1) % 4 == 0);
604 	TaggedUnion!S1 s2;
605 	assert((cast(ubyte*)&s2.vValue() - cast(ubyte*)&s2) % 8 == 0);
606 	TaggedUnion!S1 s3;
607 	version (D_LP64)
608 		assert((cast(ubyte*)&s3.vValue() - cast(ubyte*)&s3) % 8 == 0);
609 	else
610 		assert((cast(ubyte*)&s3.vValue() - cast(ubyte*)&s3) % 4 == 0);
611 }
612 
613 unittest
614 { // toString
615 	import std.conv : to;
616 
617 	static struct NoCopy
618 	{
619 		@disable this(this);
620 		string toString() const
621 		{
622 			return "foo";
623 		}
624 	}
625 
626 	union U
627 	{
628 		Void empty;
629 		int i;
630 		NoCopy noCopy;
631 	}
632 
633 	TaggedUnion!U val;
634 	assert(val.to!string == "empty");
635 	val.setI(42);
636 	assert(val.to!string == "(i: 42)");
637 	val.setNoCopy(NoCopy.init);
638 	assert(val.to!string == "(noCopy: foo)");
639 }
640 
641 unittest
642 { // null members should be assignable
643 	union U
644 	{
645 		int i;
646 		typeof(null) Null;
647 	}
648 
649 	TaggedUnion!U val;
650 	val = null;
651 	assert(val.kind == val.Kind.Null);
652 	val = TaggedUnion!U(null);
653 	assert(val.kind == val.Kind.Null);
654 }
655 
656 unittest
657 { // make sure field names don't conflict with function names
658 	union U
659 	{
660 		int to;
661 		int move;
662 		int swap;
663 	}
664 
665 	TaggedUnion!U val;
666 	val.setMove(1);
667 }
668 
669 unittest
670 { // support trailing underscores properly
671 	union U
672 	{
673 		int int_;
674 	}
675 
676 	TaggedUnion!U val;
677 
678 	val = TaggedUnion!U.int_(10);
679 	assert(val.int_Value == 10);
680 	assert(val.intValue == 10);
681 	assert(val.isInt_);
682 	assert(val.isInt);
683 	val.setInt_(20);
684 	val.setInt(20);
685 	assert(val.intValue == 20);
686 }
687 
688 @property auto forceNothrowPostblit()
689 {
690 	if (!__ctfe)
691 		assert(false, "@forceNothrowPostblit must only be used as a UDA.");
692 	static struct R
693 	{
694 	}
695 
696 	return R.init;
697 }
698 
699 nothrow unittest
700 {
701 	static struct S
702 	{
703 		this(this) nothrow
704 		{
705 		}
706 	}
707 
708 	@forceNothrowPostblit struct U
709 	{
710 		S s;
711 	}
712 
713 	alias TU = TaggedUnion!U;
714 
715 	TU tu, tv;
716 	tu = S.init;
717 	tv = tu;
718 }
719 
720 enum isUnitType(T) = is(T == Void) || is(T == void) || is(T == typeof(null));
721 
722 private string pascalCase(string camel_case)
723 {
724 	if (!__ctfe)
725 		assert(false);
726 	import std.ascii : toUpper;
727 
728 	return camel_case[0].toUpper ~ camel_case[1 .. $];
729 }
730 
731 /** Maps a kind enumeration value to the corresponding field type.
732 
733 	`kind` must be a value of the `TaggedAlgebraic!T.Kind` enumeration.
734 */
735 template TypeOf(alias kind) if (is(typeof(kind) == enum))
736 {
737 	import std.traits : FieldTypeTuple, TemplateArgsOf;
738 	import std.typecons : ReplaceType;
739 
740 	static if (isInstanceOf!(UnionFieldEnum, typeof(kind)))
741 	{
742 		alias U = TemplateArgsOf!(typeof(kind));
743 		alias FT = FieldTypeTuple!U[kind];
744 	}
745 	else
746 	{
747 		alias U = typeof(kind);
748 		alias Types = UnionKindTypes!(typeof(kind));
749 		alias uda = AliasSeq!(__traits(getAttributes, kind));
750 		static if (uda.length == 0)
751 			alias FT = void;
752 		else
753 			alias FT = uda[0];
754 	}
755 
756 	// NOTE: ReplaceType has issues with certain types, such as a class
757 	//       declaration like this: class C : D!C {}
758 	//       For this reason, we test first if it compiles and only then use it.
759 	//       It also replaces a type with the contained "alias this" type under
760 	//       certain conditions, so we make a second check to see heuristically
761 	//       if This is actually present in FT
762 	//
763 	//       Phobos issues: 19696, 19697
764 	static if (is(ReplaceType!(This, U, FT)) && !is(ReplaceType!(This, void, FT)))
765 		alias TypeOf = ReplaceType!(This, U, FT);
766 	else
767 		alias TypeOf = FT;
768 }
769 
770 ///
771 unittest
772 {
773 	static struct S
774 	{
775 		int a;
776 		string b;
777 		string c;
778 	}
779 
780 	alias TU = TaggedUnion!S;
781 
782 	static assert(is(TypeOf!(TU.Kind.a) == int));
783 	static assert(is(TypeOf!(TU.Kind.b) == string));
784 	static assert(is(TypeOf!(TU.Kind.c) == string));
785 }
786 
787 unittest
788 {
789 	struct S
790 	{
791 		TaggedUnion!This[] test;
792 	}
793 
794 	alias TU = TaggedUnion!S;
795 
796 	TypeOf!(TU.Kind.test) a;
797 
798 	static assert(is(TypeOf!(TU.Kind.test) == TaggedUnion!S[]));
799 }
800 
801 /// Convenience type that can be used for union fields that have no value (`void` is not allowed).
802 struct Void
803 {
804 }
805 
806 /** Special type used as a placeholder for `U` within the definition of `U` to
807 	enable self-referential types.
808 
809 	Note that this is recognized only if used as the first argument to a
810 	template type.
811 */
812 struct This
813 {
814 	Void nothing;
815 }
816 
817 ///
818 unittest
819 {
820 	union U
821 	{
822 		TaggedUnion!This[] list;
823 		int number;
824 		string text;
825 	}
826 
827 	alias Node = TaggedUnion!U;
828 
829 	auto n = Node([Node(12), Node("foo")]);
830 	assert(n.isList);
831 	assert(n.listValue == [Node(12), Node("foo")]);
832 }
833 
834 package template UnionFieldEnum(U)
835 {
836 	static if (is(U == enum))
837 		alias UnionFieldEnum = U;
838 	else
839 	{
840 		import std.array : join;
841 		import std.traits : FieldNameTuple;
842 
843 		mixin("enum UnionFieldEnum { " ~ [FieldNameTuple!U].join(", ") ~ " }");
844 	}
845 }
846 
847 deprecated alias TypeEnum(U) = UnionFieldEnum!U;
848 
849 package alias UnionKindTypes(FieldEnum) = staticMap!(TypeOf, EnumMembers!FieldEnum);
850 package alias UnionKindNames(FieldEnum) = AliasSeq!(__traits(allMembers, FieldEnum));
851 
852 package template UniqueTypes(Types...)
853 {
854 	template impl(size_t i)
855 	{
856 		static if (i < Types.length)
857 		{
858 			alias T = Types[i];
859 			static if (staticIndexOf!(T, Types) == i && staticIndexOf!(T, Types[i + 1 .. $]) < 0)
860 				alias impl = AliasSeq!(i, impl!(i + 1));
861 			else
862 				alias impl = AliasSeq!(impl!(i + 1));
863 		}
864 		else
865 			alias impl = AliasSeq!();
866 	}
867 
868 	alias UniqueTypes = impl!0;
869 }
870 
871 package template AmbiguousTypes(Types...)
872 {
873 	template impl(size_t i)
874 	{
875 		static if (i < Types.length)
876 		{
877 			alias T = Types[i];
878 			static if (staticIndexOf!(T, Types) == i && staticIndexOf!(T, Types[i + 1 .. $]) >= 0)
879 				alias impl = AliasSeq!(i, impl!(i + 1));
880 			else
881 				alias impl = impl!(i + 1);
882 		}
883 		else
884 			alias impl = AliasSeq!();
885 	}
886 
887 	alias AmbiguousTypes = impl!0;
888 }
889 
890 /// Computes the minimum alignment necessary to align all types correctly
891 private size_t commonAlignment(TYPES...)()
892 {
893 	import std.numeric : gcd;
894 
895 	size_t ret = 1;
896 	foreach (T; TYPES)
897 		ret = (T.alignof * ret) / gcd(T.alignof, ret);
898 	return ret;
899 }
900 
901 unittest
902 {
903 	align(2) struct S1
904 	{
905 		ubyte x;
906 	}
907 
908 	align(4) struct S2
909 	{
910 		ubyte x;
911 	}
912 
913 	align(8) struct S3
914 	{
915 		ubyte x;
916 	}
917 
918 	static if (__VERSION__ > 2076)
919 	{ // DMD 2.076 ignores the alignment
920 		assert(commonAlignment!S1 == 2);
921 		assert(commonAlignment!S2 == 4);
922 		assert(commonAlignment!S3 == 8);
923 		assert(commonAlignment!(S1, S3) == 8);
924 		assert(commonAlignment!(S1, S2, S3) == 8);
925 		assert(commonAlignment!(S2, S2, S1) == 4);
926 	}
927 }
928 
929 package void rawEmplace(T)(void[] dst, ref T src)
930 {
931 	T[] tdst = () @trusted { return cast(T[]) dst[0 .. T.sizeof]; }();
932 	static if (is(T == class))
933 	{
934 		tdst[0] = src;
935 	}
936 	else
937 	{
938 		import std.conv : emplace;
939 
940 		emplace!T(&tdst[0]);
941 		rawSwap(tdst[0], src);
942 	}
943 }
944 
945 // std.algorithm.mutation.swap sometimes fails to compile due to
946 // internal errors in hasElaborateAssign!T/isAssignable!T. This is probably
947 // caused by cyclic dependencies. However, there is no reason to do these
948 // checks in this context, so we just directly move the raw memory.
949 package void rawSwap(T)(ref T a, ref T b) @trusted
950 {
951 	void[T.sizeof] tmp = void;
952 	void[] ab = (cast(void*)&a)[0 .. T.sizeof];
953 	void[] bb = (cast(void*)&b)[0 .. T.sizeof];
954 	tmp[] = ab[];
955 	ab[] = bb[];
956 	bb[] = tmp[];
957 }