[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.



git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@354264 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Serge Guelton 2019-02-18 12:07:12 +00:00
parent 846a988299
commit b6d8f1d922
2 changed files with 93 additions and 0 deletions

View File

@ -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
template <typename T> class Optional {

View File

@ -14,8 +14,15 @@
#include <array>
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 {
struct NonDefaultConstructible {
@ -45,6 +52,10 @@ unsigned NonDefaultConstructible::CopyConstructions = 0;
unsigned NonDefaultConstructible::Destructions = 0;
unsigned NonDefaultConstructible::CopyAssignments = 0;
static_assert(
!is_trivially_copyable<Optional<NonDefaultConstructible>>::value,
"not trivially copyable");
// Test fixture
class OptionalTest : public testing::Test {
};
@ -203,6 +214,10 @@ struct MultiArgConstructor {
};
unsigned MultiArgConstructor::Destructions = 0;
static_assert(
!is_trivially_copyable<Optional<MultiArgConstructor>>::value,
"not trivially copyable");
TEST_F(OptionalTest, Emplace) {
MultiArgConstructor::ResetCounts();
Optional<MultiArgConstructor> A;
@ -250,6 +265,9 @@ unsigned MoveOnly::MoveConstructions = 0;
unsigned MoveOnly::Destructions = 0;
unsigned MoveOnly::MoveAssignments = 0;
static_assert(!is_trivially_copyable<Optional<MoveOnly>>::value,
"not trivially copyable");
TEST_F(OptionalTest, MoveOnlyNull) {
MoveOnly::ResetCounts();
Optional<MoveOnly> O;
@ -351,6 +369,9 @@ private:
unsigned Immovable::Constructions = 0;
unsigned Immovable::Destructions = 0;
static_assert(!is_trivially_copyable<Optional<Immovable>>::value,
"not trivially copyable");
TEST_F(OptionalTest, ImmovableEmplace) {
Optional<Immovable> A;
Immovable::ResetCounts();