blob: 543a3999a76c3557ed0b46d887018b3d93fedfb0 [file] [log] [blame]
//===- 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 <utility>
#include <assert.h>
namespace swift {
namespace ExternalUnionImpl {
/// A helper class for finding the index of a type in a parameter pack.
/// 'indexOf<T, List...>::value' will either be the index or -1.
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);
};
/// A helper class for deriving information and rule-of-five operations
/// for the storage of a union.
template <class... Members>
struct MembersHelper;
template <unsigned NumMembers>
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 <class... Members>
struct ExternalUnionMembers {
// (private to the implementation)
using Info = ExternalUnionImpl::MembersHelper<Members...>;
/// 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 <class T>
static constexpr Index indexOf() {
static_assert(ExternalUnionImpl::indexOf<T, Members...>::value != -1,
"type not registered in union");
return Index(ExternalUnionImpl::indexOf<T, Members...>::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 <class T>
static constexpr int maybeIndexOf() {
return ExternalUnionImpl::indexOf<T, Members...>::value;
}
};
/// 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 Members>
class BasicExternalUnion {
/// The value storage.
LLVM_ALIGNAS(Members::Info::alignment)
char Storage[Members::Info::size];
template <class T>
static constexpr int maybeIndexOfMember() {
return Members::template maybeIndexOf<T>();
}
public:
enum : bool {
union_is_trivially_copyable = Members::Info::is_trivially_copyable
};
using Index = typename Members::Index;
/// Construct a union member in-place.
template <class T, class... Args>
T &emplaceWithoutIndex(Args &&... args) {
constexpr int typeIndex = maybeIndexOfMember<T>();
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(Index index, Args &&... args) {
constexpr int typeIndex = maybeIndexOfMember<T>();
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>(args)...));
}
/// Construct a union member in-place using list-initialization ({}).
template <class T, class... Args>
T &emplaceAggregateWithoutIndex(Args &&... args) {
constexpr int typeIndex = maybeIndexOfMember<T>();
static_assert(typeIndex != -1, "type not in union");
return *(::new ((void*) &Storage) T{std::forward<Args>(args)...});
}
/// Construct a union member in-place using list-initialization ({}).
template <class T, class... Args>
T &emplaceAggregate(Index index, Args &&... args) {
constexpr int typeIndex = maybeIndexOfMember<T>();
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>(args)...});
}
/// Return a reference to a union member.
template <class T>
T &getWithoutIndex() {
constexpr int typeIndex = maybeIndexOfMember<T>();
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 = maybeIndexOfMember<T>();
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(Index index) {
constexpr int typeIndex = maybeIndexOfMember<T>();
static_assert(typeIndex != -1, "type not in union");
assert(index == 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(Index index) const {
constexpr int typeIndex = maybeIndexOfMember<T>();
static_assert(typeIndex != -1, "type not in union");
assert(index == Index(typeIndex) && "current kind is wrong for access");
return reinterpret_cast<const T &>(Storage);
}
/// Destruct the current union member.
template <class T>
void resetToEmptyWithoutIndex() {
constexpr int typeIndex = maybeIndexOfMember<T>();
static_assert(typeIndex != -1, "type not in union");
reinterpret_cast<T&>(Storage).T::~T();
}
/// Destroy the current union member.
template <class T>
void resetToEmpty(Index oldIndex, Index newIndex) {
constexpr int typeIndex = maybeIndexOfMember<T>();
static_assert(typeIndex != -1, "type not in union");
constexpr int voidTypeIndex = maybeIndexOfMember<void>();
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<T&>(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, Storage);
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<void, int, std::string>;
/// static Members::Index getIndexForKind(Kind kind) {
/// switch (kind) {
/// case Kind::Nothing: return Members::indexOf<void>();
/// case Kind::Happy: return Members::indexOf<int>();
/// case Kind::Sad: return Members::indexOf<int>();
/// case Kind::Funny: return Members::indexOf<std::string>();
/// case Kind::Angry: return Members::indexOf<std::string>();
/// }
/// llvm_unreachable("bad kind");
/// }
/// ExternalUnion<Kind, Members, getIndexForKind> Storage;
///
template <class Kind, class Members,
typename Members::Index (&GetIndexForKind)(Kind)>
class ExternalUnion {
BasicExternalUnion<Members> Union;
public:
enum : bool {
union_is_trivially_copyable = decltype(Union)::union_is_trivially_copyable
};
/// 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
}
/// Construct a union member in-place using list-initialization ({}).
template <class T, class... Args>
T &emplaceAggregate(Kind kind, Args &&... args) {
#ifndef NDEBUG
return Union.template emplaceAggregate<T>(GetIndexForKind(kind),
std::forward<Args>(args)...);
#else
return Union.template emplaceAggregateWithoutIndex<T>(
std::forward<Args>(args)...);
#endif
}
/// Destroy the current member of the union and switch to a member
/// that has no storage.
template <class T>
void resetToEmpty(Kind curKind, Kind newKind) {
#ifndef NDEBUG
return Union.template resetToEmpty<T>(GetIndexForKind(curKind),
GetIndexForKind(newKind));
#else
return Union.template resetToEmptyWithoutIndex<T>();
#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));
}
};
template <class KindHelper, class Members>
class SimpleExternalUnionBase
: public ExternalUnion<typename KindHelper::Kind, Members,
KindHelper::template coerceKindToIndex<typename Members::Index>> {
public:
using Kind = typename KindHelper::Kind;
template <class T>
static constexpr Kind kindForMember() {
return KindHelper::coerceIndexToKind(Members::template indexOf<T>());
}
template <class T>
T *dyn_cast(Kind kind) {
return (kind == kindForMember<T>()
? &this->template get<T>(kind) : nullptr);
}
template <class T>
const T *dyn_cast(Kind kind) const {
return (kind == kindForMember<T>()
? &this->template get<T>(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<void, int, std::string>;
/// Union::Kind Kind : 2 = Union::kindForMember<void>();
/// ...
/// Union Storage;
template <class... Members>
class SimpleExternalUnion
: public SimpleExternalUnionBase<
ExternalUnionImpl::OptimalKindTypeHelper<sizeof...(Members)>,
ExternalUnionMembers<Members...> > {
};
namespace ExternalUnionImpl {
/// The MembersHelper base case.
template <>
struct MembersHelper<> {
enum : bool {
is_trivially_copyable = true
};
enum : size_t {
size = 1,
alignment = 1
};
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>
struct UnionMemberInfo;
/// The helper class for defining special members.
template <class H, class... T>
struct MembersHelper<H, T...> {
private:
using Member = UnionMemberInfo<H>;
using Others = MembersHelper<T...>;
public:
enum : bool {
is_trivially_copyable =
Member::is_trivially_copyable && Others::is_trivially_copyable
};
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 <class T>
struct UnionMemberInfo {
enum : bool {
is_trivially_copyable = IsTriviallyCopyable<T>::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<const T *>(other));
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
static void moveConstruct(void *self, void *other) {
::new (self) T(std::move(*static_cast<T *>(other)));
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
static void copyAssignSame(void *self, const void *other) {
*static_cast<T*>(self) = *static_cast<const T *>(other);
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
static void moveAssignSame(void *self, void *other) {
*static_cast<T*>(self) = std::move(*static_cast<T *>(other));
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
static void destruct(void *self) {
static_cast<T*>(self)->T::~T();
}
};
/// An explicit specialization of UnionMemberInfo for 'void', which
/// represents the empty state.
template <>
struct UnionMemberInfo<void> {
enum : bool {
is_trivially_copyable = 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 <unsigned NumMembers,
bool FitsInUInt8 = (NumMembers < (1 << sizeof(uint8_t))),
bool FitsInUInt16 = (NumMembers < (1 << sizeof(uint16_t)))>
struct OptimalUnderlyingType;
template <unsigned NumMembers>
struct OptimalUnderlyingType<NumMembers, true, true> {
using type = uint8_t;
};
template <unsigned NumMembers>
struct OptimalUnderlyingType<NumMembers, false, true> {
using type = uint16_t;
};
template <unsigned NumMembers>
struct OptimalUnderlyingType<NumMembers, false, false> {
using type = unsigned;
};
template <unsigned NumMembers>
struct OptimalKindTypeHelper {
private:
using UnderlyingType = typename OptimalUnderlyingType<NumMembers>::type;
public:
enum Kind : UnderlyingType {};
template <class IndexType>
static constexpr IndexType coerceKindToIndex(Kind kind) {
return IndexType(UnderlyingType(kind));
}
template <class IndexType>
static constexpr Kind coerceIndexToKind(IndexType index) {
return Kind(UnderlyingType(index));
}
};
} // end namespace ExternalUnionImpl
} // end namespace swift
#endif // SWIFT_BASIC_CLUSTEREDBITVECTOR_H