public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
* [RFC] Deprecate non-standard constructors in std::pair
@ 2021-04-07 12:30 Jonathan Wakely
  2021-04-07 12:41 ` Ville Voutilainen
  0 siblings, 1 reply; 14+ messages in thread
From: Jonathan Wakely @ 2021-04-07 12:30 UTC (permalink / raw)
  To: libstdc++

This variation on the example in https://wg21.link/lwg811 is supposed
to be ill-formed:

   pair<char*, unique_ptr<int>> p(0, std::make_unique<int>(0));

This cannot use the pair(U1&&, U2&&) constructor because it deduces
int for U1, which isn't convertible to char*.  But it also cannot use
the pair(const first_type&, const second_type&) constructor, because
second_type is move-only.

It works in libstdc++ because extra non-standard constructors were
added by https://gcc.gnu.org/PR40925 when implementing LWG 811:

   template<typename U1, /* constraints */>
   explicit(/*...*/)
   pair(U1&&, const second_type&);

   template<typename U2, /* constraints */>
   explicit(/*...*/)
   pair(const first_type&, U2&&);

I think we should deprecate these extra constructors.

Unless I'm mistaken, those non-standard constructors have two uses:

- Support literal 0 as a constructor argument for a pointer type.
   Such code should use nullptr instead. It exists to solve the problem
   of 0 not deducing as a pointer type.

- Support {} as a constructor argument for a move-only type. I don't
   think this was an intentional design choice (we don't test for it)
   but it does work. Adopting https://wg21.link/p1951 for C++23 will
   make that work anyway.

I propose that we deprecate the constructors for C++11/14/17/20 in
stage 1, and do not support them at all in C++23 mode once P1951 is
supported. I have a patch which I'll send in stage 1 (it also uses
C++20 concepts to simplify std::pair and fix PR 97930).

After a period of deprecation we could remove them, and support P1951
for -std=gnu++11/14/17/20 too so that {} continues to work.



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC] Deprecate non-standard constructors in std::pair
  2021-04-07 12:30 [RFC] Deprecate non-standard constructors in std::pair Jonathan Wakely
@ 2021-04-07 12:41 ` Ville Voutilainen
  2021-04-07 12:46   ` Jonathan Wakely
  0 siblings, 1 reply; 14+ messages in thread
From: Ville Voutilainen @ 2021-04-07 12:41 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: libstdc++

On Wed, 7 Apr 2021 at 15:31, Jonathan Wakely via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
> I propose that we deprecate the constructors for C++11/14/17/20 in
> stage 1, and do not support them at all in C++23 mode once P1951 is
> supported. I have a patch which I'll send in stage 1 (it also uses
> C++20 concepts to simplify std::pair and fix PR 97930).
>
> After a period of deprecation we could remove them, and support P1951
> for -std=gnu++11/14/17/20 too so that {} continues to work.

The proposal sounds good to me.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC] Deprecate non-standard constructors in std::pair
  2021-04-07 12:41 ` Ville Voutilainen
@ 2021-04-07 12:46   ` Jonathan Wakely
  2021-04-07 16:59     ` Jonathan Wakely
  0 siblings, 1 reply; 14+ messages in thread
From: Jonathan Wakely @ 2021-04-07 12:46 UTC (permalink / raw)
  To: Ville Voutilainen; +Cc: libstdc++

On 07/04/21 15:41 +0300, Ville Voutilainen via Libstdc++ wrote:
>On Wed, 7 Apr 2021 at 15:31, Jonathan Wakely via Libstdc++
><libstdc++@gcc.gnu.org> wrote:
>> I propose that we deprecate the constructors for C++11/14/17/20 in
>> stage 1, and do not support them at all in C++23 mode once P1951 is
>> supported. I have a patch which I'll send in stage 1 (it also uses
>> C++20 concepts to simplify std::pair and fix PR 97930).
>>
>> After a period of deprecation we could remove them, and support P1951
>> for -std=gnu++11/14/17/20 too so that {} continues to work.
>
>The proposal sounds good to me.

Thanks. I've created https://gcc.gnu.org/PR99957 so I don't forget.



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC] Deprecate non-standard constructors in std::pair
  2021-04-07 12:46   ` Jonathan Wakely
@ 2021-04-07 16:59     ` Jonathan Wakely
  2021-04-07 17:18       ` Jonathan Wakely
                         ` (2 more replies)
  0 siblings, 3 replies; 14+ messages in thread
From: Jonathan Wakely @ 2021-04-07 16:59 UTC (permalink / raw)
  To: Ville Voutilainen; +Cc: libstdc++

[-- Attachment #1: Type: text/plain, Size: 1815 bytes --]

On 07/04/21 13:46 +0100, Jonathan Wakely wrote:
>On 07/04/21 15:41 +0300, Ville Voutilainen via Libstdc++ wrote:
>>On Wed, 7 Apr 2021 at 15:31, Jonathan Wakely via Libstdc++
>><libstdc++@gcc.gnu.org> wrote:
>>>I propose that we deprecate the constructors for C++11/14/17/20 in
>>>stage 1, and do not support them at all in C++23 mode once P1951 is
>>>supported. I have a patch which I'll send in stage 1 (it also uses
>>>C++20 concepts to simplify std::pair and fix PR 97930).
>>>
>>>After a period of deprecation we could remove them, and support P1951
>>>for -std=gnu++11/14/17/20 too so that {} continues to work.
>>
>>The proposal sounds good to me.
>
>Thanks. I've created https://gcc.gnu.org/PR99957 so I don't forget.

Here's a patch to implement it, for stage 1.

     libstdc++: Deprecate non-standard std::pair constructors [PR 99957]

     This deprecates the non-standard std::pair constructors that support
     construction from an rvalue and a literal zero used as a null pointer
     constant. We can't just add the deprecated attribute to those
     constructors, because they're currently used by correct code when they
     are a better match than the constructors required by the standard e.g.

       int i = 0;
       const int j = 0;
       std::pair<int, int> p(i, j); // uses pair(U1&&, const int&)

     This patch adjusts the parameter types and constraints of those
     constructors so that they only get used for literal zeros, and the
     pair(U1&&, U2&&) constructor gets used otherwise. Once they're only used
     for initializations that should be ill-formed we can add the deprecated
     attribute.

     The deprecated attribute is used to suggest that the user code uses
     nullptr, which avoids the problem of 0 deducing as int instead of a null
     pointer constant.




[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 14435 bytes --]

commit e20794c814c5961f0f33381a8eb3dff4fc741b5a
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Apr 7 17:20:43 2021

    libstdc++: Deprecate non-standard std::pair constructors [PR 99957]
    
    This deprecates the non-standard std::pair constructors that support
    construction from an rvalue and a literal zero used as a null pointer
    constant. We can't just add the deprecated attribute to those
    constructors, because they're currently used by correct code when they
    are a better match than the constructors required by the standard e.g.
    
      int i = 0;
      const int j = 0;
      std::pair<int, int> p(i, j); // uses pair(U1&&, const int&)
    
    This patch adjusts the parameter types and constraints of those
    constructors so that they only get used for literal zeros, and the
    pair(U1&&, U2&&) constructor gets used otherwise. Once they're only used
    for initializations that should be ill-formed we can add the deprecated
    attribute.
    
    The deprecated attribute is used to suggest that the user code uses
    nullptr, which avoids the problem of 0 deducing as int instead of a null
    pointer constant.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/99957
            * include/bits/stl_pair.h (_PCC::_MoveCopyPair, _PCC::_CopyMovePair):
            Combine and replace with ...
            (_PCC::_DeprConsPair): New SFINAE helper function.
            (pair): Merge preprocessor blocks so that all C++03 members
            are defined together at the end.
            (pair::pair(const _T1&, _U2&&), pair::pair(_U1&&, const _T2&)):
            Replace _T1 and _T2 parameters with __null_ptr_constant and
            adjust constraints.
            * testsuite/20_util/pair/40925.cc: Use nullptr instead of 0.
            * testsuite/20_util/pair/cons/explicit_construct.cc: Likewise.
            * testsuite/20_util/pair/cons/99957.cc: New test.

diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
index 70262f9508f..883d7441b3d 100644
--- a/libstdc++-v3/include/bits/stl_pair.h
+++ b/libstdc++-v3/include/bits/stl_pair.h
@@ -128,34 +128,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 		      is_convertible<_U2&&, _T2>>::value;
       }
 
-      template <bool __implicit, typename _U1, typename _U2>
-      static constexpr bool _CopyMovePair()
-      {
-	using __do_converts = __and_<is_convertible<const _U1&, _T1>,
-				  is_convertible<_U2&&, _T2>>;
-	using __converts = typename conditional<__implicit,
-				       __do_converts,
-				       __not_<__do_converts>>::type;
-	return __and_<is_constructible<_T1, const _U1&>,
-		      is_constructible<_T2, _U2&&>,
-		      __converts
-		      >::value;
-      }
 
       template <bool __implicit, typename _U1, typename _U2>
-      static constexpr bool _MoveCopyPair()
+      static constexpr bool _DeprConsPair()
       {
 	using __do_converts = __and_<is_convertible<_U1&&, _T1>,
-				  is_convertible<const _U2&, _T2>>;
+				     is_convertible<_U2&&, _T2>>;
 	using __converts = typename conditional<__implicit,
-				       __do_converts,
-				       __not_<__do_converts>>::type;
+						__do_converts,
+						__not_<__do_converts>>::type;
 	return __and_<is_constructible<_T1, _U1&&>,
-		      is_constructible<_T2, const _U2&&>,
+		      is_constructible<_T2, _U2&&>,
 		      __converts
-		      >::value;
+		     >::value;
       }
-  };
+    };
 
   template <typename _T1, typename _T2>
     struct _PCC<false, _T1, _T2>
@@ -183,7 +170,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       {
 	return false;
       }
-  };
+    };
 #endif // C++11
 
   template<typename _U1, typename _U2> class __pair_base
@@ -217,22 +204,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _T1 first;                 ///< The first member
       _T2 second;                ///< The second member
 
-      // _GLIBCXX_RESOLVE_LIB_DEFECTS
-      // 265.  std::pair::pair() effects overly restrictive
+#if __cplusplus >= 201103L
+      // C++11 (and later) implementation.
+
       /** The default constructor creates @c first and @c second using their
        *  respective default constructors.  */
-#if __cplusplus >= 201103L
       template <typename _U1 = _T1,
                 typename _U2 = _T2,
                 typename enable_if<__and_<
                                      __is_implicitly_default_constructible<_U1>,
                                      __is_implicitly_default_constructible<_U2>>
                                    ::value, bool>::type = true>
-#endif
-      _GLIBCXX_CONSTEXPR pair()
+      constexpr pair()
       : first(), second() { }
 
-#if __cplusplus >= 201103L
       template <typename _U1 = _T1,
                 typename _U2 = _T2,
                 typename enable_if<__and_<
@@ -244,13 +229,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                                    ::value, bool>::type = false>
       explicit constexpr pair()
       : first(), second() { }
-#endif
 
