mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 10:42:39 +01:00
6207a8299f
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
758 lines
23 KiB
C++
758 lines
23 KiB
C++
//===- llvm/unittest/ADT/OptionalTest.cpp - Optional unit tests -----------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/ADT/Optional.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/StringMap.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "gtest/gtest-spi.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
#include <array>
|
|
|
|
|
|
using namespace llvm;
|
|
|
|
static_assert(std::is_trivially_copyable<Optional<int>>::value,
|
|
"trivially copyable");
|
|
|
|
static_assert(std::is_trivially_copyable<Optional<std::array<int, 3>>>::value,
|
|
"trivially copyable");
|
|
|
|
void OptionalWorksInConstexpr() {
|
|
constexpr auto x1 = Optional<int>();
|
|
constexpr Optional<int> x2{};
|
|
static_assert(!x1.hasValue() && !x2.hasValue(),
|
|
"Default construction and hasValue() are contexpr");
|
|
constexpr auto y1 = Optional<int>(3);
|
|
constexpr Optional<int> y2{3};
|
|
static_assert(y1.getValue() == y2.getValue() && y1.getValue() == 3,
|
|
"Construction with value and getValue() are constexpr");
|
|
static_assert(Optional<int>{3} >= 2 && Optional<int>{1} < Optional<int>{2},
|
|
"Comparisons work in constexpr");
|
|
}
|
|
|
|
namespace {
|
|
|
|
struct NonDefaultConstructible {
|
|
static unsigned CopyConstructions;
|
|
static unsigned Destructions;
|
|
static unsigned CopyAssignments;
|
|
explicit NonDefaultConstructible(int) {
|
|
}
|
|
NonDefaultConstructible(const NonDefaultConstructible&) {
|
|
++CopyConstructions;
|
|
}
|
|
NonDefaultConstructible &operator=(const NonDefaultConstructible&) {
|
|
++CopyAssignments;
|
|
return *this;
|
|
}
|
|
~NonDefaultConstructible() {
|
|
++Destructions;
|
|
}
|
|
static void ResetCounts() {
|
|
CopyConstructions = 0;
|
|
Destructions = 0;
|
|
CopyAssignments = 0;
|
|
}
|
|
};
|
|
|
|
unsigned NonDefaultConstructible::CopyConstructions = 0;
|
|
unsigned NonDefaultConstructible::Destructions = 0;
|
|
unsigned NonDefaultConstructible::CopyAssignments = 0;
|
|
|
|
static_assert(
|
|
!std::is_trivially_copyable<Optional<NonDefaultConstructible>>::value,
|
|
"not trivially copyable");
|
|
|
|
TEST(OptionalTest, NonDefaultConstructibleTest) {
|
|
Optional<NonDefaultConstructible> O;
|
|
EXPECT_FALSE(O);
|
|
}
|
|
|
|
TEST(OptionalTest, ResetTest) {
|
|
NonDefaultConstructible::ResetCounts();
|
|
Optional<NonDefaultConstructible> O(NonDefaultConstructible(3));
|
|
EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);
|
|
EXPECT_EQ(1u, NonDefaultConstructible::Destructions);
|
|
NonDefaultConstructible::ResetCounts();
|
|
O.reset();
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);
|
|
EXPECT_EQ(1u, NonDefaultConstructible::Destructions);
|
|
}
|
|
|
|
TEST(OptionalTest, InitializationLeakTest) {
|
|
NonDefaultConstructible::ResetCounts();
|
|
Optional<NonDefaultConstructible>(NonDefaultConstructible(3));
|
|
EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);
|
|
EXPECT_EQ(2u, NonDefaultConstructible::Destructions);
|
|
}
|
|
|
|
TEST(OptionalTest, CopyConstructionTest) {
|
|
NonDefaultConstructible::ResetCounts();
|
|
{
|
|
Optional<NonDefaultConstructible> A(NonDefaultConstructible(3));
|
|
EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);
|
|
EXPECT_EQ(1u, NonDefaultConstructible::Destructions);
|
|
NonDefaultConstructible::ResetCounts();
|
|
Optional<NonDefaultConstructible> B(A);
|
|
EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::Destructions);
|
|
NonDefaultConstructible::ResetCounts();
|
|
}
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);
|
|
EXPECT_EQ(2u, NonDefaultConstructible::Destructions);
|
|
}
|
|
|
|
TEST(OptionalTest, ConstructingCopyAssignmentTest) {
|
|
NonDefaultConstructible::ResetCounts();
|
|
{
|
|
Optional<NonDefaultConstructible> A(NonDefaultConstructible(3));
|
|
Optional<NonDefaultConstructible> B;
|
|
EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);
|
|
EXPECT_EQ(1u, NonDefaultConstructible::Destructions);
|
|
NonDefaultConstructible::ResetCounts();
|
|
B = A;
|
|
EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::Destructions);
|
|
NonDefaultConstructible::ResetCounts();
|
|
}
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);
|
|
EXPECT_EQ(2u, NonDefaultConstructible::Destructions);
|
|
}
|
|
|
|
TEST(OptionalTest, CopyingCopyAssignmentTest) {
|
|
NonDefaultConstructible::ResetCounts();
|
|
{
|
|
Optional<NonDefaultConstructible> A(NonDefaultConstructible(3));
|
|
Optional<NonDefaultConstructible> B(NonDefaultConstructible(4));
|
|
EXPECT_EQ(2u, NonDefaultConstructible::CopyConstructions);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);
|
|
EXPECT_EQ(2u, NonDefaultConstructible::Destructions);
|
|
NonDefaultConstructible::ResetCounts();
|
|
B = A;
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions);
|
|
EXPECT_EQ(1u, NonDefaultConstructible::CopyAssignments);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::Destructions);
|
|
NonDefaultConstructible::ResetCounts();
|
|
}
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);
|
|
EXPECT_EQ(2u, NonDefaultConstructible::Destructions);
|
|
}
|
|
|
|
TEST(OptionalTest, DeletingCopyAssignmentTest) {
|
|
NonDefaultConstructible::ResetCounts();
|
|
{
|
|
Optional<NonDefaultConstructible> A;
|
|
Optional<NonDefaultConstructible> B(NonDefaultConstructible(3));
|
|
EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);
|
|
EXPECT_EQ(1u, NonDefaultConstructible::Destructions);
|
|
NonDefaultConstructible::ResetCounts();
|
|
B = A;
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);
|
|
EXPECT_EQ(1u, NonDefaultConstructible::Destructions);
|
|
NonDefaultConstructible::ResetCounts();
|
|
}
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::Destructions);
|
|
}
|
|
|
|
TEST(OptionalTest, NullCopyConstructionTest) {
|
|
NonDefaultConstructible::ResetCounts();
|
|
{
|
|
Optional<NonDefaultConstructible> A;
|
|
Optional<NonDefaultConstructible> B;
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::Destructions);
|
|
NonDefaultConstructible::ResetCounts();
|
|
B = A;
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::Destructions);
|
|
NonDefaultConstructible::ResetCounts();
|
|
}
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments);
|
|
EXPECT_EQ(0u, NonDefaultConstructible::Destructions);
|
|
}
|
|
|
|
TEST(OptionalTest, GetValueOr) {
|
|
Optional<int> A;
|
|
EXPECT_EQ(42, A.getValueOr(42));
|
|
|
|
A = 5;
|
|
EXPECT_EQ(5, A.getValueOr(42));
|
|
}
|
|
|
|
struct MultiArgConstructor {
|
|
int x, y;
|
|
MultiArgConstructor(int x, int y) : x(x), y(y) {}
|
|
explicit MultiArgConstructor(int x, bool positive)
|
|
: x(x), y(positive ? x : -x) {}
|
|
|
|
MultiArgConstructor(const MultiArgConstructor &) = delete;
|
|
MultiArgConstructor(MultiArgConstructor &&) = delete;
|
|
MultiArgConstructor &operator=(const MultiArgConstructor &) = delete;
|
|
MultiArgConstructor &operator=(MultiArgConstructor &&) = delete;
|
|
|
|
static unsigned Destructions;
|
|
~MultiArgConstructor() {
|
|
++Destructions;
|
|
}
|
|
static void ResetCounts() {
|
|
Destructions = 0;
|
|
}
|
|
};
|
|
unsigned MultiArgConstructor::Destructions = 0;
|
|
|
|
static_assert(!std::is_trivially_copyable<Optional<MultiArgConstructor>>::value,
|
|
"not trivially copyable");
|
|
|
|
TEST(OptionalTest, Emplace) {
|
|
MultiArgConstructor::ResetCounts();
|
|
Optional<MultiArgConstructor> A;
|
|
|
|
A.emplace(1, 2);
|
|
EXPECT_TRUE(A.hasValue());
|
|
EXPECT_EQ(1, A->x);
|
|
EXPECT_EQ(2, A->y);
|
|
EXPECT_EQ(0u, MultiArgConstructor::Destructions);
|
|
|
|
A.emplace(5, false);
|
|
EXPECT_TRUE(A.hasValue());
|
|
EXPECT_EQ(5, A->x);
|
|
EXPECT_EQ(-5, A->y);
|
|
EXPECT_EQ(1u, MultiArgConstructor::Destructions);
|
|
}
|
|
|
|
struct MoveOnly {
|
|
static unsigned MoveConstructions;
|
|
static unsigned Destructions;
|
|
static unsigned MoveAssignments;
|
|
int val;
|
|
explicit MoveOnly(int val) : val(val) {
|
|
}
|
|
MoveOnly(MoveOnly&& other) {
|
|
val = other.val;
|
|
++MoveConstructions;
|
|
}
|
|
MoveOnly &operator=(MoveOnly&& other) {
|
|
val = other.val;
|
|
++MoveAssignments;
|
|
return *this;
|
|
}
|
|
~MoveOnly() {
|
|
++Destructions;
|
|
}
|
|
static void ResetCounts() {
|
|
MoveConstructions = 0;
|
|
Destructions = 0;
|
|
MoveAssignments = 0;
|
|
}
|
|
};
|
|
|
|
unsigned MoveOnly::MoveConstructions = 0;
|
|
unsigned MoveOnly::Destructions = 0;
|
|
unsigned MoveOnly::MoveAssignments = 0;
|
|
|
|
static_assert(!std::is_trivially_copyable<Optional<MoveOnly>>::value,
|
|
"not trivially copyable");
|
|
|
|
TEST(OptionalTest, MoveOnlyNull) {
|
|
MoveOnly::ResetCounts();
|
|
Optional<MoveOnly> O;
|
|
EXPECT_EQ(0u, MoveOnly::MoveConstructions);
|
|
EXPECT_EQ(0u, MoveOnly::MoveAssignments);
|
|
EXPECT_EQ(0u, MoveOnly::Destructions);
|
|
}
|
|
|
|
TEST(OptionalTest, MoveOnlyConstruction) {
|
|
MoveOnly::ResetCounts();
|
|
Optional<MoveOnly> O(MoveOnly(3));
|
|
EXPECT_TRUE((bool)O);
|
|
EXPECT_EQ(3, O->val);
|
|
EXPECT_EQ(1u, MoveOnly::MoveConstructions);
|
|
EXPECT_EQ(0u, MoveOnly::MoveAssignments);
|
|
EXPECT_EQ(1u, MoveOnly::Destructions);
|
|
}
|
|
|
|
TEST(OptionalTest, MoveOnlyMoveConstruction) {
|
|
Optional<MoveOnly> A(MoveOnly(3));
|
|
MoveOnly::ResetCounts();
|
|
Optional<MoveOnly> B(std::move(A));
|
|
EXPECT_TRUE((bool)A);
|
|
EXPECT_TRUE((bool)B);
|
|
EXPECT_EQ(3, B->val);
|
|
EXPECT_EQ(1u, MoveOnly::MoveConstructions);
|
|
EXPECT_EQ(0u, MoveOnly::MoveAssignments);
|
|
EXPECT_EQ(0u, MoveOnly::Destructions);
|
|
}
|
|
|
|
TEST(OptionalTest, MoveOnlyAssignment) {
|
|
MoveOnly::ResetCounts();
|
|
Optional<MoveOnly> O;
|
|
O = MoveOnly(3);
|
|
EXPECT_TRUE((bool)O);
|
|
EXPECT_EQ(3, O->val);
|
|
EXPECT_EQ(1u, MoveOnly::MoveConstructions);
|
|
EXPECT_EQ(0u, MoveOnly::MoveAssignments);
|
|
EXPECT_EQ(1u, MoveOnly::Destructions);
|
|
}
|
|
|
|
TEST(OptionalTest, MoveOnlyInitializingAssignment) {
|
|
Optional<MoveOnly> A(MoveOnly(3));
|
|
Optional<MoveOnly> B;
|
|
MoveOnly::ResetCounts();
|
|
B = std::move(A);
|
|
EXPECT_TRUE((bool)A);
|
|
EXPECT_TRUE((bool)B);
|
|
EXPECT_EQ(3, B->val);
|
|
EXPECT_EQ(1u, MoveOnly::MoveConstructions);
|
|
EXPECT_EQ(0u, MoveOnly::MoveAssignments);
|
|
EXPECT_EQ(0u, MoveOnly::Destructions);
|
|
}
|
|
|
|
TEST(OptionalTest, MoveOnlyNullingAssignment) {
|
|
Optional<MoveOnly> A;
|
|
Optional<MoveOnly> B(MoveOnly(3));
|
|
MoveOnly::ResetCounts();
|
|
B = std::move(A);
|
|
EXPECT_FALSE((bool)A);
|
|
EXPECT_FALSE((bool)B);
|
|
EXPECT_EQ(0u, MoveOnly::MoveConstructions);
|
|
EXPECT_EQ(0u, MoveOnly::MoveAssignments);
|
|
EXPECT_EQ(1u, MoveOnly::Destructions);
|
|
}
|
|
|
|
TEST(OptionalTest, MoveOnlyAssigningAssignment) {
|
|
Optional<MoveOnly> A(MoveOnly(3));
|
|
Optional<MoveOnly> B(MoveOnly(4));
|
|
MoveOnly::ResetCounts();
|
|
B = std::move(A);
|
|
EXPECT_TRUE((bool)A);
|
|
EXPECT_TRUE((bool)B);
|
|
EXPECT_EQ(3, B->val);
|
|
EXPECT_EQ(0u, MoveOnly::MoveConstructions);
|
|
EXPECT_EQ(1u, MoveOnly::MoveAssignments);
|
|
EXPECT_EQ(0u, MoveOnly::Destructions);
|
|
}
|
|
|
|
struct Immovable {
|
|
static unsigned Constructions;
|
|
static unsigned Destructions;
|
|
int val;
|
|
explicit Immovable(int val) : val(val) {
|
|
++Constructions;
|
|
}
|
|
~Immovable() {
|
|
++Destructions;
|
|
}
|
|
static void ResetCounts() {
|
|
Constructions = 0;
|
|
Destructions = 0;
|
|
}
|
|
private:
|
|
// This should disable all move/copy operations.
|
|
Immovable(Immovable&& other) = delete;
|
|
};
|
|
|
|
unsigned Immovable::Constructions = 0;
|
|
unsigned Immovable::Destructions = 0;
|
|
|
|
static_assert(!std::is_trivially_copyable<Optional<Immovable>>::value,
|
|
"not trivially copyable");
|
|
|
|
TEST(OptionalTest, ImmovableEmplace) {
|
|
Optional<Immovable> A;
|
|
Immovable::ResetCounts();
|
|
A.emplace(4);
|
|
EXPECT_TRUE((bool)A);
|
|
EXPECT_EQ(4, A->val);
|
|
EXPECT_EQ(1u, Immovable::Constructions);
|
|
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 && !defined(__clang__)
|
|
// 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 && !defined(__clang__)
|
|
// 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.");
|
|
}
|
|
|
|
class NoCopyStringMap {
|
|
public:
|
|
NoCopyStringMap() = default;
|
|
|
|
private:
|
|
llvm::StringMap<std::unique_ptr<int>> Map;
|
|
};
|
|
|
|
TEST(OptionalTest, DeletedCopyStringMap) {
|
|
// Old versions of gcc (7.3 and prior) instantiate the copy constructor when
|
|
// std::is_trivially_copyable is instantiated. This test will fail
|
|
// compilation if std::is_trivially_copyable is used in the OptionalStorage
|
|
// specialization condition by gcc <= 7.3.
|
|
Optional<NoCopyStringMap> TestInstantiation;
|
|
}
|
|
|
|
#if LLVM_HAS_RVALUE_REFERENCE_THIS
|
|
|
|
TEST(OptionalTest, MoveGetValueOr) {
|
|
Optional<MoveOnly> A;
|
|
|
|
MoveOnly::ResetCounts();
|
|
EXPECT_EQ(42, std::move(A).getValueOr(MoveOnly(42)).val);
|
|
EXPECT_EQ(1u, MoveOnly::MoveConstructions);
|
|
EXPECT_EQ(0u, MoveOnly::MoveAssignments);
|
|
EXPECT_EQ(2u, MoveOnly::Destructions);
|
|
|
|
A = MoveOnly(5);
|
|
MoveOnly::ResetCounts();
|
|
EXPECT_EQ(5, std::move(A).getValueOr(MoveOnly(42)).val);
|
|
EXPECT_EQ(1u, MoveOnly::MoveConstructions);
|
|
EXPECT_EQ(0u, MoveOnly::MoveAssignments);
|
|
EXPECT_EQ(2u, MoveOnly::Destructions);
|
|
}
|
|
|
|
#endif // LLVM_HAS_RVALUE_REFERENCE_THIS
|
|
|
|
struct EqualTo {
|
|
template <typename T, typename U> static bool apply(const T &X, const U &Y) {
|
|
return X == Y;
|
|
}
|
|
};
|
|
|
|
struct NotEqualTo {
|
|
template <typename T, typename U> static bool apply(const T &X, const U &Y) {
|
|
return X != Y;
|
|
}
|
|
};
|
|
|
|
struct Less {
|
|
template <typename T, typename U> static bool apply(const T &X, const U &Y) {
|
|
return X < Y;
|
|
}
|
|
};
|
|
|
|
struct Greater {
|
|
template <typename T, typename U> static bool apply(const T &X, const U &Y) {
|
|
return X > Y;
|
|
}
|
|
};
|
|
|
|
struct LessEqual {
|
|
template <typename T, typename U> static bool apply(const T &X, const U &Y) {
|
|
return X <= Y;
|
|
}
|
|
};
|
|
|
|
struct GreaterEqual {
|
|
template <typename T, typename U> static bool apply(const T &X, const U &Y) {
|
|
return X >= Y;
|
|
}
|
|
};
|
|
|
|
template <typename OperatorT, typename T>
|
|
void CheckRelation(const Optional<T> &Lhs, const Optional<T> &Rhs,
|
|
bool Expected) {
|
|
EXPECT_EQ(Expected, OperatorT::apply(Lhs, Rhs));
|
|
|
|
if (Lhs)
|
|
EXPECT_EQ(Expected, OperatorT::apply(*Lhs, Rhs));
|
|
else
|
|
EXPECT_EQ(Expected, OperatorT::apply(None, Rhs));
|
|
|
|
if (Rhs)
|
|
EXPECT_EQ(Expected, OperatorT::apply(Lhs, *Rhs));
|
|
else
|
|
EXPECT_EQ(Expected, OperatorT::apply(Lhs, None));
|
|
}
|
|
|
|
struct EqualityMock {};
|
|
const Optional<EqualityMock> NoneEq, EqualityLhs((EqualityMock())),
|
|
EqualityRhs((EqualityMock()));
|
|
bool IsEqual;
|
|
|
|
bool operator==(const EqualityMock &Lhs, const EqualityMock &Rhs) {
|
|
EXPECT_EQ(&*EqualityLhs, &Lhs);
|
|
EXPECT_EQ(&*EqualityRhs, &Rhs);
|
|
return IsEqual;
|
|
}
|
|
|
|
TEST(OptionalTest, OperatorEqual) {
|
|
CheckRelation<EqualTo>(NoneEq, NoneEq, true);
|
|
CheckRelation<EqualTo>(NoneEq, EqualityRhs, false);
|
|
CheckRelation<EqualTo>(EqualityLhs, NoneEq, false);
|
|
|
|
IsEqual = false;
|
|
CheckRelation<EqualTo>(EqualityLhs, EqualityRhs, IsEqual);
|
|
IsEqual = true;
|
|
CheckRelation<EqualTo>(EqualityLhs, EqualityRhs, IsEqual);
|
|
}
|
|
|
|
TEST(OptionalTest, OperatorNotEqual) {
|
|
CheckRelation<NotEqualTo>(NoneEq, NoneEq, false);
|
|
CheckRelation<NotEqualTo>(NoneEq, EqualityRhs, true);
|
|
CheckRelation<NotEqualTo>(EqualityLhs, NoneEq, true);
|
|
|
|
IsEqual = false;
|
|
CheckRelation<NotEqualTo>(EqualityLhs, EqualityRhs, !IsEqual);
|
|
IsEqual = true;
|
|
CheckRelation<NotEqualTo>(EqualityLhs, EqualityRhs, !IsEqual);
|
|
}
|
|
|
|
struct InequalityMock {};
|
|
const Optional<InequalityMock> NoneIneq, InequalityLhs((InequalityMock())),
|
|
InequalityRhs((InequalityMock()));
|
|
bool IsLess;
|
|
|
|
bool operator<(const InequalityMock &Lhs, const InequalityMock &Rhs) {
|
|
EXPECT_EQ(&*InequalityLhs, &Lhs);
|
|
EXPECT_EQ(&*InequalityRhs, &Rhs);
|
|
return IsLess;
|
|
}
|
|
|
|
TEST(OptionalTest, OperatorLess) {
|
|
CheckRelation<Less>(NoneIneq, NoneIneq, false);
|
|
CheckRelation<Less>(NoneIneq, InequalityRhs, true);
|
|
CheckRelation<Less>(InequalityLhs, NoneIneq, false);
|
|
|
|
IsLess = false;
|
|
CheckRelation<Less>(InequalityLhs, InequalityRhs, IsLess);
|
|
IsLess = true;
|
|
CheckRelation<Less>(InequalityLhs, InequalityRhs, IsLess);
|
|
}
|
|
|
|
TEST(OptionalTest, OperatorGreater) {
|
|
CheckRelation<Greater>(NoneIneq, NoneIneq, false);
|
|
CheckRelation<Greater>(NoneIneq, InequalityRhs, false);
|
|
CheckRelation<Greater>(InequalityLhs, NoneIneq, true);
|
|
|
|
IsLess = false;
|
|
CheckRelation<Greater>(InequalityRhs, InequalityLhs, IsLess);
|
|
IsLess = true;
|
|
CheckRelation<Greater>(InequalityRhs, InequalityLhs, IsLess);
|
|
}
|
|
|
|
TEST(OptionalTest, OperatorLessEqual) {
|
|
CheckRelation<LessEqual>(NoneIneq, NoneIneq, true);
|
|
CheckRelation<LessEqual>(NoneIneq, InequalityRhs, true);
|
|
CheckRelation<LessEqual>(InequalityLhs, NoneIneq, false);
|
|
|
|
IsLess = false;
|
|
CheckRelation<LessEqual>(InequalityRhs, InequalityLhs, !IsLess);
|
|
IsLess = true;
|
|
CheckRelation<LessEqual>(InequalityRhs, InequalityLhs, !IsLess);
|
|
}
|
|
|
|
TEST(OptionalTest, OperatorGreaterEqual) {
|
|
CheckRelation<GreaterEqual>(NoneIneq, NoneIneq, true);
|
|
CheckRelation<GreaterEqual>(NoneIneq, InequalityRhs, false);
|
|
CheckRelation<GreaterEqual>(InequalityLhs, NoneIneq, true);
|
|
|
|
IsLess = false;
|
|
CheckRelation<GreaterEqual>(InequalityLhs, InequalityRhs, !IsLess);
|
|
IsLess = true;
|
|
CheckRelation<GreaterEqual>(InequalityLhs, InequalityRhs, !IsLess);
|
|
}
|
|
|
|
struct ComparableAndStreamable {
|
|
friend bool operator==(ComparableAndStreamable,
|
|
ComparableAndStreamable) LLVM_ATTRIBUTE_USED {
|
|
return true;
|
|
}
|
|
|
|
friend raw_ostream &operator<<(raw_ostream &OS, ComparableAndStreamable) {
|
|
return OS << "ComparableAndStreamable";
|
|
}
|
|
|
|
static Optional<ComparableAndStreamable> get() {
|
|
return ComparableAndStreamable();
|
|
}
|
|
};
|
|
|
|
TEST(OptionalTest, StreamOperator) {
|
|
auto to_string = [](Optional<ComparableAndStreamable> O) {
|
|
SmallString<16> S;
|
|
raw_svector_ostream OS(S);
|
|
OS << O;
|
|
return S;
|
|
};
|
|
EXPECT_EQ("ComparableAndStreamable",
|
|
to_string(ComparableAndStreamable::get()));
|
|
EXPECT_EQ("None", to_string(None));
|
|
}
|
|
|
|
struct Comparable {
|
|
friend bool operator==(Comparable, Comparable) LLVM_ATTRIBUTE_USED {
|
|
return true;
|
|
}
|
|
static Optional<Comparable> get() { return Comparable(); }
|
|
};
|
|
|
|
TEST(OptionalTest, UseInUnitTests) {
|
|
// Test that we invoke the streaming operators when pretty-printing values in
|
|
// EXPECT macros.
|
|
EXPECT_NONFATAL_FAILURE(EXPECT_EQ(llvm::None, ComparableAndStreamable::get()),
|
|
"Expected: llvm::None\n"
|
|
" Which is: None\n"
|
|
"To be equal to: ComparableAndStreamable::get()\n"
|
|
" Which is: ComparableAndStreamable");
|
|
|
|
// Test that it is still possible to compare objects which do not have a
|
|
// custom streaming operator.
|
|
EXPECT_NONFATAL_FAILURE(EXPECT_EQ(llvm::None, Comparable::get()), "object");
|
|
}
|
|
|
|
TEST(OptionalTest, HashValue) {
|
|
// Check that None, false, and true all hash differently.
|
|
Optional<bool> B, B0 = false, B1 = true;
|
|
EXPECT_NE(hash_value(B0), hash_value(B));
|
|
EXPECT_NE(hash_value(B1), hash_value(B));
|
|
EXPECT_NE(hash_value(B1), hash_value(B0));
|
|
|
|
// Check that None, 0, and 1 all hash differently.
|
|
Optional<int> I, I0 = 0, I1 = 1;
|
|
EXPECT_NE(hash_value(I0), hash_value(I));
|
|
EXPECT_NE(hash_value(I1), hash_value(I));
|
|
EXPECT_NE(hash_value(I1), hash_value(I0));
|
|
|
|
// Check None hash the same way regardless of type.
|
|
EXPECT_EQ(hash_value(B), hash_value(I));
|
|
}
|
|
|
|
} // end anonymous namespace
|