[NFC] Make Optional<T> trivially copyable when T is trivially copyable
This is a follow-up to r354246 and a reimplementation of https://reviews.llvm.org/D57097?id=186600 that should not trigger any UB thanks to the use of an union. This may still be subject to the problem solved by std::launder, but I'm unsure how it interacts whith union. /me plans to revert if this triggers any relevant bot failure. At least this validates in Release mode with clang 6.0.1 and gcc 4.8.5. llvm-svn: 354264
This commit is contained in:
parent
515e7cdec0
commit
6e0b562bf6
|
@ -139,6 +139,78 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T> class OptionalStorage<T, true> {
|
||||||
|
union {
|
||||||
|
char empty;
|
||||||
|
T value;
|
||||||
|
};
|
||||||
|
bool hasVal = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
~OptionalStorage() = default;
|
||||||
|
|
||||||
|
OptionalStorage() noexcept : empty{} {}
|
||||||
|
|
||||||
|
OptionalStorage(OptionalStorage const &other) = default;
|
||||||
|
OptionalStorage(OptionalStorage &&other) = default;
|
||||||
|
|
||||||
|
OptionalStorage &operator=(OptionalStorage const &other) = default;
|
||||||
|
OptionalStorage &operator=(OptionalStorage &&other) = default;
|
||||||
|
|
||||||
|
template <class... Args>
|
||||||
|
explicit OptionalStorage(in_place_t, Args &&... args)
|
||||||
|
: value(std::forward<Args>(args)...), hasVal(true) {}
|
||||||
|
|
||||||
|
void reset() noexcept {
|
||||||
|
if (hasVal) {
|
||||||
|
value.~T();
|
||||||
|
hasVal = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasValue() const noexcept { return hasVal; }
|
||||||
|
|
||||||
|
T &getValue() LLVM_LVALUE_FUNCTION noexcept {
|
||||||
|
assert(hasVal);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
T const &getValue() const LLVM_LVALUE_FUNCTION noexcept {
|
||||||
|
assert(hasVal);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
#if LLVM_HAS_RVALUE_REFERENCE_THIS
|
||||||
|
T &&getValue() && noexcept {
|
||||||
|
assert(hasVal);
|
||||||
|
return std::move(value);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <class... Args> void emplace(Args &&... args) {
|
||||||
|
reset();
|
||||||
|
::new ((void *)std::addressof(value)) T(std::forward<Args>(args)...);
|
||||||
|
hasVal = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionalStorage &operator=(T const &y) {
|
||||||
|
if (hasValue()) {
|
||||||
|
value = y;
|
||||||
|
} else {
|
||||||
|
::new ((void *)std::addressof(value)) T(y);
|
||||||
|
hasVal = true;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
OptionalStorage &operator=(T &&y) {
|
||||||
|
if (hasValue()) {
|
||||||
|
value = std::move(y);
|
||||||
|
} else {
|
||||||
|
::new ((void *)std::addressof(value)) T(std::move(y));
|
||||||
|
hasVal = true;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace optional_detail
|
} // namespace optional_detail
|
||||||
|
|
||||||
template <typename T> class Optional {
|
template <typename T> class Optional {
|
||||||
|
|
|
@ -14,8 +14,15 @@
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
|
||||||
|
static_assert(is_trivially_copyable<Optional<int>>::value,
|
||||||
|
"trivially copyable");
|
||||||
|
|
||||||
|
static_assert(is_trivially_copyable<Optional<std::array<int, 3>>>::value,
|
||||||
|
"trivially copyable");
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct NonDefaultConstructible {
|
struct NonDefaultConstructible {
|
||||||
|
@ -45,6 +52,10 @@ unsigned NonDefaultConstructible::CopyConstructions = 0;
|
||||||
unsigned NonDefaultConstructible::Destructions = 0;
|
unsigned NonDefaultConstructible::Destructions = 0;
|
||||||
unsigned NonDefaultConstructible::CopyAssignments = 0;
|
unsigned NonDefaultConstructible::CopyAssignments = 0;
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
!is_trivially_copyable<Optional<NonDefaultConstructible>>::value,
|
||||||
|
"not trivially copyable");
|
||||||
|
|
||||||
// Test fixture
|
// Test fixture
|
||||||
class OptionalTest : public testing::Test {
|
class OptionalTest : public testing::Test {
|
||||||
};
|
};
|
||||||
|
@ -203,6 +214,10 @@ struct MultiArgConstructor {
|
||||||
};
|
};
|
||||||
unsigned MultiArgConstructor::Destructions = 0;
|
unsigned MultiArgConstructor::Destructions = 0;
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
!is_trivially_copyable<Optional<MultiArgConstructor>>::value,
|
||||||
|
"not trivially copyable");
|
||||||
|
|
||||||
TEST_F(OptionalTest, Emplace) {
|
TEST_F(OptionalTest, Emplace) {
|
||||||
MultiArgConstructor::ResetCounts();
|
MultiArgConstructor::ResetCounts();
|
||||||
Optional<MultiArgConstructor> A;
|
Optional<MultiArgConstructor> A;
|
||||||
|
@ -250,6 +265,9 @@ unsigned MoveOnly::MoveConstructions = 0;
|
||||||
unsigned MoveOnly::Destructions = 0;
|
unsigned MoveOnly::Destructions = 0;
|
||||||
unsigned MoveOnly::MoveAssignments = 0;
|
unsigned MoveOnly::MoveAssignments = 0;
|
||||||
|
|
||||||
|
static_assert(!is_trivially_copyable<Optional<MoveOnly>>::value,
|
||||||
|
"not trivially copyable");
|
||||||
|
|
||||||
TEST_F(OptionalTest, MoveOnlyNull) {
|
TEST_F(OptionalTest, MoveOnlyNull) {
|
||||||
MoveOnly::ResetCounts();
|
MoveOnly::ResetCounts();
|
||||||
Optional<MoveOnly> O;
|
Optional<MoveOnly> O;
|
||||||
|
@ -351,6 +369,9 @@ private:
|
||||||
unsigned Immovable::Constructions = 0;
|
unsigned Immovable::Constructions = 0;
|
||||||
unsigned Immovable::Destructions = 0;
|
unsigned Immovable::Destructions = 0;
|
||||||
|
|
||||||
|
static_assert(!is_trivially_copyable<Optional<Immovable>>::value,
|
||||||
|
"not trivially copyable");
|
||||||
|
|
||||||
TEST_F(OptionalTest, ImmovableEmplace) {
|
TEST_F(OptionalTest, ImmovableEmplace) {
|
||||||
Optional<Immovable> A;
|
Optional<Immovable> A;
|
||||||
Immovable::ResetCounts();
|
Immovable::ResetCounts();
|
||||||
|
|
Loading…
Reference in New Issue