-#if __cplusplus < 201103L
-      /// Two objects may be passed to a @c pair constructor to be copied.
-      pair(const _T1& __a, const _T2& __b)
-      : first(__a), second(__b) { }
-#else
       // Shortcut for constraining the templates that don't take pairs.
       /// @cond undocumented
       using _PCCP = _PCC<true, _T1, _T2>;
@@ -275,14 +254,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                          bool>::type=false>
       explicit constexpr pair(const _T1& __a, const _T2& __b)
       : first(__a), second(__b) { }
-#endif
 
-#if __cplusplus < 201103L
-      /// There is also a templated constructor to convert from other pairs.
-      template<typename _U1, typename _U2>
-	pair(const pair<_U1, _U2>& __p)
-	: first(__p.first), second(__p.second) { }
-#else
       // Shortcut for constraining the templates that take pairs.
       /// @cond undocumented
       template <typename _U1, typename _U2>
@@ -308,40 +280,68 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                          bool>::type=false>
 	explicit constexpr pair(const pair<_U1, _U2>& __p)
 	: first(__p.first), second(__p.second) { }
-#endif
 
-#if __cplusplus >= 201103L
       constexpr pair(const pair&) = default;	///< Copy constructor
       constexpr pair(pair&&) = default;		///< Move constructor
 
-      // DR 811.
-      template<typename _U1, typename
-	       enable_if<_PCCP::template
-			   _MoveCopyPair<true, _U1, _T2>(),
-                         bool>::type=true>
-       constexpr pair(_U1&& __x, const _T2& __y)
-       : first(std::forward<_U1>(__x)), second(__y) { }
+#if _GLIBCXX_USE_DEPRECATED
+    private:
+      /// @cond undocumented
 
-      template<typename _U1, typename
-	       enable_if<_PCCP::template
-			   _MoveCopyPair<false, _U1, _T2>(),
-                         bool>::type=false>
-       explicit constexpr pair(_U1&& __x, const _T2& __y)
-       : first(std::forward<_U1>(__x)), second(__y) { }
+      // A type which can be constructed from literal zero, but not nullptr
+      struct __null_ptr_constant
+      {
+	__null_ptr_constant(int __null_ptr_constant::*) { }
+	template<typename _Tp,
+		 typename = __enable_if_t<is_null_pointer<_Tp>::value>>
+	__null_ptr_constant(_Tp) = delete;
+      };
 
-      template<typename _U2, typename
-	       enable_if<_PCCP::template
-			   _CopyMovePair<true, _T1, _U2>(),
-                         bool>::type=true>
-       constexpr pair(const _T1& __x, _U2&& __y)
-       : first(__x), second(std::forward<_U2>(__y)) { }
+      // 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&>>;
 
