diff --git a/source/taggedalgebraic/taggedunion.d b/source/taggedalgebraic/taggedunion.d index 9c1afb9..49803bc 100644 --- a/source/taggedalgebraic/taggedunion.d +++ b/source/taggedalgebraic/taggedunion.d @@ -29,7 +29,8 @@ import std.traits : EnumMembers, FieldNameTuple, Unqual, isInstanceOf; $(LI `getFoo` - equivalent to `get!(Kind.foo)`) ) */ -struct TaggedUnion(U) if (is(U == union) || is(U == struct) || is(U == enum)) +template TaggedUnion(U) if (is(U == union) || is(U == struct) || is(U == enum)) { +align(commonAlignment!(UnionKindTypes!(UnionFieldEnum!U))) struct TaggedUnion { import std.traits : FieldTypeTuple, FieldNameTuple, Largest, hasElaborateCopyConstructor, hasElaborateDestructor, isCopyable; @@ -294,6 +295,7 @@ struct TaggedUnion(U) if (is(U == union) || is(U == struct) || is(U == enum)) package @trusted @property ref inout(T) trustedGet(T)() inout { return *cast(inout(T)*)m_data.ptr; } } +} /// @safe nothrow unittest { @@ -409,6 +411,33 @@ unittest { // non-copyable types tu.setS(S.init); } +unittest { // alignment + union S1 { int v; } + union S2 { ulong v; } + union S3 { void* v; } + + // sanity check for the actual checks - this may differ on non-x86 architectures + static assert(S1.alignof == 4); + static assert(S2.alignof == 8); + version (D_LP64) static assert(S3.alignof == 8); + else static assert(S3.alignof == 4); + + // test external struct alignment + static assert(TaggedUnion!S1.alignof == 4); + static assert(TaggedUnion!S2.alignof == 8); + version (D_LP64) static assert(TaggedUnion!S3.alignof == 8); + else static assert(TaggedUnion!S3.alignof == 4); + + // test internal struct alignment + TaggedUnion!S1 s1; + assert((cast(ubyte*)&s1.vValue() - cast(ubyte*)&s1) % 4 == 0); + TaggedUnion!S1 s2; + assert((cast(ubyte*)&s2.vValue() - cast(ubyte*)&s2) % 8 == 0); + TaggedUnion!S1 s3; + version (D_LP64) assert((cast(ubyte*)&s3.vValue() - cast(ubyte*)&s3) % 8 == 0); + else assert((cast(ubyte*)&s3.vValue() - cast(ubyte*)&s3) % 4 == 0); +} + /** Dispatches the value contained on a `TaggedUnion` to a set of visitors. @@ -751,6 +780,31 @@ package template AmbiguousTypes(Types...) { alias AmbiguousTypes = impl!0; } +/// Computes the minimum alignment necessary to align all types correctly +private size_t commonAlignment(TYPES...)() +{ + import std.numeric : gcd; + + size_t ret = 1; + foreach (T; TYPES) + ret = (T.alignof * ret) / gcd(T.alignof, ret); + return ret; +} + +unittest { + align(2) struct S1 { ubyte x; } + align(4) struct S2 { ubyte x; } + align(8) struct S3 { ubyte x; } + + static if (__VERSION__ > 2076) { // DMD 2.076 ignores the alignment + assert(commonAlignment!S1 == 2); + assert(commonAlignment!S2 == 4); + assert(commonAlignment!S3 == 8); + assert(commonAlignment!(S1, S3) == 8); + assert(commonAlignment!(S1, S2, S3) == 8); + assert(commonAlignment!(S2, S2, S1) == 4); + } +} package void rawEmplace(T)(void[] dst, ref T src) {