Skip to content

Commit 6c3f8e6

Browse files
committed
[libc++] Makes `unique_ptr operator*() noexcept.
This implements - LWG2762 unique_ptr operator*() should be noexcept. Differential Revision: https://reviews.llvm.org/D128214
1 parent f5b9e11 commit 6c3f8e6

File tree

11 files changed

+75
-65
lines changed

11 files changed

+75
-65
lines changed

libcxx/docs/Status/Cxx23Issues.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@
9999
"","","","","",""
100100
`2191 <https://wg21.link/LWG2191>`__,"Incorrect specification of ``match_results(match_results&&)``","October 2021","|Nothing To Do|",""
101101
`2381 <https://wg21.link/LWG2381>`__,"Inconsistency in parsing floating point numbers","October 2021","|Complete|","19.0"
102-
`2762 <https://wg21.link/LWG2762>`__,"``unique_ptr operator*()`` should be ``noexcept``","October 2021","",""
102+
`2762 <https://wg21.link/LWG2762>`__,"``unique_ptr operator*()`` should be ``noexcept``","October 2021","|Complete|","19.0"
103103
`3121 <https://wg21.link/LWG3121>`__,"``tuple`` constructor constraints for ``UTypes&&...`` overloads","October 2021","",""
104104
`3123 <https://wg21.link/LWG3123>`__,"``duration`` constructor from representation shouldn't be effectively non-throwing","October 2021","","","|chrono|"
105105
`3146 <https://wg21.link/LWG3146>`__,"Excessive unwrapping in ``std::ref/cref``","October 2021","|Complete|","14.0"

libcxx/include/__memory/unique_ptr.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@
3636
#include <__type_traits/is_trivially_relocatable.h>
3737
#include <__type_traits/is_void.h>
3838
#include <__type_traits/remove_extent.h>
39+
#include <__type_traits/remove_pointer.h>
3940
#include <__type_traits/type_identity.h>
41+
#include <__utility/declval.h>
4042
#include <__utility/forward.h>
4143
#include <__utility/move.h>
4244
#include <cstddef>
@@ -50,6 +52,20 @@ _LIBCPP_PUSH_MACROS
5052

5153
_LIBCPP_BEGIN_NAMESPACE_STD
5254

