//===- ExternalUnion.h - A union with an external discriminator -*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file defines the ExternalUnion class, which allows clients to // conveniently define unions of possibly non-trivial types whose // discriminator will be provided externally. // // It's the client's responsibility to call the appropriate // "special members" within its own special members. // //===----------------------------------------------------------------------===// #ifndef SWIFT_BASIC_EXTERNALUNION_H #define SWIFT_BASIC_EXTERNALUNION_H #include "llvm/Support/ErrorHandling.h" #include "swift/Basic/type_traits.h" #include #include #include namespace swift { namespace ExternalUnionImpl { /// A helper class for finding the index of a type in a parameter pack. /// 'indexOf::value' will either be the index or -1. template struct indexOf; template struct indexOf { constexpr static const int value = -1; }; template struct indexOf { private: constexpr static const int indexInTail = indexOf::value; public: constexpr static const int value = (std::is_same::value ? 0 : indexInTail != -1 ? indexInTail + 1 : -1); }; /// A helper class for deriving information and rule-of-five operations /// for the storage of a union. template struct MembersHelper; template struct OptimalKindTypeHelper; } // end namespace ExternalUnionImpl /// A class used to define the list of member types which need to be /// stored in an ExternalUnion. As a special case, you may use 'void' /// to indicate that an empty state is required in the union. template struct ExternalUnionMembers { // (private to the implementation) using Info = ExternalUnionImpl::MembersHelper; enum : bool { is_copy_constructible = Info::is_copy_constructible, is_nothrow_copy_constructible = Info::is_nothrow_copy_constructible, is_move_constructible = Info::is_move_constructible, is_nothrow_move_constructible = Info::is_nothrow_move_constructible, is_copy_assignable = Info::is_copy_assignable, is_nothrow_copy_assignable = Info::is_nothrow_copy_assignable, is_move_assignable = Info::is_move_assignable, is_nothrow_move_assignable = Info::is_nothrow_move_assignable, }; /// The type of indices into the union member type list. enum Index : unsigned {}; /// Return the index for the given type, asserting that it is a member /// of the list. template static constexpr Index indexOf() { static_assert(ExternalUnionImpl::indexOf::value != -1, "type not registered in union"); return Index(ExternalUnionImpl::indexOf::value); } /// Return the index for the given type or -1 if it is not a member /// of the list. If it is not -1, it may be safely converted to an Index. template static constexpr int maybeIndexOf() { return ExternalUnionImpl::indexOf::value; } template static constexpr bool contains() { return ExternalUnionImpl::indexOf::value != -1; } }; /// An external union that uses the member-list index as the user-facing /// discriminator kind. /// /// This type can be used directly, but it's generally better to use /// ExternalUnion instead. If nothing else, it's not a good idea to /// cement the assumption that you won't have two cases that need the /// same storage. /// /// The external union itself is a trivial type, and it is the /// responsibility of the client to call the "special member functions" /// at the appropriate time. template class BasicExternalUnion { /// The value storage. alignas(Members::Info::alignment) char Storage[Members::Info::size]; template static constexpr int maybeIndexOfMember() { return Members::template maybeIndexOf(); } public: enum : bool { union_is_trivially_copyable = Members::Info::is_trivially_copyable }; using Index = typename Members::Index; /// Construct a union member in-place. template T &emplaceWithoutIndex(Args &&... args) { constexpr int typeIndex = maybeIndexOfMember(); static_assert(typeIndex != -1, "type not in union"); return *(::new ((void*) &Storage) T(std::forward(args)...)); } /// Construct a union member in-place. template T &emplace(Index index, Args &&... args) { constexpr int typeIndex = maybeIndexOfMember(); static_assert(typeIndex != -1, "type not in union"); assert(index == Index(typeIndex) && "current kind is wrong for value"); return *(::new ((void*) &Storage) T(std::forward(args)...)); } /// Construct a union member in-place using list-initialization ({}). template T &emplaceAggregateWithoutIndex(Args &&... args) { constexpr int typeIndex = maybeIndexOfMember(); static_assert(typeIndex != -1, "type not in union"); return *(::new ((void*) &Storage) T{std::forward(args)...}); } /// Construct a union member in-place using list-initialization ({}). template T &emplaceAggregate(Index index, Args &&... args) { constexpr int typeIndex = maybeIndexOfMember(); static_assert(typeIndex != -1, "type not in union"); assert(index == Index(typeIndex) && "current kind is wrong for value"); return *(::new ((void*) &Storage) T{std::forward(args)...}); } /// Return a reference to a union member. template T &getWithoutIndex() { constexpr int typeIndex = maybeIndexOfMember(); static_assert(typeIndex != -1, "type not in union"); return reinterpret_cast(Storage); } /// Return a reference to a union member. template const T &getWithoutIndex() const { constexpr int typeIndex = maybeIndexOfMember(); static_assert(typeIndex != -1, "type not in union"); return reinterpret_cast(Storage); } /// Return a reference to a union member, asserting that the current /// kind matches the type being extracted. template T &get(Index index) { constexpr int typeIndex = maybeIndexOfMember(); static_assert(typeIndex != -1, "type not in union"); assert(index == Index(typeIndex) && "current kind is wrong for access"); return reinterpret_cast(Storage); } /// Return a reference to a union member, asserting that the current /// kind matches the type being extracted. template const T &get(Index index) const { constexpr int typeIndex = maybeIndexOfMember(); static_assert(typeIndex != -1, "type not in union"); assert(index == Index(typeIndex) && "current kind is wrong for access"); return reinterpret_cast(Storage); } /// Destruct the current union member. template void resetToEmptyWithoutIndex() { constexpr int typeIndex = maybeIndexOfMember(); static_assert(typeIndex != -1, "type not in union"); reinterpret_cast(Storage).T::~T(); } /// Destroy the current union member. template void resetToEmpty(Index oldIndex, Index newIndex) { constexpr int typeIndex = maybeIndexOfMember(); static_assert(typeIndex != -1, "type not in union"); constexpr int voidTypeIndex = maybeIndexOfMember(); static_assert(voidTypeIndex != -1, "union has not empty storage"); assert(oldIndex == Index(typeIndex) && "current kind is wrong for value"); assert(newIndex == Index(voidTypeIndex) && "new kind is not in union"); reinterpret_cast(Storage).T::~T(); } /// Copy-construct the union from another union. void copyConstruct(Index index, const BasicExternalUnion &other) { Members::Info::copyConstruct(Storage, unsigned(index), other.Storage); } /// Move-construct the union from another union. void moveConstruct(Index index, BasicExternalUnion &&other) { Members::Info::moveConstruct(Storage, unsigned(index), other.Storage); } /// Copy-assign the union from another union. void copyAssign(Index thisIndex, Index otherIndex, const BasicExternalUnion &other) { if (this == &other) { // do nothing } else if (thisIndex == otherIndex) { Members::Info::copyAssignSame(unsigned(thisIndex), Storage, other.Storage); } else { destruct(thisIndex); copyConstruct(otherIndex, other); } } /// Move-assign the union from another union. void moveAssign(Index thisIndex, Index otherIndex, BasicExternalUnion &&other) { assert(this != &other && "move-constructing value into itself?"); if (thisIndex == otherIndex) { Members::Info::moveAssignSame(unsigned(thisIndex), Storage, other.Storage); } else { destruct(thisIndex); moveConstruct(otherIndex, std::move(other)); } } /// Destroy the union from another union. void destruct(Index index) { Members::Info::destruct(unsigned(index), Storage); } }; /// An external union whose membership is determined by a kind type /// whose members are not necessarily 1-1 with the members of the union. /// /// Clients must provide a function which translates the kind type /// into an index into the union's members list. The expected pattern /// here is something like this: /// /// using Members = ExternalUnionMembers; /// static Members::Index getIndexForKind(Kind kind) { /// switch (kind) { /// case Kind::Nothing: return Members::indexOf(); /// case Kind::Happy: return Members::indexOf(); /// case Kind::Sad: return Members::indexOf(); /// case Kind::Funny: return Members::indexOf(); /// case Kind::Angry: return Members::indexOf(); /// } /// llvm_unreachable("bad kind"); /// } /// ExternalUnion Storage; /// template class ExternalUnion { BasicExternalUnion Union; public: enum : bool { union_is_trivially_copyable = decltype(Union)::union_is_trivially_copyable }; /// Construct a union member in-place. template T &emplace(Kind kind, Args &&... args) { #ifndef NDEBUG return Union.template emplace(GetIndexForKind(kind), std::forward(args)...); #else return Union.template emplaceWithoutIndex(std::forward(args)...); #endif } /// Construct a union member in-place using list-initialization ({}). template T &emplaceAggregate(Kind kind, Args &&... args) { #ifndef NDEBUG return Union.template emplaceAggregate(GetIndexForKind(kind), std::forward(args)...); #else return Union.template emplaceAggregateWithoutIndex( std::forward(args)...); #endif } /// Destroy the current member of the union and switch to a member /// that has no storage. template void resetToEmpty(Kind curKind, Kind newKind) { #ifndef NDEBUG return Union.template resetToEmpty(GetIndexForKind(curKind), GetIndexForKind(newKind)); #else return Union.template resetToEmptyWithoutIndex(); #endif } /// Return a reference to a union member, asserting that the current /// kind is right. template T &get(Kind kind) { #ifndef NDEBUG return Union.template get(GetIndexForKind(kind)); #else return Union.template getWithoutIndex(); #endif } /// Return a reference to a union member, asserting that the current /// kind is right. template const T &get(Kind kind) const { #ifndef NDEBUG return Union.template get(GetIndexForKind(kind)); #else return Union.template getWithoutIndex(); #endif } /// Copy-construct the union from another union. void copyConstruct(Kind kind, const ExternalUnion &other) { Union.copyConstruct(GetIndexForKind(kind), other.Union); } /// Move-construct the union from another union. void moveConstruct(Kind kind, ExternalUnion &&other) { Union.moveConstruct(GetIndexForKind(kind), std::move(other.Union)); } /// Copy-assign the union from another union. void copyAssign(Kind thisKind, Kind otherKind, const ExternalUnion &other) { Union.copyAssign(GetIndexForKind(thisKind), GetIndexForKind(otherKind), other.Union); } /// Move-assign the union from another union. void moveAssign(Kind thisKind, Kind otherKind, ExternalUnion &&other) { Union.moveAssign(GetIndexForKind(thisKind), GetIndexForKind(otherKind), std::move(other.Union)); } /// Destroy the union from another union. void destruct(Kind kind) { Union.destruct(GetIndexForKind(kind)); } }; template class SimpleExternalUnionBase : public ExternalUnion> { public: using Kind = typename KindHelper::Kind; template static constexpr Kind kindForMember() { return KindHelper::coerceIndexToKind(Members::template indexOf()); } template T *dyn_cast(Kind kind) { return (kind == kindForMember() ? &this->template get(kind) : nullptr); } template const T *dyn_cast(Kind kind) const { return (kind == kindForMember() ? &this->template get(kind) : nullptr); } }; /// A particularly simple form of ExternalUnion suitable for unions where /// the kind only exists to distinguish between cases of the union. /// /// Recommended usage: /// using Union = SimpleExternalUnion; /// Union::Kind Kind : 2 = Union::kindForMember(); /// ... /// Union Storage; template class SimpleExternalUnion : public SimpleExternalUnionBase< ExternalUnionImpl::OptimalKindTypeHelper, ExternalUnionMembers > { }; namespace ExternalUnionImpl { /// The MembersHelper base case. template <> struct MembersHelper<> { enum : bool { is_trivially_copyable = true, is_copy_constructible = true, is_nothrow_copy_constructible = true, is_move_constructible = true, is_nothrow_move_constructible = true, is_copy_assignable = true, is_nothrow_copy_assignable = true, is_move_assignable = true, is_nothrow_move_assignable = true, }; enum : size_t { size = 1, alignment = 1 }; LLVM_ATTRIBUTE_ALWAYS_INLINE static void copyConstruct(void *self, int index, const void *other) { assert(false && "bad index"); } LLVM_ATTRIBUTE_ALWAYS_INLINE static void moveConstruct(void *self, int index, void *other) { assert(false && "bad index"); } LLVM_ATTRIBUTE_ALWAYS_INLINE static void copyAssignSame(int index, void *self, const void *other) { assert(false && "bad index"); } LLVM_ATTRIBUTE_ALWAYS_INLINE static void moveAssignSame(int index, void *self, void *other) { assert(false && "bad index"); } LLVM_ATTRIBUTE_ALWAYS_INLINE static void destruct(int index, void *self) { assert(false && "bad index"); } }; template struct UnionMemberInfo; /// The helper class for defining special members. template struct MembersHelper { private: using Member = UnionMemberInfo; using Others = MembersHelper; public: enum : bool { is_trivially_copyable = Member::is_trivially_copyable && Others::is_trivially_copyable, is_copy_constructible = Member::is_copy_constructible && Others::is_copy_constructible, is_nothrow_copy_constructible = Member::is_nothrow_copy_constructible && Others::is_nothrow_copy_constructible, is_move_constructible = Member::is_move_constructible && Others::is_move_constructible, is_nothrow_move_constructible = Member::is_nothrow_move_constructible && Others::is_nothrow_move_constructible, is_copy_assignable = Member::is_copy_assignable && Others::is_copy_assignable, is_nothrow_copy_assignable = Member::is_nothrow_copy_assignable && Others::is_nothrow_copy_assignable, is_move_assignable = Member::is_move_assignable && Others::is_move_assignable, is_nothrow_move_assignable = Member::is_nothrow_move_assignable && Others::is_nothrow_move_assignable, }; enum : size_t { size = Member::size > Others::size ? Member::size : Others::size, alignment = Member::alignment > Others::alignment ? Member::alignment : Others::alignment }; LLVM_ATTRIBUTE_ALWAYS_INLINE static void copyConstruct(void *self, unsigned index, const void *other) { if (index == 0) { Member::copyConstruct(self, other); } else { Others::copyConstruct(self, index - 1, other); } } LLVM_ATTRIBUTE_ALWAYS_INLINE static void moveConstruct(void *self, unsigned index, void *other) { if (index == 0) { Member::moveConstruct(self, other); } else { Others::moveConstruct(self, index - 1, other); } } LLVM_ATTRIBUTE_ALWAYS_INLINE static void copyAssignSame(unsigned index, void *self, const void *other) { if (index == 0) { Member::copyAssignSame(self, other); } else { Others::copyAssignSame(index - 1, self, other); } } LLVM_ATTRIBUTE_ALWAYS_INLINE static void moveAssignSame(unsigned index, void *self, void *other) { if (index == 0) { Member::moveAssignSame(self, other); } else { Others::moveAssignSame(index - 1, self, other); } } LLVM_ATTRIBUTE_ALWAYS_INLINE static void destruct(int index, void *self) { if (index == 0) { Member::destruct(self); } else { Others::destruct(index - 1, self); } } }; /// The standard implementation of UnionMemberInfo. template struct UnionMemberInfo { enum : bool { is_trivially_copyable = IsTriviallyCopyable::value, is_copy_constructible = std::is_copy_constructible::value, is_nothrow_copy_constructible = std::is_nothrow_copy_constructible::value, is_move_constructible = std::is_move_constructible::value, is_nothrow_move_constructible = std::is_nothrow_move_constructible::value, is_copy_assignable = std::is_copy_assignable::value, is_nothrow_copy_assignable = std::is_nothrow_copy_assignable::value, is_move_assignable = std::is_move_assignable::value, is_nothrow_move_assignable = std::is_nothrow_move_assignable::value, }; enum : size_t { size = sizeof(T), alignment = alignof(T) }; LLVM_ATTRIBUTE_ALWAYS_INLINE static void copyConstruct(void *self, const void *other) { ::new (self) T(*static_cast(other)); } LLVM_ATTRIBUTE_ALWAYS_INLINE static void moveConstruct(void *self, void *other) { ::new (self) T(std::move(*static_cast(other))); } LLVM_ATTRIBUTE_ALWAYS_INLINE static void copyAssignSame(void *self, const void *other) { *static_cast(self) = *static_cast(other); } LLVM_ATTRIBUTE_ALWAYS_INLINE static void moveAssignSame(void *self, void *other) { *static_cast(self) = std::move(*static_cast(other)); } LLVM_ATTRIBUTE_ALWAYS_INLINE static void destruct(void *self) { static_cast(self)->T::~T(); } }; /// An explicit specialization of UnionMemberInfo for 'void', which /// represents the empty state. template <> struct UnionMemberInfo { enum : bool { is_trivially_copyable = true, is_copy_constructible = true, is_nothrow_copy_constructible = true, is_move_constructible = true, is_nothrow_move_constructible = true, is_copy_assignable = true, is_nothrow_copy_assignable = true, is_move_assignable = true, is_nothrow_move_assignable = true, }; enum : size_t { size = 0, alignment = 1 }; LLVM_ATTRIBUTE_ALWAYS_INLINE static void copyConstruct(void *self, const void *other) {} LLVM_ATTRIBUTE_ALWAYS_INLINE static void moveConstruct(void *self, void *other) {} LLVM_ATTRIBUTE_ALWAYS_INLINE static void copyAssignSame(void *self, const void *other) {} LLVM_ATTRIBUTE_ALWAYS_INLINE static void moveAssignSame(void *self, void *other) {} LLVM_ATTRIBUTE_ALWAYS_INLINE static void destruct(void *self) {} }; template struct OptimalUnderlyingType; template struct OptimalUnderlyingType { using type = uint8_t; }; template struct OptimalUnderlyingType { using type = uint16_t; }; template struct OptimalUnderlyingType { using type = unsigned; }; template struct OptimalKindTypeHelper { private: using UnderlyingType = typename OptimalUnderlyingType::type; public: enum Kind : UnderlyingType {}; template static constexpr IndexType coerceKindToIndex(Kind kind) { return IndexType(UnderlyingType(kind)); } template static constexpr Kind coerceIndexToKind(IndexType index) { return Kind(UnderlyingType(index)); } }; } // end namespace ExternalUnionImpl } // end namespace swift #endif // SWIFT_BASIC_CLUSTEREDBITVECTOR_H