[llvm] r353927 - Make llvm::Optional<T> trivially copyable when T is trivially copyable
Serge Guelton via llvm-commits
llvm-commits at lists.llvm.org
Wed Feb 13 01:31:23 PST 2019
Author: serge_sans_paille
Date: Wed Feb 13 01:31:22 2019
New Revision: 353927
URL: http://llvm.org/viewvc/llvm-project?rev=353927&view=rev
Log:
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.
Differential Revision: https://reviews.llvm.org/D57097
Modified:
llvm/trunk/include/llvm/ADT/Optional.h
llvm/trunk/unittests/ADT/OptionalTest.cpp
Modified: llvm/trunk/include/llvm/ADT/Optional.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ADT/Optional.h?rev=353927&r1=353926&r2=353927&view=diff
==============================================================================
--- llvm/trunk/include/llvm/ADT/Optional.h (original)
+++ llvm/trunk/include/llvm/ADT/Optional.h Wed Feb 13 01:31:22 2019
@@ -29,36 +29,77 @@ namespace llvm {
class raw_ostream;
namespace optional_detail {
+
/// Storage for any type.
-template <typename T, bool = is_trivially_copyable<T>::value> struct OptionalStorage {
+//
+template <class T> struct OptionalTrivialStorage {
AlignedCharArrayUnion<T> storage;
bool hasVal = false;
+ OptionalTrivialStorage() = default;
+ OptionalTrivialStorage(OptionalTrivialStorage const &) = default;
+ OptionalTrivialStorage(OptionalTrivialStorage &&) = default;
+ OptionalTrivialStorage &operator=(OptionalTrivialStorage const &) = default;
+ OptionalTrivialStorage &operator=(OptionalTrivialStorage &&) = default;
+ ~OptionalTrivialStorage() = default;
+
+ OptionalTrivialStorage(const T &y) : hasVal(true) {
+ new (storage.buffer) T(y);
+ }
+ OptionalTrivialStorage(T &&y) : hasVal(true) {
+ new (storage.buffer) T(std::move(y));
+ }
+
+ OptionalTrivialStorage &operator=(const T &y) {
+ new (storage.buffer) T(y);
+ hasVal = true;
+ return *this;
+ }
+ OptionalTrivialStorage &operator=(T &&y) {
+ new (storage.buffer) T(std::move(y));
+ hasVal = true;
+ return *this;
+ }
+
+ T *getPointer() {
+ assert(hasVal);
+ return reinterpret_cast<T *>(storage.buffer);
+ }
+ const T *getPointer() const {
+ assert(hasVal);
+ return reinterpret_cast<const T *>(storage.buffer);
+ }
+ void reset() { hasVal = false; }
+};
+
+template <typename T> struct OptionalStorage : OptionalTrivialStorage<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(T &&y) : hasVal(true) {
- new (storage.buffer) T(std::forward<T>(y));
+ OptionalStorage(const T &y) : OptionalTrivialStorage<T>(y) {}
+ OptionalStorage(T &&y) : OptionalTrivialStorage<T>(std::move(y)) {}
+
+ OptionalStorage(const OptionalStorage &O) : OptionalTrivialStorage<T>() {
+ this->hasVal = O.hasVal;
+ if (this->hasVal)
+ new (this->storage.buffer) T(*O.getPointer());
}
- OptionalStorage(OptionalStorage &&O) : hasVal(O.hasVal) {
+
+ OptionalStorage(OptionalStorage &&O) : OptionalTrivialStorage<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;
+ OptionalTrivialStorage<T>::operator=(std::move(y));
}
return *this;
}
+
OptionalStorage &operator=(OptionalStorage &&O) {
if (!O.hasVal)
reset();
@@ -74,11 +115,10 @@ template <typename T, bool = is_triviall
// 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;
+ OptionalTrivialStorage<T>::operator=(y);
}
return *this;
}
@@ -93,26 +133,19 @@ template <typename T, bool = is_triviall
~OptionalStorage() { reset(); }
void reset() {
- if (hasVal) {
- (*getPointer()).~T();
- hasVal = false;
+ if (this->hasVal) {
+ (*this->getPointer()).~T();
+ OptionalTrivialStorage<T>::reset();
}
}
-
- T *getPointer() {
- assert(hasVal);
- return reinterpret_cast<T *>(storage.buffer);
- }
- const T *getPointer() const {
- assert(hasVal);
- return reinterpret_cast<const T *>(storage.buffer);
- }
};
} // namespace optional_detail
template <typename T> class Optional {
- optional_detail::OptionalStorage<T> Storage;
+ typename std::conditional<is_trivially_copyable<T>::value,
+ optional_detail::OptionalTrivialStorage<T>,
+ optional_detail::OptionalStorage<T>>::type Storage;
public:
using value_type = T;
Modified: llvm/trunk/unittests/ADT/OptionalTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ADT/OptionalTest.cpp?rev=353927&r1=353926&r2=353927&view=diff
==============================================================================
--- llvm/trunk/unittests/ADT/OptionalTest.cpp (original)
+++ llvm/trunk/unittests/ADT/OptionalTest.cpp Wed Feb 13 01:31:22 2019
@@ -16,6 +16,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;
@@ -43,6 +49,10 @@ unsigned NonDefaultConstructible::CopyCo
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 {
};
@@ -201,6 +211,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;
@@ -248,6 +262,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;
@@ -349,6 +366,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();
More information about the llvm-commits
mailing list