libstdc++: Fix ambiguous std::pair constructors [PR101124]
authorJonathan Wakely <jwakely@redhat.com>
Tue, 18 Jan 2022 15:10:06 +0000 (15:10 +0000)
committerJonathan Wakely <jwakely@redhat.com>
Tue, 18 Jan 2022 16:31:03 +0000 (16:31 +0000)
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.

libstdc++-v3/include/bits/stl_pair.h
libstdc++-v3/testsuite/20_util/pair/cons/99957.cc

index abf2120..8564fd1 100644 (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
index 82ec54c..8432856 100644 (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" }