From a28eda7e401f37a2fe42ca9bba96b6e662b60cf0 Mon Sep 17 00:00:00 2001 From: David Blaikie Date: Wed, 20 Feb 2013 00:26:04 +0000 Subject: [PATCH] Allow llvm::Optional to work with types without default constructors. This generalizes Optional to require less from the T type by using aligned storage for backing & placement new/deleting the T into it when necessary. Also includes unit tests. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@175580 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/ADT/Optional.h | 58 ++++++++--- unittests/ADT/CMakeLists.txt | 1 + unittests/ADT/OptionalTest.cpp | 173 +++++++++++++++++++++++++++++++++ 3 files changed, 221 insertions(+), 11 deletions(-) create mode 100644 unittests/ADT/OptionalTest.cpp diff --git a/include/llvm/ADT/Optional.h b/include/llvm/ADT/Optional.h index 6f58ffa48b9..cc6065c062a 100644 --- a/include/llvm/ADT/Optional.h +++ b/include/llvm/ADT/Optional.h @@ -17,6 +17,7 @@ #define LLVM_ADT_OPTIONAL_H #include "llvm/Support/Compiler.h" +#include "llvm/Support/AlignOf.h" #include #if LLVM_HAS_RVALUE_REFERENCES @@ -27,14 +28,22 @@ namespace llvm { template class Optional { - T x; + AlignedCharArrayUnion storage; bool hasVal; public: - explicit Optional() : x(), hasVal(false) {} - Optional(const T &y) : x(y), hasVal(true) {} + explicit Optional() : hasVal(false) {} + Optional(const T &y) : hasVal(true) { + new (storage.buffer) T(y); + } + Optional(const Optional &O) : hasVal(O.hasVal) { + if (hasVal) + new (storage.buffer) T(*O); + } #if LLVM_HAS_RVALUE_REFERENCES - Optional(T &&y) : x(std::forward(y)), hasVal(true) {} + Optional(T &&y) : hasVal(true) { + new (storage.buffer) T(std::forward(y)); + } #endif static inline Optional create(const T* y) { @@ -42,22 +51,49 @@ public: } Optional &operator=(const T &y) { - x = y; - hasVal = true; + if (hasVal) + **this = y; + else { + new (storage.buffer) T(y); + hasVal = true; + } + return *this; + } + + Optional &operator=(const Optional &O) { + if (!O) + Reset(); + else + *this = *O; return *this; } + + void Reset() { + if (hasVal) { + (*this)->~T(); + hasVal = false; + } + } + + ~Optional() { + Reset(); + } - const T* getPointer() const { assert(hasVal); return &x; } - const T& getValue() const LLVM_LVALUE_FUNCTION { assert(hasVal); return x; } + const T* getPointer() const { assert(hasVal); return reinterpret_cast(storage.buffer); } + T* getPointer() { assert(hasVal); return reinterpret_cast(storage.buffer); } + const T& getValue() const LLVM_LVALUE_FUNCTION { assert(hasVal); return *getPointer(); } + T& getValue() LLVM_LVALUE_FUNCTION { assert(hasVal); return *getPointer(); } operator bool() const { return hasVal; } bool hasValue() const { return hasVal; } const T* operator->() const { return getPointer(); } - const T& operator*() const LLVM_LVALUE_FUNCTION { assert(hasVal); return x; } + T* operator->() { return getPointer(); } + const T& operator*() const LLVM_LVALUE_FUNCTION { assert(hasVal); return *getPointer(); } + T& operator*() LLVM_LVALUE_FUNCTION { assert(hasVal); return *getPointer(); } #if LLVM_HAS_RVALUE_REFERENCE_THIS - T&& getValue() && { assert(hasVal); return std::move(x); } - T&& operator*() && { assert(hasVal); return std::move(x); } + T&& getValue() && { assert(hasVal); return std::move(*getPointer()); } + T&& operator*() && { assert(hasVal); return std::move(*getPointer()); } #endif }; diff --git a/unittests/ADT/CMakeLists.txt b/unittests/ADT/CMakeLists.txt index 0f39e02c1ff..9aad793d8bc 100644 --- a/unittests/ADT/CMakeLists.txt +++ b/unittests/ADT/CMakeLists.txt @@ -19,6 +19,7 @@ set(ADTSources IntervalMapTest.cpp IntrusiveRefCntPtrTest.cpp MapVectorTest.cpp + OptionalTest.cpp PackedVectorTest.cpp SCCIteratorTest.cpp SmallPtrSetTest.cpp diff --git a/unittests/ADT/OptionalTest.cpp b/unittests/ADT/OptionalTest.cpp new file mode 100644 index 00000000000..8bdea45c5a9 --- /dev/null +++ b/unittests/ADT/OptionalTest.cpp @@ -0,0 +1,173 @@ +//===- llvm/unittest/ADT/OptionalTest.cpp - Optional unit tests -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include "llvm/ADT/Optional.h" +using namespace llvm; + +namespace { + +struct NonDefaultConstructible { + static unsigned CopyConstructions; + static unsigned Destructions; + static unsigned CopyAssignments; + explicit NonDefaultConstructible(int) { + } + NonDefaultConstructible(const NonDefaultConstructible&) { + ++CopyConstructions; + } + NonDefaultConstructible &operator=(const NonDefaultConstructible&) { + ++CopyAssignments; + return *this; + } + ~NonDefaultConstructible() { + ++Destructions; + } + static void ResetCounts() { + CopyConstructions = 0; + Destructions = 0; + CopyAssignments = 0; + } +}; + +unsigned NonDefaultConstructible::CopyConstructions = 0; +unsigned NonDefaultConstructible::Destructions = 0; +unsigned NonDefaultConstructible::CopyAssignments = 0; + +// Test fixture +class OptionalTest : public testing::Test { +}; + +TEST_F(OptionalTest, NonDefaultConstructibleTest) { + Optional O; + EXPECT_FALSE(O); +} + +TEST_F(OptionalTest, ResetTest) { + NonDefaultConstructible::ResetCounts(); + Optional O(NonDefaultConstructible(3)); + EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(1u, NonDefaultConstructible::Destructions); + NonDefaultConstructible::ResetCounts(); + O.Reset(); + EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(1u, NonDefaultConstructible::Destructions); +} + +TEST_F(OptionalTest, InitializationLeakTest) { + NonDefaultConstructible::ResetCounts(); + Optional(NonDefaultConstructible(3)); + EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(2u, NonDefaultConstructible::Destructions); +} + +TEST_F(OptionalTest, CopyConstructionTest) { + NonDefaultConstructible::ResetCounts(); + { + Optional A(NonDefaultConstructible(3)); + EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(1u, NonDefaultConstructible::Destructions); + NonDefaultConstructible::ResetCounts(); + Optional B(A); + EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(0u, NonDefaultConstructible::Destructions); + NonDefaultConstructible::ResetCounts(); + } + EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(2u, NonDefaultConstructible::Destructions); +} + +TEST_F(OptionalTest, ConstructingCopyAssignmentTest) { + NonDefaultConstructible::ResetCounts(); + { + Optional A(NonDefaultConstructible(3)); + Optional B; + EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(1u, NonDefaultConstructible::Destructions); + NonDefaultConstructible::ResetCounts(); + B = A; + EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(0u, NonDefaultConstructible::Destructions); + NonDefaultConstructible::ResetCounts(); + } + EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(2u, NonDefaultConstructible::Destructions); +} + +TEST_F(OptionalTest, CopyingCopyAssignmentTest) { + NonDefaultConstructible::ResetCounts(); + { + Optional A(NonDefaultConstructible(3)); + Optional B(NonDefaultConstructible(4)); + EXPECT_EQ(2u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(2u, NonDefaultConstructible::Destructions); + NonDefaultConstructible::ResetCounts(); + B = A; + EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(1u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(0u, NonDefaultConstructible::Destructions); + NonDefaultConstructible::ResetCounts(); + } + EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(2u, NonDefaultConstructible::Destructions); +} + +TEST_F(OptionalTest, DeletingCopyAssignmentTest) { + NonDefaultConstructible::ResetCounts(); + { + Optional A; + Optional B(NonDefaultConstructible(3)); + EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(1u, NonDefaultConstructible::Destructions); + NonDefaultConstructible::ResetCounts(); + B = A; + EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(1u, NonDefaultConstructible::Destructions); + NonDefaultConstructible::ResetCounts(); + } + EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(0u, NonDefaultConstructible::Destructions); +} + +TEST_F(OptionalTest, NullCopyConstructionTest) { + NonDefaultConstructible::ResetCounts(); + { + Optional A; + Optional B; + EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(0u, NonDefaultConstructible::Destructions); + NonDefaultConstructible::ResetCounts(); + B = A; + EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(0u, NonDefaultConstructible::Destructions); + NonDefaultConstructible::ResetCounts(); + } + EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(0u, NonDefaultConstructible::Destructions); +} + +} // end anonymous namespace + -- 2.34.1