From 9fbfd20dbd144fd10e1dc4ff63a31999bfb34b71 Mon Sep 17 00:00:00 2001 From: Simon Truscott Date: Sun, 17 Oct 2021 07:22:43 +1100 Subject: [PATCH 1/9] Add is-nothrow-move type trait tests --- tests/constructors.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/constructors.cpp b/tests/constructors.cpp index df168ba..1138e8a 100644 --- a/tests/constructors.cpp +++ b/tests/constructors.cpp @@ -73,6 +73,8 @@ TEST_CASE("Constructors", "[constructors]") { # if !defined(TL_EXPECTED_GCC49) REQUIRE(std::is_trivially_move_constructible::value); REQUIRE(std::is_trivially_move_assignable::value); + REQUIRE(std::is_nothrow_move_constructible::value); + REQUIRE(std::is_nothrow_move_assignable::value); # endif } @@ -88,6 +90,8 @@ TEST_CASE("Constructors", "[constructors]") { # if !defined(TL_EXPECTED_GCC49) REQUIRE(!std::is_trivially_move_constructible::value); REQUIRE(!std::is_trivially_move_assignable::value); + REQUIRE(std::is_nothrow_move_constructible::value); + REQUIRE(std::is_nothrow_move_assignable::value); # endif } @@ -103,6 +107,8 @@ TEST_CASE("Constructors", "[constructors]") { # if !defined(TL_EXPECTED_GCC49) REQUIRE(!std::is_trivially_move_constructible::value); REQUIRE(!std::is_trivially_move_assignable::value); + REQUIRE(std::is_nothrow_move_constructible::value); + REQUIRE(std::is_nothrow_move_assignable::value); # endif } @@ -118,6 +124,8 @@ TEST_CASE("Constructors", "[constructors]") { # if !defined(TL_EXPECTED_GCC49) REQUIRE(!std::is_trivially_move_constructible::value); REQUIRE(!std::is_trivially_move_assignable::value); + REQUIRE(std::is_nothrow_move_constructible::value); + REQUIRE(std::is_nothrow_move_assignable::value); # endif } From af2523bccca92678d88e333495c915923c5d4885 Mon Sep 17 00:00:00 2001 From: Simon Truscott Date: Sun, 17 Oct 2021 07:31:31 +1100 Subject: [PATCH 2/9] Add type trait tests for when T is void --- tests/constructors.cpp | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/constructors.cpp b/tests/constructors.cpp index 1138e8a..458bf31 100644 --- a/tests/constructors.cpp +++ b/tests/constructors.cpp @@ -139,4 +139,38 @@ TEST_CASE("Constructors", "[constructors]") { REQUIRE(!e); REQUIRE(e.error() == 42); } + + { + tl::expected e; + REQUIRE(std::is_default_constructible::value); + REQUIRE(std::is_copy_constructible::value); + REQUIRE(std::is_move_constructible::value); + REQUIRE(std::is_copy_assignable::value); + REQUIRE(std::is_move_assignable::value); + REQUIRE(TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value); + REQUIRE(TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value); +# if !defined(TL_EXPECTED_GCC49) + REQUIRE(std::is_trivially_move_constructible::value); + REQUIRE(std::is_trivially_move_assignable::value); + REQUIRE(std::is_nothrow_move_constructible::value); + REQUIRE(std::is_nothrow_move_assignable::value); +# endif + } + + { + tl::expected e; + REQUIRE(std::is_default_constructible::value); + REQUIRE(std::is_copy_constructible::value); + REQUIRE(std::is_move_constructible::value); + REQUIRE(std::is_copy_assignable::value); + REQUIRE(std::is_move_assignable::value); + REQUIRE(!TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value); + REQUIRE(!TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value); +# if !defined(TL_EXPECTED_GCC49) + REQUIRE(!std::is_trivially_move_constructible::value); + REQUIRE(!std::is_trivially_move_assignable::value); + REQUIRE(!std::is_nothrow_move_constructible::value); + REQUIRE(!std::is_nothrow_move_assignable::value); +# endif + } } From 17f2c9348c809acb34ac445b62697feda0d6bcbe Mon Sep 17 00:00:00 2001 From: Simon Truscott Date: Sun, 17 Oct 2021 07:36:46 +1100 Subject: [PATCH 3/9] Fix noexcept specifier expression in expected_move_base T being void should not prevent the move constructor from being declared noexcept. --- include/tl/expected.hpp | 2 +- tests/constructors.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/tl/expected.hpp b/include/tl/expected.hpp index 31b130a..dc86251 100644 --- a/include/tl/expected.hpp +++ b/include/tl/expected.hpp @@ -951,7 +951,7 @@ struct expected_move_base : expected_copy_base { expected_move_base(const expected_move_base &rhs) = default; expected_move_base(expected_move_base &&rhs) noexcept( - std::is_nothrow_move_constructible::value) + is_void_or>::value) : expected_copy_base(no_init) { if (rhs.has_value()) { this->construct_with(std::move(rhs)); diff --git a/tests/constructors.cpp b/tests/constructors.cpp index 458bf31..e2b99f9 100644 --- a/tests/constructors.cpp +++ b/tests/constructors.cpp @@ -169,7 +169,7 @@ TEST_CASE("Constructors", "[constructors]") { # if !defined(TL_EXPECTED_GCC49) REQUIRE(!std::is_trivially_move_constructible::value); REQUIRE(!std::is_trivially_move_assignable::value); - REQUIRE(!std::is_nothrow_move_constructible::value); + REQUIRE(std::is_nothrow_move_constructible::value); REQUIRE(!std::is_nothrow_move_assignable::value); # endif } From 3b2a7db07f87bc4f88075fcb51228380e9f782ca Mon Sep 17 00:00:00 2001 From: Simon Truscott Date: Sun, 17 Oct 2021 07:49:23 +1100 Subject: [PATCH 4/9] Fix noexcept specifier expression in expected_move_assign_base T being void should not prevent the move assignment operator from being declared noexcept. --- include/tl/expected.hpp | 4 ++-- tests/constructors.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/tl/expected.hpp b/include/tl/expected.hpp index dc86251..c06b684 100644 --- a/include/tl/expected.hpp +++ b/include/tl/expected.hpp @@ -1028,8 +1028,8 @@ struct expected_move_assign_base expected_move_assign_base & operator=(expected_move_assign_base &&rhs) noexcept( - std::is_nothrow_move_constructible::value - &&std::is_nothrow_move_assignable::value) { + is_void_or, + std::is_nothrow_move_assignable>>::value) { this->assign(std::move(rhs)); return *this; } diff --git a/tests/constructors.cpp b/tests/constructors.cpp index e2b99f9..2198a39 100644 --- a/tests/constructors.cpp +++ b/tests/constructors.cpp @@ -170,7 +170,7 @@ TEST_CASE("Constructors", "[constructors]") { REQUIRE(!std::is_trivially_move_constructible::value); REQUIRE(!std::is_trivially_move_assignable::value); REQUIRE(std::is_nothrow_move_constructible::value); - REQUIRE(!std::is_nothrow_move_assignable::value); + REQUIRE(std::is_nothrow_move_assignable::value); # endif } } From 84307ddd5af85b8944ce8f88fdb921042217daa0 Mon Sep 17 00:00:00 2001 From: Simon Truscott Date: Sun, 17 Oct 2021 08:06:54 +1100 Subject: [PATCH 5/9] Add type trait tests for when moving T or E can throw --- tests/constructors.cpp | 77 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/tests/constructors.cpp b/tests/constructors.cpp index 2198a39..9cc4878 100644 --- a/tests/constructors.cpp +++ b/tests/constructors.cpp @@ -13,6 +13,15 @@ struct takes_init_and_variadic { : v(l), t(std::forward(args)...) {} }; +struct canthrow_move { + canthrow_move() = default; + canthrow_move(const canthrow_move &) = default; + canthrow_move & operator=(const canthrow_move &) = default; + + canthrow_move(canthrow_move &&) noexcept(false) {} + canthrow_move & operator=(canthrow_move &&) noexcept(false) { return *this; } +}; + TEST_CASE("Constructors", "[constructors]") { { tl::expected e; @@ -129,6 +138,57 @@ TEST_CASE("Constructors", "[constructors]") { # endif } + { + tl::expected e; + REQUIRE(std::is_default_constructible::value); + REQUIRE(std::is_copy_constructible::value); + REQUIRE(std::is_move_constructible::value); + REQUIRE(std::is_copy_assignable::value); + REQUIRE(std::is_move_assignable::value); + REQUIRE(TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value); + REQUIRE(TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value); +# if !defined(TL_EXPECTED_GCC49) + REQUIRE(!std::is_trivially_move_constructible::value); + REQUIRE(!std::is_trivially_move_assignable::value); + REQUIRE(std::is_nothrow_move_constructible::value); + REQUIRE(std::is_nothrow_move_assignable::value); +# endif + } + + { + tl::expected e; + REQUIRE(std::is_default_constructible::value); + REQUIRE(std::is_copy_constructible::value); + REQUIRE(std::is_move_constructible::value); + REQUIRE(std::is_copy_assignable::value); + REQUIRE(std::is_move_assignable::value); + REQUIRE(TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value); + REQUIRE(TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value); +# if !defined(TL_EXPECTED_GCC49) + REQUIRE(!std::is_trivially_move_constructible::value); + REQUIRE(!std::is_trivially_move_assignable::value); + REQUIRE(!std::is_nothrow_move_constructible::value); + REQUIRE(!std::is_nothrow_move_assignable::value); +# endif + } + + { + tl::expected e; + REQUIRE(std::is_default_constructible::value); + REQUIRE(std::is_copy_constructible::value); + REQUIRE(std::is_move_constructible::value); + REQUIRE(std::is_copy_assignable::value); + REQUIRE(std::is_move_assignable::value); + REQUIRE(TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value); + REQUIRE(TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value); +# if !defined(TL_EXPECTED_GCC49) + REQUIRE(!std::is_trivially_move_constructible::value); + REQUIRE(!std::is_trivially_move_assignable::value); + REQUIRE(!std::is_nothrow_move_constructible::value); + REQUIRE(!std::is_nothrow_move_assignable::value); +# endif + } + { tl::expected e; REQUIRE(e); @@ -171,6 +231,23 @@ TEST_CASE("Constructors", "[constructors]") { REQUIRE(!std::is_trivially_move_assignable::value); REQUIRE(std::is_nothrow_move_constructible::value); REQUIRE(std::is_nothrow_move_assignable::value); +# endif + } + + { + tl::expected e; + REQUIRE(std::is_default_constructible::value); + REQUIRE(std::is_copy_constructible::value); + REQUIRE(std::is_move_constructible::value); + REQUIRE(std::is_copy_assignable::value); + REQUIRE(std::is_move_assignable::value); + REQUIRE(TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value); + REQUIRE(TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value); +# if !defined(TL_EXPECTED_GCC49) + REQUIRE(!std::is_trivially_move_constructible::value); + REQUIRE(!std::is_trivially_move_assignable::value); + REQUIRE(std::is_nothrow_move_constructible::value); + REQUIRE(std::is_nothrow_move_assignable::value); # endif } } From 92f430fef892308cbb1631a01a0343854a1f5359 Mon Sep 17 00:00:00 2001 From: Simon Truscott Date: Sun, 17 Oct 2021 08:43:25 +1100 Subject: [PATCH 6/9] Fix noexcept specifier expression in expected_move_base The move constructor should not be declared noexcept if E is not nothrow-move-constructible. --- include/tl/expected.hpp | 3 ++- tests/constructors.cpp | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/tl/expected.hpp b/include/tl/expected.hpp index c06b684..db32955 100644 --- a/include/tl/expected.hpp +++ b/include/tl/expected.hpp @@ -951,7 +951,8 @@ struct expected_move_base : expected_copy_base { expected_move_base(const expected_move_base &rhs) = default; expected_move_base(expected_move_base &&rhs) noexcept( - is_void_or>::value) + is_void_or>::value + &&std::is_nothrow_move_constructible::value) : expected_copy_base(no_init) { if (rhs.has_value()) { this->construct_with(std::move(rhs)); diff --git a/tests/constructors.cpp b/tests/constructors.cpp index 9cc4878..5e2b4be 100644 --- a/tests/constructors.cpp +++ b/tests/constructors.cpp @@ -150,7 +150,7 @@ TEST_CASE("Constructors", "[constructors]") { # if !defined(TL_EXPECTED_GCC49) REQUIRE(!std::is_trivially_move_constructible::value); REQUIRE(!std::is_trivially_move_assignable::value); - REQUIRE(std::is_nothrow_move_constructible::value); + REQUIRE(!std::is_nothrow_move_constructible::value); REQUIRE(std::is_nothrow_move_assignable::value); # endif } @@ -246,7 +246,7 @@ TEST_CASE("Constructors", "[constructors]") { # if !defined(TL_EXPECTED_GCC49) REQUIRE(!std::is_trivially_move_constructible::value); REQUIRE(!std::is_trivially_move_assignable::value); - REQUIRE(std::is_nothrow_move_constructible::value); + REQUIRE(!std::is_nothrow_move_constructible::value); REQUIRE(std::is_nothrow_move_assignable::value); # endif } From 0707b46c3876f4252c7eb30a5e59e9a21a019d93 Mon Sep 17 00:00:00 2001 From: Simon Truscott Date: Sun, 17 Oct 2021 08:53:23 +1100 Subject: [PATCH 7/9] Fix noexcept specifier expression in expected_move_assign_base The move assignment operator should not be declared noexcept if E is not nothrow-move-constructible or is not nothrow-move-assignable. --- include/tl/expected.hpp | 4 +++- tests/constructors.cpp | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/tl/expected.hpp b/include/tl/expected.hpp index db32955..222bb20 100644 --- a/include/tl/expected.hpp +++ b/include/tl/expected.hpp @@ -1030,7 +1030,9 @@ struct expected_move_assign_base expected_move_assign_base & operator=(expected_move_assign_base &&rhs) noexcept( is_void_or, - std::is_nothrow_move_assignable>>::value) { + std::is_nothrow_move_assignable>>::value + &&std::is_nothrow_move_constructible::value + &&std::is_nothrow_move_assignable::value) { this->assign(std::move(rhs)); return *this; } diff --git a/tests/constructors.cpp b/tests/constructors.cpp index 5e2b4be..07fb8fb 100644 --- a/tests/constructors.cpp +++ b/tests/constructors.cpp @@ -151,7 +151,7 @@ TEST_CASE("Constructors", "[constructors]") { REQUIRE(!std::is_trivially_move_constructible::value); REQUIRE(!std::is_trivially_move_assignable::value); REQUIRE(!std::is_nothrow_move_constructible::value); - REQUIRE(std::is_nothrow_move_assignable::value); + REQUIRE(!std::is_nothrow_move_assignable::value); # endif } @@ -247,7 +247,7 @@ TEST_CASE("Constructors", "[constructors]") { REQUIRE(!std::is_trivially_move_constructible::value); REQUIRE(!std::is_trivially_move_assignable::value); REQUIRE(!std::is_nothrow_move_constructible::value); - REQUIRE(std::is_nothrow_move_assignable::value); + REQUIRE(!std::is_nothrow_move_assignable::value); # endif } } From 313eca59638a9277a8f21a6656742c89a848251d Mon Sep 17 00:00:00 2001 From: Simon Truscott Date: Sun, 17 Oct 2021 10:49:33 +1100 Subject: [PATCH 8/9] Apply clang-format Was not applied to the change made in commit 96d547c03d2feab8db64c53c3744a9b4a7c8f2c5 --- include/tl/expected.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/tl/expected.hpp b/include/tl/expected.hpp index 222bb20..a25e71e 100644 --- a/include/tl/expected.hpp +++ b/include/tl/expected.hpp @@ -1223,8 +1223,9 @@ class expected : private detail::expected_move_assign_base, "T must not be in_place_t"); static_assert(!std::is_same::type>::value, "T must not be unexpect_t"); - static_assert(!std::is_same>::type>::value, - "T must not be unexpected"); + static_assert( + !std::is_same>::type>::value, + "T must not be unexpected"); static_assert(!std::is_reference::value, "E must not be a reference"); T *valptr() { return std::addressof(this->m_val); } From 385ef63d368b474111859be8ef5b2dca04a19ad6 Mon Sep 17 00:00:00 2001 From: Simon Truscott Date: Sun, 24 Oct 2021 14:43:06 +1100 Subject: [PATCH 9/9] Tweak test class Remove noexcept(false) specifier from move assignment operator. This is how the other can-throw-move test classes are defined. And a non-noexcept move assignment operator is not required for the current tests. --- tests/constructors.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/constructors.cpp b/tests/constructors.cpp index 07fb8fb..0457cbc 100644 --- a/tests/constructors.cpp +++ b/tests/constructors.cpp @@ -19,7 +19,7 @@ struct canthrow_move { canthrow_move & operator=(const canthrow_move &) = default; canthrow_move(canthrow_move &&) noexcept(false) {} - canthrow_move & operator=(canthrow_move &&) noexcept(false) { return *this; } + canthrow_move & operator=(canthrow_move &&) = default; }; TEST_CASE("Constructors", "[constructors]") {