libstdc++: Fix ambiguous std::pair constructors [PR101124]

The deprecated non-standard std::pair constructors that allow
constructing std::pair<move-only-type, pointer-type> from an rvalue and
a literal zero where not sufficiently constrained. They were viable when
constructing std::pair<copyable-type, pointer-type>, and that case
should work fine using the standard constructors.

Replace the constraints on the non-standard constructors so they are
only viable in cases that should actually be ill-formed according to the
standard.

Also rename __null_ptr_constant to __zero_as_null_pointer_constant so it
matches the name of the -Wzero-as-null-pointer-constant warning. Also
make the text of the deprecated warning describe the problem in more
detail.

libstdc++-v3/ChangeLog:

	PR libstdc++/101124
	* include/bits/stl_pair.h (pair): Adjust constraints on
	deprecated constructors accepting literal zero as null pointer
	constant. Improve wording of deprecated attribute.
	* testsuite/20_util/pair/cons/99957.cc: Check that deprecated
	constructors do not cause ambiguities for copyable types.
This commit is contained in:
Jonathan Wakely 2022-01-18 15:10:06 +00:00
parent 50bc6e463b
commit 302343d8dd
2 changed files with 62 additions and 35 deletions

View File

@ -462,62 +462,81 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: first(__p.first), second(__p.second) { }
#if _GLIBCXX_USE_DEPRECATED
#if defined(__DEPRECATED)
# define _GLIBCXX_DEPRECATED_PAIR_CTOR \
__attribute__ ((__deprecated__ ("use 'nullptr' instead of '0' to " \
"initialize std::pair of move-only " \
"type and pointer")))
#else
# define _GLIBCXX_DEPRECATED_PAIR_CTOR
#endif
private:
/// @cond undocumented
// A type which can be constructed from literal zero, but not nullptr
struct __null_ptr_constant
struct __zero_as_null_pointer_constant
{
__null_ptr_constant(int __null_ptr_constant::*) { }
__zero_as_null_pointer_constant(int __zero_as_null_pointer_constant::*)
{ }
template<typename _Tp,
typename = __enable_if_t<is_null_pointer<_Tp>::value>>
__null_ptr_constant(_Tp) = delete;
__zero_as_null_pointer_constant(_Tp) = delete;
};
// True if type _Up is one of _Tp& or const _Tp&
template<typename _Up, typename _Tp>
using __is_lvalue_of
= __or_<is_same<_Up, const _Tp&>, is_same<_Up, _Tp&>>;
/// @endcond
public:
// Deprecated extensions to DR 811.
// These allow construction from an rvalue and a literal zero,
// in cases where the standard says the zero should be deduced as int
template<typename _U1,
__enable_if_t<!__is_lvalue_of<_U1, _T1>::value
&& _PCCP::template
_DeprConsPair<true, _U1, nullptr_t>(),
__enable_if_t<__and_<__not_<is_reference<_U1>>,
is_pointer<_T2>,
is_constructible<_T1, _U1>,
__not_<is_constructible<_T1, const _U1&>>,
is_convertible<_U1, _T1>>::value,
bool> = true>
_GLIBCXX_DEPRECATED_SUGGEST("nullptr")
constexpr pair(_U1&& __x, __null_ptr_constant)
: first(std::forward<_U1>(__x)), second(nullptr) { }
_GLIBCXX_DEPRECATED_PAIR_CTOR
constexpr
pair(_U1&& __x, __zero_as_null_pointer_constant, ...)
: first(std::forward<_U1>(__x)), second(nullptr) { }
template<typename _U1,
__enable_if_t<!__is_lvalue_of<_U1, _T1>::value
&& _PCCP::template
_DeprConsPair<false, _U1, nullptr_t>(),
__enable_if_t<__and_<__not_<is_reference<_U1>>,
is_pointer<_T2>,
is_constructible<_T1, _U1>,
__not_<is_constructible<_T1, const _U1&>>,
__not_<is_convertible<_U1, _T1>>>::value,
bool> = false>
_GLIBCXX_DEPRECATED_SUGGEST("nullptr")
explicit constexpr pair(_U1&& __x, __null_ptr_constant)
: first(std::forward<_U1>(__x)), second(nullptr) { }
_GLIBCXX_DEPRECATED_PAIR_CTOR
explicit constexpr
pair(_U1&& __x, __zero_as_null_pointer_constant, ...)
: first(std::forward<_U1>(__x)), second(nullptr) { }
template<typename _U2,
__enable_if_t<!__is_lvalue_of<_U2, _T2>::value
&& _PCCP::template
_DeprConsPair<true, nullptr_t, _U2>(),
__enable_if_t<__and_<is_pointer<_T1>,
__not_<is_reference<_U2>>,
is_constructible<_T2, _U2>,
__not_<is_constructible<_T2, const _U2&>>,
is_convertible<_U2, _T2>>::value,
bool> = true>
_GLIBCXX_DEPRECATED_SUGGEST("nullptr")
constexpr pair(__null_ptr_constant, _U2&& __y)
: first(nullptr), second(std::forward<_U2>(__y)) { }
_GLIBCXX_DEPRECATED_PAIR_CTOR
constexpr
pair(__zero_as_null_pointer_constant, _U2&& __y, ...)
: first(nullptr), second(std::forward<_U2>(__y)) { }
template<typename _U2,
__enable_if_t<!__is_lvalue_of<_U2, _T2>::value
&& _PCCP::template
_DeprConsPair<false, nullptr_t, _U2>(),
__enable_if_t<__and_<is_pointer<_T1>,
__not_<is_reference<_U2>>,
is_constructible<_T2, _U2>,
__not_<is_constructible<_T2, const _U2&>>,
__not_<is_convertible<_U2, _T2>>>::value,
bool> = false>
_GLIBCXX_DEPRECATED_SUGGEST("nullptr")
explicit pair(__null_ptr_constant, _U2&& __y)
: first(nullptr), second(std::forward<_U2>(__y)) { }
_GLIBCXX_DEPRECATED_PAIR_CTOR
explicit constexpr
pair(__zero_as_null_pointer_constant, _U2&& __y, ...)
: first(nullptr), second(std::forward<_U2>(__y)) { }
#undef _GLIBCXX_DEPRECATED_PAIR_CTOR
#endif
template<typename _U1, typename _U2, typename

View File

@ -22,8 +22,16 @@ struct ExplicitMoveOnly
// PR libstdc++/99957
// check non-standard constructors are deprecated
pair<int*, ExplicitMoveOnly> v14{0, MoveOnly{}}; // { dg-warning "deprecated" }
pair<ExplicitMoveOnly, int*> v15{MoveOnly{}, 0}; // { dg-warning "deprecated" }
pair<int*, ExplicitMoveOnly> v14(0, MoveOnly{}); // { dg-warning "deprecated" }
pair<ExplicitMoveOnly, int*> v15(MoveOnly{}, 0); // { dg-warning "deprecated" }
pair<int*, MoveOnly> v16 = {0, MoveOnly{}}; // { dg-warning "deprecated" }
pair<MoveOnly, int*> v17 = {MoveOnly{}, 0}; // { dg-warning "deprecated" }
// PR libstdc++/101124
// check deprecated constructors don't cause unwanted ambiguities
std::pair<long*, int> p(0, 0); // { dg-bogus "ambiguous" }
struct X { } x;
std::pair<const X, void*> p2(x, 0); // { dg-bogus "ambiguous" }