//===- 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 #include #include namespace swift { template struct static_max; template struct static_max { static const size_t value = Value; }; template struct static_max { static const size_t value = (Head > static_max::value ? Head : static_max::value); }; 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); }; template 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 BasicExternalUnion { /// The value storage. LLVM_ALIGNAS(static_max::value) char Storage[static_max::value]; public: /// Construct a union member in-place. template T &emplaceWithoutIndex(Args &&... args) { constexpr int typeIndex = indexOf::value; 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(int index, Args &&... args) { constexpr int typeIndex = indexOf::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)...)); } /// Return a reference to a union member. template T &getWithoutIndex() { constexpr int typeIndex = indexOf::value; 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 = indexOf::value; 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(int index) { constexpr int typeIndex = indexOf::value; static_assert(typeIndex != -1, "type not in union"); assert(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(int index) const { constexpr int typeIndex = indexOf::value; static_assert(typeIndex != -1, "type not in union"); assert(index == typeIndex && "current kind is wrong for access"); return reinterpret_cast(Storage); } /// Copy-construct the union from another union. void copyConstruct(int index, const BasicExternalUnion &other) { if (index != -1) { SpecialMembers::copyConstruct(Storage, index, other.Storage); } } /// Move-construct the union from another union. void moveConstruct(int index, BasicExternalUnion &&other) { if (index != -1) { SpecialMembers::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::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::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::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 ExternalUnion { BasicExternalUnion Union; public: /// 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 } /// 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)); } }; /// 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 struct SpecialMembers { LLVM_ATTRIBUTE_ALWAYS_INLINE static void copyConstruct(void *self, int index, const void *other) { if (index == 0) { ::new (self) T(*static_cast(other)); } else { SpecialMembers::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(other))); } else { SpecialMembers::moveConstruct(self, index - 1, other); } } LLVM_ATTRIBUTE_ALWAYS_INLINE static void copyAssignSame(int index, void *self, const void *other) { if (index == 0) { *static_cast(self) = *static_cast(other); } else { SpecialMembers::copyAssignSame(index - 1, self, other); } } LLVM_ATTRIBUTE_ALWAYS_INLINE static void moveAssignSame(int index, void *self, void *other) { if (index == 0) { *static_cast(self) = std::move(*static_cast(other)); } else { SpecialMembers::moveAssignSame(index - 1, self, other); } } LLVM_ATTRIBUTE_ALWAYS_INLINE static void destruct(int index, void *self) { if (index == 0) { static_cast(self)->T::~T(); } else { SpecialMembers::destruct(index - 1, self); } } }; } // end namespace swift #endif // SWIFT_BASIC_CLUSTEREDBITVECTOR_H