55+
#ifndef _LIBCPP_CXX03_LANG
56+
// Dereferencing _Ptr directly in noexcept fails for a void pointer.
57+
// This is not SFINAE-ed away leading to a hard error.
58+
// The issue was originally triggered by
59+
// test/std/utilities/memory/unique.ptr/iterator_concept_conformance.compile.pass.cpp
60+
template <class _Ptr>
61+
struct __is_noexcept_deref_or_void {
62+
static constexpr bool value = noexcept(*std::declval<_Ptr>());
63+
};
64+
65+
template <>
66+
struct __is_noexcept_deref_or_void<void*> : true_type {};
67+
#endif
68+
5369
template <class _Tp>
5470
struct _LIBCPP_TEMPLATE_VIS default_delete {
5571
static_assert(!is_function<_Tp>::value, "default_delete cannot be instantiated for function types");
@@ -252,7 +268,11 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr {
252268
return *this;
253269
}
254270

255-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 __add_lvalue_reference_t<_Tp> operator*() const {
271+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 __add_lvalue_reference_t<_Tp> operator*() const
272+
#ifndef _LIBCPP_CXX03_LANG
273+
noexcept(__is_noexcept_deref_or_void<pointer>::value)
274+
#endif
275+
{
256276
return *__ptr_.first();
257277
}
258278
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 pointer operator->() const _NOEXCEPT { return __ptr_.first(); }

libcxx/include/memory

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,8 @@ public:
451451
constexpr unique_ptr& operator=(nullptr_t) noexcept; // constexpr since C++23
452452
453453
// observers
454-
typename constexpr add_lvalue_reference<T>::type operator*() const; // constexpr since C++23
454+
constexpr
455+
add_lvalue_reference<T>::type operator*() const noexcept(see below); // constexpr since C++23
455456
constexpr pointer operator->() const noexcept; // constexpr since C++23
456457
constexpr pointer get() const noexcept; // constexpr since C++23
457458
constexpr deleter_type& get_deleter() noexcept; // constexpr since C++23

libcxx/include/optional

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,12 @@ namespace std {
136136
void swap(optional &) noexcept(see below ); // constexpr in C++20
137137
138138
// [optional.observe], observers
139-
constexpr T const *operator->() const;
140-
constexpr T *operator->();
141-
constexpr T const &operator*() const &;
142-
constexpr T &operator*() &;
143-
constexpr T &&operator*() &&;
144-
constexpr const T &&operator*() const &&;
139+
constexpr T const *operator->() const noexcept;
140+
constexpr T *operator->() noexcept;
141+
constexpr T const &operator*() const & noexcept;
142+
constexpr T &operator*() & noexcept;
143+
constexpr T &&operator*() && noexcept;
144+
constexpr const T &&operator*() const && noexcept;
145145
constexpr explicit operator bool() const noexcept;
146146
constexpr bool has_value() const noexcept;
147147
constexpr T const &value() const &;
@@ -783,12 +783,12 @@ public:
783783
}
784784
}
785785

786-
_LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<value_type const> operator->() const {
786+
_LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<value_type const> operator->() const noexcept {
787787
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
788788
return std::addressof(this->__get());
789789
}
790790

791-
_LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<value_type> operator->() {
791+
_LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<value_type> operator->() noexcept {
792792
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
793793
return std::addressof(this->__get());
794794
}

libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference.pass.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,7 @@ int main(int, char**)
4444
{
4545
optional<X> opt; ((void)opt);
4646
ASSERT_SAME_TYPE(decltype(*opt), X&);
47-
LIBCPP_STATIC_ASSERT(noexcept(*opt));
48-
// ASSERT_NOT_NOEXCEPT(*opt);
49-
// FIXME: This assertion fails with GCC because it can see that
50-
// (A) operator*() is constexpr, and
51-
// (B) there is no path through the function that throws.
52-
// It's arguable if this is the correct behavior for the noexcept
53-
// operator.
54-
// Regardless this function should still be noexcept(false) because
55-
// it has a narrow contract.
47+
ASSERT_NOEXCEPT(*opt);
5648
}
5749
{
5850
optional<X> opt(X{});

libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const.pass.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,7 @@ int main(int, char**)
3737
{
3838
const optional<X> opt; ((void)opt);
3939
ASSERT_SAME_TYPE(decltype(*opt), X const&);
40-
LIBCPP_STATIC_ASSERT(noexcept(*opt));
41-
// ASSERT_NOT_NOEXCEPT(*opt);
42-
// FIXME: This assertion fails with GCC because it can see that
43-
// (A) operator*() is constexpr, and
44-
// (B) there is no path through the function that throws.
45-
// It's arguable if this is the correct behavior for the noexcept
46-
// operator.
47-
// Regardless this function should still be noexcept(false) because
48-
// it has a narrow contract.
40+
ASSERT_NOEXCEPT(*opt);
4941
}
5042
{
5143
constexpr optional<X> opt(X{});

libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const_rvalue.pass.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,7 @@ int main(int, char**)
3737
{
3838
const optional<X> opt; ((void)opt);
3939
ASSERT_SAME_TYPE(decltype(*std::move(opt)), X const &&);
40-
LIBCPP_STATIC_ASSERT(noexcept(*opt));
41-
// ASSERT_NOT_NOEXCEPT(*std::move(opt));
42-
// FIXME: This assertion fails with GCC because it can see that
43-
// (A) operator*() is constexpr, and
44-
// (B) there is no path through the function that throws.
45-
// It's arguable if this is the correct behavior for the noexcept
46-
// operator.
47-
// Regardless this function should still be noexcept(false) because
48-
// it has a narrow contract.
40+
ASSERT_NOEXCEPT(*std::move(opt));
4941
}
5042
{
5143
constexpr optional<X> opt(X{});

libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_rvalue.pass.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,7 @@ int main(int, char**)
4444
{
4545
optional<X> opt; ((void)opt);
4646
ASSERT_SAME_TYPE(decltype(*std::move(opt)), X&&);
47-
LIBCPP_STATIC_ASSERT(noexcept(*opt));
48-
// ASSERT_NOT_NOEXCEPT(*std::move(opt));
49-
// FIXME: This assertion fails with GCC because it can see that
50-
// (A) operator*() is constexpr, and
51-
// (B) there is no path through the function that throws.
52-
// It's arguable if this is the correct behavior for the noexcept
53-
// operator.
54-
// Regardless this function should still be noexcept(false) because
55-
// it has a narrow contract.
47+
ASSERT_NOEXCEPT(*std::move(opt));
5648
}
5749
{
5850
optional<X> opt(X{});

libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow.pass.cpp

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,7 @@ int main(int, char**)
4141
{
4242
std::optional<X> opt; ((void)opt);
4343
ASSERT_SAME_TYPE(decltype(opt.operator->()), X*);
44-
// ASSERT_NOT_NOEXCEPT(opt.operator->());
45-
// FIXME: This assertion fails with GCC because it can see that
46-
// (A) operator->() is constexpr, and
47-
// (B) there is no path through the function that throws.
48-
// It's arguable if this is the correct behavior for the noexcept
49-
// operator.
50-
// Regardless this function should still be noexcept(false) because
51-
// it has a narrow contract.
44+
ASSERT_NOEXCEPT(opt.operator->());
5245
}
5346
{
5447
optional<X> opt(X{});

libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow_const.pass.cpp

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,7 @@ int main(int, char**)
4040
{
4141
const std::optional<X> opt; ((void)opt);
4242
ASSERT_SAME_TYPE(decltype(opt.operator->()), X const*);
43-
// ASSERT_NOT_NOEXCEPT(opt.operator->());
44-
// FIXME: This assertion fails with GCC because it can see that
45-
// (A) operator->() is constexpr, and
46-
// (B) there is no path through the function that throws.
47-
// It's arguable if this is the correct behavior for the noexcept
48-
// operator.
49-
// Regardless this function should still be noexcept(false) because
50-
// it has a narrow contract.
43+
ASSERT_NOEXCEPT(opt.operator->());
5144
}
5245
{
5346
constexpr optional<X> opt(X{});

libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/dereference.single.pass.cpp

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,47 @@
1414

1515
#include <memory>
1616
#include <cassert>
17+
#include <vector>
1718

1819
#include "test_macros.h"
1920

21+
#if TEST_STD_VER >= 11
22+
struct ThrowDereference {
23+
TEST_CONSTEXPR_CXX23 ThrowDereference& operator*() noexcept(false);
24+
TEST_CONSTEXPR_CXX23 operator bool() const { return false; }
25+
};
26+
27+
struct Deleter {
28+
using pointer = ThrowDereference;
29+
TEST_CONSTEXPR_CXX23 void operator()(ThrowDereference&) const {}
30+
};
31+
#endif
32+
2033
TEST_CONSTEXPR_CXX23 bool test() {
21-
std::unique_ptr<int> p(new int(3));
22-
assert(*p == 3);
34+
ASSERT_NOEXCEPT(*(std::unique_ptr<void>{}));
35+
{
36+
std::unique_ptr<int> p(new int(3));
37+
assert(*p == 3);
38+
ASSERT_NOEXCEPT(*p);
39+
}
40+
#if TEST_STD_VER >= 11
41+
{
42+
std::unique_ptr<std::vector<int>> p(new std::vector<int>{3, 4, 5});
43+
assert((*p)[0] == 3);
44+
assert((*p)[1] == 4);
45+
assert((*p)[2] == 5);
46+
ASSERT_NOEXCEPT(*p);
47+
}
48+
{
49+
std::unique_ptr<ThrowDereference> p;
50+
ASSERT_NOEXCEPT(*p);
51+
}
52+
{
53+
// The noexcept status of *unique_ptr<>::pointer should be propagated.
54+
std::unique_ptr<ThrowDereference, Deleter> p;
55+
ASSERT_NOT_NOEXCEPT(*p);
56+
}
57+
#endif
2358

2459
return true;
2560
}

0 commit comments

Comments
 (0)