diff --git a/libcxx/docs/Status/Cxx2bIssues.csv b/libcxx/docs/Status/Cxx2bIssues.csv --- a/libcxx/docs/Status/Cxx2bIssues.csv +++ b/libcxx/docs/Status/Cxx2bIssues.csv @@ -99,7 +99,7 @@ "","","","","" `2191 `__,"Incorrect specification of ``match_results(match_results&&)``","October 2021","|Nothing To Do|","" `2381 `__,"Inconsistency in parsing floating point numbers","October 2021","","" -`2762 `__,"``unique_ptr operator*()`` should be ``noexcept``","October 2021","","" +`2762 `__,"``unique_ptr operator*()`` should be ``noexcept``","October 2021","|Complete|","15.0" `3121 `__,"``tuple`` constructor constraints for ``UTypes&&...`` overloads","October 2021","","" `3123 `__,"``duration`` constructor from representation shouldn't be effectively non-throwing","October 2021","","","|chrono|" `3146 `__,"Excessive unwrapping in ``std::ref/cref``","October 2021","|Complete|","14.0" diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h --- a/libcxx/include/__memory/unique_ptr.h +++ b/libcxx/include/__memory/unique_ptr.h @@ -267,9 +267,23 @@ return *this; } - _LIBCPP_INLINE_VISIBILITY - typename add_lvalue_reference<_Tp>::type - operator*() const { + _LIBCPP_INLINE_VISIBILITY typename add_lvalue_reference<_Tp>::type + operator*() const +#if _LIBCPP_STD_VER > 17 + noexcept( + [] { + if constexpr (is_void>::value) + return true; + else + // This avoids evaluating noexcept(*declval() when pointer == void*. + // The issue was triggered by + // test/std/utilities/memory/unique.ptr/iterator_concept_conformance.compile.pass.cpp + return noexcept(*declval()); + } ()) +#else + _NOEXCEPT_(noexcept(*declval())) +#endif + { return *__ptr_.first(); } _LIBCPP_INLINE_VISIBILITY diff --git a/libcxx/include/memory b/libcxx/include/memory --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -448,7 +448,7 @@ unique_ptr& operator=(nullptr_t) noexcept; // observers - typename add_lvalue_reference::type operator*() const; + typename add_lvalue_reference::type operator*() const noexcept(see below); pointer operator->() const noexcept; pointer get() const noexcept; deleter_type& get_deleter() noexcept; diff --git a/libcxx/include/optional b/libcxx/include/optional --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -117,12 +117,12 @@ void swap(optional &) noexcept(see below ); // constexpr in C++20 // 23.6.3.5, observers - constexpr T const *operator->() const; - constexpr T *operator->(); - constexpr T const &operator*() const &; - constexpr T &operator*() &; - constexpr T &&operator*() &&; - constexpr const T &&operator*() const &&; + constexpr T const *operator->() const noexcept; + constexpr T *operator->() noexcept; + constexpr T const &operator*() const & noexcept; + constexpr T &operator*() & noexcept; + constexpr T &&operator*() && noexcept; + constexpr const T &&operator*() const && noexcept; constexpr explicit operator bool() const noexcept; constexpr bool has_value() const noexcept; constexpr T const &value() const &; @@ -926,7 +926,7 @@ _LIBCPP_INLINE_VISIBILITY constexpr add_pointer_t - operator->() const + operator->() const noexcept { _LIBCPP_ASSERT(this->has_value(), "optional operator-> called on a disengaged value"); return _VSTD::addressof(this->__get()); @@ -935,7 +935,7 @@ _LIBCPP_INLINE_VISIBILITY constexpr add_pointer_t - operator->() + operator->() noexcept { _LIBCPP_ASSERT(this->has_value(), "optional operator-> called on a disengaged value"); return _VSTD::addressof(this->__get()); diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference.pass.cpp --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference.pass.cpp @@ -44,15 +44,7 @@ { optional opt; ((void)opt); ASSERT_SAME_TYPE(decltype(*opt), X&); - LIBCPP_STATIC_ASSERT(noexcept(*opt)); - // ASSERT_NOT_NOEXCEPT(*opt); - // FIXME: 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. + ASSERT_NOEXCEPT(*opt); } { optional opt(X{}); diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const.pass.cpp --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const.pass.cpp @@ -37,15 +37,7 @@ { const optional opt; ((void)opt); ASSERT_SAME_TYPE(decltype(*opt), X const&); - LIBCPP_STATIC_ASSERT(noexcept(*opt)); - // ASSERT_NOT_NOEXCEPT(*opt); - // FIXME: 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. + ASSERT_NOEXCEPT(*opt); } { constexpr optional opt(X{}); diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const_rvalue.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const_rvalue.pass.cpp --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const_rvalue.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const_rvalue.pass.cpp @@ -37,15 +37,7 @@ { const optional opt; ((void)opt); ASSERT_SAME_TYPE(decltype(*std::move(opt)), X const &&); - LIBCPP_STATIC_ASSERT(noexcept(*opt)); - // ASSERT_NOT_NOEXCEPT(*std::move(opt)); - // FIXME: 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. + ASSERT_NOEXCEPT(*std::move(opt)); } { constexpr optional opt(X{}); diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_rvalue.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_rvalue.pass.cpp --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_rvalue.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_rvalue.pass.cpp @@ -44,15 +44,7 @@ { optional opt; ((void)opt); ASSERT_SAME_TYPE(decltype(*std::move(opt)), X&&); - LIBCPP_STATIC_ASSERT(noexcept(*opt)); - // ASSERT_NOT_NOEXCEPT(*std::move(opt)); - // FIXME: 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. + ASSERT_NOEXCEPT(*std::move(opt)); } { optional opt(X{}); diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow.pass.cpp --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow.pass.cpp @@ -41,14 +41,7 @@ { std::optional opt; ((void)opt); ASSERT_SAME_TYPE(decltype(opt.operator->()), X*); - // ASSERT_NOT_NOEXCEPT(opt.operator->()); - // FIXME: 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. + ASSERT_NOEXCEPT(opt.operator->()); } { optional opt(X{}); diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow_const.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow_const.pass.cpp --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow_const.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow_const.pass.cpp @@ -40,14 +40,7 @@ { const std::optional opt; ((void)opt); ASSERT_SAME_TYPE(decltype(opt.operator->()), X const*); - // ASSERT_NOT_NOEXCEPT(opt.operator->()); - // FIXME: 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. + ASSERT_NOEXCEPT(opt.operator->()); } { constexpr optional opt(X{}); diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/dereference.single.pass.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/dereference.single.pass.cpp --- a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/dereference.single.pass.cpp +++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/dereference.single.pass.cpp @@ -17,9 +17,31 @@ #include "test_macros.h" +#if TEST_STD_VER >= 11 +struct ThrowDereference { + ThrowDereference& operator*() noexcept(false); + operator bool() const { return false; } +}; + +struct Deleter { + using pointer = ThrowDereference; + void operator()(ThrowDereference&) const {} +}; +#endif + int main(int, char**) { - std::unique_ptr p(new int(3)); - assert(*p == 3); + { + std::unique_ptr p(new int(3)); + assert(*p == 3); + ASSERT_NOEXCEPT(*p); + } +#if TEST_STD_VER >= 11 + { + // The noexcept status of *unique_ptr<>::pointer should be propagated. + std::unique_ptr p; + ASSERT_NOT_NOEXCEPT(*p); + } +#endif return 0; }