mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-12-13 19:24:21 +00:00
Re-commit rL353927, patch included
Make llvm::Optional<T> trivially copyable when T is trivially copyable This is an ever-recurring issue (see https://bugs.llvm.org/show_bug.cgi?id=39427 and https://bugs.llvm.org/show_bug.cgi?id=35978) but I believe that thanks to https://reviews.llvm.org/D54472 we can now ship a decent implementation of this. Basically the fact that llvm::is_trivially_copyable has a consistent behavior across compilers should prevent any ABI issue, and using in-place new instead of memcpy should keep compiler bugs away. This patch is slightly different from the original revision https://reviews.llvm.org/rL353927 but achieves the same goal. It just avoids going through std::conditional which may the code more explicit. llvm-svn: 353962
This commit is contained in:
parent
0f30a3b68f
commit
f688293393
@ -29,33 +29,61 @@ namespace llvm {
|
||||
class raw_ostream;
|
||||
|
||||
namespace optional_detail {
|
||||
/// Storage for any type.
|
||||
template <typename T, bool = is_trivially_copyable<T>::value> struct OptionalStorage {
|
||||
template <typename T> struct OptionalStorageBase {
|
||||
AlignedCharArrayUnion<T> storage;
|
||||
bool hasVal = false;
|
||||
|
||||
OptionalStorageBase() = default;
|
||||
OptionalStorageBase(const T &y) : hasVal(true) { new (storage.buffer) T(y); }
|
||||
OptionalStorageBase(T &&y) : hasVal(true) {
|
||||
new (storage.buffer) T(std::move(y));
|
||||
}
|
||||
|
||||
T *getPointer() {
|
||||
assert(hasVal);
|
||||
return reinterpret_cast<T *>(storage.buffer);
|
||||
}
|
||||
const T *getPointer() const {
|
||||
assert(hasVal);
|
||||
return reinterpret_cast<const T *>(storage.buffer);
|
||||
}
|
||||
OptionalStorageBase &operator=(T &&y) {
|
||||
hasVal = true;
|
||||
new (this->storage.buffer) T(std::move(y));
|
||||
return *this;
|
||||
}
|
||||
OptionalStorageBase &operator=(const T &y) {
|
||||
hasVal = true;
|
||||
new (this->storage.buffer) T(y);
|
||||
return *this;
|
||||
}
|
||||
void reset() { this->hasVal = false; }
|
||||
};
|
||||
|
||||
/// Storage for any type.
|
||||
template <typename T, bool = is_trivially_copyable<T>::value>
|
||||
struct OptionalStorage : OptionalStorageBase<T> {
|
||||
OptionalStorage() = default;
|
||||
|
||||
OptionalStorage(const T &y) : hasVal(true) { new (storage.buffer) T(y); }
|
||||
OptionalStorage(const OptionalStorage &O) : hasVal(O.hasVal) {
|
||||
if (hasVal)
|
||||
new (storage.buffer) T(*O.getPointer());
|
||||
OptionalStorage(const T &y) : OptionalStorageBase<T>(y) {}
|
||||
OptionalStorage(const OptionalStorage &O) : OptionalStorageBase<T>() {
|
||||
this->hasVal = O.hasVal;
|
||||
if (this->hasVal)
|
||||
new (this->storage.buffer) T(*O.getPointer());
|
||||
}
|
||||
OptionalStorage(T &&y) : hasVal(true) {
|
||||
new (storage.buffer) T(std::forward<T>(y));
|
||||
}
|
||||
OptionalStorage(OptionalStorage &&O) : hasVal(O.hasVal) {
|
||||
OptionalStorage(T &&y) : OptionalStorageBase<T>(std::move(y)) {}
|
||||
OptionalStorage(OptionalStorage &&O) : OptionalStorageBase<T>() {
|
||||
this->hasVal = O.hasVal;
|
||||
if (O.hasVal) {
|
||||
new (storage.buffer) T(std::move(*O.getPointer()));
|
||||
new (this->storage.buffer) T(std::move(*O.getPointer()));
|
||||
}
|
||||
}
|
||||
|
||||
OptionalStorage &operator=(T &&y) {
|
||||
if (hasVal)
|
||||
*getPointer() = std::move(y);
|
||||
if (this->hasVal)
|
||||
*this->getPointer() = std::move(y);
|
||||
else {
|
||||
new (storage.buffer) T(std::move(y));
|
||||
hasVal = true;
|
||||
OptionalStorageBase<T>::operator=(std::move(y));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@ -74,11 +102,10 @@ template <typename T, bool = is_trivially_copyable<T>::value> struct OptionalSto
|
||||
// requirements (notably: the existence of a default ctor) when implemented
|
||||
// in that way. Careful SFINAE to avoid such pitfalls would be required.
|
||||
OptionalStorage &operator=(const T &y) {
|
||||
if (hasVal)
|
||||
*getPointer() = y;
|
||||
if (this->hasVal)
|
||||
*this->getPointer() = y;
|
||||
else {
|
||||
new (storage.buffer) T(y);
|
||||
hasVal = true;
|
||||
OptionalStorageBase<T>::operator=(y);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@ -93,20 +120,30 @@ template <typename T, bool = is_trivially_copyable<T>::value> struct OptionalSto
|
||||
~OptionalStorage() { reset(); }
|
||||
|
||||
void reset() {
|
||||
if (hasVal) {
|
||||
(*getPointer()).~T();
|
||||
hasVal = false;
|
||||
if (this->hasVal) {
|
||||
(*this->getPointer()).~T();
|
||||
}
|
||||
OptionalStorageBase<T>::reset();
|
||||
}
|
||||
};
|
||||
|
||||
T *getPointer() {
|
||||
assert(hasVal);
|
||||
return reinterpret_cast<T *>(storage.buffer);
|
||||
template <typename T> struct OptionalStorage<T, true> : OptionalStorageBase<T> {
|
||||
OptionalStorage() = default;
|
||||
OptionalStorage(const T &y) : OptionalStorageBase<T>(y) {}
|
||||
OptionalStorage(const OptionalStorage &O) = default;
|
||||
OptionalStorage(T &&y) : OptionalStorageBase<T>(std::move(y)) {}
|
||||
OptionalStorage(OptionalStorage &&O) = default;
|
||||
OptionalStorage &operator=(T &&y) {
|
||||
OptionalStorageBase<T>::operator=(std::move(y));
|
||||
return *this;
|
||||
}
|
||||
const T *getPointer() const {
|
||||
assert(hasVal);
|
||||
return reinterpret_cast<const T *>(storage.buffer);
|
||||
OptionalStorage &operator=(OptionalStorage &&O) = default;
|
||||
OptionalStorage &operator=(const T &y) {
|
||||
OptionalStorageBase<T>::operator=(y);
|
||||
return *this;
|
||||
}
|
||||
OptionalStorage &operator=(const OptionalStorage &O) = default;
|
||||
~OptionalStorage() = default;
|
||||
};
|
||||
|
||||
} // namespace optional_detail
|
||||
|
@ -18,6 +18,12 @@ using namespace llvm;
|
||||
|
||||
namespace {
|
||||
|
||||
static_assert(llvm::is_trivially_copyable<Optional<int>>::value,
|
||||
"trivially copyable");
|
||||
|
||||
static_assert(llvm::is_trivially_copyable<Optional<std::array<int, 3>>>::value,
|
||||
"trivially copyable");
|
||||
|
||||
struct NonDefaultConstructible {
|
||||
static unsigned CopyConstructions;
|
||||
static unsigned Destructions;
|
||||
@ -45,6 +51,10 @@ unsigned NonDefaultConstructible::CopyConstructions = 0;
|
||||
unsigned NonDefaultConstructible::Destructions = 0;
|
||||
unsigned NonDefaultConstructible::CopyAssignments = 0;
|
||||
|
||||
static_assert(
|
||||
!llvm::is_trivially_copyable<Optional<NonDefaultConstructible>>::value,
|
||||
"not trivially copyable");
|
||||
|
||||
// Test fixture
|
||||
class OptionalTest : public testing::Test {
|
||||
};
|
||||
@ -203,6 +213,10 @@ struct MultiArgConstructor {
|
||||
};
|
||||
unsigned MultiArgConstructor::Destructions = 0;
|
||||
|
||||
static_assert(
|
||||
!llvm::is_trivially_copyable<Optional<MultiArgConstructor>>::value,
|
||||
"not trivially copyable");
|
||||
|
||||
TEST_F(OptionalTest, Emplace) {
|
||||
MultiArgConstructor::ResetCounts();
|
||||
Optional<MultiArgConstructor> A;
|
||||
@ -250,6 +264,9 @@ unsigned MoveOnly::MoveConstructions = 0;
|
||||
unsigned MoveOnly::Destructions = 0;
|
||||
unsigned MoveOnly::MoveAssignments = 0;
|
||||
|
||||
static_assert(!llvm::is_trivially_copyable<Optional<MoveOnly>>::value,
|
||||
"not trivially copyable");
|
||||
|
||||
TEST_F(OptionalTest, MoveOnlyNull) {
|
||||
MoveOnly::ResetCounts();
|
||||
Optional<MoveOnly> O;
|
||||
@ -351,6 +368,9 @@ private:
|
||||
unsigned Immovable::Constructions = 0;
|
||||
unsigned Immovable::Destructions = 0;
|
||||
|
||||
static_assert(!llvm::is_trivially_copyable<Optional<Immovable>>::value,
|
||||
"not trivially copyable");
|
||||
|
||||
TEST_F(OptionalTest, ImmovableEmplace) {
|
||||
Optional<Immovable> A;
|
||||
Immovable::ResetCounts();
|
||||
|
Loading…
Reference in New Issue
Block a user