-      template<typename _U2, typename
-	       enable_if<_PCCP::template
-			   _CopyMovePair<false, _T1, _U2>(),
-                         bool>::type=false>
-       explicit pair(const _T1& __x, _U2&& __y)
-       : first(__x), second(std::forward<_U2>(__y)) { }
+      /// @endcond
+    public:
+
+      // Deprecated extensions to DR 811.
+      template<typename _U1,
+	       __enable_if_t<!__is_lvalue_of<_U1, _T1>::value
+			     && _PCCP::template
+			       _DeprConsPair<true, _U1, nullptr_t>(),
+			     bool> = true>
+       _GLIBCXX_DEPRECATED_SUGGEST("nullptr")
+       constexpr pair(_U1&& __x, __null_ptr_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>(),
+			     bool> = false>
+       _GLIBCXX_DEPRECATED_SUGGEST("nullptr")
+       explicit constexpr pair(_U1&& __x, __null_ptr_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>(),
+			     bool> = true>
+       _GLIBCXX_DEPRECATED_SUGGEST("nullptr")
+       constexpr pair(__null_ptr_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>(),
+			     bool> = false>
+       _GLIBCXX_DEPRECATED_SUGGEST("nullptr")
+       explicit pair(__null_ptr_constant, _U2&& __y)
+       : first(nullptr), second(std::forward<_U2>(__y)) { }
+#endif // _GLIBCXX_USE_DEPRECATED
 
       template<typename _U1, typename _U2, typename
 	       enable_if<_PCCP::template
@@ -451,6 +451,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_GLIBCXX20_CONSTEXPR
         pair(tuple<_Args1...>&, tuple<_Args2...>&,
 	     _Index_tuple<_Indexes1...>, _Index_tuple<_Indexes2...>);
+#else
+      // C++03 implementation
+
+      // _GLIBCXX_RESOLVE_LIB_DEFECTS
+      // 265.  std::pair::pair() effects overly restrictive
+      /** The default constructor creates @c first and @c second using their
+       *  respective default constructors.  */
+      pair() : first(), second() { }
+
+      /// Two objects may be passed to a `pair` constructor to be copied.
+      pair(const _T1& __a, const _T2& __b)
+      : first(__a), second(__b) { }
+
+      /// Templated constructor to convert from other pairs.
+      template<typename _U1, typename _U2>
+	pair(const pair<_U1, _U2>& __p)
+	: first(__p.first), second(__p.second) { }
 #endif // C++11
     };
 
diff --git a/libstdc++-v3/testsuite/20_util/pair/40925.cc b/libstdc++-v3/testsuite/20_util/pair/40925.cc
index 157bef621be..c95cb380b18 100644
--- a/libstdc++-v3/testsuite/20_util/pair/40925.cc
+++ b/libstdc++-v3/testsuite/20_util/pair/40925.cc
@@ -20,7 +20,7 @@
 #include <utility>
 
 struct X
-{ 
+{
   explicit X(int, int) { }
 
 private:
@@ -36,7 +36,7 @@ private:
   move_only(const move_only&) = delete;
 };
 
-// libstdc++/40925
+// libstdc++/40925 and LWG 811
 void test01()
 {
   int *ip = 0;
@@ -52,10 +52,12 @@ void test01()
   std::pair<int X::*, int X::*> p7(0, mp);
   std::pair<int X::*, int X::*> p8(mp, mp);
 
-  std::pair<int*, move_only> p9(0, move_only());
-  std::pair<int X::*, move_only> p10(0, move_only());
-  std::pair<move_only, int*> p11(move_only(), 0);
-  std::pair<move_only, int X::*> p12(move_only(), 0);
+  // LWG 811 resolution doesn't support move-only types,
+  // so we have to use nullptr here not a literal 0.
+  std::pair<int*, move_only> p9(nullptr, move_only());
+  std::pair<int X::*, move_only> p10(nullptr, move_only());
+  std::pair<move_only, int*> p11(move_only(), nullptr);
+  std::pair<move_only, int X::*> p12(move_only(), nullptr);
 
   std::pair<int*, move_only> p13(ip, move_only());
   std::pair<int X::*, move_only> p14(mp, move_only());
diff --git a/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc b/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
new file mode 100644
index 00000000000..b3114131a9d
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
@@ -0,0 +1,45 @@
+// Copyright (C) 2021 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-options "-Wdeprecated" }
+// { dg-do compile { target { c++11 } } }
+
+#include <utility>
+
+using std::pair;
+
+struct MoveOnly
+{
+  MoveOnly() = default;
+  MoveOnly(MoveOnly&&) {}
+};
+
+struct ExplicitMoveOnly
+{
+  ExplicitMoveOnly() = default;
+  ExplicitMoveOnly(ExplicitMoveOnly&&) {}
+  explicit ExplicitMoveOnly(MoveOnly&&) {}
+};
+
+// 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*, MoveOnly> v16 = {0, MoveOnly{}}; // { dg-warning "deprecated" }
+pair<MoveOnly, int*> v17 = {MoveOnly{}, 0}; // { dg-warning "deprecated" }
diff --git a/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc b/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
index 3d75e6dbb91..508ca32ecb7 100644
--- a/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
+++ b/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
@@ -126,10 +126,10 @@ struct ExplicitMoveOnly
   explicit ExplicitMoveOnly(MoveOnly&&) {}
 };
 
-std::pair<int*, ExplicitMoveOnly> v14{0, MoveOnly{}};
-std::pair<ExplicitMoveOnly, int*> v15{MoveOnly{}, 0};
+std::pair<int*, ExplicitMoveOnly> v14{nullptr, MoveOnly{}};
+std::pair<ExplicitMoveOnly, int*> v15{MoveOnly{}, nullptr};
 
 std::pair<int*, ExplicitMoveOnly> v16 =
-  {0, MoveOnly{}}; // { dg-error "could not convert" }
+  {nullptr, MoveOnly{}}; // { dg-error "could not convert" }
 std::pair<ExplicitMoveOnly, int*> v17 =
-  {MoveOnly{}, 0}; // { dg-error "could not convert" }
+  {MoveOnly{}, nullptr}; // { dg-error "could not convert" }

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC] Deprecate non-standard constructors in std::pair
  2021-04-07 16:59     ` Jonathan Wakely
@ 2021-04-07 17:18       ` Jonathan Wakely
  2021-04-28 16:57         ` Jonathan Wakely
  2021-04-07 18:00       ` Jonathan Wakely
  2021-04-28 16:57       ` Jonathan Wakely
  2 siblings, 1 reply; 14+ messages in thread
From: Jonathan Wakely @ 2021-04-07 17:18 UTC (permalink / raw)
  To: Ville Voutilainen; +Cc: libstdc++

[-- Attachment #1: Type: text/plain, Size: 947 bytes --]

On 07/04/21 17:59 +0100, Jonathan Wakely wrote:
>On 07/04/21 13:46 +0100, Jonathan Wakely wrote:
>>On 07/04/21 15:41 +0300, Ville Voutilainen via Libstdc++ wrote:
>>>On Wed, 7 Apr 2021 at 15:31, Jonathan Wakely via Libstdc++
>>><libstdc++@gcc.gnu.org> wrote:
>>>>I propose that we deprecate the constructors for C++11/14/17/20 in
>>>>stage 1, and do not support them at all in C++23 mode once P1951 is
>>>>supported. I have a patch which I'll send in stage 1 (it also uses
>>>>C++20 concepts to simplify std::pair and fix PR 97930).
>>>>
>>>>After a period of deprecation we could remove them, and support P1951
>>>>for -std=gnu++11/14/17/20 too so that {} continues to work.
>>>
>>>The proposal sounds good to me.
>>
>>Thanks. I've created https://gcc.gnu.org/PR99957 so I don't forget.
>
>Here's a patch to implement it, for stage 1.

And here's a patch to simplify the std::pair constraints using
concepts, also for consideration in stage 1.



[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 11226 bytes --]

commit 9e184c70f6a8e8ed8e34d8ffcb9c98e079dd3c31
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Apr 7 18:12:08 2021

    libstdc++: Simplify std::pair constraints using concepts
    
    This re-implements the constraints on the std::pair constructors and
    assignment operators in C++20 mode, to use concepts.
    
    The non-standard constructors deprecated for PR 99957 are no longer
    supported in C++20 mode, which requires some minor testsuite changes.
    Otherwise all tests pass in C++20 mode.
    
    libstdc++-v3/ChangeLog:
    
            * include/bits/stl_pair.h (pair):
            * testsuite/20_util/pair/cons/99957.cc: Disable for C++20 and
            later.
            * testsuite/20_util/pair/cons/explicit_construct.cc: Adjust
            expected error messages to also match C++20 errors.

diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
index 883d7441b3d..bd3f911abb9 100644
--- a/libstdc++-v3/include/bits/stl_pair.h
+++ b/libstdc++-v3/include/bits/stl_pair.h
@@ -92,6 +92,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<size_t...>
     struct _Index_tuple;
 
+#if ! __cpp_lib_concepts
   // Concept utility functions, reused in conditionally-explicit
   // constructors.
   // See PR 70437, don't look at is_constructible or
@@ -171,11 +172,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	return false;
       }
     };
+#endif // lib concepts
 #endif // C++11
 
   template<typename _U1, typename _U2> class __pair_base
   {
-#if __cplusplus >= 201103L
+#if __cplusplus >= 201103L && ! __cpp_lib_concepts
     template<typename _T1, typename _T2> friend struct pair;
     __pair_base() = default;
     ~__pair_base() = default;
@@ -196,7 +198,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
    */
   template<typename _T1, typename _T2>
     struct pair
-    : private __pair_base<_T1, _T2>
+    : public __pair_base<_T1, _T2>
     {
       typedef _T1 first_type;    ///< The type of the `first` member
       typedef _T2 second_type;   ///< The type of the `second` member
@@ -205,7 +207,186 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _T2 second;                ///< The second member
 
 #if __cplusplus >= 201103L
-      // C++11 (and later) implementation.
+      constexpr pair(const pair&) = default;	///< Copy constructor
+      constexpr pair(pair&&) = default;		///< Move constructor
+
+      template<typename... _Args1, typename... _Args2>
+	_GLIBCXX20_CONSTEXPR
+	pair(piecewise_construct_t, tuple<_Args1...>, tuple<_Args2...>);
+
+      /// Swap the first members and then the second members.
+      _GLIBCXX20_CONSTEXPR void
+      swap(pair& __p)
+      noexcept(__and_<__is_nothrow_swappable<_T1>,
+		      __is_nothrow_swappable<_T2>>::value)
+      {
+	using std::swap;
+	swap(first, __p.first);
+	swap(second, __p.second);
+      }
+
+    private:
+      template<typename... _Args1, size_t... _Indexes1,
+	       typename... _Args2, size_t... _Indexes2>
+	_GLIBCXX20_CONSTEXPR
+	pair(tuple<_Args1...>&, tuple<_Args2...>&,
+	     _Index_tuple<_Indexes1...>, _Index_tuple<_Indexes2...>);
+    public:
+
+#if __cpp_lib_concepts
+      // C++20 implementation using concepts, explicit(bool), fully constexpr.
+
+      /// Default constructor
+      constexpr
+      explicit(__not_<__and_<__is_implicitly_default_constructible<_T1>,
+			     __is_implicitly_default_constructible<_T2>>>())
+      pair()
+      requires is_default_constructible_v<_T1>
+	       && is_default_constructible_v<_T2>
+      : first(), second()
+      { }
+
+    private:
+
+      /// @cond undocumented
+      template<typename _U1, typename _U2>
+	static constexpr bool
+	_S_constructible()
+	{
+	  if constexpr (is_constructible_v<_T1, _U1>)
+	    return is_constructible_v<_T2, _U2>;
+	  return false;
+	}
+
+      template<typename _U1, typename _U2>
+	static constexpr bool
+	_S_nothrow_constructible()
+	{
+	  if constexpr (is_nothrow_constructible_v<_T1, _U1>)
+	    return is_nothrow_constructible_v<_T2, _U2>;
+	  return false;
+	}
+
+      template<typename _U1, typename _U2>
+	static constexpr bool
+	_S_convertible()
+	{
+	  if constexpr (is_convertible_v<_U1, _T1>)
+	    return is_convertible_v<_U2, _T2>;
+	  return false;
+	}
+      /// @endcond
+
+    public:
+
+      /// Constructor accepting lvalues of `first_type` and `second_type`
+      constexpr explicit(!_S_convertible<const _T1&, const _T2&>())
+      pair(const _T1& __x, const _T2& __y)
+      noexcept(_S_nothrow_constructible<const _T1&, const _T2&>())
+      requires (_S_constructible<const _T1&, const _T2&>())
+      : first(__x), second(__y)
+      { }
+
+      /// Constructor accepting two values of arbitrary types
+      template<typename _U1, typename _U2>
+	requires (_S_constructible<_U1, _U2>())
+	constexpr explicit(!_S_convertible<_U1, _U2>())
+	pair(_U1&& __x, _U2&& __y)
+	noexcept(_S_nothrow_constructible<_U1, _U2>())
+	: first(std::forward<_U1>(__x)), second(std::forward<_U2>(__y))
+	{ }
+
+      /// Converting constructor from a `pair<U1, U2>` lvalue
+      template<typename _U1, typename _U2>
+	requires (_S_constructible<const _U1&, const _U2&>())
+	constexpr explicit(!_S_convertible<const _U1&, const _U2&>())
+	pair(const pair<_U1, _U2>& __p)
+	noexcept(_S_nothrow_constructible<const _U1&, const _U2&>())
+	: first(__p.first), second(__p.second)
+	{ }
+
+      /// Converting constructor from a `pair<U1, U2>` rvalue
+      template<typename _U1, typename _U2>
+	requires (_S_constructible<_U1, _U2>())
+	constexpr explicit(!_S_convertible<_U1, _U2>())
+	pair(pair<_U1, _U2>&& __p)
+	noexcept(_S_nothrow_constructible<_U1, _U2>())
+	: first(std::forward<_U1>(__p.first)),
+	  second(std::forward<_U2>(__p.second))
+	{ }
+
+  private:
+      /// @cond undocumented
+      template<typename _U1, typename _U2>
+	static constexpr bool
+	_S_assignable()
+	{
+	  if constexpr (is_assignable_v<_T1&, _U1>)
+	    return is_assignable_v<_T2&, _U2>;
+	  return false;
+	}
+
+      template<typename _U1, typename _U2>
+	static constexpr bool
+	_S_nothrow_assignable()
+	{
+	  if constexpr (is_nothrow_assignable_v<_T1&, _U1>)
+	    return is_nothrow_assignable_v<_T2&, _U2>;
+	  return false;
+	}
+      /// @endcond
+
+  public:
+
+      pair& operator=(const pair&) = delete;
+
+      /// Copy assignment operator
+      constexpr pair&
+      operator=(const pair& __p)
+      noexcept(_S_nothrow_assignable<const _T1&, const _T2&>())
+      requires (_S_assignable<const _T1&, const _T2&>())
+      {
+	first = __p.first;
+	second = __p.second;
+	return *this;
+      }
+
+      /// Move assignment operator
+      constexpr pair&
+      operator=(pair&& __p)
+      noexcept(_S_nothrow_assignable<_T1, _T2>())
+      requires (_S_assignable<_T1, _T2>())
+      {
+	first = std::forward<first_type>(__p.first);
+	second = std::forward<second_type>(__p.second);
+	return *this;
+      }
+
+      /// Converting assignment from a `pair<U1, U2>` lvalue
+      template<typename _U1, typename _U2>
+	constexpr pair&
+	operator=(const pair<_U1, _U2>& __p)
+	noexcept(_S_nothrow_assignable<const _U1&, const _U2&>())
+	requires (_S_assignable<const _U1&, const _U2&>())
+	{
+	  first = __p.first;
+	  second = __p.second;
+	  return *this;
+	}
+
+      /// Converting assignment from a `pair<U1, U2>` rvalue
+      template<typename _U1, typename _U2>
+	constexpr pair&
+	operator=(pair<_U1, _U2>&& __p)
+	noexcept(_S_nothrow_assignable<_U1, _U2>())
+	requires (_S_assignable<_U1, _U2>())
+	{
+	  first = std::forward<_U1>(__p.first);
+	  second = std::forward<_U2>(__p.second);
+	  return *this;
+	}
+#else
+      // C++11/14/17 implementation using enable_if, partially constexpr.
 
       /** The default constructor creates @c first and @c second using their
        *  respective default constructors.  */
@@ -281,9 +462,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	explicit constexpr pair(const pair<_U1, _U2>& __p)
 	: first(__p.first), second(__p.second) { }
 
-      constexpr pair(const pair&) = default;	///< Copy constructor
-      constexpr pair(pair&&) = default;		///< Move constructor
-
 #if _GLIBCXX_USE_DEPRECATED
     private:
       /// @cond undocumented
@@ -341,7 +519,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        _GLIBCXX_DEPRECATED_SUGGEST("nullptr")
        explicit pair(__null_ptr_constant, _U2&& __y)
        : first(nullptr), second(std::forward<_U2>(__y)) { }
-#endif // _GLIBCXX_USE_DEPRECATED
+#endif
 
       template<typename _U1, typename _U2, typename
 	       enable_if<_PCCP::template
@@ -382,10 +560,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	: first(std::forward<_U1>(__p.first)),
 	  second(std::forward<_U2>(__p.second)) { }
 
-      template<typename... _Args1, typename... _Args2>
-	_GLIBCXX20_CONSTEXPR
-        pair(piecewise_construct_t, tuple<_Args1...>, tuple<_Args2...>);
-
       _GLIBCXX20_CONSTEXPR pair&
       operator=(typename conditional<
 		__and_<is_copy_assignable<_T1>,
@@ -433,24 +607,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  second = std::forward<_U2>(__p.second);
 	  return *this;
 	}
-
-      /// Swap the first members and then the second members.
-      _GLIBCXX20_CONSTEXPR void
-      swap(pair& __p)
-      noexcept(__and_<__is_nothrow_swappable<_T1>,
-                      __is_nothrow_swappable<_T2>>::value)
-      {
-	using std::swap;
-	swap(first, __p.first);
-	swap(second, __p.second);
-      }
-
-    private:
-      template<typename... _Args1, size_t... _Indexes1,
-	       typename... _Args2, size_t... _Indexes2>
-	_GLIBCXX20_CONSTEXPR
-        pair(tuple<_Args1...>&, tuple<_Args2...>&,
-	     _Index_tuple<_Indexes1...>, _Index_tuple<_Indexes2...>);
+#endif // lib concepts
 #else
       // C++03 implementation
 
diff --git a/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc b/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
index d300292b463..a3e6a452583 100644
--- a/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
+++ b/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
@@ -16,7 +16,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-options "-Wdeprecated" }
-// { dg-do compile { target c++11 } }
+// { dg-do compile { target { c++11 && { ! c++20 } } } }
 
 #include <utility>
 
diff --git a/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc b/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
index 508ca32ecb7..ecd5acf9375 100644
--- a/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
+++ b/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
@@ -83,12 +83,12 @@ void f7(std::pair<long, long>) {}
 
 std::pair<ExplicitDefault, int> f8()
 {
-  return {}; // { dg-error "could not convert" }
+  return {}; // { dg-error "convert" }
 }
 
 std::pair<ExplicitDefaultDefault, int> f9()
 {
-  return {}; // { dg-error "could not convert" }
+  return {}; // { dg-error "convert" }
 }
 
 void f10(std::pair<ExplicitDefault, int>) {}
@@ -107,8 +107,8 @@ void test_arg_passing()
   f7({1,2});
   f7(std::pair<int, int>{});
   f7(std::pair<long, long>{});
-  f10({}); // { dg-error "could not convert" }
-  f11({}); // { dg-error "could not convert" }
+  f10({}); // { dg-error "convert" }
+  f11({}); // { dg-error "convert" }
   f10(std::pair<ExplicitDefault, int>{});
   f11(std::pair<ExplicitDefaultDefault, int>{});
 }

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC] Deprecate non-standard constructors in std::pair
  2021-04-07 16:59     ` Jonathan Wakely
  2021-04-07 17:18       ` Jonathan Wakely
@ 2021-04-07 18:00       ` Jonathan Wakely
  2021-04-07 18:17         ` Jonathan Wakely
  2021-04-28 16:57       ` Jonathan Wakely
  2 siblings, 1 reply; 14+ messages in thread
From: Jonathan Wakely @ 2021-04-07 18:00 UTC (permalink / raw)
  To: Ville Voutilainen; +Cc: libstdc++

On 07/04/21 17:59 +0100, Jonathan Wakely wrote:
>On 07/04/21 13:46 +0100, Jonathan Wakely wrote:
>>On 07/04/21 15:41 +0300, Ville Voutilainen via Libstdc++ wrote:
>>>On Wed, 7 Apr 2021 at 15:31, Jonathan Wakely via Libstdc++
>>><libstdc++@gcc.gnu.org> wrote:
>>>>I propose that we deprecate the constructors for C++11/14/17/20 in
>>>>stage 1, and do not support them at all in C++23 mode once P1951 is
>>>>supported. I have a patch which I'll send in stage 1 (it also uses
>>>>C++20 concepts to simplify std::pair and fix PR 97930).
>>>>
>>>>After a period of deprecation we could remove them, and support P1951
>>>>for -std=gnu++11/14/17/20 too so that {} continues to work.
>>>
>>>The proposal sounds good to me.
>>
>>Thanks. I've created https://gcc.gnu.org/PR99957 so I don't forget.
>
>Here's a patch to implement it, for stage 1.


>diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
>index 70262f9508f..883d7441b3d 100644
>--- a/libstdc++-v3/include/bits/stl_pair.h
>+++ b/libstdc++-v3/include/bits/stl_pair.h
>@@ -128,34 +128,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 		      is_convertible<_U2&&, _T2>>::value;
>       }
> 
>-      template <bool __implicit, typename _U1, typename _U2>
>-      static constexpr bool _CopyMovePair()
>-      {
>-	using __do_converts = __and_<is_convertible<const _U1&, _T1>,
>-				  is_convertible<_U2&&, _T2>>;
>-	using __converts = typename conditional<__implicit,
>-				       __do_converts,
>-				       __not_<__do_converts>>::type;
>-	return __and_<is_constructible<_T1, const _U1&>,
>-		      is_constructible<_T2, _U2&&>,
>-		      __converts
>-		      >::value;
>-      }
> 
>       template <bool __implicit, typename _U1, typename _U2>
>-      static constexpr bool _MoveCopyPair()
>+      static constexpr bool _DeprConsPair()
>       {
> 	using __do_converts = __and_<is_convertible<_U1&&, _T1>,
>-				  is_convertible<const _U2&, _T2>>;
>+				     is_convertible<_U2&&, _T2>>;
> 	using __converts = typename conditional<__implicit,
>-				       __do_converts,
>-				       __not_<__do_converts>>::type;
>+						__do_converts,
>+						__not_<__do_converts>>::type;
> 	return __and_<is_constructible<_T1, _U1&&>,
>-		      is_constructible<_T2, const _U2&&>,
>+		      is_constructible<_T2, _U2&&>,

N.B. this fixes a bug in the line above, where const _U2&& is used in
place of const _U2&.

I'll create a testcase that tickles the bug and report it to bugzilla
tomorrow.



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC] Deprecate non-standard constructors in std::pair
  2021-04-07 18:00       ` Jonathan Wakely
@ 2021-04-07 18:17         ` Jonathan Wakely
  2021-04-07 18:25           ` Ville Voutilainen
  0 siblings, 1 reply; 14+ messages in thread
From: Jonathan Wakely @ 2021-04-07 18:17 UTC (permalink / raw)
  To: Ville Voutilainen; +Cc: libstdc++

On 07/04/21 19:00 +0100, Jonathan Wakely wrote:
>On 07/04/21 17:59 +0100, Jonathan Wakely wrote:
>>On 07/04/21 13:46 +0100, Jonathan Wakely wrote:
>>>On 07/04/21 15:41 +0300, Ville Voutilainen via Libstdc++ wrote:
>>>>On Wed, 7 Apr 2021 at 15:31, Jonathan Wakely via Libstdc++
>>>><libstdc++@gcc.gnu.org> wrote:
>>>>>I propose that we deprecate the constructors for C++11/14/17/20 in
>>>>>stage 1, and do not support them at all in C++23 mode once P1951 is
>>>>>supported. I have a patch which I'll send in stage 1 (it also uses
>>>>>C++20 concepts to simplify std::pair and fix PR 97930).
>>>>>
>>>>>After a period of deprecation we could remove them, and support P1951
>>>>>for -std=gnu++11/14/17/20 too so that {} continues to work.
>>>>
>>>>The proposal sounds good to me.
>>>
>>>Thanks. I've created https://gcc.gnu.org/PR99957 so I don't forget.
>>
>>Here's a patch to implement it, for stage 1.
>
>
>>diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
>>index 70262f9508f..883d7441b3d 100644
>>--- a/libstdc++-v3/include/bits/stl_pair.h
>>+++ b/libstdc++-v3/include/bits/stl_pair.h
>>@@ -128,34 +128,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>		      is_convertible<_U2&&, _T2>>::value;
>>      }
>>
>>-      template <bool __implicit, typename _U1, typename _U2>
>>-      static constexpr bool _CopyMovePair()
>>-      {
>>-	using __do_converts = __and_<is_convertible<const _U1&, _T1>,
>>-				  is_convertible<_U2&&, _T2>>;
>>-	using __converts = typename conditional<__implicit,
>>-				       __do_converts,
>>-				       __not_<__do_converts>>::type;
>>-	return __and_<is_constructible<_T1, const _U1&>,
>>-		      is_constructible<_T2, _U2&&>,
>>-		      __converts
>>-		      >::value;
>>-      }
>>
>>      template <bool __implicit, typename _U1, typename _U2>
>>-      static constexpr bool _MoveCopyPair()
>>+      static constexpr bool _DeprConsPair()
>>      {
>>	using __do_converts = __and_<is_convertible<_U1&&, _T1>,
>>-				  is_convertible<const _U2&, _T2>>;
>>+				     is_convertible<_U2&&, _T2>>;
>>	using __converts = typename conditional<__implicit,
>>-				       __do_converts,
>>-				       __not_<__do_converts>>::type;
>>+						__do_converts,
>>+						__not_<__do_converts>>::type;
>>	return __and_<is_constructible<_T1, _U1&&>,
>>-		      is_constructible<_T2, const _U2&&>,
>>+		      is_constructible<_T2, _U2&&>,
>
>N.B. this fixes a bug in the line above, where const _U2&& is used in
>place of const _U2&.
>
>I'll create a testcase that tickles the bug and report it to bugzilla
>tomorrow.

This fails to compile because of that bug:

#include <utility>

struct X {
   X(void* = 0) { }
   X(const X&) = default;
   X(const X&&) = delete;
};

struct move_only {
   move_only() = default;
   move_only(move_only&&) = default;
};

std::pair<move_only, X> p0(move_only(), 0);
std::pair<move_only, X> p1(move_only(), {});

The pair(U1&&, const T2&) constructor should be viable, but it fails
the _MoveCopyPair constraint check because X(const X&&) is deleted.

I'm not sure I care about this though. It would only work because of
those non-standard constructors which we're talking about deprecating.
I'm not very motivated to fix them so they accept this, when we're
going to deprecate them anyway.


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC] Deprecate non-standard constructors in std::pair
  2021-04-07 18:17         ` Jonathan Wakely
