forked from OSchip/llvm-project
Fix llvm::Optional build breaks in MSVC using std::is_trivially_copyable
Current code breaks this version of MSVC due to a mismatch between `std::is_trivially_copyable` and `llvm::is_trivially_copyable` for `std::pair` instantiations. Hence I was attempting to use `std::is_trivially_copyable` to set `llvm::is_trivially_copyable<T>::value`. I spent some time root causing an `llvm::Optional` build error on MSVC 16.8.3 related to the change described above: ``` 62>C:\src\ocg_llvm\llvm-project\llvm\include\llvm/ADT/BreadthFirstIterator.h(96,12): error C2280: 'llvm::Optional<std::pair<std::pair<unsigned int,llvm::Graph<4>::NodeSubset> *,llvm::Optional<llvm::Graph<4>::ChildIterator>>> &llvm::Optional<std::pair<std::pair<unsigned int,llvm::Graph<4>::NodeSubset> *,llvm::Optional<llvm::Graph<4>::ChildIterator>>>::operator =(const llvm::Optional<std::pair<std::pair<unsigned int,llvm::Graph<4>::NodeSubset> *,llvm::Optional<llvm::Graph<4>::ChildIterator>>> &)': attempting to reference a deleted function (compiling source file C:\src\ocg_llvm\llvm-project\llvm\unittests\ADT\BreadthFirstIteratorTest.cpp) ... ``` The "trivial" specialization of `optional_detail::OptionalStorage` assumes that the value type is trivially copy constructible and trivially copy assignable. The specialization is invoked based on a check of `is_trivially_copyable` alone, which does not imply both `is_trivially_copy_assignable` and `is_trivially_copy_constructible` are true. [[ https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable | According to the spec ]], a deleted assignment operator does not make `is_trivially_copyable` false. So I think all these properties need to be checked explicitly in order to specialize `OptionalStorage` to the "trivial" version: ``` /// Storage for any type. template <typename T, bool = std::is_trivially_copy_constructible<T>::value && std::is_trivially_copy_assignable<T>::value> class OptionalStorage { ``` Above fixed my build break in MSVC, but I think we need to explicitly check `is_trivially_copy_constructible` too since it might be possible the copy constructor is deleted. Also would be ideal to move over to `std::is_trivially_copyable` instead of the `llvm` namespace verson. Reviewed By: dblaikie Differential Revision: https://reviews.llvm.org/D93510
This commit is contained in:
parent
7ec8f43659
commit
854f0984f0
|
@ -33,7 +33,12 @@ namespace optional_detail {
|
|||
struct in_place_t {};
|
||||
|
||||
/// Storage for any type.
|
||||
template <typename T, bool = is_trivially_copyable<T>::value>
|
||||
template <typename T, bool = (std::is_trivially_copy_constructible<T>::value &&
|
||||
std::is_trivially_copy_assignable<T>::value &&
|
||||
(std::is_trivially_move_constructible<T>::value ||
|
||||
!std::is_move_constructible<T>::value) &&
|
||||
(std::is_trivially_move_assignable<T>::value ||
|
||||
!std::is_move_assignable<T>::value))>
|
||||
class OptionalStorage {
|
||||
union {
|
||||
char empty;
|
||||
|
|
|
@ -390,6 +390,127 @@ TEST(OptionalTest, ImmovableEmplace) {
|
|||
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 <typename Self = NonTCopy>
|
||||
NonTCopy(Self const &Other) : Val(Other.Val) {}
|
||||
|
||||
NonTCopy &operator=(NonTCopy const &) = default;
|
||||
|
||||
int Val{0};
|
||||
};
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1927
|
||||
// Currently only true on recent MSVC releases.
|
||||
static_assert(std::is_trivially_copyable<NonTCopy>::value,
|
||||
"Expect NonTCopy to be trivially copyable");
|
||||
|
||||
static_assert(!std::is_trivially_copy_constructible<NonTCopy>::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
|
||||
// optional_detail::OptionalStorage is chosen.
|
||||
using NonTCopyOptT = Optional<NonTCopy>;
|
||||
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 <typename Self = NonTAssign>
|
||||
NonTAssign &operator=(Self const &Other) {
|
||||
A = Other.A;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int A{0};
|
||||
};
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1927
|
||||
// Currently only true on recent MSVC releases.
|
||||
static_assert(std::is_trivially_copyable<NonTAssign>::value,
|
||||
"Expect NonTAssign to be trivially copyable");
|
||||
|
||||
static_assert(!std::is_trivially_copy_assignable<NonTAssign>::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 NonTAssignOptT = Optional<NonTAssign>;
|
||||
NonTAssignOptT NonTAssign1;
|
||||
|
||||
// Check that the Optional can be copy constructed.
|
||||
NonTAssignOptT NonTAssign2{NonTAssign1};
|
||||
|
||||
// Check that the Optional can be copy assigned.
|
||||
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<NoTMove>;
|
||||
|
||||
NoTMoveOptT NonTMove1;
|
||||
NoTMoveOptT NonTMove2{std::move(NonTMove1)};
|
||||
|
||||
NonTMove1 = std::move(NonTMove2);
|
||||
|
||||
static_assert(
|
||||
std::is_trivially_copyable<NoTMoveOptT>::value,
|
||||
"Expect Optional<NoTMove> to still use the trivial specialization "
|
||||
"of OptionalStorage despite the deleted move constructor / assignment.");
|
||||
}
|
||||
|
||||
#if LLVM_HAS_RVALUE_REFERENCE_THIS
|
||||
|
||||
TEST(OptionalTest, MoveGetValueOr) {
|
||||
|
|
Loading…
Reference in New Issue