libstdc++: Fix regressions in unique_ptr::swap (PR 93562)

The requirements for this function are only that the deleter is
swappable, but we incorrectly require that the element type is complete
and that the deleter can be swapped using std::swap (which requires it
to be move cosntructible and move assignable).

The fix is to add __uniq_ptr_impl::swap which swaps the pointer and
deleter individually, instead of using the generic std::swap on the
tuple containing them.

	PR libstdc++/93562
	* include/bits/unique_ptr.h (__uniq_ptr_impl::swap): Define.
	(unique_ptr::swap, unique_ptr<T[], D>::swap): Call it.
	* testsuite/20_util/unique_ptr/modifiers/93562.cc: New test.
This commit is contained in:
Jonathan Wakely 2020-02-04 12:59:14 +00:00
parent 59afd6ad83
commit 9962493ca2
3 changed files with 117 additions and 4 deletions

View File

@ -1,3 +1,10 @@
2020-02-04 Jonathan Wakely <jwakely@redhat.com>
PR libstdc++/93562
* include/bits/unique_ptr.h (__uniq_ptr_impl::swap): Define.
(unique_ptr::swap, unique_ptr<T[], D>::swap): Call it.
* testsuite/20_util/unique_ptr/modifiers/93562.cc: New test.
2020-02-01 Andrew Burgess <andrew.burgess@embecosm.com>
* configure: Regenerate.

View File

@ -185,6 +185,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return __p;
}
void
swap(__uniq_ptr_impl& __rhs) noexcept
{
using std::swap;
swap(this->_M_ptr(), __rhs._M_ptr());
swap(this->_M_deleter(), __rhs._M_deleter());
}
private:
tuple<pointer, _Dp> _M_t;
};
@ -448,8 +456,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
void
swap(unique_ptr& __u) noexcept
{
using std::swap;
swap(_M_t, __u._M_t);
static_assert(__is_swappable<_Dp>::value, "deleter must be swappable");
_M_t.swap(__u._M_t);
}
// Disable copy from lvalue.
@ -703,8 +711,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
void
swap(unique_ptr& __u) noexcept
{
using std::swap;
swap(_M_t, __u._M_t);
static_assert(__is_swappable<_Dp>::value, "deleter must be swappable");
_M_t.swap(__u._M_t);
}
// Disable copy from lvalue.

View File

@ -0,0 +1,98 @@
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-do run { target c++11 } }
#include <memory>
#include <testsuite_hooks.h>
struct incomplete;
// This function isn't called, we just need to check it compiles.
void
test01(std::unique_ptr<incomplete>& p1, std::unique_ptr<incomplete>& p2)
{
// PR libstdc++/93562
p1.swap(p2);
swap(p1, p2);
}
// This function isn't called, we just need to check it compiles.
void
test02(std::unique_ptr<incomplete[]>& p1, std::unique_ptr<incomplete[]>& p2)
{
// PR libstdc++/93562
p1.swap(p2);
swap(p1, p2);
}
namespace A
{
struct Deleter
{
Deleter& operator=(const Deleter&) = delete;
void operator()(int* p) const noexcept { delete p; }
// found by ADL
friend void swap(Deleter& lhs, Deleter& rhs) noexcept
{ std::swap(lhs.id, rhs.id); }
int id;
};
static_assert(!std::is_move_assignable<Deleter>::value, "not assignable");
#if __cplusplus >= 201703L
static_assert(std::is_swappable_v<Deleter>, "but swappable");
#endif
} // namespace A
void
test03()
{
std::unique_ptr<int, A::Deleter> p1(new int(1), { -1 });
std::unique_ptr<int, A::Deleter> p2(new int(2), { -2 });
int* const pi1 = p1.get();
int* const pi2 = p2.get();
// This type must swappable even though the deleter is not move-assignable:
swap(p1, p2);
VERIFY(p1.get() == pi2);
VERIFY(p1.get_deleter().id == -2);
VERIFY(p2.get() == pi1);
VERIFY(p2.get_deleter().id == -1);
}
void
test04()
{
std::unique_ptr<int[], A::Deleter> p1(new int[1]{1}, { -1 });
std::unique_ptr<int[], A::Deleter> p2(new int[2]{2, 2}, { -2 });
int* const pi1 = p1.get();
int* const pi2 = p2.get();
// This type must swappable even though the deleter is not move-assignable:
swap(p1, p2);
VERIFY(p1.get() == pi2);
VERIFY(p1.get_deleter().id == -2);
VERIFY(p2.get() == pi1);
VERIFY(p2.get_deleter().id == -1);
}
int main()
{
test03();
test04();
}