Merge pull request #29 from s-ludwig/alignment_fix

Compute the proper struct alignment for TaggedUnion.
This commit is contained in:
Sönke Ludwig 2019-04-16 12:02:13 +02:00 committed by GitHub
commit 78158dbae8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -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)
{