diff --git a/llvm/include/llvm/ADT/Optional.h b/llvm/include/llvm/ADT/Optional.h --- a/llvm/include/llvm/ADT/Optional.h +++ b/llvm/include/llvm/ADT/Optional.h @@ -33,7 +33,9 @@ struct in_place_t {}; /// Storage for any type. -template ::value> +template ::value + &&std::is_trivially_copy_constructible::value + &&std::is_trivially_copy_assignable::value> class OptionalStorage { union { char empty; diff --git a/llvm/unittests/ADT/OptionalTest.cpp b/llvm/unittests/ADT/OptionalTest.cpp --- a/llvm/unittests/ADT/OptionalTest.cpp +++ b/llvm/unittests/ADT/OptionalTest.cpp @@ -390,6 +390,81 @@ EXPECT_EQ(0u, Immovable::Destructions); } +// Craft a class which is_trivially_copyable, but not +// is_trivially_copy_constructible. +struct NonTCopy { + NonTCopy() = default; + + // Delete the volatile copy constructor to engage the "rule of 3" and delete + // any unspecified copy assignment or constructor. + NonTCopy(volatile NonTCopy const &) = delete; + + // Leave the non-volatile default copy constructor unspecified (deleted by + // rule of 3) + + // This template can serve as the copy constructor, but isn't chosen + // by =default in a class with a 'NonTCopy' member. + template + NonTCopy(Self const &Other) : Val(Other.Val) {} + + NonTCopy &operator=(NonTCopy const &) = default; + + int Val{0}; +}; + +TEST(OptionalTest, DeletedCopyConstructor) { + + // Expect compile to fail if 'trivial' version of + // optional_detail::OptionalStorage is chosen. + using NonTCopyOptT = Optional; + NonTCopyOptT NonTCopy1; + + // Check that the Optional can be copy constructed. + NonTCopyOptT NonTCopy2{NonTCopy1}; + + // Check that the Optional can be copy assigned. + NonTCopy1 = NonTCopy2; +} + +// Craft a class which is_trivially_copyable, but not +// is_trivially_copy_assignable. +class NonTAssign { +public: + NonTAssign() = default; + NonTAssign(NonTAssign const &) = default; + + // Delete the volatile copy assignment to engage the "rule of 3" and delete + // any unspecified copy assignment or constructor. + NonTAssign &operator=(volatile NonTAssign const &) = delete; + + // Leave the non-volatile default copy assignment unspecified (deleted by rule + // of 3). + + // This template can serve as the copy assignment, but isn't chosen + // by =default in a class with a 'NonTAssign' member. + template + NonTAssign &operator=(Self const &Other) { + A = Other.A; + return *this; + } + + int A{0}; +}; + +TEST(OptionalTest, DeletedCopyAssignment) { + + // Expect compile to fail if 'trivial' version of + // optional_detail::OptionalStorage is chosen. + using NonTAssignOptT = Optional; + NonTAssignOptT NonTAssign1; + + // Check that the Optional can be copy constructed. + NonTAssignOptT NonTAssign2{NonTAssign1}; + + // Check that the Optional can be copy assigned. + NonTAssign1 = NonTAssign2; +} + #if LLVM_HAS_RVALUE_REFERENCE_THIS TEST(OptionalTest, MoveGetValueOr) {