Skip to content

Commit 52dbc44

Browse files
[libc++] LWG2899: Constrain move special functions of tuple and unique_ptr
libc++'s `tuple`'s move constructor is well-constrained when initially implemented. So this patch only adds test cases. For `unique_ptr`, its move constructor and move assignment operator were previously unconstrained and thus this patch changes them. - In C++20 and later, I think it's better to use obviously correct `requires`-clauses. - In C++17 and earlier, there doesn't seem "obviously correct" approach for constraining. I'm attempting to use `_nat` trick to avoid turning the functions into templates (which are not move special functions). Some tests case are adjusted because false positive of move-assignability of `unique_ptr` is reduced. Comments are updated to reflect that move-constructibility is not actually required for `unique_ptr`'s deleter. This patch also explicitly deletes copy functions of `unique_ptr` in all modes. Previously, they are implicitly deleted since C++11 mode, although the standard wording always explicitly deletes them. Clang seems to be somehow buggy on propagating deleted-ness of special member functions from unnamed structs, while the explicit deletion can act as a workaround. The title of this patch is adjusted to reflect the final resolution of LWG2899.
1 parent 93d445c commit 52dbc44

File tree

10 files changed

+94
-26
lines changed

10 files changed

+94
-26
lines changed

libcxx/docs/Status/Cxx20Issues.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@
143143
"`LWG3180 <https://wg21.link/LWG3180>`__","Inconsistently named return type for ``ranges::minmax_element``\ ","2019-02 (Kona)","|Complete|","15","`#103844 <https://github.com/llvm/llvm-project/issues/103844>`__",""
144144
"`LWG3182 <https://wg21.link/LWG3182>`__","Specification of ``Same``\ could be clearer","2019-02 (Kona)","|Complete|","15","`#103845 <https://github.com/llvm/llvm-project/issues/103845>`__",""
145145
"","","","","","",""
146-
"`LWG2899 <https://wg21.link/LWG2899>`__","``is_(nothrow_)move_constructible``\ and ``tuple``\ , ``optional``\ and ``unique_ptr``\ ","2019-07 (Cologne)","","","`#100255 <https://github.com/llvm/llvm-project/issues/100255>`__",""
146+
"`LWG2899 <https://wg21.link/LWG2899>`__","``is_(nothrow_)move_constructible``\ and ``tuple``\ , ``optional``\ and ``unique_ptr``\ ","2019-07 (Cologne)","|Complete|","22","`#100255 <https://github.com/llvm/llvm-project/issues/100255>`__",""
147147
"`LWG3055 <https://wg21.link/LWG3055>`__","``path::operator+=(*single-character*)``\ misspecified","2019-07 (Cologne)","|Complete|","7","`#103846 <https://github.com/llvm/llvm-project/issues/103846>`__",""
148148
"`LWG3158 <https://wg21.link/LWG3158>`__","``tuple(allocator_arg_t, const Alloc&)``\ should be conditionally explicit","2019-07 (Cologne)","|Complete|","10","`#103847 <https://github.com/llvm/llvm-project/issues/103847>`__",""
149149
"`LWG3169 <https://wg21.link/LWG3169>`__","``ranges``\ permutation generators discard useful information","2019-07 (Cologne)","|Complete|","15","`#103848 <https://github.com/llvm/llvm-project/issues/103848>`__",""

libcxx/include/__memory/unique_ptr.h

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#include <__type_traits/is_trivially_relocatable.h>
4646
#include <__type_traits/is_unbounded_array.h>
4747
#include <__type_traits/is_void.h>
48+
#include <__type_traits/nat.h>
4849
#include <__type_traits/remove_extent.h>
4950
#include <__type_traits/type_identity.h>
5051
#include <__utility/declval.h>
@@ -208,9 +209,15 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
208209
template <bool _Dummy = true, class = _EnableIfDeleterConstructible<_BadRValRefType<_Dummy> > >
209210
_LIBCPP_HIDE_FROM_ABI unique_ptr(pointer __p, _BadRValRefType<_Dummy> __d) = delete;
210211

