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,9 +33,12 @@ struct in_place_t {}; /// Storage for any type. -template ::value - &&std::is_trivially_copy_constructible::value - &&std::is_trivially_copy_assignable::value> +template ::value && + std::is_trivially_copy_assignable::value && + (std::is_trivially_move_constructible::value || + !std::is_move_constructible::value) && + (std::is_trivially_move_assignable::value || + !std::is_move_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 @@ -395,10 +395,12 @@ struct NonTCopy { NonTCopy() = default; - // Delete the volatile copy constructor. + // 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 default copy constructor unspecified (deleted) + // 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. @@ -410,6 +412,15 @@ int Val{0}; }; +#if defined(_MSC_VER) && _MSC_VER >= 1927 +// Currently only true on recent MSVC releases. +static_assert(std::is_trivially_copyable::value, + "Expect NonTCopy to be trivially copyable"); + +static_assert(!std::is_trivially_copy_constructible::value, + "Expect NonTCopy not to be trivially copy constructible."); +#endif // defined(_MSC_VER) && _MSC_VER >= 1927 + TEST(OptionalTest, DeletedCopyConstructor) { // Expect compile to fail if 'trivial' version of @@ -431,10 +442,12 @@ NonTAssign() = default; NonTAssign(NonTAssign const &) = default; - // Delete the volatile copy assignment. + // 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 default copy assignment unspecified (deleted). + // 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. @@ -447,18 +460,55 @@ int A{0}; }; +#if defined(_MSC_VER) && _MSC_VER >= 1927 +// Currently only true on recent MSVC releases. +static_assert(std::is_trivially_copyable::value, + "Expect NonTAssign to be trivially copyable"); + +static_assert(!std::is_trivially_copy_assignable::value, + "Expect NonTAssign not to be trivially assignable."); +#endif // defined(_MSC_VER) && _MSC_VER >= 1927 + TEST(OptionalTest, DeletedCopyAssignment) { // Expect compile to fail if 'trivial' version of // optional_detail::OptionalStorage is chosen. - using NoAssignOptT = Optional; - NoAssignOptT NoAssign1; + using NonTAssignOptT = Optional; + NonTAssignOptT NonTAssign1; // Check that the Optional can be copy constructed. - NoAssignOptT NoAssign2{NoAssign1}; + NonTAssignOptT NonTAssign2{NonTAssign1}; // Check that the Optional can be copy assigned. - NoAssign1 = NoAssign2; + NonTAssign1 = NonTAssign2; +} + +struct NoTMove { + NoTMove() = default; + NoTMove(NoTMove const &) = default; + NoTMove &operator=(NoTMove const &) = default; + + // Delete move constructor / assignment. Compiler should fall-back to the + // trivial copy constructor / assignment in the trivial OptionalStorage + // specialization. + NoTMove(NoTMove &&) = delete; + NoTMove &operator=(NoTMove &&) = delete; + + int Val{0}; +}; + +TEST(OptionalTest, DeletedMoveConstructor) { + using NoTMoveOptT = Optional; + + NoTMoveOptT NonTMove1; + NoTMoveOptT NonTMove2{std::move(NonTMove1)}; + + NonTMove1 = std::move(NonTMove2); + + static_assert( + std::is_trivially_copyable::value, + "Expect Optional to still use the trivial specialization " + "of OptionalStorage despite the deleted move constructor / assignment."); } #if LLVM_HAS_RVALUE_REFERENCE_THIS