| //===- 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/Compiler.h" |
| #include <type_traits> |
| #include <utility> |
| #include <assert.h> |
| |
| namespace swift { |
| |
| template <size_t... Values> |
| struct static_max; |
| |
| template <size_t Value> |
| struct static_max<Value> { |
| static const size_t value = Value; |
| }; |
| |
| template <size_t Head, size_t... Tail> |
| struct static_max<Head, Tail...> { |
| static const size_t value = |
| (Head > static_max<Tail...>::value ? Head : static_max<Tail...>::value); |
| }; |
| |
| template <class T, class... Members> |
| struct indexOf; |
| |
| template <class T> |
| struct indexOf<T> { |
| constexpr static const int value = -1; |
| }; |
| |
| template <class T, class U, class... Members> |
| struct indexOf<T, U, Members...> { |
| private: |
| constexpr static const int indexInTail = indexOf<T, Members...>::value; |
| public: |
| constexpr static const int value = |
| (std::is_same<T, U>::value ? 0 : |
| indexInTail != -1 ? indexInTail + 1 : -1); |
| }; |
| |
| template <class... Members> |
| struct SpecialMembers; |
| |
| /// An external union whose discriminator is just a position in |
| /// the template arguments. |
| /// |
| /// 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... Members> |
| class BasicExternalUnion { |
| /// The value storage. |
| LLVM_ALIGNAS(static_max<alignof(Members)...>::value) |
| char Storage[static_max<sizeof(Members)...>::value]; |
| |
| public: |
| /// Construct a union member in-place. |
| template <class T, class... Args> |
| T &emplaceWithoutIndex(Args &&... args) { |
| constexpr int typeIndex = indexOf<T, Members...>::value; |
| static_assert(typeIndex != -1, "type not in union"); |
| |
| return *(::new ((void*) &Storage) T(std::forward<Args>(args)...)); |
| } |
| |
| /// Construct a union member in-place. |
| template <class T, class... Args> |
| T &emplace(int index, Args &&... args) { |
| constexpr int typeIndex = indexOf<T, Members...>::value; |
| static_assert(typeIndex != -1, "type not in union"); |
| assert(index == typeIndex && "current kind is wrong for value"); |
| |
| return *(::new ((void*) &Storage) T(std::forward<Args>(args)...)); |
| } |
| |
| /// Return a reference to a union member. |
| template <class T> |
| T &getWithoutIndex() { |
| constexpr int typeIndex = indexOf<T, Members...>::value; |
| static_assert(typeIndex != -1, "type not in union"); |
| |
| return reinterpret_cast<T &>(Storage); |
| } |
| |
| /// Return a reference to a union member. |
| template <class T> |
| const T &getWithoutIndex() const { |
| constexpr int typeIndex = indexOf<T, Members...>::value; |
| static_assert(typeIndex != -1, "type not in union"); |
| |
| return reinterpret_cast<const T &>(Storage); |
| } |
| |
| /// Return a reference to a union member, asserting that the current |
| /// kind matches the type being extracted. |
| template <class T> |
| T &get(int index) { |
| constexpr int typeIndex = indexOf<T, Members...>::value; |
| static_assert(typeIndex != -1, "type not in union"); |
| assert(index == typeIndex && "current kind is wrong for access"); |
| |
| return reinterpret_cast<T &>(Storage); |
| } |
| |
| /// Return a reference to a union member, asserting that the current |
| /// kind matches the type being extracted. |
| template <class T> |
| const T &get(int index) const { |
| constexpr int typeIndex = indexOf<T, Members...>::value; |
| static_assert(typeIndex != -1, "type not in union"); |
| assert(index == typeIndex && "current kind is wrong for access"); |
| |
| return reinterpret_cast<const T &>(Storage); |
| } |
| |
| /// Copy-construct the union from another union. |
| void copyConstruct(int index, const BasicExternalUnion &other) { |
| if (index != -1) { |
| SpecialMembers<Members...>::copyConstruct(Storage, index, other.Storage); |
| } |
| } |
| |
| /// Move-construct the union from another union. |
| void moveConstruct(int index, BasicExternalUnion &&other) { |
| if (index != -1) { |
| SpecialMembers<Members...>::moveConstruct(Storage, index, other.Storage); |
| } |
| } |
| |
| /// Copy-assign the union from another union. |
| void copyAssign(int thisIndex, int otherIndex, |
| const BasicExternalUnion &other) { |
| if (this == &other) { |
| // do nothing |
| } else if (thisIndex == otherIndex) { |
| if (thisIndex != -1) { |
| SpecialMembers<Members...>::copyAssignSame(thisIndex, Storage, |
| other.Storage); |
| } |
| } else { |
| destruct(thisIndex, Storage); |
| copyConstruct(otherIndex, other); |
| } |
| } |
| |
| /// Move-assign the union from another union. |
| void moveAssign(int thisIndex, int otherIndex, |
| BasicExternalUnion &&other) { |
| assert(this != &other && "move-constructing value into itself?"); |
| |
| if (thisIndex == otherIndex) { |
| if (thisIndex != -1) { |
| SpecialMembers<Members...>::moveAssignSame(thisIndex, Storage, |
| other.Storage); |
| } |
| } else { |
| destruct(thisIndex); |
| moveConstruct(otherIndex, std::move(other)); |
| } |
| } |
| |
| /// Destroy the union from another union. |
| void destruct(int index) { |
| if (index != -1) { |
| SpecialMembers<Members...>::destruct(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, or -1 for kind values |
| /// which do not require data in the union. |
| template <class Kind, int (&GetIndexForKind)(Kind), class... Members> |
| class ExternalUnion { |
| BasicExternalUnion<Members...> Union; |
| |
| public: |
| /// Construct a union member in-place. |
| template <class T, class... Args> |
| T &emplace(Kind kind, Args &&... args) { |
| #ifndef NDEBUG |
| return Union.template emplace<T>(GetIndexForKind(kind), |
| std::forward<Args>(args)...); |
| #else |
| return Union.template emplaceWithoutIndex<T>(std::forward<Args>(args)...); |
| #endif |
| } |
| |
| /// Return a reference to a union member, asserting that the current |
| /// kind is right. |
| template <class T> |
| T &get(Kind kind) { |
| #ifndef NDEBUG |
| return Union.template get<T>(GetIndexForKind(kind)); |
| #else |
| return Union.template getWithoutIndex<T>(); |
| #endif |
| } |
| |
| /// Return a reference to a union member, asserting that the current |
| /// kind is right. |
| template <class T> |
| const T &get(Kind kind) const { |
| #ifndef NDEBUG |
| return Union.template get<T>(GetIndexForKind(kind)); |
| #else |
| return Union.template getWithoutIndex<T>(); |
| #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)); |
| } |
| }; |
| |
| /// A helper class for defining special members. |
| template <> |
| struct SpecialMembers<> { |
| LLVM_ATTRIBUTE_ALWAYS_INLINE |
| static void copyConstruct(void *self, int index, const void *other) { |
| llvm_unreachable("bad index"); |
| } |
| |
| LLVM_ATTRIBUTE_ALWAYS_INLINE |
| static void moveConstruct(void *self, int index, void *other) { |
| llvm_unreachable("bad index"); |
| } |
| |
| LLVM_ATTRIBUTE_ALWAYS_INLINE |
| static void copyAssignSame(int index, void *self, const void *other) { |
| llvm_unreachable("bad index"); |
| } |
| |
| LLVM_ATTRIBUTE_ALWAYS_INLINE |
| static void moveAssignSame(int index, void *self, void *other) { |
| llvm_unreachable("bad index"); |
| } |
| |
| LLVM_ATTRIBUTE_ALWAYS_INLINE |
| static void destruct(int index, void *self) { |
| llvm_unreachable("bad index"); |
| } |
| }; |
| |
| template <class T, class... Others> |
| struct SpecialMembers<T, Others...> { |
| LLVM_ATTRIBUTE_ALWAYS_INLINE |
| static void copyConstruct(void *self, int index, const void *other) { |
| if (index == 0) { |
| ::new (self) T(*static_cast<const T *>(other)); |
| } else { |
| SpecialMembers<Others...>::copyConstruct(self, index - 1, other); |
| } |
| } |
| |
| LLVM_ATTRIBUTE_ALWAYS_INLINE |
| static void moveConstruct(void *self, int index, void *other) { |
| if (index == 0) { |
| ::new (self) T(std::move(*static_cast<T *>(other))); |
| } else { |
| SpecialMembers<Others...>::moveConstruct(self, index - 1, other); |
| } |
| } |
| |
| LLVM_ATTRIBUTE_ALWAYS_INLINE |
| static void copyAssignSame(int index, void *self, const void *other) { |
| if (index == 0) { |
| *static_cast<T*>(self) = *static_cast<const T *>(other); |
| } else { |
| SpecialMembers<Others...>::copyAssignSame(index - 1, self, other); |
| } |
| } |
| |
| LLVM_ATTRIBUTE_ALWAYS_INLINE |
| static void moveAssignSame(int index, void *self, void *other) { |
| if (index == 0) { |
| *static_cast<T*>(self) = std::move(*static_cast<T *>(other)); |
| } else { |
| SpecialMembers<Others...>::moveAssignSame(index - 1, self, other); |
| } |
| } |
| |
| LLVM_ATTRIBUTE_ALWAYS_INLINE |
| static void destruct(int index, void *self) { |
| if (index == 0) { |
| static_cast<T*>(self)->T::~T(); |
| } else { |
| SpecialMembers<Others...>::destruct(index - 1, self); |
| } |
| } |
| }; |
| |
| } // end namespace swift |
| |
| #endif // SWIFT_BASIC_CLUSTEREDBITVECTOR_H |