@ 2021-04-07 18:25           ` Ville Voutilainen
  2021-04-07 18:26             ` Ville Voutilainen
  0 siblings, 1 reply; 14+ messages in thread
From: Ville Voutilainen @ 2021-04-07 18:25 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: libstdc++

On Wed, 7 Apr 2021 at 21:17, Jonathan Wakely <jwakely@redhat.com> wrote:
> This fails to compile because of that bug:
>
> #include <utility>
>
> struct X {
>    X(void* = 0) { }
>    X(const X&) = default;
>    X(const X&&) = delete;
> };
>
> struct move_only {
>    move_only() = default;
>    move_only(move_only&&) = default;
> };
>
> std::pair<move_only, X> p0(move_only(), 0);
> std::pair<move_only, X> p1(move_only(), {});
>
> The pair(U1&&, const T2&) constructor should be viable, but it fails
> the _MoveCopyPair constraint check because X(const X&&) is deleted.
>
> I'm not sure I care about this though. It would only work because of
> those non-standard constructors which we're talking about deprecating.
> I'm not very motivated to fix them so they accept this, when we're
> going to deprecate them anyway.

Nah, let's not bother. The type X has an explicitly deleted move
constructor, which the library
doesn't go to heroic extents to support.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC] Deprecate non-standard constructors in std::pair
  2021-04-07 18:25           ` Ville Voutilainen
@ 2021-04-07 18:26             ` Ville Voutilainen
  0 siblings, 0 replies; 14+ messages in thread
From: Ville Voutilainen @ 2021-04-07 18:26 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: libstdc++

On Wed, 7 Apr 2021 at 21:25, Ville Voutilainen
<ville.voutilainen@gmail.com> wrote:
>
> On Wed, 7 Apr 2021 at 21:17, Jonathan Wakely <jwakely@redhat.com> wrote:
> > This fails to compile because of that bug:
> >
> > #include <utility>
> >
> > struct X {
> >    X(void* = 0) { }
> >    X(const X&) = default;
> >    X(const X&&) = delete;
> > };
> >
> > struct move_only {
> >    move_only() = default;
> >    move_only(move_only&&) = default;
> > };
> >
> > std::pair<move_only, X> p0(move_only(), 0);
> > std::pair<move_only, X> p1(move_only(), {});
> >
> > The pair(U1&&, const T2&) constructor should be viable, but it fails
> > the _MoveCopyPair constraint check because X(const X&&) is deleted.
> >
> > I'm not sure I care about this though. It would only work because of
> > those non-standard constructors which we're talking about deprecating.
> > I'm not very motivated to fix them so they accept this, when we're
> > going to deprecate them anyway.
>
> Nah, let's not bother. The type X has an explicitly deleted move
> constructor, which the library
> doesn't go to heroic extents to support.

