From 27d70246aec3253141e617ddd7cd88bd9e078e5c Mon Sep 17 00:00:00 2001 From: Omar Saber Date: Sat, 4 Dec 2021 18:45:02 +0200 Subject: [PATCH 1/5] implemented the proposed resolution for LWG-2762 --- stl/inc/memory | 2 +- stl/inc/optional | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/stl/inc/memory b/stl/inc/memory index 8de1c1561f2..2c0bb9d5e31 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -3242,7 +3242,7 @@ public: return _Mypair._Get_first(); } - _NODISCARD add_lvalue_reference_t<_Ty> operator*() const noexcept /* strengthened */ { + _NODISCARD add_lvalue_reference_t<_Ty> operator*() const noexcept(noexcept(*declval())) { return *_Mypair._Myval2; } diff --git a/stl/inc/optional b/stl/inc/optional index f0fd6f996c1..b1f2dac53b6 100644 --- a/stl/inc/optional +++ b/stl/inc/optional @@ -342,38 +342,38 @@ public: } } - _NODISCARD constexpr const _Ty* operator->() const { + _NODISCARD constexpr const _Ty* operator->() const noexcept { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(this->_Has_value, "Cannot access value of empty optional"); #endif // _CONTAINER_DEBUG_LEVEL > 0 return _STD addressof(this->_Value); } - _NODISCARD constexpr _Ty* operator->() { + _NODISCARD constexpr _Ty* operator->() noexcept { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(this->_Has_value, "Cannot access value of empty optional"); #endif // _CONTAINER_DEBUG_LEVEL > 0 return _STD addressof(this->_Value); } - _NODISCARD constexpr const _Ty& operator*() const& { + _NODISCARD constexpr const _Ty& operator*() const& noexcept { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(this->_Has_value, "Cannot access value of empty optional"); #endif // _CONTAINER_DEBUG_LEVEL > 0 return this->_Value; } - _NODISCARD constexpr _Ty& operator*() & { + _NODISCARD constexpr _Ty& operator*() & noexcept { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(this->_Has_value, "Cannot access value of empty optional"); #endif // _CONTAINER_DEBUG_LEVEL > 0 return this->_Value; } - _NODISCARD constexpr _Ty&& operator*() && { + _NODISCARD constexpr _Ty&& operator*() && noexcept { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(this->_Has_value, "Cannot access value of empty optional"); #endif // _CONTAINER_DEBUG_LEVEL > 0 return _STD move(this->_Value); } - _NODISCARD constexpr const _Ty&& operator*() const&& { + _NODISCARD constexpr const _Ty&& operator*() const&& noexcept { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(this->_Has_value, "Cannot access value of empty optional"); #endif // _CONTAINER_DEBUG_LEVEL > 0 From 6047aadeeadc7df87038573226eb3a224392da1f Mon Sep 17 00:00:00 2001 From: Omar Saber Date: Sun, 5 Dec 2021 01:36:45 +0200 Subject: [PATCH 2/5] fully-qualified declval to avoid problems with ADL --- stl/inc/memory | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/memory b/stl/inc/memory index 2c0bb9d5e31..00e5fa3f9fb 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -3242,7 +3242,7 @@ public: return _Mypair._Get_first(); } - _NODISCARD add_lvalue_reference_t<_Ty> operator*() const noexcept(noexcept(*declval())) { + _NODISCARD add_lvalue_reference_t<_Ty> operator*() const noexcept(noexcept(*_STD declval())) { return *_Mypair._Myval2; } From 49cbf512c042c98cca8f9fefbccfe474117365bf Mon Sep 17 00:00:00 2001 From: Omar Saber Date: Sun, 5 Dec 2021 04:04:27 +0200 Subject: [PATCH 3/5] added test coverage for optional noexcept deref operator --- tests/std/tests/P0220R1_optional/test.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/std/tests/P0220R1_optional/test.cpp b/tests/std/tests/P0220R1_optional/test.cpp index 72f0928000a..c6b9bfd5ee8 100644 --- a/tests/std/tests/P0220R1_optional/test.cpp +++ b/tests/std/tests/P0220R1_optional/test.cpp @@ -4776,6 +4776,7 @@ int run_test() { optional opt; ((void)opt); ASSERT_SAME_TYPE(decltype(*opt), X&); + STATIC_ASSERT(noexcept(*opt)); // ASSERT_NOT_NOEXCEPT(*opt); // TODO: This assertion fails with GCC because it can see that // (A) operator*() is constexpr, and @@ -4847,6 +4848,7 @@ int run_test() { const optional opt; ((void)opt); ASSERT_SAME_TYPE(decltype(*opt), X const&); + STATIC_ASSERT(noexcept(*opt)); // ASSERT_NOT_NOEXCEPT(*opt); // TODO: This assertion fails with GCC because it can see that // (A) operator*() is constexpr, and @@ -4921,6 +4923,7 @@ int run_test() { const optional opt; ((void)opt); ASSERT_SAME_TYPE(decltype(*std::move(opt)), X const &&); + STATIC_ASSERT(noexcept(*std::move(opt))); // ASSERT_NOT_NOEXCEPT(*std::move(opt)); // TODO: This assertion fails with GCC because it can see that // (A) operator*() is constexpr, and @@ -5002,6 +5005,7 @@ int run_test() { optional opt; ((void)opt); ASSERT_SAME_TYPE(decltype(*std::move(opt)), X&&); + STATIC_ASSERT(noexcept(*std::move(opt))); // ASSERT_NOT_NOEXCEPT(*std::move(opt)); // TODO: This assertion fails with GCC because it can see that // (A) operator*() is constexpr, and From 60f41ecaab8062ff41bdfad3e7f63982eb24b8a7 Mon Sep 17 00:00:00 2001 From: Omar Saber Date: Sun, 5 Dec 2021 04:18:18 +0200 Subject: [PATCH 4/5] added test coverage for optional noexcept arrow operator --- tests/std/tests/P0220R1_optional/test.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/std/tests/P0220R1_optional/test.cpp b/tests/std/tests/P0220R1_optional/test.cpp index c6b9bfd5ee8..b7ee180abdd 100644 --- a/tests/std/tests/P0220R1_optional/test.cpp +++ b/tests/std/tests/P0220R1_optional/test.cpp @@ -5123,6 +5123,7 @@ int run_test() { std::optional opt; ((void)opt); ASSERT_SAME_TYPE(decltype(opt.operator->()), X*); + STATIC_ASSERT(noexcept(opt.operator->())); // ASSERT_NOT_NOEXCEPT(opt.operator->()); // TODO: This assertion fails with GCC because it can see that // (A) operator->() is constexpr, and @@ -5199,6 +5200,7 @@ int run_test() { const std::optional opt; ((void)opt); ASSERT_SAME_TYPE(decltype(opt.operator->()), X const*); + STATIC_ASSERT(noexcept(opt.operator->())); // ASSERT_NOT_NOEXCEPT(opt.operator->()); // TODO: This assertion fails with GCC because it can see that // (A) operator->() is constexpr, and From 36d3d1743745bed6d79cfdc865a768c94c1f1ebe Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 6 Dec 2021 21:51:49 -0800 Subject: [PATCH 5/5] Remove outdated comments. --- tests/std/tests/P0220R1_optional/test.cpp | 48 ----------------------- 1 file changed, 48 deletions(-) diff --git a/tests/std/tests/P0220R1_optional/test.cpp b/tests/std/tests/P0220R1_optional/test.cpp index b7ee180abdd..ad7ea5f3fdc 100644 --- a/tests/std/tests/P0220R1_optional/test.cpp +++ b/tests/std/tests/P0220R1_optional/test.cpp @@ -4777,14 +4777,6 @@ int run_test() optional opt; ((void)opt); ASSERT_SAME_TYPE(decltype(*opt), X&); STATIC_ASSERT(noexcept(*opt)); - // ASSERT_NOT_NOEXCEPT(*opt); - // TODO: This assertion fails with GCC because it can see that - // (A) operator*() is constexpr, and - // (B) there is no path through the function that throws. - // It's arguable if this is the correct behavior for the noexcept - // operator. - // Regardless this function should still be noexcept(false) because - // it has a narrow contract. } { optional opt(X{}); @@ -4849,14 +4841,6 @@ int run_test() const optional opt; ((void)opt); ASSERT_SAME_TYPE(decltype(*opt), X const&); STATIC_ASSERT(noexcept(*opt)); - // ASSERT_NOT_NOEXCEPT(*opt); - // TODO: This assertion fails with GCC because it can see that - // (A) operator*() is constexpr, and - // (B) there is no path through the function that throws. - // It's arguable if this is the correct behavior for the noexcept - // operator. - // Regardless this function should still be noexcept(false) because - // it has a narrow contract. } { constexpr optional opt(X{}); @@ -4924,14 +4908,6 @@ int run_test() const optional opt; ((void)opt); ASSERT_SAME_TYPE(decltype(*std::move(opt)), X const &&); STATIC_ASSERT(noexcept(*std::move(opt))); - // ASSERT_NOT_NOEXCEPT(*std::move(opt)); - // TODO: This assertion fails with GCC because it can see that - // (A) operator*() is constexpr, and - // (B) there is no path through the function that throws. - // It's arguable if this is the correct behavior for the noexcept - // operator. - // Regardless this function should still be noexcept(false) because - // it has a narrow contract. } { constexpr optional opt(X{}); @@ -5006,14 +4982,6 @@ int run_test() optional opt; ((void)opt); ASSERT_SAME_TYPE(decltype(*std::move(opt)), X&&); STATIC_ASSERT(noexcept(*std::move(opt))); - // ASSERT_NOT_NOEXCEPT(*std::move(opt)); - // TODO: This assertion fails with GCC because it can see that - // (A) operator*() is constexpr, and - // (B) there is no path through the function that throws. - // It's arguable if this is the correct behavior for the noexcept - // operator. - // Regardless this function should still be noexcept(false) because - // it has a narrow contract. } { optional opt(X{}); @@ -5124,14 +5092,6 @@ int run_test() std::optional opt; ((void)opt); ASSERT_SAME_TYPE(decltype(opt.operator->()), X*); STATIC_ASSERT(noexcept(opt.operator->())); - // ASSERT_NOT_NOEXCEPT(opt.operator->()); - // TODO: This assertion fails with GCC because it can see that - // (A) operator->() is constexpr, and - // (B) there is no path through the function that throws. - // It's arguable if this is the correct behavior for the noexcept - // operator. - // Regardless this function should still be noexcept(false) because - // it has a narrow contract. } { optional opt(X{}); @@ -5201,14 +5161,6 @@ int run_test() const std::optional opt; ((void)opt); ASSERT_SAME_TYPE(decltype(opt.operator->()), X const*); STATIC_ASSERT(noexcept(opt.operator->())); - // ASSERT_NOT_NOEXCEPT(opt.operator->()); - // TODO: This assertion fails with GCC because it can see that - // (A) operator->() is constexpr, and - // (B) there is no path through the function that throws. - // It's arguable if this is the correct behavior for the noexcept - // operator. - // Regardless this function should still be noexcept(false) because - // it has a narrow contract. } { constexpr optional opt(X{});