211-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unique_ptr(unique_ptr&& __u) _NOEXCEPT
212-
: __ptr_(__u.release()),
213-
__deleter_(std::forward<deleter_type>(__u.get_deleter())) {}
212+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23
213+
#if _LIBCPP_STD_VER >= 20
214+
unique_ptr(unique_ptr&& __u) noexcept
215+
requires is_move_constructible_v<_Dp>
216+
#else
217+
unique_ptr(_If<is_move_constructible<_Dp>::value, unique_ptr&&, __nat> __u) _NOEXCEPT
218+
#endif
219+
: __ptr_(__u.release()), __deleter_(std::forward<deleter_type>(__u.get_deleter())) {
220+
}
214221

215222
template <class _Up,
216223
class _Ep,
@@ -226,7 +233,14 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
226233
_LIBCPP_HIDE_FROM_ABI unique_ptr(auto_ptr<_Up>&& __p) _NOEXCEPT : __ptr_(__p.release()), __deleter_() {}
227234
#endif
228235

229-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unique_ptr& operator=(unique_ptr&& __u) _NOEXCEPT {
236+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unique_ptr&
237+
#if _LIBCPP_STD_VER >= 20
238+
operator=(unique_ptr&& __u) noexcept
239+
requires is_move_assignable_v<_Dp>
240+
#else
241+
operator=(_If<is_move_assignable<_Dp>::value, unique_ptr&&, __nat> __u) _NOEXCEPT
242+
#endif
243+
{
230244
reset(__u.release());
231245
__deleter_ = std::forward<deleter_type>(__u.get_deleter());
232246
return *this;
@@ -251,10 +265,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
251265
}
252266
#endif
253267

254-
#ifdef _LIBCPP_CXX03_LANG
255268
unique_ptr(unique_ptr const&) = delete;
256269
unique_ptr& operator=(unique_ptr const&) = delete;
257-
#endif
258270

259271
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 ~unique_ptr() { reset(); }
260272

@@ -532,12 +544,26 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr<_Tp[], _Dp> {
532544
class = _EnableIfPointerConvertible<_Pp> >
533545
_LIBCPP_HIDE_FROM_ABI unique_ptr(_Pp __ptr, _BadRValRefType<_Dummy> __deleter) = delete;
534546

535-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unique_ptr(unique_ptr&& __u) _NOEXCEPT
547+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23
548+
#if _LIBCPP_STD_VER >= 20
549+
unique_ptr(unique_ptr&& __u) noexcept
550+
requires is_move_constructible_v<_Dp>
551+
#else
552+
unique_ptr(_If<is_move_constructible<_Dp>::value, unique_ptr&&, __nat> __u) _NOEXCEPT
553+
#endif
536554
: __ptr_(__u.release()),
537555
__deleter_(std::forward<deleter_type>(__u.get_deleter())),
538-
__checker_(std::move(__u.__checker_)) {}
556+
__checker_(std::move(__u.__checker_)) {
557+
}
539558

540-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unique_ptr& operator=(unique_ptr&& __u) _NOEXCEPT {
559+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unique_ptr&
560+
#if _LIBCPP_STD_VER >= 20
561+
operator=(unique_ptr&& __u) noexcept
562+
requires is_move_assignable_v<_Dp>
563+
#else
564+
operator=(_If<is_move_assignable<_Dp>::value, unique_ptr&&, __nat> __u) _NOEXCEPT
565+
#endif
566+
{
541567
reset(__u.release());
542568
__deleter_ = std::forward<deleter_type>(__u.get_deleter());
543569
__checker_ = std::move(__u.__checker_);
@@ -564,10 +590,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr<_Tp[], _Dp> {
564590
return *this;
565591
}
566592

567-
#ifdef _LIBCPP_CXX03_LANG
568593
unique_ptr(unique_ptr const&) = delete;
569594
unique_ptr& operator=(unique_ptr const&) = delete;
570-
#endif
571595

572596
public:
573597
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 ~unique_ptr() { reset(); }

libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.asgn/move.pass.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,19 +112,26 @@ TEST_CONSTEXPR_CXX23 void test_sfinae() {
112112
static_assert(!std::is_assignable<U, const U&&>::value, "");
113113
static_assert(std::is_nothrow_assignable<U, U&&>::value, "");
114114
}
115+
{
116+
typedef std::unique_ptr<VT, NCDeleter<VT> > U;
117+
static_assert(!std::is_assignable<U, U&>::value, "");
118+
static_assert(!std::is_assignable<U, const U&>::value, "");
119+
static_assert(!std::is_assignable<U, const U&&>::value, "");
120+
static_assert(!std::is_assignable<U, U&&>::value, "");
121+
}
115122
{
116123
typedef std::unique_ptr<VT, NCDeleter<VT>&> U;
117124
static_assert(!std::is_assignable<U, U&>::value, "");
118125
static_assert(!std::is_assignable<U, const U&>::value, "");
119126
static_assert(!std::is_assignable<U, const U&&>::value, "");
120-
static_assert(std::is_nothrow_assignable<U, U&&>::value, "");
127+
static_assert(!std::is_assignable<U, U&&>::value, "");
121128
}
122129
{
123130
typedef std::unique_ptr<VT, const NCDeleter<VT>&> U;
124131
static_assert(!std::is_assignable<U, U&>::value, "");
125132
static_assert(!std::is_assignable<U, const U&>::value, "");
126133
static_assert(!std::is_assignable<U, const U&&>::value, "");
127-
static_assert(std::is_nothrow_assignable<U, U&&>::value, "");
134+
static_assert(!std::is_assignable<U, U&&>::value, "");
128135
}
129136
}
130137

libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.asgn/move_convert.pass.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ TEST_CONSTEXPR_CXX23 void test_sfinae() {
220220
static_assert(!std::is_assignable<U1, U5&&>::value, "");
221221

222222
using U1C = std::unique_ptr<const VT, GenericConvertingDeleter<0> const&>;
223-
static_assert(std::is_nothrow_assignable<U1C, U1&&>::value, "");
223+
static_assert(!std::is_assignable<U1C, U1&&>::value, "");
224224
}
225225
{ // Test that if the deleter assignment is not valid the assignment operator
226226
// SFINAEs.
@@ -296,7 +296,7 @@ TEST_CONSTEXPR_CXX23 void test_noexcept() {
296296
{
297297
typedef std::unique_ptr<const VT, NCDeleter<const VT>&> APtr;
298298
typedef std::unique_ptr<VT, NCDeleter<const VT>&> BPtr;
299-
static_assert(std::is_nothrow_assignable<APtr, BPtr>::value, "");
299+
static_assert(!std::is_assignable<APtr, BPtr>::value, "");
300300
}
301301
{
302302
typedef std::unique_ptr<const VT, const NCConstDeleter<const VT>&> APtr;

libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.asgn/move_convert.runtime.pass.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ void test_sfinae() {
8989
static_assert(!std::is_assignable<UAC, const UA&>::value, "");
9090
}
9191
{ // cannot move if the deleter-types cannot convert
92-
static_assert(std::is_assignable<UACD, UAD&&>::value, "");
92+
static_assert(!std::is_assignable<UACD, UAD&&>::value, "");
9393
static_assert(!std::is_assignable<UACD, UAC&&>::value, "");
9494
static_assert(!std::is_assignable<UAC, UACD&&>::value, "");
9595
}

libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.asgn/move_convert.single.pass.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ TEST_CONSTEXPR_CXX23 void test_sfinae() {
8787
static_assert(!std::is_assignable<UA, const UB&>::value, "");
8888
}
8989
{ // cannot move if the deleter-types cannot convert
90-
static_assert(std::is_assignable<UAD, UBD&&>::value, "");
90+
static_assert(!std::is_assignable<UAD, UBD&&>::value, "");
9191
static_assert(!std::is_assignable<UAD, UB&&>::value, "");
9292
static_assert(!std::is_assignable<UA, UBD&&>::value, "");
9393
}

libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.ctor/move.pass.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
// Test unique_ptr move ctor
1414

15+
// XFAIL: FROZEN-CXX03-HEADERS-FIXME
16+
1517
#include <memory>
1618
#include <utility>
1719
#include <cassert>
@@ -24,10 +26,8 @@
2426
//
2527
// Concerns
2628
// 1 The moved from pointer is empty and the new pointer stores the old value.
27-
// 2 The only requirement on the deleter is that it is MoveConstructible
28-
// or a reference.
29-
// 3 The constructor works for explicitly moved values (i.e. std::move(x))
30-
// 4 The constructor works for true temporaries (e.g. a return value)
29+
// 2 The constructor works for explicitly moved values (i.e. std::move(x))
30+
// 3 The constructor works for true temporaries (e.g. a return value)
3131
//
3232
// Plan
3333
// 1 Explicitly construct unique_ptr<T, D> for various deleter types 'D'.
@@ -73,10 +73,22 @@ void sink3(std::unique_ptr<VT, NCDeleter<VT>&> p) {
7373

7474
template <class ValueT>
7575
TEST_CONSTEXPR_CXX23 void test_sfinae() {
76-
typedef std::unique_ptr<ValueT> U;
77-
{ // Ensure unique_ptr is non-copyable
76+
// Ensure that
77+
// - unique_ptr is non-copyable, and
78+
// - unique_ptr's move-constructibility is correctly propagated from its deleter's.
79+
{
80+
typedef std::unique_ptr<ValueT> U;
7881
static_assert((!std::is_constructible<U, U const&>::value), "");
7982
static_assert((!std::is_constructible<U, U&>::value), "");
83+
static_assert(std::is_move_constructible<U>::value, "");
84+
static_assert(!std::is_constructible<U, const U>::value, "");
85+
}
86+
{
87+
typedef std::unique_ptr<ValueT, NCDeleter<ValueT> > U;
88+
static_assert(!std::is_constructible<U, U const&>::value, "");
89+
static_assert(!std::is_constructible<U, U&>::value, "");
90+
static_assert(!std::is_move_constructible<U>::value, "");
91+
static_assert(!std::is_constructible<U, const U>::value, "");
8092
}
8193
}
8294

libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,19 @@ struct move_only_large final {
4949
int value;
5050
};
5151

52+
// a non-movable type
53+
struct nonmovable {
54+
nonmovable(const nonmovable&) = default;
55+
nonmovable(nonmovable&&) = delete;
56+
};
57+
58+
// a non-movable type with a usable copy constructor
59+
// verifying that tuple's move constructor is not confused to select that copy constructor
60+
struct nonmovable_with_copy_ctor {
61+
nonmovable_with_copy_ctor(const nonmovable_with_copy_ctor&) = default;
62+
nonmovable_with_copy_ctor(nonmovable_with_copy_ctor&&) = delete;
63+
};
64+
5265
template <class Elem>
5366
void test_sfinae() {
5467
using Tup = std::tuple<Elem>;
@@ -123,6 +136,18 @@ int main(int, char**)
123136
test_sfinae<move_only_ebo>();
124137
test_sfinae<move_only_large>();
125138
}
139+
// non-movable types
140+
{
141+
using Alloc = std::allocator<int>;
142+
using Tag = std::allocator_arg_t;
143+
144+
static_assert(!std::is_move_constructible<nonmovable>::value, "");
145+
static_assert(!std::is_constructible<nonmovable, Tag, Alloc, nonmovable>::value, "");
146+
147+
static_assert(!std::is_move_constructible<nonmovable_with_copy_ctor>::value, "");
148+
static_assert(
149+
!std::is_constructible<nonmovable_with_copy_ctor, Tag, Alloc, nonmovable_with_copy_ctor>::value, "");
150+
}
126151

127152
return 0;
128153
}

libcxx/test/std/utilities/tuple/tuple.tuple/tuple.elem/tuple.by.type.verify.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ void test_bad_index() {
3030
void test_bad_return_type() {
3131
typedef std::unique_ptr<int> upint;
3232
std::tuple<upint> t;
33-
upint p = std::get<upint>(t); // expected-error{{deleted copy constructor}}
33+
upint p = std::get<upint>(t); // expected-error{{deleted constructor}}
3434
}
3535

3636
void f() {

libcxx/test/std/utilities/utility/pairs/pair.astuple/pairs.by.type3.verify.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@
1414
void f() {
1515
typedef std::unique_ptr<int> Ptr;
1616
std::pair<Ptr, int> t(Ptr(new int(4)), 23);
17-
Ptr p = std::get<Ptr>(t); // expected-error {{call to implicitly-deleted copy constructor of 'Ptr'}}
17+
Ptr p = std::get<Ptr>(t); // expected-error {{call to deleted constructor of 'Ptr'}}
1818
}

0 commit comments

Comments
 (0)