And more than that, it's trying to be a "copy-only" type, which the
library doesn't try to support
at all, quite the contrary.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC] Deprecate non-standard constructors in std::pair
  2021-04-07 16:59     ` Jonathan Wakely
  2021-04-07 17:18       ` Jonathan Wakely
  2021-04-07 18:00       ` Jonathan Wakely
@ 2021-04-28 16:57       ` Jonathan Wakely
  2021-04-28 17:00         ` Jonathan Wakely
  2 siblings, 1 reply; 14+ messages in thread
From: Jonathan Wakely @ 2021-04-28 16:57 UTC (permalink / raw)
  To: Ville Voutilainen; +Cc: libstdc++, gcc-patches

On 07/04/21 17:59 +0100, Jonathan Wakely wrote:
>On 07/04/21 13:46 +0100, Jonathan Wakely wrote:
>>On 07/04/21 15:41 +0300, Ville Voutilainen via Libstdc++ wrote:
>>>On Wed, 7 Apr 2021 at 15:31, Jonathan Wakely via Libstdc++
>>><libstdc++@gcc.gnu.org> wrote:
>>>>I propose that we deprecate the constructors for C++11/14/17/20 in
>>>>stage 1, and do not support them at all in C++23 mode once P1951 is
>>>>supported. I have a patch which I'll send in stage 1 (it also uses
>>>>C++20 concepts to simplify std::pair and fix PR 97930).
>>>>
>>>>After a period of deprecation we could remove them, and support P1951
>>>>for -std=gnu++11/14/17/20 too so that {} continues to work.
>>>
>>>The proposal sounds good to me.
>>
>>Thanks. I've created https://gcc.gnu.org/PR99957 so I don't forget.
>
>Here's a patch to implement it, for stage 1.
>
>    libstdc++: Deprecate non-standard std::pair constructors [PR 99957]
>
>    This deprecates the non-standard std::pair constructors that support
>    construction from an rvalue and a literal zero used as a null pointer
>    constant. We can't just add the deprecated attribute to those
>    constructors, because they're currently used by correct code when they
>    are a better match than the constructors required by the standard e.g.
>
>      int i = 0;
>      const int j = 0;
>      std::pair<int, int> p(i, j); // uses pair(U1&&, const int&)
>
>    This patch adjusts the parameter types and constraints of those
>    constructors so that they only get used for literal zeros, and the
>    pair(U1&&, U2&&) constructor gets used otherwise. Once they're only used
>    for initializations that should be ill-formed we can add the deprecated
>    attribute.
>
>    The deprecated attribute is used to suggest that the user code uses
>    nullptr, which avoids the problem of 0 deducing as int instead of a null
>    pointer constant.


I've pushed this to trunk, after testing on powerpc64le-linux.


>commit e20794c814c5961f0f33381a8eb3dff4fc741b5a
>Author: Jonathan Wakely <jwakely@redhat.com>
>Date:   Wed Apr 7 17:20:43 2021
>
>    libstdc++: Deprecate non-standard std::pair constructors [PR 99957]
>    
>    This deprecates the non-standard std::pair constructors that support
>    construction from an rvalue and a literal zero used as a null pointer
>    constant. We can't just add the deprecated attribute to those
>    constructors, because they're currently used by correct code when they
>    are a better match than the constructors required by the standard e.g.
>    
>      int i = 0;
>      const int j = 0;
>      std::pair<int, int> p(i, j); // uses pair(U1&&, const int&)
>    
>    This patch adjusts the parameter types and constraints of those
>    constructors so that they only get used for literal zeros, and the
>    pair(U1&&, U2&&) constructor gets used otherwise. Once they're only used
>    for initializations that should be ill-formed we can add the deprecated
>    attribute.
>    
>    The deprecated attribute is used to suggest that the user code uses
>    nullptr, which avoids the problem of 0 deducing as int instead of a null
>    pointer constant.
>    
>    libstdc++-v3/ChangeLog:
>    
>            PR libstdc++/99957
>            * include/bits/stl_pair.h (_PCC::_MoveCopyPair, _PCC::_CopyMovePair):
>            Combine and replace with ...
>            (_PCC::_DeprConsPair): New SFINAE helper function.
>            (pair): Merge preprocessor blocks so that all C++03 members
>            are defined together at the end.
>            (pair::pair(const _T1&, _U2&&), pair::pair(_U1&&, const _T2&)):
>            Replace _T1 and _T2 parameters with __null_ptr_constant and
>            adjust constraints.
>            * testsuite/20_util/pair/40925.cc: Use nullptr instead of 0.
>            * testsuite/20_util/pair/cons/explicit_construct.cc: Likewise.
>            * testsuite/20_util/pair/cons/99957.cc: New test.
>
>diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
>index 70262f9508f..883d7441b3d 100644
>--- a/libstdc++-v3/include/bits/stl_pair.h
>+++ b/libstdc++-v3/include/bits/stl_pair.h
>@@ -128,34 +128,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 		      is_convertible<_U2&&, _T2>>::value;
>       }
> 
>-      template <bool __implicit, typename _U1, typename _U2>
>-      static constexpr bool _CopyMovePair()
>-      {
>-	using __do_converts = __and_<is_convertible<const _U1&, _T1>,
>-				  is_convertible<_U2&&, _T2>>;
>-	using __converts = typename conditional<__implicit,
>-				       __do_converts,
>-				       __not_<__do_converts>>::type;
>-	return __and_<is_constructible<_T1, const _U1&>,
>-		      is_constructible<_T2, _U2&&>,
>-		      __converts
>-		      >::value;
>-      }
> 
>       template <bool __implicit, typename _U1, typename _U2>
>-      static constexpr bool _MoveCopyPair()
>+      static constexpr bool _DeprConsPair()
>       {
> 	using __do_converts = __and_<is_convertible<_U1&&, _T1>,
>-				  is_convertible<const _U2&, _T2>>;
>+				     is_convertible<_U2&&, _T2>>;
> 	using __converts = typename conditional<__implicit,
>-				       __do_converts,
>-				       __not_<__do_converts>>::type;
>+						__do_converts,
>+						__not_<__do_converts>>::type;
> 	return __and_<is_constructible<_T1, _U1&&>,
>-		      is_constructible<_T2, const _U2&&>,
>+		      is_constructible<_T2, _U2&&>,
> 		      __converts
>-		      >::value;
>+		     >::value;
>       }
>-  };
>+    };
> 
>   template <typename _T1, typename _T2>
>     struct _PCC<false, _T1, _T2>
>@@ -183,7 +170,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       {
> 	return false;
>       }
>-  };
>+    };
> #endif // C++11
> 
>   template<typename _U1, typename _U2> class __pair_base
>@@ -217,22 +204,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       _T1 first;                 ///< The first member
>       _T2 second;                ///< The second member
> 
>-      // _GLIBCXX_RESOLVE_LIB_DEFECTS
>-      // 265.  std::pair::pair() effects overly restrictive
>+#if __cplusplus >= 201103L
>+      // C++11 (and later) implementation.
>+
>       /** The default constructor creates @c first and @c second using their
>        *  respective default constructors.  */
>-#if __cplusplus >= 201103L
>       template <typename _U1 = _T1,
>                 typename _U2 = _T2,
>                 typename enable_if<__and_<
>                                      __is_implicitly_default_constructible<_U1>,
>                                      __is_implicitly_default_constructible<_U2>>
>                                    ::value, bool>::type = true>
>-#endif
>-      _GLIBCXX_CONSTEXPR pair()
>+      constexpr pair()
>       : first(), second() { }
> 
>-#if __cplusplus >= 201103L
>       template <typename _U1 = _T1,
>                 typename _U2 = _T2,
>                 typename enable_if<__and_<
>@@ -244,13 +229,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>                                    ::value, bool>::type = false>
>       explicit constexpr pair()
>       : first(), second() { }
>-#endif
> 
>-#if __cplusplus < 201103L
>-      /// Two objects may be passed to a @c pair constructor to be copied.
>-      pair(const _T1& __a, const _T2& __b)
>-      : first(__a), second(__b) { }
>-#else
>       // Shortcut for constraining the templates that don't take pairs.
>       /// @cond undocumented
>       using _PCCP = _PCC<true, _T1, _T2>;
>@@ -275,14 +254,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>                          bool>::type=false>
>       explicit constexpr pair(const _T1& __a, const _T2& __b)
>       : first(__a), second(__b) { }
>-#endif
> 
>-#if __cplusplus < 201103L
>-      /// There is also a templated constructor to convert from other pairs.
>-      template<typename _U1, typename _U2>
>-	pair(const pair<_U1, _U2>& __p)
>-	: first(__p.first), second(__p.second) { }
>-#else
>       // Shortcut for constraining the templates that take pairs.
>       /// @cond undocumented
>       template <typename _U1, typename _U2>
>@@ -308,40 +280,68 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>                          bool>::type=false>
> 	explicit constexpr pair(const pair<_U1, _U2>& __p)
> 	: first(__p.first), second(__p.second) { }
>-#endif
> 
>-#if __cplusplus >= 201103L
>       constexpr pair(const pair&) = default;	///< Copy constructor
>       constexpr pair(pair&&) = default;		///< Move constructor
> 
>-      // DR 811.
>-      template<typename _U1, typename
>-	       enable_if<_PCCP::template
>-			   _MoveCopyPair<true, _U1, _T2>(),
>-                         bool>::type=true>
>-       constexpr pair(_U1&& __x, const _T2& __y)
>-       : first(std::forward<_U1>(__x)), second(__y) { }
>+#if _GLIBCXX_USE_DEPRECATED
>+    private:
>+      /// @cond undocumented
> 
>-      template<typename _U1, typename
>-	       enable_if<_PCCP::template
>-			   _MoveCopyPair<false, _U1, _T2>(),
>-                         bool>::type=false>
>-       explicit constexpr pair(_U1&& __x, const _T2& __y)
>-       : first(std::forward<_U1>(__x)), second(__y) { }
>+      // A type which can be constructed from literal zero, but not nullptr
>+      struct __null_ptr_constant
>+      {
>+	__null_ptr_constant(int __null_ptr_constant::*) { }
>+	template<typename _Tp,
>+		 typename = __enable_if_t<is_null_pointer<_Tp>::value>>
>+	__null_ptr_constant(_Tp) = delete;
>+      };
> 
>-      template<typename _U2, typename
>-	       enable_if<_PCCP::template
>-			   _CopyMovePair<true, _T1, _U2>(),
>-                         bool>::type=true>
>-       constexpr pair(const _T1& __x, _U2&& __y)
>-       : first(__x), second(std::forward<_U2>(__y)) { }
>+      // 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&>>;
> 
>-      template<typename _U2, typename
>-	       enable_if<_PCCP::template
>-			   _CopyMovePair<false, _T1, _U2>(),
>-                         bool>::type=false>
>-       explicit pair(const _T1& __x, _U2&& __y)
>-       : first(__x), second(std::forward<_U2>(__y)) { }
>+      /// @endcond
>+    public:
>+
>+      // Deprecated extensions to DR 811.
>+      template<typename _U1,
>+	       __enable_if_t<!__is_lvalue_of<_U1, _T1>::value
>+			     && _PCCP::template
>+			       _DeprConsPair<true, _U1, nullptr_t>(),
>+			     bool> = true>
>+       _GLIBCXX_DEPRECATED_SUGGEST("nullptr")
>+       constexpr pair(_U1&& __x, __null_ptr_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>(),
>+			     bool> = false>
>+       _GLIBCXX_DEPRECATED_SUGGEST("nullptr")
>+       explicit constexpr pair(_U1&& __x, __null_ptr_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>(),
>+			     bool> = true>
>+       _GLIBCXX_DEPRECATED_SUGGEST("nullptr")
>+       constexpr pair(__null_ptr_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>(),
>+			     bool> = false>
>+       _GLIBCXX_DEPRECATED_SUGGEST("nullptr")
>+       explicit pair(__null_ptr_constant, _U2&& __y)
>+       : first(nullptr), second(std::forward<_U2>(__y)) { }
>+#endif // _GLIBCXX_USE_DEPRECATED
> 
>       template<typename _U1, typename _U2, typename
> 	       enable_if<_PCCP::template
>@@ -451,6 +451,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	_GLIBCXX20_CONSTEXPR
>         pair(tuple<_Args1...>&, tuple<_Args2...>&,
> 	     _Index_tuple<_Indexes1...>, _Index_tuple<_Indexes2...>);
>+#else
>+      // C++03 implementation
>+
>+      // _GLIBCXX_RESOLVE_LIB_DEFECTS
>+      // 265.  std::pair::pair() effects overly restrictive
>+      /** The default constructor creates @c first and @c second using their
>+       *  respective default constructors.  */
>+      pair() : first(), second() { }
>+
>+      /// Two objects may be passed to a `pair` constructor to be copied.
>+      pair(const _T1& __a, const _T2& __b)
>+      : first(__a), second(__b) { }
>+
>+      /// Templated constructor to convert from other pairs.
>+      template<typename _U1, typename _U2>
>+	pair(const pair<_U1, _U2>& __p)
>+	: first(__p.first), second(__p.second) { }
> #endif // C++11
>     };
> 
>diff --git a/libstdc++-v3/testsuite/20_util/pair/40925.cc b/libstdc++-v3/testsuite/20_util/pair/40925.cc
>index 157bef621be..c95cb380b18 100644
>--- a/libstdc++-v3/testsuite/20_util/pair/40925.cc
>+++ b/libstdc++-v3/testsuite/20_util/pair/40925.cc
>@@ -20,7 +20,7 @@
> #include <utility>
> 
> struct X
>-{ 
>+{
>   explicit X(int, int) { }
> 
> private:
>@@ -36,7 +36,7 @@ private:
>   move_only(const move_only&) = delete;
> };
> 
>-// libstdc++/40925
>+// libstdc++/40925 and LWG 811
> void test01()
> {
>   int *ip = 0;
>@@ -52,10 +52,12 @@ void test01()
>   std::pair<int X::*, int X::*> p7(0, mp);
>   std::pair<int X::*, int X::*> p8(mp, mp);
> 
>-  std::pair<int*, move_only> p9(0, move_only());
>-  std::pair<int X::*, move_only> p10(0, move_only());
>-  std::pair<move_only, int*> p11(move_only(), 0);
>-  std::pair<move_only, int X::*> p12(move_only(), 0);
>+  // LWG 811 resolution doesn't support move-only types,
>+  // so we have to use nullptr here not a literal 0.
>+  std::pair<int*, move_only> p9(nullptr, move_only());
>+  std::pair<int X::*, move_only> p10(nullptr, move_only());
>+  std::pair<move_only, int*> p11(move_only(), nullptr);
>+  std::pair<move_only, int X::*> p12(move_only(), nullptr);
> 
>   std::pair<int*, move_only> p13(ip, move_only());
>   std::pair<int X::*, move_only> p14(mp, move_only());
>diff --git a/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc b/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
>new file mode 100644
>index 00000000000..b3114131a9d
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
>@@ -0,0 +1,45 @@
>+// Copyright (C) 2021 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-options "-Wdeprecated" }
>+// { dg-do compile { target { c++11 } } }
>+
>+#include <utility>
>+
>+using std::pair;
>+
>+struct MoveOnly
>+{
>+  MoveOnly() = default;
>+  MoveOnly(MoveOnly&&) {}
>+};
>+
>+struct ExplicitMoveOnly
>+{
>+  ExplicitMoveOnly() = default;
>+  ExplicitMoveOnly(ExplicitMoveOnly&&) {}
>+  explicit ExplicitMoveOnly(MoveOnly&&) {}
>+};
>+
>+// 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*, MoveOnly> v16 = {0, MoveOnly{}}; // { dg-warning "deprecated" }
>+pair<MoveOnly, int*> v17 = {MoveOnly{}, 0}; // { dg-warning "deprecated" }
>diff --git a/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc b/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
>index 3d75e6dbb91..508ca32ecb7 100644
>--- a/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
>+++ b/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
>@@ -126,10 +126,10 @@ struct ExplicitMoveOnly
>   explicit ExplicitMoveOnly(MoveOnly&&) {}
> };
> 
>-std::pair<int*, ExplicitMoveOnly> v14{0, MoveOnly{}};
>-std::pair<ExplicitMoveOnly, int*> v15{MoveOnly{}, 0};
>+std::pair<int*, ExplicitMoveOnly> v14{nullptr, MoveOnly{}};
>+std::pair<ExplicitMoveOnly, int*> v15{MoveOnly{}, nullptr};
> 
> std::pair<int*, ExplicitMoveOnly> v16 =
>-  {0, MoveOnly{}}; // { dg-error "could not convert" }
>+  {nullptr, MoveOnly{}}; // { dg-error "could not convert" }
> std::pair<ExplicitMoveOnly, int*> v17 =
>-  {MoveOnly{}, 0}; // { dg-error "could not convert" }
>+  {MoveOnly{}, nullptr}; // { dg-error "could not convert" }


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC] Deprecate non-standard constructors in std::pair
  2021-04-07 17:18       ` Jonathan Wakely
@ 2021-04-28 16:57         ` Jonathan Wakely
  2021-04-28 17:19           ` Jonathan Wakely
  0 siblings, 1 reply; 14+ messages in thread
From: Jonathan Wakely @ 2021-04-28 16:57 UTC (permalink / raw)
  To: Ville Voutilainen; +Cc: libstdc++, gcc-patches

On 07/04/21 18:18 +0100, Jonathan Wakely wrote:
>On 07/04/21 17:59 +0100, Jonathan Wakely wrote:
>>On 07/04/21 13:46 +0100, Jonathan Wakely wrote:
>>>On 07/04/21 15:41 +0300, Ville Voutilainen via Libstdc++ wrote:
>>>>On Wed, 7 Apr 2021 at 15:31, Jonathan Wakely via Libstdc++
>>>><libstdc++@gcc.gnu.org> wrote:
>>>>>I propose that we deprecate the constructors for C++11/14/17/20 in
>>>>>stage 1, and do not support them at all in C++23 mode once P1951 is
>>>>>supported. I have a patch which I'll send in stage 1 (it also uses
>>>>>C++20 concepts to simplify std::pair and fix PR 97930).
>>>>>
>>>>>After a period of deprecation we could remove them, and support P1951
>>>>>for -std=gnu++11/14/17/20 too so that {} continues to work.
>>>>
>>>>The proposal sounds good to me.
>>>
>>>Thanks. I've created https://gcc.gnu.org/PR99957 so I don't forget.
>>
>>Here's a patch to implement it, for stage 1.
>
>And here's a patch to simplify the std::pair constraints using
>concepts, also for consideration in stage 1.

I've pushed this to trunk too, after testing on powerpc64le-linux.

>commit 9e184c70f6a8e8ed8e34d8ffcb9c98e079dd3c31
>Author: Jonathan Wakely <jwakely@redhat.com>
>Date:   Wed Apr 7 18:12:08 2021
>
>    libstdc++: Simplify std::pair constraints using concepts
>    
>    This re-implements the constraints on the std::pair constructors and
>    assignment operators in C++20 mode, to use concepts.
>    
>    The non-standard constructors deprecated for PR 99957 are no longer
>    supported in C++20 mode, which requires some minor testsuite changes.
>    Otherwise all tests pass in C++20 mode.
>    
>    libstdc++-v3/ChangeLog:
>    
>            * include/bits/stl_pair.h (pair):
>            * testsuite/20_util/pair/cons/99957.cc: Disable for C++20 and
>            later.
>            * testsuite/20_util/pair/cons/explicit_construct.cc: Adjust
>            expected error messages to also match C++20 errors.
>
>diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
>index 883d7441b3d..bd3f911abb9 100644
>--- a/libstdc++-v3/include/bits/stl_pair.h
>+++ b/libstdc++-v3/include/bits/stl_pair.h
>@@ -92,6 +92,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   template<size_t...>
>     struct _Index_tuple;
> 
>+#if ! __cpp_lib_concepts
>   // Concept utility functions, reused in conditionally-explicit
>   // constructors.
>   // See PR 70437, don't look at is_constructible or
>@@ -171,11 +172,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	return false;
>       }
>     };
>+#endif // lib concepts
> #endif // C++11
> 
>   template<typename _U1, typename _U2> class __pair_base
>   {
>-#if __cplusplus >= 201103L
>+#if __cplusplus >= 201103L && ! __cpp_lib_concepts
>     template<typename _T1, typename _T2> friend struct pair;
>     __pair_base() = default;
>     ~__pair_base() = default;
>@@ -196,7 +198,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    */
>   template<typename _T1, typename _T2>
>     struct pair
>-    : private __pair_base<_T1, _T2>
>+    : public __pair_base<_T1, _T2>
>     {
>       typedef _T1 first_type;    ///< The type of the `first` member
>       typedef _T2 second_type;   ///< The type of the `second` member
>@@ -205,7 +207,186 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       _T2 second;                ///< The second member
> 
> #if __cplusplus >= 201103L
>-      // C++11 (and later) implementation.
>+      constexpr pair(const pair&) = default;	///< Copy constructor
>+      constexpr pair(pair&&) = default;		///< Move constructor
>+
>+      template<typename... _Args1, typename... _Args2>
>+	_GLIBCXX20_CONSTEXPR
>+	pair(piecewise_construct_t, tuple<_Args1...>, tuple<_Args2...>);
>+
>+      /// Swap the first members and then the second members.
>+      _GLIBCXX20_CONSTEXPR void
>+      swap(pair& __p)
>+      noexcept(__and_<__is_nothrow_swappable<_T1>,
>+		      __is_nothrow_swappable<_T2>>::value)
>+      {
>+	using std::swap;
>+	swap(first, __p.first);
>+	swap(second, __p.second);
>+      }
>+
>+    private:
>+      template<typename... _Args1, size_t... _Indexes1,
>+	       typename... _Args2, size_t... _Indexes2>
>+	_GLIBCXX20_CONSTEXPR
>+	pair(tuple<_Args1...>&, tuple<_Args2...>&,
>+	     _Index_tuple<_Indexes1...>, _Index_tuple<_Indexes2...>);
>+    public:
>+
>+#if __cpp_lib_concepts
>+      // C++20 implementation using concepts, explicit(bool), fully constexpr.
>+
>+      /// Default constructor
>+      constexpr
>+      explicit(__not_<__and_<__is_implicitly_default_constructible<_T1>,
>+			     __is_implicitly_default_constructible<_T2>>>())
>+      pair()
>+      requires is_default_constructible_v<_T1>
>+	       && is_default_constructible_v<_T2>
>+      : first(), second()
>+      { }
>+
>+    private:
>+
>+      /// @cond undocumented
>+      template<typename _U1, typename _U2>
>+	static constexpr bool
>+	_S_constructible()
>+	{
>+	  if constexpr (is_constructible_v<_T1, _U1>)
>+	    return is_constructible_v<_T2, _U2>;
>+	  return false;
>+	}
>+
>+      template<typename _U1, typename _U2>
>+	static constexpr bool
>+	_S_nothrow_constructible()
>+	{
>+	  if constexpr (is_nothrow_constructible_v<_T1, _U1>)
>+	    return is_nothrow_constructible_v<_T2, _U2>;
>+	  return false;
>+	}
>+
>+      template<typename _U1, typename _U2>
>+	static constexpr bool
>+	_S_convertible()
>+	{
>+	  if constexpr (is_convertible_v<_U1, _T1>)
>+	    return is_convertible_v<_U2, _T2>;
>+	  return false;
>+	}
>+      /// @endcond
>+
>+    public:
>+
>+      /// Constructor accepting lvalues of `first_type` and `second_type`
>+      constexpr explicit(!_S_convertible<const _T1&, const _T2&>())
>+      pair(const _T1& __x, const _T2& __y)
>+      noexcept(_S_nothrow_constructible<const _T1&, const _T2&>())
>+      requires (_S_constructible<const _T1&, const _T2&>())
>+      : first(__x), second(__y)
>+      { }
>+
>+      /// Constructor accepting two values of arbitrary types
>+      template<typename _U1, typename _U2>
>+	requires (_S_constructible<_U1, _U2>())
>+	constexpr explicit(!_S_convertible<_U1, _U2>())
>+	pair(_U1&& __x, _U2&& __y)
>+	noexcept(_S_nothrow_constructible<_U1, _U2>())
>+	: first(std::forward<_U1>(__x)), second(std::forward<_U2>(__y))
>+	{ }
>+
>+      /// Converting constructor from a `pair<U1, U2>` lvalue
>+      template<typename _U1, typename _U2>
>+	requires (_S_constructible<const _U1&, const _U2&>())
>+	constexpr explicit(!_S_convertible<const _U1&, const _U2&>())
>+	pair(const pair<_U1, _U2>& __p)
>+	noexcept(_S_nothrow_constructible<const _U1&, const _U2&>())
>+	: first(__p.first), second(__p.second)
>+	{ }
>+
>+      /// Converting constructor from a `pair<U1, U2>` rvalue
>+      template<typename _U1, typename _U2>
>+	requires (_S_constructible<_U1, _U2>())
>+	constexpr explicit(!_S_convertible<_U1, _U2>())
>+	pair(pair<_U1, _U2>&& __p)
>+	noexcept(_S_nothrow_constructible<_U1, _U2>())
>+	: first(std::forward<_U1>(__p.first)),
>+	  second(std::forward<_U2>(__p.second))
>+	{ }
>+
>+  private:
>+      /// @cond undocumented
>+      template<typename _U1, typename _U2>
>+	static constexpr bool
>+	_S_assignable()
>+	{
>+	  if constexpr (is_assignable_v<_T1&, _U1>)
>+	    return is_assignable_v<_T2&, _U2>;
>+	  return false;
>+	}
>+
>+      template<typename _U1, typename _U2>
>+	static constexpr bool
>+	_S_nothrow_assignable()
>+	{
>+	  if constexpr (is_nothrow_assignable_v<_T1&, _U1>)
>+	    return is_nothrow_assignable_v<_T2&, _U2>;
>+	  return false;
>+	}
>+      /// @endcond
>+
>+  public:
>+
>+      pair& operator=(const pair&) = delete;
>+
>+      /// Copy assignment operator
>+      constexpr pair&
>+      operator=(const pair& __p)
>+      noexcept(_S_nothrow_assignable<const _T1&, const _T2&>())
>+      requires (_S_assignable<const _T1&, const _T2&>())
>+      {
>+	first = __p.first;
>+	second = __p.second;
>+	return *this;
>+      }
>+
>+      /// Move assignment operator
>+      constexpr pair&
>+      operator=(pair&& __p)
>+      noexcept(_S_nothrow_assignable<_T1, _T2>())
>+      requires (_S_assignable<_T1, _T2>())
>+      {
>+	first = std::forward<first_type>(__p.first);
>+	second = std::forward<second_type>(__p.second);
>+	return *this;
>+      }
>+
>+      /// Converting assignment from a `pair<U1, U2>` lvalue
>+      template<typename _U1, typename _U2>
>+	constexpr pair&
>+	operator=(const pair<_U1, _U2>& __p)
>+	noexcept(_S_nothrow_assignable<const _U1&, const _U2&>())
>+	requires (_S_assignable<const _U1&, const _U2&>())
>+	{
>+	  first = __p.first;
>+	  second = __p.second;
>+	  return *this;
>+	}
>+
>+      /// Converting assignment from a `pair<U1, U2>` rvalue
>+      template<typename _U1, typename _U2>
>+	constexpr pair&
>+	operator=(pair<_U1, _U2>&& __p)
>+	noexcept(_S_nothrow_assignable<_U1, _U2>())
>+	requires (_S_assignable<_U1, _U2>())
>+	{
>+	  first = std::forward<_U1>(__p.first);
>+	  second = std::forward<_U2>(__p.second);
>+	  return *this;
>+	}
>+#else
>+      // C++11/14/17 implementation using enable_if, partially constexpr.
> 
>       /** The default constructor creates @c first and @c second using their
>        *  respective default constructors.  */
>@@ -281,9 +462,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	explicit constexpr pair(const pair<_U1, _U2>& __p)
> 	: first(__p.first), second(__p.second) { }
> 
>-      constexpr pair(const pair&) = default;	///< Copy constructor
>-      constexpr pair(pair&&) = default;		///< Move constructor
>-
> #if _GLIBCXX_USE_DEPRECATED
>     private:
>       /// @cond undocumented
>@@ -341,7 +519,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        _GLIBCXX_DEPRECATED_SUGGEST("nullptr")
>        explicit pair(__null_ptr_constant, _U2&& __y)
>        : first(nullptr), second(std::forward<_U2>(__y)) { }
>-#endif // _GLIBCXX_USE_DEPRECATED
>+#endif
> 
>       template<typename _U1, typename _U2, typename
> 	       enable_if<_PCCP::template
>@@ -382,10 +560,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	: first(std::forward<_U1>(__p.first)),
> 	  second(std::forward<_U2>(__p.second)) { }
> 
>-      template<typename... _Args1, typename... _Args2>
>-	_GLIBCXX20_CONSTEXPR
>-        pair(piecewise_construct_t, tuple<_Args1...>, tuple<_Args2...>);
>-
>       _GLIBCXX20_CONSTEXPR pair&
>       operator=(typename conditional<
> 		__and_<is_copy_assignable<_T1>,
>@@ -433,24 +607,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	  second = std::forward<_U2>(__p.second);
> 	  return *this;
> 	}
>-
>-      /// Swap the first members and then the second members.
>-      _GLIBCXX20_CONSTEXPR void
>-      swap(pair& __p)
>-      noexcept(__and_<__is_nothrow_swappable<_T1>,
>-                      __is_nothrow_swappable<_T2>>::value)
>-      {
>-	using std::swap;
>-	swap(first, __p.first);
>-	swap(second, __p.second);
>-      }
>-
>-    private:
>-      template<typename... _Args1, size_t... _Indexes1,
>-	       typename... _Args2, size_t... _Indexes2>
>-	_GLIBCXX20_CONSTEXPR
>-        pair(tuple<_Args1...>&, tuple<_Args2...>&,
>-	     _Index_tuple<_Indexes1...>, _Index_tuple<_Indexes2...>);
>+#endif // lib concepts
> #else
>       // C++03 implementation
> 
>diff --git a/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc b/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
>index d300292b463..a3e6a452583 100644
>--- a/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
>+++ b/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
>@@ -16,7 +16,7 @@
> // <http://www.gnu.org/licenses/>.
> 
> // { dg-options "-Wdeprecated" }
>-// { dg-do compile { target c++11 } }
>+// { dg-do compile { target { c++11 && { ! c++20 } } } }
> 
> #include <utility>
> 
>diff --git a/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc b/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
>index 508ca32ecb7..ecd5acf9375 100644
>--- a/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
>+++ b/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
>@@ -83,12 +83,12 @@ void f7(std::pair<long, long>) {}
> 
> std::pair<ExplicitDefault, int> f8()
> {
>-  return {}; // { dg-error "could not convert" }
>+  return {}; // { dg-error "convert" }
> }
> 
> std::pair<ExplicitDefaultDefault, int> f9()
> {
>-  return {}; // { dg-error "could not convert" }
>+  return {}; // { dg-error "convert" }
> }
> 
> void f10(std::pair<ExplicitDefault, int>) {}
>@@ -107,8 +107,8 @@ void test_arg_passing()
>   f7({1,2});
>   f7(std::pair<int, int>{});
>   f7(std::pair<long, long>{});
>-  f10({}); // { dg-error "could not convert" }
>-  f11({}); // { dg-error "could not convert" }
>+  f10({}); // { dg-error "convert" }
>+  f11({}); // { dg-error "convert" }
>   f10(std::pair<ExplicitDefault, int>{});
>   f11(std::pair<ExplicitDefaultDefault, int>{});
> }


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC] Deprecate non-standard constructors in std::pair
  2021-04-28 16:57       ` Jonathan Wakely
@ 2021-04-28 17:00         ` Jonathan Wakely
  2021-04-28 17:11           ` Jonathan Wakely
  0 siblings, 1 reply; 14+ messages in thread
From: Jonathan Wakely @ 2021-04-28 17:00 UTC (permalink / raw)
  To: Ville Voutilainen; +Cc: libstdc++, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 2100 bytes --]

On 28/04/21 17:57 +0100, Jonathan Wakely wrote:
>On 07/04/21 17:59 +0100, Jonathan Wakely wrote:
>>On 07/04/21 13:46 +0100, Jonathan Wakely wrote:
>>>On 07/04/21 15:41 +0300, Ville Voutilainen via Libstdc++ wrote:
>>>>On Wed, 7 Apr 2021 at 15:31, Jonathan Wakely via Libstdc++
>>>><libstdc++@gcc.gnu.org> wrote:
>>>>>I propose that we deprecate the constructors for C++11/14/17/20 in
>>>>>stage 1, and do not support them at all in C++23 mode once P1951 is
>>>>>supported. I have a patch which I'll send in stage 1 (it also uses
>>>>>C++20 concepts to simplify std::pair and fix PR 97930).
>>>>>
>>>>>After a period of deprecation we could remove them, and support P1951
>>>>>for -std=gnu++11/14/17/20 too so that {} continues to work.
>>>>
>>>>The proposal sounds good to me.
>>>
>>>Thanks. I've created https://gcc.gnu.org/PR99957 so I don't forget.
>>
>>Here's a patch to implement it, for stage 1.
>>
>>   libstdc++: Deprecate non-standard std::pair constructors [PR 99957]
>>
>>   This deprecates the non-standard std::pair constructors that support
>>   construction from an rvalue and a literal zero used as a null pointer
>>   constant. We can't just add the deprecated attribute to those
>>   constructors, because they're currently used by correct code when they
>>   are a better match than the constructors required by the standard e.g.
>>
>>     int i = 0;
>>     const int j = 0;
>>     std::pair<int, int> p(i, j); // uses pair(U1&&, const int&)
>>
>>   This patch adjusts the parameter types and constraints of those
>>   constructors so that they only get used for literal zeros, and the
>>   pair(U1&&, U2&&) constructor gets used otherwise. Once they're only used
>>   for initializations that should be ill-formed we can add the deprecated
>>   attribute.
>>
>>   The deprecated attribute is used to suggest that the user code uses
>>   nullptr, which avoids the problem of 0 deducing as int instead of a null
>>   pointer constant.
>
>
>I've pushed this to trunk, after testing on powerpc64le-linux.

And here's the wwwdocs patch announcing the deprecation.

Pushed to wwwdocs.


[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 941 bytes --]

commit 24151c175c08222c268d9c37cc8fa255e2b2384c
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Apr 28 17:59:50 2021 +0100

    Document deprecation of non-standard std::pair constructors for GCC 12

diff --git a/htdocs/gcc-12/changes.html b/htdocs/gcc-12/changes.html
index e0ac986e..912fc59b 100644
--- a/htdocs/gcc-12/changes.html
+++ b/htdocs/gcc-12/changes.html
@@ -30,6 +30,13 @@ a work-in-progress.</p>
 <!-- .................................................................. -->
 <h2>Caveats</h2>
 <ul>
+  <li>
+    Two non-standard <tt>std::pair</tt> constructors have been deprecated.
+    These allowed the use of an rvalue and a literal <tt>0</tt> to construct
+    a pair containing a move-only type and a pointer. The <tt>nullptr</tt>
+    keyword should be used to initialize the pointer member instead of a
+    literal <tt>0</tt>, as this is portable to other C++ implementations.
+  </li>
   <li>...</li>
 </ul>
 

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC] Deprecate non-standard constructors in std::pair
  2021-04-28 17:00         ` Jonathan Wakely
@ 2021-04-28 17:11           ` Jonathan Wakely
  0 siblings, 0 replies; 14+ messages in thread
From: Jonathan Wakely @ 2021-04-28 17:11 UTC (permalink / raw)
  To: Ville Voutilainen; +Cc: libstdc++, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 2219 bytes --]

On 28/04/21 18:00 +0100, Jonathan Wakely wrote:
>On 28/04/21 17:57 +0100, Jonathan Wakely wrote:
>>On 07/04/21 17:59 +0100, Jonathan Wakely wrote:
>>>On 07/04/21 13:46 +0100, Jonathan Wakely wrote:
>>>>On 07/04/21 15:41 +0300, Ville Voutilainen via Libstdc++ wrote:
>>>>>On Wed, 7 Apr 2021 at 15:31, Jonathan Wakely via Libstdc++
>>>>><libstdc++@gcc.gnu.org> wrote:
>>>>>>I propose that we deprecate the constructors for C++11/14/17/20 in
>>>>>>stage 1, and do not support them at all in C++23 mode once P1951 is
>>>>>>supported. I have a patch which I'll send in stage 1 (it also uses
>>>>>>C++20 concepts to simplify std::pair and fix PR 97930).
>>>>>>
>>>>>>After a period of deprecation we could remove them, and support P1951
>>>>>>for -std=gnu++11/14/17/20 too so that {} continues to work.
>>>>>
>>>>>The proposal sounds good to me.
>>>>
>>>>Thanks. I've created https://gcc.gnu.org/PR99957 so I don't forget.
>>>
>>>Here's a patch to implement it, for stage 1.
>>>
>>>  libstdc++: Deprecate non-standard std::pair constructors [PR 99957]
>>>
>>>  This deprecates the non-standard std::pair constructors that support
>>>  construction from an rvalue and a literal zero used as a null pointer
>>>  constant. We can't just add the deprecated attribute to those
>>>  constructors, because they're currently used by correct code when they
>>>  are a better match than the constructors required by the standard e.g.
>>>
>>>    int i = 0;
>>>    const int j = 0;
>>>    std::pair<int, int> p(i, j); // uses pair(U1&&, const int&)
>>>
>>>  This patch adjusts the parameter types and constraints of those
>>>  constructors so that they only get used for literal zeros, and the
>>>  pair(U1&&, U2&&) constructor gets used otherwise. Once they're only used
>>>  for initializations that should be ill-formed we can add the deprecated
>>>  attribute.
>>>
>>>  The deprecated attribute is used to suggest that the user code uses
>>>  nullptr, which avoids the problem of 0 deducing as int instead of a null
>>>  pointer constant.
>>
>>
>>I've pushed this to trunk, after testing on powerpc64le-linux.
>
>And here's the wwwdocs patch announcing the deprecation.

And the inevitable html validation fix.

Pushed to wwwdocs.



[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 1250 bytes --]

commit b04fd89cb8bc48bad63fb59c91400bbf70e1f510
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Apr 28 18:08:33 2021 +0100

    Replace <tt> elements with <code> for HTML5 compatibility

diff --git a/htdocs/gcc-12/changes.html b/htdocs/gcc-12/changes.html
index 912fc59b..23f71411 100644
--- a/htdocs/gcc-12/changes.html
+++ b/htdocs/gcc-12/changes.html
@@ -31,11 +31,12 @@ a work-in-progress.</p>
 <h2>Caveats</h2>
 <ul>
   <li>
-    Two non-standard <tt>std::pair</tt> constructors have been deprecated.
-    These allowed the use of an rvalue and a literal <tt>0</tt> to construct
-    a pair containing a move-only type and a pointer. The <tt>nullptr</tt>
-    keyword should be used to initialize the pointer member instead of a
-    literal <tt>0</tt>, as this is portable to other C++ implementations.
+    Two non-standard <code>std::pair</code> constructors have been deprecated.
+    These allowed the use of an rvalue and a literal <code>0</code> to
+    construct a pair containing a move-only type and a pointer.
+    The <code>nullptr</code> keyword should be used to initialize the pointer
+    member instead of a literal <code>0</code>, as this is portable to other
+    C++ implementations.
   </li>
   <li>...</li>
 </ul>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC] Deprecate non-standard constructors in std::pair
  2021-04-28 16:57         ` Jonathan Wakely
@ 2021-04-28 17:19           ` Jonathan Wakely
  0 siblings, 0 replies; 14+ messages in thread
From: Jonathan Wakely @ 2021-04-28 17:19 UTC (permalink / raw)
  To: Ville Voutilainen; +Cc: libstdc++, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1318 bytes --]

On 28/04/21 17:57 +0100, Jonathan Wakely wrote:
>On 07/04/21 18:18 +0100, Jonathan Wakely wrote:
>>On 07/04/21 17:59 +0100, Jonathan Wakely wrote:
>>>On 07/04/21 13:46 +0100, Jonathan Wakely wrote:
>>>>On 07/04/21 15:41 +0300, Ville Voutilainen via Libstdc++ wrote:
>>>>>On Wed, 7 Apr 2021 at 15:31, Jonathan Wakely via Libstdc++
>>>>><libstdc++@gcc.gnu.org> wrote:
>>>>>>I propose that we deprecate the constructors for C++11/14/17/20 in
>>>>>>stage 1, and do not support them at all in C++23 mode once P1951 is
>>>>>>supported. I have a patch which I'll send in stage 1 (it also uses
>>>>>>C++20 concepts to simplify std::pair and fix PR 97930).
>>>>>>
>>>>>>After a period of deprecation we could remove them, and support P1951
>>>>>>for -std=gnu++11/14/17/20 too so that {} continues to work.
>>>>>
>>>>>The proposal sounds good to me.
>>>>
>>>>Thanks. I've created https://gcc.gnu.org/PR99957 so I don't forget.
>>>
>>>Here's a patch to implement it, for stage 1.
>>
>>And here's a patch to simplify the std::pair constraints using
>>concepts, also for consideration in stage 1.
>
>I've pushed this to trunk too, after testing on powerpc64le-linux.

And this adds a testcase to verify that the simplified version using
concepts actually fixes PR 97930, as claimed above.

Tested x86_64- linux, pushed to trunk.



[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 1066 bytes --]

commit c8767ee9f9355a63bfeb8318df32bc39c5b0f3ad
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Apr 28 18:14:05 2021

    libstdc++: Add testcase for std::pair as a structural type [PR 97930]
    
    This PR was fixed by r12-221-ge1543e694dadf1ea70eb72325219bc0cdc914a35
    (for compilers that support C++20 Concepts) so this adds the testcase.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/97930
            * testsuite/20_util/pair/requirements/structural.cc: New test.

diff --git a/libstdc++-v3/testsuite/20_util/pair/requirements/structural.cc b/libstdc++-v3/testsuite/20_util/pair/requirements/structural.cc
new file mode 100644
index 00000000000..d4df20197ee
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/pair/requirements/structural.cc
@@ -0,0 +1,9 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do compile { target c++20 } }
+
+#include <utility>
+
+// C++20 20.4.2 [pairs.pair]
+// pair<T, U> is a structural type (13.2) if T and U are both structural types.
+
+template<std::pair<int, int>> struct S; // PR libstdc++/97930

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2021-04-28 17:19 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-07 12:30 [RFC] Deprecate non-standard constructors in std::pair Jonathan Wakely
2021-04-07 12:41 ` Ville Voutilainen
2021-04-07 12:46   ` Jonathan Wakely
2021-04-07 16:59     ` Jonathan Wakely
2021-04-07 17:18       ` Jonathan Wakely
2021-04-28 16:57         ` Jonathan Wakely
2021-04-28 17:19           ` Jonathan Wakely
2021-04-07 18:00       ` Jonathan Wakely
2021-04-07 18:17         ` Jonathan Wakely
2021-04-07 18:25           ` Ville Voutilainen
2021-04-07 18:26             ` Ville Voutilainen
2021-04-28 16:57       ` Jonathan Wakely
2021-04-28 17:00         ` Jonathan Wakely
2021-04-28 17:11           ` Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).