diff mbox series

PR libstdc++/78420 Make std::less etc. yield total order for pointers

Message ID CAH6eHdQ5=_zoCMiThc4_c3kAAPJ94S+0bmxTZ9tB3Se6c+LoXQ@mail.gmail.com
State New
Headers show
Series PR libstdc++/78420 Make std::less etc. yield total order for pointers | expand

Commit Message

Jonathan Wakely March 9, 2018, 12:41 a.m. UTC
In order to meet the total order requirements of [comparisons] p2 we
need to cast unrelated pointers to uintptr_t before comparing them.
Those casts aren't allowed in constant expressions, so only cast
when __builtin_constant_p says the result of the comparison is not a
compile-time constant (because the arguments are not constants, or the
result of the comparison is unspecified). When the result is constant
just compare the pointers directly without casting.

This ensures that the function can be called in constant expressions
with suitable arguments, but still yields a total order even for
otherwise unspecified pointer comparisons.

        PR libstdc++/78420
        * include/bits/stl_function.h (__ptr_rel_ops): New struct providing
        relational operators defining total order on pointers.
        (greater<_Tp*>, less<_Tp*>, greater_equal<_Tp*>, less_equal<_Tp>*):
        New partial specializations for pointers, using __ptr_rel_ops.
        (greater<void>, less<void>, greater_equal<void>, less_equal<void*):
        Dispatch to __ptr_rel_ops when both arguments are pointers.
        * testsuite/20_util/function_objects/comparisons_pointer.cc: New.

Tested powerpc64le-linux. I intend to commit this to trunk and
backport to the active branches, unless anybody sees a problem with
this solution, or has any suggestions for improvement.
commit e03605434a5c378c55e1961402319a20af75a909
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Mar 8 20:27:04 2018 +0000

    PR libstdc++/78420 Make std::less etc. yield total order for pointers
    
    In order to meet the total order requirements of [comparisons] p2 we
    need to cast unrelated pointers to uintptr_t before comparing them.
    Those casts aren't allowed in constant expressions, so only cast
    when __builtin_constant_p says the result of the comparison is not a
    compile-time constant (because the arguments are not constants, or the
    result of the comparison is unspecified). When the result is constant
    just compare the pointers directly without casting.
    
    This ensures that the function can be called in constant expressions
    with suitable arguments, but still yields a total order even for
    otherwise unspecified pointer comparisons.
    
            PR libstdc++/78420
            * include/bits/stl_function.h (__ptr_rel_ops): New struct providing
            relational operators defining total order on pointers.
            (greater<_Tp*>, less<_Tp*>, greater_equal<_Tp*>, less_equal<_Tp>*):
            New partial specializations for pointers, using __ptr_rel_ops.
            (greater<void>, less<void>, greater_equal<void>, less_equal<void*):
            Dispatch to __ptr_rel_ops when both arguments are pointers.
            * testsuite/20_util/function_objects/comparisons_pointer.cc: New.

Comments

Jonathan Wakely March 13, 2018, 4:30 p.m. UTC | #1
On 9 March 2018 at 00:41, Jonathan Wakely wrote:
> In order to meet the total order requirements of [comparisons] p2 we
> need to cast unrelated pointers to uintptr_t before comparing them.
> Those casts aren't allowed in constant expressions, so only cast
> when __builtin_constant_p says the result of the comparison is not a
> compile-time constant (because the arguments are not constants, or the
> result of the comparison is unspecified). When the result is constant
> just compare the pointers directly without casting.
>
> This ensures that the function can be called in constant expressions
> with suitable arguments, but still yields a total order even for
> otherwise unspecified pointer comparisons.
>
>         PR libstdc++/78420
>         * include/bits/stl_function.h (__ptr_rel_ops): New struct providing
>         relational operators defining total order on pointers.
>         (greater<_Tp*>, less<_Tp*>, greater_equal<_Tp*>, less_equal<_Tp>*):
>         New partial specializations for pointers, using __ptr_rel_ops.
>         (greater<void>, less<void>, greater_equal<void>, less_equal<void*):
>         Dispatch to __ptr_rel_ops when both arguments are pointers.
>         * testsuite/20_util/function_objects/comparisons_pointer.cc: New.
>
> Tested powerpc64le-linux. I intend to commit this to trunk and
> backport to the active branches, unless anybody sees a problem with
> this solution, or has any suggestions for improvement.

I intentionally used "_Tp* const&" for the parameter types of the new
partial specializations like greater<_Tp*>, because that's what the
standard says for the primary templates. But it would be more
efficient to pass pointers by value, and 20.5.5.5 [member.functions]
p2 allows us to do that. Here's a new patch with that change.
commit e783a8e1c3cc0d41221796a9cadea796269a95a0
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Mar 8 20:27:04 2018 +0000

    PR libstdc++/78420 Make std::less etc. yield total order for pointers
    
    In order to meet the total order requirements of [comparisons] p2 we
    need to cast unrelated pointers to uintptr_t before comparing them.
    Those casts aren't allowed in constant expressions, so only cast
    when __builtin_constant_p says the result of the comparison is not a
    compile-time constant (because the arguments are not constants, or the
    result of the comparison is unspecified). When the result is constant
    just compare the pointers directly without casting.
    
    This ensures that the function can be called in constant expressions
    with suitable arguments, but still yields a total order even for
    otherwise unspecified pointer comparisons.
    
            PR libstdc++/78420
            * include/bits/stl_function.h (__ptr_rel_ops): New struct providing
            relational operators defining total order on pointers.
            (greater<_Tp*>, less<_Tp*>, greater_equal<_Tp*>, less_equal<_Tp>*):
            New partial specializations for pointers, using __ptr_rel_ops.
            (greater<void>, less<void>, greater_equal<void>, less_equal<void*):
            Dispatch to __ptr_rel_ops when both arguments are pointers.
            * testsuite/20_util/function_objects/comparisons_pointer.cc: New.

diff --git a/libstdc++-v3/include/bits/stl_function.h b/libstdc++-v3/include/bits/stl_function.h
index f5f98b25395..b2c356c1b13 100644
--- a/libstdc++-v3/include/bits/stl_function.h
+++ b/libstdc++-v3/include/bits/stl_function.h
@@ -406,14 +406,91 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { return __x <= __y; }
     };
 
-#if __cplusplus > 201103L
+  struct __ptr_rel_ops
+  {
+    __ptr_rel_ops() = delete;
+    ~__ptr_rel_ops() = delete;
+
+    template<typename _Tp, typename _Up>
+      static _GLIBCXX14_CONSTEXPR bool
+      _S_greater(_Tp* __x, _Up* __y)
+      {
+	if (__builtin_constant_p (__x > __y))
+	  return __x > __y;
+	return (__UINTPTR_TYPE__)__x > (__UINTPTR_TYPE__)__y;
+      }
+
+    template<typename _Tp, typename _Up>
+      static _GLIBCXX14_CONSTEXPR bool
+      _S_less(_Tp* __x, _Up* __y)
+      {
+	if (__builtin_constant_p (__x < __y))
+	  return __x < __y;
+	return (__UINTPTR_TYPE__)__x < (__UINTPTR_TYPE__)__y;
+      }
+
+    template<typename _Tp, typename _Up>
+      static _GLIBCXX14_CONSTEXPR bool
+      _S_greater_equal(_Tp* __x, _Up* __y)
+      {
+	if (__builtin_constant_p (__x >= __y))
+	  return __x >= __y;
+	return (__UINTPTR_TYPE__)__x >= (__UINTPTR_TYPE__)__y;
+      }
+
+    template<typename _Tp, typename _Up>
+      static _GLIBCXX14_CONSTEXPR bool
+      _S_less_equal(_Tp* __x, _Up* __y)
+      {
+	if (__builtin_constant_p (__x <= __y))
+	  return __x <= __y;
+	return (__UINTPTR_TYPE__)__x <= (__UINTPTR_TYPE__)__y;
+      }
+  };
+
+  // Partial specialization of std::greater for pointers.
+  template<typename _Tp>
+    struct greater<_Tp*> : public binary_function<_Tp*, _Tp*, bool>
+    {
+      _GLIBCXX14_CONSTEXPR bool
+      operator()(_Tp* __x, _Tp* __y) const _GLIBCXX_NOTHROW
+      { return __ptr_rel_ops::_S_greater(__x, __y); }
+    };
+
+  // Partial specialization of std::less for pointers.
+  template<typename _Tp>
+    struct less<_Tp*> : public binary_function<_Tp*, _Tp*, bool>
+    {
+      _GLIBCXX14_CONSTEXPR bool
+      operator()(_Tp* __x, _Tp* __y) const _GLIBCXX_NOTHROW
+      { return __ptr_rel_ops::_S_less(__x, __y); }
+    };
+
+  // Partial specialization of std::greater_equal for pointers.
+  template<typename _Tp>
+    struct greater_equal<_Tp*> : public binary_function<_Tp*, _Tp*, bool>
+    {
+      _GLIBCXX14_CONSTEXPR bool
+      operator()(_Tp* __x, _Tp* __y) const _GLIBCXX_NOTHROW
+      { return __ptr_rel_ops::_S_greater_equal(__x, __y); }
+    };
+
+  // Partial specialization of std::less_equal for pointers.
+  template<typename _Tp>
+    struct less_equal<_Tp*> : public binary_function<_Tp*, _Tp*, bool>
+    {
+      _GLIBCXX14_CONSTEXPR bool
+      operator()(_Tp* __x, _Tp* __y) const _GLIBCXX_NOTHROW
+      { return __ptr_rel_ops::_S_less_equal(__x, __y); }
+    };
+
+#if __cplusplus >= 201402L
   /// One of the @link comparison_functors comparison functors@endlink.
   template<>
     struct equal_to<void>
     {
       template <typename _Tp, typename _Up>
-	_GLIBCXX14_CONSTEXPR
-	auto
+	constexpr auto
 	operator()(_Tp&& __t, _Up&& __u) const
 	noexcept(noexcept(std::forward<_Tp>(__t) == std::forward<_Up>(__u)))
 	-> decltype(std::forward<_Tp>(__t) == std::forward<_Up>(__u))
@@ -427,8 +504,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct not_equal_to<void>
     {
       template <typename _Tp, typename _Up>
-	_GLIBCXX14_CONSTEXPR
-	auto
+	constexpr auto
 	operator()(_Tp&& __t, _Up&& __u) const
 	noexcept(noexcept(std::forward<_Tp>(__t) != std::forward<_Up>(__u)))
 	-> decltype(std::forward<_Tp>(__t) != std::forward<_Up>(__u))
@@ -442,14 +518,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct greater<void>
     {
       template <typename _Tp, typename _Up>
-	_GLIBCXX14_CONSTEXPR
-	auto
+	constexpr auto
 	operator()(_Tp&& __t, _Up&& __u) const
 	noexcept(noexcept(std::forward<_Tp>(__t) > std::forward<_Up>(__u)))
 	-> decltype(std::forward<_Tp>(__t) > std::forward<_Up>(__u))
-	{ return std::forward<_Tp>(__t) > std::forward<_Up>(__u); }
+	{
+	  using _Ptrs = __and_<is_pointer<decay_t<_Tp>>, is_pointer<decay_t<_Up>>>;
+	  return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u), _Ptrs());
+	}
 
       typedef __is_transparent is_transparent;
+
+    private:
+      template<typename _Tp, typename _Up>
+	static constexpr decltype(auto)
+	_S_cmp(_Tp&& __t, _Up&& __u, false_type)
+	{ return std::forward<_Tp>(__t) > std::forward<_Up>(__u); }
+
+      template<typename _Tp, typename _Up>
+	static constexpr bool
+	_S_cmp(_Tp* __t, _Up* __u, true_type) noexcept
+	{ return __ptr_rel_ops::_S_greater(__t, __u); }
     };
 
   /// One of the @link comparison_functors comparison functors@endlink.
@@ -457,14 +546,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct less<void>
     {
       template <typename _Tp, typename _Up>
-	_GLIBCXX14_CONSTEXPR
-	auto
+	constexpr auto
 	operator()(_Tp&& __t, _Up&& __u) const
 	noexcept(noexcept(std::forward<_Tp>(__t) < std::forward<_Up>(__u)))
 	-> decltype(std::forward<_Tp>(__t) < std::forward<_Up>(__u))
-	{ return std::forward<_Tp>(__t) < std::forward<_Up>(__u); }
+	{
+	  using _Ptrs = __and_<is_pointer<decay_t<_Tp>>, is_pointer<decay_t<_Up>>>;
+	  return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u), _Ptrs());
+	}
 
       typedef __is_transparent is_transparent;
+
+    private:
+      template<typename _Tp, typename _Up>
+	static constexpr decltype(auto)
+	_S_cmp(_Tp&& __t, _Up&& __u, false_type)
+	{ return std::forward<_Tp>(__t) < std::forward<_Up>(__u); }
+
+      template<typename _Tp, typename _Up>
+	static constexpr bool
+	_S_cmp(_Tp* __t, _Up* __u, true_type) noexcept
+	{ return __ptr_rel_ops::_S_less(__t, __u); }
     };
 
   /// One of the @link comparison_functors comparison functors@endlink.
@@ -472,14 +574,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct greater_equal<void>
     {
       template <typename _Tp, typename _Up>
-	_GLIBCXX14_CONSTEXPR
-	auto
+	constexpr auto
 	operator()(_Tp&& __t, _Up&& __u) const
 	noexcept(noexcept(std::forward<_Tp>(__t) >= std::forward<_Up>(__u)))
 	-> decltype(std::forward<_Tp>(__t) >= std::forward<_Up>(__u))
-	{ return std::forward<_Tp>(__t) >= std::forward<_Up>(__u); }
+	{
+	  using _Ptrs = __and_<is_pointer<decay_t<_Tp>>, is_pointer<decay_t<_Up>>>;
+	  return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u), _Ptrs());
+	}
 
       typedef __is_transparent is_transparent;
+
+    private:
+      template<typename _Tp, typename _Up>
+	static constexpr decltype(auto)
+	_S_cmp(_Tp&& __t, _Up&& __u, false_type)
+	{ return std::forward<_Tp>(__t) >= std::forward<_Up>(__u); }
+
+      template<typename _Tp, typename _Up>
+	static constexpr bool
+	_S_cmp(_Tp* __t, _Up* __u, true_type) noexcept
+	{ return __ptr_rel_ops::_S_greater_equal(__t, __u); }
     };
 
   /// One of the @link comparison_functors comparison functors@endlink.
@@ -487,16 +602,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct less_equal<void>
     {
       template <typename _Tp, typename _Up>
-	_GLIBCXX14_CONSTEXPR
-	auto
+	constexpr auto
 	operator()(_Tp&& __t, _Up&& __u) const
 	noexcept(noexcept(std::forward<_Tp>(__t) <= std::forward<_Up>(__u)))
 	-> decltype(std::forward<_Tp>(__t) <= std::forward<_Up>(__u))
-	{ return std::forward<_Tp>(__t) <= std::forward<_Up>(__u); }
+	{
+	  using _Ptrs = __and_<is_pointer<decay_t<_Tp>>, is_pointer<decay_t<_Up>>>;
+	  return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u), _Ptrs());
+	}
 
       typedef __is_transparent is_transparent;
+
+    private:
+      template<typename _Tp, typename _Up>
+	static constexpr decltype(auto)
+	_S_cmp(_Tp&& __t, _Up&& __u, false_type)
+	{ return std::forward<_Tp>(__t) <= std::forward<_Up>(__u); }
+
+      template<typename _Tp, typename _Up>
+	static constexpr bool
+	_S_cmp(_Tp* __t, _Up* __u, true_type) noexcept
+	{ return __ptr_rel_ops::_S_less_equal(__t, __u); }
     };
-#endif
+#endif // C++14
   /** @}  */
 
   // 20.3.4 logical operations
diff --git a/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc b/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc
new file mode 100644
index 00000000000..317e42d8f11
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc
@@ -0,0 +1,166 @@
+// Copyright (C) 2018 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run }
+
+#include <functional>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+int b[8];
+int a[8];
+
+void
+test01()
+{
+  auto p = a + 8;
+  std::greater<int*> gt;
+
+  std::stringstream ss;
+  ss << gt(p, b) << ' ' << gt(b, p) << ' ' << (!gt(p, b) && !gt(b, p));
+  int sum = 0, n = 0;
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+#if __cplusplus >= 201402L
+  static_assert( gt(a+1, a), "constexpr greater<int*>" );
+  static_assert( !gt(a, a+1), "constexpr greater<int*>" );
+
+  ss.str("");
+  ss.clear();
+  sum = 0;
+  auto p2 = a + 8;
+  std::greater<> gt2;
+  ss << gt2(p2, b) << ' ' << gt2(b, p2) << ' ' << (!gt2(p2, b) && !gt2(b, p2));
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+  static_assert( gt2(a+1, a), "constexpr greater<>" );
+  static_assert( !gt2(a, a+1), "constexpr greater<>" );
+#endif
+}
+
+void
+test02()
+{
+  auto p = a + 8;
+  std::less<int*> lt;
+
+  std::stringstream ss;
+  ss << lt(p, b) << ' ' << lt(b, p) << ' ' << (!lt(p, b) && !lt(b, p));
+  int sum = 0, n = 0;
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+#if __cplusplus >= 201402L
+  static_assert( lt(a, a+1), "constexpr less<int*>" );
+  static_assert( !lt(a+1, a), "constexpr less<int*>" );
+
+  ss.str("");
+  ss.clear();
+  sum = 0;
+  auto p2 = a + 8;
+  std::less<> lt2;
+  ss << lt2(p2, b) << ' ' << lt2(b, p2) << ' ' << (!lt2(p2, b) && !lt2(b, p2));
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+  static_assert( lt2(a, a+1), "constexpr less<>" );
+  static_assert( !lt2(a+1, a), "constexpr less<>" );
+#endif
+}
+
+void
+test03()
+{
+  auto p = a + 8;
+  std::greater_equal<int*> ge;
+
+  std::stringstream ss;
+  ss << !ge(p, b) << ' ' << !ge(b, p) << ' ' << (ge(p, b) && ge(b, p));
+  int sum = 0, n = 0;
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+#if __cplusplus >= 201402L
+  static_assert( !ge(a, a+1), "constexpr greater_equal<int*>" );
+  static_assert( ge(a, a), "constexpr greater_equal<int*>" );
+  static_assert( ge(a+1, a), "constexpr greater_equal<int*>" );
+
+  ss.str("");
+  ss.clear();
+  sum = 0;
+  auto p2 = a + 8;
+  std::greater_equal<> ge2;
+  ss << !ge2(p2, b) << ' ' << !ge2(b, p2) << ' ' << (ge2(p2, b) && ge2(b, p2));
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+  static_assert( !ge2(a, a+1), "constexpr greater_equal<>" );
+  static_assert( ge2(a, a), "constexpr greater_equal<>" );
+  static_assert( ge2(a+1, a), "constexpr greater_equal<>" );
+#endif
+}
+
+void
+test04()
+{
+  auto p = a + 8;
+  std::less_equal<int*> le;
+
+  std::stringstream ss;
+  ss << !le(p, b) << ' ' << !le(b, p) << ' ' << (le(p, b) && le(b, p));
+  int sum = 0, n = 0;
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+#if __cplusplus >= 201402L
+  static_assert( !le(a+1, a), "constexpr less_equal<int*>" );
+  static_assert( le(a, a), "constexpr less_equal<int*>" );
+  static_assert( le(a, a+1), "constexpr less_equal<int*>" );
+
+  ss.str("");
+  ss.clear();
+  sum = 0;
+  auto p2 = a + 8;
+  std::less_equal<> le2;
+  ss << !le2(p2, b) << ' ' << !le2(b, p2) << ' ' << (le2(p2, b) && le2(b, p2));
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+  static_assert( !le2(a+1, a), "constexpr less_equal<>" );
+  static_assert( le2(a, a), "constexpr less_equal<>" );
+  static_assert( le2(a, a+1), "constexpr less_equal<>" );
+#endif
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+}
Jonathan Wakely March 14, 2018, 11:01 p.m. UTC | #2
Here's a very different patch. This gets rid of the __ptr_rel_ops and
just puts the special handling for pointers directly in the
std::less<_Tp*> etc. specializations. Then std::less<void> uses
std::less<P*> for some pointer type P*. I've also added a ton of ugly
metaprogramming to detect the "if the call operator calls a built-in
operator comparing pointers" condition. The idea is borrowed from
Casey Carter and Eric Niebler's Ranges work, but basically it checks
if operator<(T,U) or T.operator<(U) can be called, and if not the
comparison must be using a built-in operator. If both T and U are
convertible to pointers (specifically, to const volatile void* which
is the most accepting of all pointers) then we assume we're using a
built-in operator comparing pointers, and delegate to std::less<const
volatile void*>, which ensures a total order.

Tested powerpc64le-linux, committed to trunk. This fixes a regression,
but I'm not sure about backporting it yet, I haven't even tried
testing it on the branches.
commit 00c52c235b8dea85000f7b5e1dcff0526216a9a5
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Mar 14 19:12:21 2018 +0000

    PR libstdc++/78420 Make std::less etc. yield total order for pointers
    
    In order for std::less<T*> etc. to meet the total order requirements of
    [comparisons] p2 we need to cast unrelated pointers to uintptr_t before
    comparing them. Those casts aren't allowed in constant expressions, so
    only cast when __builtin_constant_p says the result of the comparison is
    not a compile-time constant (because the arguments are not constants, or
    the result of the comparison is unspecified). When the result is
    constant just compare the pointers directly without casting.
    
    This ensures that the function can be called in constant expressions
    with suitable arguments, but still yields a total order even for
    otherwise unspecified pointer comparisons.
    
    For std::less<void> etc. add new overloads for pointers, which use
    std::less<common_type_t<T*,U*>> directly. Also change the generic
    overloads to detect when the comparison would call a built-in relational
    operator with pointer operands, and dispatch that case to the
    corresponding specialization for void pointers.
    
            PR libstdc++/78420
            * include/bits/stl_function.h (greater<_Tp*>, less<_Tp*>)
            (greater_equal<_Tp*>, less_equal<_Tp>*): Add partial specializations
            to ensure total order for pointers.
            (greater<void>, less<void>, greater_equal<void>, less_equal<void>):
            Add operator() overloads for pointer arguments and make generic
            overloads dispatch to new _S_cmp functions when comparisons would
            use built-in operators for pointers.
            * testsuite/20_util/function_objects/comparisons_pointer.cc: New.

diff --git a/libstdc++-v3/include/bits/stl_function.h b/libstdc++-v3/include/bits/stl_function.h
index f5f98b25395..0affaf7da3a 100644
--- a/libstdc++-v3/include/bits/stl_function.h
+++ b/libstdc++-v3/include/bits/stl_function.h
@@ -406,14 +406,65 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { return __x <= __y; }
     };
 
-#if __cplusplus > 201103L
+  // Partial specialization of std::greater for pointers.
+  template<typename _Tp>
+    struct greater<_Tp*> : public binary_function<_Tp*, _Tp*, bool>
+    {
+      _GLIBCXX14_CONSTEXPR bool
+      operator()(_Tp* __x, _Tp* __y) const _GLIBCXX_NOTHROW
+      {
+	if (__builtin_constant_p (__x > __y))
+	  return __x > __y;
+	return (__UINTPTR_TYPE__)__x > (__UINTPTR_TYPE__)__y;
+      }
+    };
+
+  // Partial specialization of std::less for pointers.
+  template<typename _Tp>
+    struct less<_Tp*> : public binary_function<_Tp*, _Tp*, bool>
+    {
+      _GLIBCXX14_CONSTEXPR bool
+      operator()(_Tp* __x, _Tp* __y) const _GLIBCXX_NOTHROW
+      {
+	if (__builtin_constant_p (__x < __y))
+	  return __x < __y;
+	return (__UINTPTR_TYPE__)__x < (__UINTPTR_TYPE__)__y;
+      }
+    };
+
+  // Partial specialization of std::greater_equal for pointers.
+  template<typename _Tp>
+    struct greater_equal<_Tp*> : public binary_function<_Tp*, _Tp*, bool>
+    {
+      _GLIBCXX14_CONSTEXPR bool
+      operator()(_Tp* __x, _Tp* __y) const _GLIBCXX_NOTHROW
+      {
+	if (__builtin_constant_p (__x >= __y))
+	  return __x >= __y;
+	return (__UINTPTR_TYPE__)__x >= (__UINTPTR_TYPE__)__y;
+      }
+    };
+
+  // Partial specialization of std::less_equal for pointers.
+  template<typename _Tp>
+    struct less_equal<_Tp*> : public binary_function<_Tp*, _Tp*, bool>
+    {
+      _GLIBCXX14_CONSTEXPR bool
+      operator()(_Tp* __x, _Tp* __y) const _GLIBCXX_NOTHROW
+      {
+	if (__builtin_constant_p (__x <= __y))
+	  return __x <= __y;
+	return (__UINTPTR_TYPE__)__x <= (__UINTPTR_TYPE__)__y;
+      }
+    };
+
+#if __cplusplus >= 201402L
   /// One of the @link comparison_functors comparison functors@endlink.
   template<>
     struct equal_to<void>
     {
       template <typename _Tp, typename _Up>
-	_GLIBCXX14_CONSTEXPR
-	auto
+	constexpr auto
 	operator()(_Tp&& __t, _Up&& __u) const
 	noexcept(noexcept(std::forward<_Tp>(__t) == std::forward<_Up>(__u)))
 	-> decltype(std::forward<_Tp>(__t) == std::forward<_Up>(__u))
@@ -427,8 +478,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct not_equal_to<void>
     {
       template <typename _Tp, typename _Up>
-	_GLIBCXX14_CONSTEXPR
-	auto
+	constexpr auto
 	operator()(_Tp&& __t, _Up&& __u) const
 	noexcept(noexcept(std::forward<_Tp>(__t) != std::forward<_Up>(__u)))
 	-> decltype(std::forward<_Tp>(__t) != std::forward<_Up>(__u))
@@ -442,14 +492,62 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct greater<void>
     {
       template <typename _Tp, typename _Up>
-	_GLIBCXX14_CONSTEXPR
-	auto
+	constexpr auto
 	operator()(_Tp&& __t, _Up&& __u) const
 	noexcept(noexcept(std::forward<_Tp>(__t) > std::forward<_Up>(__u)))
 	-> decltype(std::forward<_Tp>(__t) > std::forward<_Up>(__u))
-	{ return std::forward<_Tp>(__t) > std::forward<_Up>(__u); }
+	{
+	  return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u),
+			__ptr_cmp<_Tp, _Up>{});
+	}
+
+      template<typename _Tp, typename _Up>
+	constexpr bool
+	operator()(_Tp* __t, _Up* __u) const noexcept
+	{ return greater<common_type_t<_Tp*, _Up*>>{}(__t, __u); }
 
       typedef __is_transparent is_transparent;
+
+    private:
+      template <typename _Tp, typename _Up>
+	static constexpr decltype(auto)
+	_S_cmp(_Tp&& __t, _Up&& __u, false_type)
+	{ return std::forward<_Tp>(__t) > std::forward<_Up>(__u); }
+
+      template <typename _Tp, typename _Up>
+	static constexpr bool
+	_S_cmp(_Tp&& __t, _Up&& __u, true_type) noexcept
+	{
+	  return greater<const volatile void*>{}(
+	      static_cast<const volatile void*>(std::forward<_Tp>(__t)),
+	      static_cast<const volatile void*>(std::forward<_Up>(__u)));
+	}
+
+      template<typename _Tp, typename _Up, typename = void>
+	struct __not_overloaded;
+
+      // False if we can call operator>(T,U)
+      template<typename _Tp, typename _Up>
+	struct __not_overloaded<_Tp, _Up, __void_t<
+	  decltype(operator>(std::declval<_Tp>(), std::declval<_Up>()))>>
+	: false_type { };
+
+      template<typename _Tp, typename _Up, typename = void>
+	struct __not_overloaded2 : true_type { };
+
+      // False if we can call T.operator>(U)
+      template<typename _Tp, typename _Up>
+	struct __not_overloaded2<_Tp, _Up, __void_t<
+	  decltype(std::declval<_Tp>().operator>(std::declval<_Up>()))>>
+	: false_type { };
+
+      template<typename _Tp, typename _Up>
+	struct __not_overloaded<_Tp, _Up> : __not_overloaded2<_Tp, _Up> { };
+
+      template<typename _Tp, typename _Up>
+	using __ptr_cmp = __and_<__not_overloaded<_Tp, _Up>,
+	      is_convertible<_Tp, const volatile void*>,
+	      is_convertible<_Up, const volatile void*>>;
     };
 
   /// One of the @link comparison_functors comparison functors@endlink.
@@ -457,14 +555,62 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct less<void>
     {
       template <typename _Tp, typename _Up>
-	_GLIBCXX14_CONSTEXPR
-	auto
+	constexpr auto
 	operator()(_Tp&& __t, _Up&& __u) const
 	noexcept(noexcept(std::forward<_Tp>(__t) < std::forward<_Up>(__u)))
 	-> decltype(std::forward<_Tp>(__t) < std::forward<_Up>(__u))
-	{ return std::forward<_Tp>(__t) < std::forward<_Up>(__u); }
+	{
+	  return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u),
+			__ptr_cmp<_Tp, _Up>{});
+	}
+
+      template<typename _Tp, typename _Up>
+	constexpr bool
+	operator()(_Tp* __t, _Up* __u) const noexcept
+	{ return less<common_type_t<_Tp*, _Up*>>{}(__t, __u); }
 
       typedef __is_transparent is_transparent;
+
+    private:
+      template <typename _Tp, typename _Up>
+	static constexpr decltype(auto)
+	_S_cmp(_Tp&& __t, _Up&& __u, false_type)
+	{ return std::forward<_Tp>(__t) < std::forward<_Up>(__u); }
+
+      template <typename _Tp, typename _Up>
+	static constexpr bool
+	_S_cmp(_Tp&& __t, _Up&& __u, true_type) noexcept
+	{
+	  return less<const volatile void*>{}(
+	      static_cast<const volatile void*>(std::forward<_Tp>(__t)),
+	      static_cast<const volatile void*>(std::forward<_Up>(__u)));
+	}
+
+      template<typename _Tp, typename _Up, typename = void>
+	struct __not_overloaded;
+
+      // False if we can call operator<(T,U)
+      template<typename _Tp, typename _Up>
+	struct __not_overloaded<_Tp, _Up, __void_t<
+	  decltype(operator<(std::declval<_Tp>(), std::declval<_Up>()))>>
+	: false_type { };
+
+      template<typename _Tp, typename _Up, typename = void>
+	struct __not_overloaded2 : true_type { };
+
+      // False if we can call T.operator<(U)
+      template<typename _Tp, typename _Up>
+	struct __not_overloaded2<_Tp, _Up, __void_t<
+	  decltype(std::declval<_Tp>().operator<(std::declval<_Up>()))>>
+	: false_type { };
+
+      template<typename _Tp, typename _Up>
+	struct __not_overloaded<_Tp, _Up> : __not_overloaded2<_Tp, _Up> { };
+
+      template<typename _Tp, typename _Up>
+	using __ptr_cmp = __and_<__not_overloaded<_Tp, _Up>,
+	      is_convertible<_Tp, const volatile void*>,
+	      is_convertible<_Up, const volatile void*>>;
     };
 
   /// One of the @link comparison_functors comparison functors@endlink.
@@ -472,14 +618,62 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct greater_equal<void>
     {
       template <typename _Tp, typename _Up>
-	_GLIBCXX14_CONSTEXPR
-	auto
+	constexpr auto
 	operator()(_Tp&& __t, _Up&& __u) const
 	noexcept(noexcept(std::forward<_Tp>(__t) >= std::forward<_Up>(__u)))
 	-> decltype(std::forward<_Tp>(__t) >= std::forward<_Up>(__u))
-	{ return std::forward<_Tp>(__t) >= std::forward<_Up>(__u); }
+	{
+	  return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u),
+			__ptr_cmp<_Tp, _Up>{});
+	}
+
+      template<typename _Tp, typename _Up>
+	constexpr bool
+	operator()(_Tp* __t, _Up* __u) const noexcept
+	{ return greater_equal<common_type_t<_Tp*, _Up*>>{}(__t, __u); }
 
       typedef __is_transparent is_transparent;
+
+    private:
+      template <typename _Tp, typename _Up>
+	static constexpr decltype(auto)
+	_S_cmp(_Tp&& __t, _Up&& __u, false_type)
+	{ return std::forward<_Tp>(__t) >= std::forward<_Up>(__u); }
+
+      template <typename _Tp, typename _Up>
+	static constexpr bool
+	_S_cmp(_Tp&& __t, _Up&& __u, true_type) noexcept
+	{
+	  return greater_equal<const volatile void*>{}(
+	      static_cast<const volatile void*>(std::forward<_Tp>(__t)),
+	      static_cast<const volatile void*>(std::forward<_Up>(__u)));
+	}
+
+      template<typename _Tp, typename _Up, typename = void>
+	struct __not_overloaded;
+
+      // False if we can call operator>=(T,U)
+      template<typename _Tp, typename _Up>
+	struct __not_overloaded<_Tp, _Up, __void_t<
+	  decltype(operator>=(std::declval<_Tp>(), std::declval<_Up>()))>>
+	: false_type { };
+
+      template<typename _Tp, typename _Up, typename = void>
+	struct __not_overloaded2 : true_type { };
+
+      // False if we can call T.operator>=(U)
+      template<typename _Tp, typename _Up>
+	struct __not_overloaded2<_Tp, _Up, __void_t<
+	  decltype(std::declval<_Tp>().operator>=(std::declval<_Up>()))>>
+	: false_type { };
+
+      template<typename _Tp, typename _Up>
+	struct __not_overloaded<_Tp, _Up> : __not_overloaded2<_Tp, _Up> { };
+
+      template<typename _Tp, typename _Up>
+	using __ptr_cmp = __and_<__not_overloaded<_Tp, _Up>,
+	      is_convertible<_Tp, const volatile void*>,
+	      is_convertible<_Up, const volatile void*>>;
     };
 
   /// One of the @link comparison_functors comparison functors@endlink.
@@ -487,16 +681,64 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct less_equal<void>
     {
       template <typename _Tp, typename _Up>
-	_GLIBCXX14_CONSTEXPR
-	auto
+	constexpr auto
 	operator()(_Tp&& __t, _Up&& __u) const
 	noexcept(noexcept(std::forward<_Tp>(__t) <= std::forward<_Up>(__u)))
 	-> decltype(std::forward<_Tp>(__t) <= std::forward<_Up>(__u))
-	{ return std::forward<_Tp>(__t) <= std::forward<_Up>(__u); }
+	{
+	  return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u),
+			__ptr_cmp<_Tp, _Up>{});
+	}
+
+      template<typename _Tp, typename _Up>
+	constexpr bool
+	operator()(_Tp* __t, _Up* __u) const noexcept
+	{ return less_equal<common_type_t<_Tp*, _Up*>>{}(__t, __u); }
 
       typedef __is_transparent is_transparent;
+
+    private:
+      template <typename _Tp, typename _Up>
+	static constexpr decltype(auto)
+	_S_cmp(_Tp&& __t, _Up&& __u, false_type)
+	{ return std::forward<_Tp>(__t) <= std::forward<_Up>(__u); }
+
+      template <typename _Tp, typename _Up>
+	static constexpr bool
+	_S_cmp(_Tp&& __t, _Up&& __u, true_type) noexcept
+	{
+	  return less_equal<const volatile void*>{}(
+	      static_cast<const volatile void*>(std::forward<_Tp>(__t)),
+	      static_cast<const volatile void*>(std::forward<_Up>(__u)));
+	}
+
+      template<typename _Tp, typename _Up, typename = void>
+	struct __not_overloaded;
+
+      // False if we can call operator<=(T,U)
+      template<typename _Tp, typename _Up>
+	struct __not_overloaded<_Tp, _Up, __void_t<
+	  decltype(operator<=(std::declval<_Tp>(), std::declval<_Up>()))>>
+	: false_type { };
+
+      template<typename _Tp, typename _Up, typename = void>
+	struct __not_overloaded2 : true_type { };
+
+      // False if we can call T.operator<=(U)
+      template<typename _Tp, typename _Up>
+	struct __not_overloaded2<_Tp, _Up, __void_t<
+	  decltype(std::declval<_Tp>().operator<=(std::declval<_Up>()))>>
+	: false_type { };
+
+      template<typename _Tp, typename _Up>
+	struct __not_overloaded<_Tp, _Up> : __not_overloaded2<_Tp, _Up> { };
+
+      template<typename _Tp, typename _Up>
+	using __ptr_cmp = __and_<__not_overloaded<_Tp, _Up>,
+	      is_convertible<_Tp, const volatile void*>,
+	      is_convertible<_Up, const volatile void*>>;
     };
-#endif
+#endif // C++14
   /** @}  */
 
   // 20.3.4 logical operations
diff --git a/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc b/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc
new file mode 100644
index 00000000000..6b2b8be0f51
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc
@@ -0,0 +1,206 @@
+// Copyright (C) 2018 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run }
+
+#include <functional>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+int b[8];
+int a[8];
+
+void
+test01()
+{
+  auto p = a + 8;
+  std::greater<int*> gt;
+
+  std::stringstream ss;
+  ss << gt(p, b) << ' ' << gt(b, p) << ' ' << (!gt(p, b) && !gt(b, p));
+  int sum = 0, n = 0;
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+#if __cplusplus >= 201402L
+  static_assert( gt(a+1, a), "constexpr greater<int*>" );
+  static_assert( !gt(a, a+1), "constexpr greater<int*>" );
+
+  ss.str("");
+  ss.clear();
+  sum = 0;
+  auto p2 = a + 8;
+  std::greater<> gt2;
+  ss << gt2(p2, b) << ' ' << gt2(b, p2) << ' ' << (!gt2(p2, b) && !gt2(b, p2));
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+  static_assert( gt2(a+1, a), "constexpr greater<>" );
+  static_assert( !gt2(a, a+1), "constexpr greater<>" );
+#endif
+}
+
+void
+test02()
+{
+  auto p = a + 8;
+  std::less<int*> lt;
+
+  std::stringstream ss;
+  ss << lt(p, b) << ' ' << lt(b, p) << ' ' << (!lt(p, b) && !lt(b, p));
+  int sum = 0, n = 0;
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+#if __cplusplus >= 201402L
+  static_assert( lt(a, a+1), "constexpr less<int*>" );
+  static_assert( !lt(a+1, a), "constexpr less<int*>" );
+
+  ss.str("");
+  ss.clear();
+  sum = 0;
+  auto p2 = a + 8;
+  std::less<> lt2;
+  ss << lt2(p2, b) << ' ' << lt2(b, p2) << ' ' << (!lt2(p2, b) && !lt2(b, p2));
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+  static_assert( lt2(a, a+1), "constexpr less<>" );
+  static_assert( !lt2(a+1, a), "constexpr less<>" );
+#endif
+}
+
+void
+test03()
+{
+  auto p = a + 8;
+  std::greater_equal<int*> ge;
+
+  std::stringstream ss;
+  ss << !ge(p, b) << ' ' << !ge(b, p) << ' ' << (ge(p, b) && ge(b, p));
+  int sum = 0, n = 0;
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+#if __cplusplus >= 201402L
+  static_assert( !ge(a, a+1), "constexpr greater_equal<int*>" );
+  static_assert( ge(a, a), "constexpr greater_equal<int*>" );
+  static_assert( ge(a+1, a), "constexpr greater_equal<int*>" );
+
+  ss.str("");
+  ss.clear();
+  sum = 0;
+  auto p2 = a + 8;
+  std::greater_equal<> ge2;
+  ss << !ge2(p2, b) << ' ' << !ge2(b, p2) << ' ' << (ge2(p2, b) && ge2(b, p2));
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+  static_assert( !ge2(a, a+1), "constexpr greater_equal<>" );
+  static_assert( ge2(a, a), "constexpr greater_equal<>" );
+  static_assert( ge2(a+1, a), "constexpr greater_equal<>" );
+#endif
+}
+
+void
+test04()
+{
+  auto p = a + 8;
+  std::less_equal<int*> le;
+
+  std::stringstream ss;
+  ss << !le(p, b) << ' ' << !le(b, p) << ' ' << (le(p, b) && le(b, p));
+  int sum = 0, n = 0;
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+#if __cplusplus >= 201402L
+  static_assert( !le(a+1, a), "constexpr less_equal<int*>" );
+  static_assert( le(a, a), "constexpr less_equal<int*>" );
+  static_assert( le(a, a+1), "constexpr less_equal<int*>" );
+
+  ss.str("");
+  ss.clear();
+  sum = 0;
+  auto p2 = a + 8;
+  std::less_equal<> le2;
+  ss << !le2(p2, b) << ' ' << !le2(b, p2) << ' ' << (le2(p2, b) && le2(b, p2));
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+  static_assert( !le2(a+1, a), "constexpr less_equal<>" );
+  static_assert( le2(a, a), "constexpr less_equal<>" );
+  static_assert( le2(a, a+1), "constexpr less_equal<>" );
+#endif
+}
+
+struct X {
+  operator const X*() const { return this; }
+};
+
+X x;
+X y[4];
+
+void
+test05()
+{
+  std::less<const X*> lt;
+  auto p = y + 4;
+  std::stringstream ss;
+  ss << lt(x, p) << ' ' << lt(p, x) << ' ' << (!lt(p, x) && !lt(x, p));
+  int sum = 0;
+  int n = 0;
+  while (ss >> n)
+    sum += n;
+  assert( sum == 1 );
+
+#if __cplusplus >= 201402L
+  static_assert( lt(y, y+1), "constexpr less<const X*>" );
+  static_assert( !lt(y+1, y), "constexpr less<const X*>" );
+
+  ss.str("");
+  ss.clear();
+  sum = 0;
+  auto p2 = y + 4;
+  std::less<> lt2;
+  ss << lt2(x, p2) << ' ' << lt2(p2, x) << ' ' << (!lt2(x, p2) && !lt2(p2, x));
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+  static_assert( lt2(y, y+1), "constexpr less<>" );
+  static_assert( !lt2(y+1, y), "constexpr less<>" );
+#endif
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+}
Jonathan Wakely March 17, 2018, 1:46 p.m. UTC | #3
On 14 March 2018 at 23:01, Jonathan Wakely wrote:
> Here's a very different patch. This gets rid of the __ptr_rel_ops and
> just puts the special handling for pointers directly in the
> std::less<_Tp*> etc. specializations. Then std::less<void> uses
> std::less<P*> for some pointer type P*. I've also added a ton of ugly
> metaprogramming to detect the "if the call operator calls a built-in
> operator comparing pointers" condition. The idea is borrowed from
> Casey Carter and Eric Niebler's Ranges work, but basically it checks
> if operator<(T,U) or T.operator<(U) can be called, and if not the
> comparison must be using a built-in operator. If both T and U are
> convertible to pointers (specifically, to const volatile void* which
> is the most accepting of all pointers) then we assume we're using a
> built-in operator comparing pointers, and delegate to std::less<const
> volatile void*>, which ensures a total order.
>
> Tested powerpc64le-linux, committed to trunk. This fixes a regression,
> but I'm not sure about backporting it yet, I haven't even tried
> testing it on the branches.

This adjusts the new test to avoid using 'auto' so it works as C++98 too.
commit be039c0c67a6d18ddeb86bddc50cd9704a120f75
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Sat Mar 17 13:35:50 2018 +0000

    Fix new test that fails in C++98 mode
    
            * testsuite/20_util/function_objects/comparisons_pointer.cc: Adjust
            to compile as C++98.

diff --git a/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc b/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc
index 6b2b8be0f51..aad0bc3dd2e 100644
--- a/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc
+++ b/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc
@@ -27,7 +27,7 @@ int a[8];
 void
 test01()
 {
-  auto p = a + 8;
+  int* p = a + 8;
   std::greater<int*> gt;
 
   std::stringstream ss;
@@ -44,7 +44,7 @@ test01()
   ss.str("");
   ss.clear();
   sum = 0;
-  auto p2 = a + 8;
+  int* p2 = a + 8;
   std::greater<> gt2;
   ss << gt2(p2, b) << ' ' << gt2(b, p2) << ' ' << (!gt2(p2, b) && !gt2(b, p2));
   while (ss >> n)
@@ -59,7 +59,7 @@ test01()
 void
 test02()
 {
-  auto p = a + 8;
+  int* p = a + 8;
   std::less<int*> lt;
 
   std::stringstream ss;
@@ -76,7 +76,7 @@ test02()
   ss.str("");
   ss.clear();
   sum = 0;
-  auto p2 = a + 8;
+  int* p2 = a + 8;
   std::less<> lt2;
   ss << lt2(p2, b) << ' ' << lt2(b, p2) << ' ' << (!lt2(p2, b) && !lt2(b, p2));
   while (ss >> n)
@@ -91,7 +91,7 @@ test02()
 void
 test03()
 {
-  auto p = a + 8;
+  int* p = a + 8;
   std::greater_equal<int*> ge;
 
   std::stringstream ss;
@@ -109,7 +109,7 @@ test03()
   ss.str("");
   ss.clear();
   sum = 0;
-  auto p2 = a + 8;
+  int* p2 = a + 8;
   std::greater_equal<> ge2;
   ss << !ge2(p2, b) << ' ' << !ge2(b, p2) << ' ' << (ge2(p2, b) && ge2(b, p2));
   while (ss >> n)
@@ -125,7 +125,7 @@ test03()
 void
 test04()
 {
-  auto p = a + 8;
+  int* p = a + 8;
   std::less_equal<int*> le;
 
   std::stringstream ss;
@@ -143,7 +143,7 @@ test04()
   ss.str("");
   ss.clear();
   sum = 0;
-  auto p2 = a + 8;
+  int* p2 = a + 8;
   std::less_equal<> le2;
   ss << !le2(p2, b) << ' ' << !le2(b, p2) << ' ' << (le2(p2, b) && le2(b, p2));
   while (ss >> n)
@@ -167,7 +167,7 @@ void
 test05()
 {
   std::less<const X*> lt;
-  auto p = y + 4;
+  X* p = y + 4;
   std::stringstream ss;
   ss << lt(x, p) << ' ' << lt(p, x) << ' ' << (!lt(p, x) && !lt(x, p));
   int sum = 0;
@@ -183,7 +183,7 @@ test05()
   ss.str("");
   ss.clear();
   sum = 0;
-  auto p2 = y + 4;
+  X* p2 = y + 4;
   std::less<> lt2;
   ss << lt2(x, p2) << ' ' << lt2(p2, x) << ' ' << (!lt2(x, p2) && !lt2(p2, x));
   while (ss >> n)
Jonathan Wakely March 22, 2018, 2:24 p.m. UTC | #4
On 14 March 2018 at 23:01, Jonathan Wakely wrote:
> Here's a very different patch. This gets rid of the __ptr_rel_ops and
> just puts the special handling for pointers directly in the
> std::less<_Tp*> etc. specializations. Then std::less<void> uses
> std::less<P*> for some pointer type P*. I've also added a ton of ugly
> metaprogramming to detect the "if the call operator calls a built-in
> operator comparing pointers" condition. The idea is borrowed from
> Casey Carter and Eric Niebler's Ranges work, but basically it checks
> if operator<(T,U) or T.operator<(U) can be called, and if not the
> comparison must be using a built-in operator. If both T and U are
> convertible to pointers (specifically, to const volatile void* which
> is the most accepting of all pointers) then we assume we're using a
> built-in operator comparing pointers, and delegate to std::less<const
> volatile void*>, which ensures a total order.
>
> Tested powerpc64le-linux, committed to trunk. This fixes a regression,
> but I'm not sure about backporting it yet, I haven't even tried
> testing it on the branches.

After testing the traits for detecting overloaded operators I made a
"small tweak" to reorganize them, and didn't retest. This fixes the
bug I introduced by reorganizing.

Tested powerpc64le-linux, committed to trunk.
commit 6c0e2be69e11e4ec8f9b04d3f80b9d4496737b0b
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Mar 22 13:08:52 2018 +0000

    PR libstdc++/85040 fix std::less<void> etc. ambiguities
    
            PR libstdc++/85040
            * include/bits/stl_function.h (greater::__not_overloaded)
            (less::__not_overloaded, greater_equal::__not_overloaded)
            (less_equal::__not_overloaded): Fix ambiguous specializations.
            * testsuite/20_util/function_objects/comparisons_pointer.cc: Add
            tests for type with overlaoded operators.

diff --git a/libstdc++-v3/include/bits/stl_function.h b/libstdc++-v3/include/bits/stl_function.h
index 0affaf7da3a..9e81ad3f20e 100644
--- a/libstdc++-v3/include/bits/stl_function.h
+++ b/libstdc++-v3/include/bits/stl_function.h
@@ -523,15 +523,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      static_cast<const volatile void*>(std::forward<_Up>(__u)));
 	}
 
-      template<typename _Tp, typename _Up, typename = void>
-	struct __not_overloaded;
-
-      // False if we can call operator>(T,U)
-      template<typename _Tp, typename _Up>
-	struct __not_overloaded<_Tp, _Up, __void_t<
-	  decltype(operator>(std::declval<_Tp>(), std::declval<_Up>()))>>
-	: false_type { };
-
+      // True if there is no viable operator> member function.
       template<typename _Tp, typename _Up, typename = void>
 	struct __not_overloaded2 : true_type { };
 
@@ -541,8 +533,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  decltype(std::declval<_Tp>().operator>(std::declval<_Up>()))>>
 	: false_type { };
 
+      // True if there is no overloaded operator> for these operands.
+      template<typename _Tp, typename _Up, typename = void>
+	struct __not_overloaded : __not_overloaded2<_Tp, _Up> { };
+
+      // False if we can call operator>(T,U)
       template<typename _Tp, typename _Up>
-	struct __not_overloaded<_Tp, _Up> : __not_overloaded2<_Tp, _Up> { };
+	struct __not_overloaded<_Tp, _Up, __void_t<
+	  decltype(operator>(std::declval<_Tp>(), std::declval<_Up>()))>>
+	: false_type { };
 
       template<typename _Tp, typename _Up>
 	using __ptr_cmp = __and_<__not_overloaded<_Tp, _Up>,
@@ -586,15 +585,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      static_cast<const volatile void*>(std::forward<_Up>(__u)));
 	}
 
-      template<typename _Tp, typename _Up, typename = void>
-	struct __not_overloaded;
-
-      // False if we can call operator<(T,U)
-      template<typename _Tp, typename _Up>
-	struct __not_overloaded<_Tp, _Up, __void_t<
-	  decltype(operator<(std::declval<_Tp>(), std::declval<_Up>()))>>
-	: false_type { };
-
+      // True if there is no viable operator< member function.
       template<typename _Tp, typename _Up, typename = void>
 	struct __not_overloaded2 : true_type { };
 
@@ -604,8 +595,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  decltype(std::declval<_Tp>().operator<(std::declval<_Up>()))>>
 	: false_type { };
 
+      // True if there is no overloaded operator< for these operands.
+      template<typename _Tp, typename _Up, typename = void>
+	struct __not_overloaded : __not_overloaded2<_Tp, _Up> { };
+
+      // False if we can call operator<(T,U)
       template<typename _Tp, typename _Up>
-	struct __not_overloaded<_Tp, _Up> : __not_overloaded2<_Tp, _Up> { };
+	struct __not_overloaded<_Tp, _Up, __void_t<
+	  decltype(operator<(std::declval<_Tp>(), std::declval<_Up>()))>>
+	: false_type { };
 
       template<typename _Tp, typename _Up>
 	using __ptr_cmp = __and_<__not_overloaded<_Tp, _Up>,
@@ -649,15 +647,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      static_cast<const volatile void*>(std::forward<_Up>(__u)));
 	}
 
-      template<typename _Tp, typename _Up, typename = void>
-	struct __not_overloaded;
-
-      // False if we can call operator>=(T,U)
-      template<typename _Tp, typename _Up>
-	struct __not_overloaded<_Tp, _Up, __void_t<
-	  decltype(operator>=(std::declval<_Tp>(), std::declval<_Up>()))>>
-	: false_type { };
-
+      // True if there is no viable operator>= member function.
       template<typename _Tp, typename _Up, typename = void>
 	struct __not_overloaded2 : true_type { };
 
@@ -667,8 +657,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  decltype(std::declval<_Tp>().operator>=(std::declval<_Up>()))>>
 	: false_type { };
 
+      // True if there is no overloaded operator>= for these operands.
+      template<typename _Tp, typename _Up, typename = void>
+	struct __not_overloaded : __not_overloaded2<_Tp, _Up> { };
+
+      // False if we can call operator>=(T,U)
       template<typename _Tp, typename _Up>
-	struct __not_overloaded<_Tp, _Up> : __not_overloaded2<_Tp, _Up> { };
+	struct __not_overloaded<_Tp, _Up, __void_t<
+	  decltype(operator>=(std::declval<_Tp>(), std::declval<_Up>()))>>
+	: false_type { };
 
       template<typename _Tp, typename _Up>
 	using __ptr_cmp = __and_<__not_overloaded<_Tp, _Up>,
@@ -712,15 +709,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      static_cast<const volatile void*>(std::forward<_Up>(__u)));
 	}
 
-      template<typename _Tp, typename _Up, typename = void>
-	struct __not_overloaded;
-
-      // False if we can call operator<=(T,U)
-      template<typename _Tp, typename _Up>
-	struct __not_overloaded<_Tp, _Up, __void_t<
-	  decltype(operator<=(std::declval<_Tp>(), std::declval<_Up>()))>>
-	: false_type { };
-
+      // True if there is no viable operator<= member function.
       template<typename _Tp, typename _Up, typename = void>
 	struct __not_overloaded2 : true_type { };
 
@@ -730,8 +719,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  decltype(std::declval<_Tp>().operator<=(std::declval<_Up>()))>>
 	: false_type { };
 
+      // True if there is no overloaded operator<= for these operands.
+      template<typename _Tp, typename _Up, typename = void>
+	struct __not_overloaded : __not_overloaded2<_Tp, _Up> { };
+
+      // False if we can call operator<=(T,U)
       template<typename _Tp, typename _Up>
-	struct __not_overloaded<_Tp, _Up> : __not_overloaded2<_Tp, _Up> { };
+	struct __not_overloaded<_Tp, _Up, __void_t<
+	  decltype(operator<=(std::declval<_Tp>(), std::declval<_Up>()))>>
+	: false_type { };
 
       template<typename _Tp, typename _Up>
 	using __ptr_cmp = __and_<__not_overloaded<_Tp, _Up>,
diff --git a/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc b/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc
index 474190cdf81..7cec294f8c2 100644
--- a/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc
+++ b/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc
@@ -195,6 +195,39 @@ test05()
 #endif
 }
 
+struct Overloaded {
+  bool operator>(int) { return true; }
+  bool operator<(int) { return false; }
+  bool operator>=(int) { return true; }
+  bool operator<=(int) { return false; }
+};
+bool operator>(Overloaded, Overloaded) { return false; }
+bool operator<(Overloaded, Overloaded) { return false; }
+bool operator>=(Overloaded, Overloaded) { return true; }
+bool operator<=(Overloaded, Overloaded) { return true; }
+
+void
+test06()
+{
+#if __cplusplus >= 201402L
+  std::greater<void> gt;
+  std::less<void> lt;
+  std::greater_equal<void> ge;
+  std::less_equal<void> le;
+
+  Overloaded o;
+  VERIFY( !gt(o, o) );
+  VERIFY( !lt(o, o) );
+  VERIFY( ge(o, o) );
+  VERIFY( le(o, o) );
+
+  VERIFY( gt(o, 1) );
+  VERIFY( !lt(o, 1) );
+  VERIFY( ge(o, 1) );
+  VERIFY( !le(o, 1) );
+#endif
+}
+
 int
 main()
 {
@@ -203,4 +236,5 @@ main()
   test03();
   test04();
   test05();
+  test06();
 }
diff mbox series

Patch

diff --git a/libstdc++-v3/include/bits/stl_function.h b/libstdc++-v3/include/bits/stl_function.h
index f5f98b25395..27739ad7089 100644
--- a/libstdc++-v3/include/bits/stl_function.h
+++ b/libstdc++-v3/include/bits/stl_function.h
@@ -406,14 +406,91 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { return __x <= __y; }
     };
 
-#if __cplusplus > 201103L
+  struct __ptr_rel_ops
+  {
+    __ptr_rel_ops() = delete;
+    ~__ptr_rel_ops() = delete;
+
+    template<typename _Tp, typename _Up>
+      static _GLIBCXX14_CONSTEXPR bool
+      _S_greater(_Tp* __x, _Up* __y)
+      {
+	if (__builtin_constant_p (__x > __y))
+	  return __x > __y;
+	return (__UINTPTR_TYPE__)__x > (__UINTPTR_TYPE__)__y;
+      }
+
+    template<typename _Tp, typename _Up>
+      static _GLIBCXX14_CONSTEXPR bool
+      _S_less(_Tp* __x, _Up* __y)
+      {
+	if (__builtin_constant_p (__x < __y))
+	  return __x < __y;
+	return (__UINTPTR_TYPE__)__x < (__UINTPTR_TYPE__)__y;
+      }
+
+    template<typename _Tp, typename _Up>
+      static _GLIBCXX14_CONSTEXPR bool
+      _S_greater_equal(_Tp* __x, _Up* __y)
+      {
+	if (__builtin_constant_p (__x >= __y))
+	  return __x >= __y;
+	return (__UINTPTR_TYPE__)__x >= (__UINTPTR_TYPE__)__y;
+      }
+
+    template<typename _Tp, typename _Up>
+      static _GLIBCXX14_CONSTEXPR bool
+      _S_less_equal(_Tp* __x, _Up* __y)
+      {
+	if (__builtin_constant_p (__x <= __y))
+	  return __x <= __y;
+	return (__UINTPTR_TYPE__)__x <= (__UINTPTR_TYPE__)__y;
+      }
+  };
+
+  // Partial specialization of std::greater for pointers.
+  template<typename _Tp>
+    struct greater<_Tp*> : public binary_function<_Tp*, _Tp*, bool>
+    {
+      _GLIBCXX14_CONSTEXPR bool
+      operator()(_Tp* const& __x, _Tp* const& __y) const _GLIBCXX_NOTHROW
+      { return __ptr_rel_ops::_S_greater(__x, __y); }
+    };
+
+  // Partial specialization of std::less for pointers.
+  template<typename _Tp>
+    struct less<_Tp*> : public binary_function<_Tp*, _Tp*, bool>
+    {
+      _GLIBCXX14_CONSTEXPR bool
+      operator()(_Tp* const& __x, _Tp* const& __y) const _GLIBCXX_NOTHROW
+      { return __ptr_rel_ops::_S_less(__x, __y); }
+    };
+
+  // Partial specialization of std::greater_equal for pointers.
+  template<typename _Tp>
+    struct greater_equal<_Tp*> : public binary_function<_Tp*, _Tp*, bool>
+    {
+      _GLIBCXX14_CONSTEXPR bool
+      operator()(_Tp* const& __x, _Tp* const& __y) const _GLIBCXX_NOTHROW
+      { return __ptr_rel_ops::_S_greater_equal(__x, __y); }
+    };
+
+  // Partial specialization of std::less_equal for pointers.
+  template<typename _Tp>
+    struct less_equal<_Tp*> : public binary_function<_Tp*, _Tp*, bool>
+    {
+      _GLIBCXX14_CONSTEXPR bool
+      operator()(_Tp* const& __x, _Tp* const& __y) const _GLIBCXX_NOTHROW
+      { return __ptr_rel_ops::_S_less_equal(__x, __y); }
+    };
+
+#if __cplusplus >= 201402L
   /// One of the @link comparison_functors comparison functors@endlink.
   template<>
     struct equal_to<void>
     {
       template <typename _Tp, typename _Up>
-	_GLIBCXX14_CONSTEXPR
-	auto
+	constexpr auto
 	operator()(_Tp&& __t, _Up&& __u) const
 	noexcept(noexcept(std::forward<_Tp>(__t) == std::forward<_Up>(__u)))
 	-> decltype(std::forward<_Tp>(__t) == std::forward<_Up>(__u))
@@ -427,8 +504,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct not_equal_to<void>
     {
       template <typename _Tp, typename _Up>
-	_GLIBCXX14_CONSTEXPR
-	auto
+	constexpr auto
 	operator()(_Tp&& __t, _Up&& __u) const
 	noexcept(noexcept(std::forward<_Tp>(__t) != std::forward<_Up>(__u)))
 	-> decltype(std::forward<_Tp>(__t) != std::forward<_Up>(__u))
@@ -442,14 +518,27 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct greater<void>
     {
       template <typename _Tp, typename _Up>
-	_GLIBCXX14_CONSTEXPR
-	auto
+	constexpr auto
 	operator()(_Tp&& __t, _Up&& __u) const
 	noexcept(noexcept(std::forward<_Tp>(__t) > std::forward<_Up>(__u)))
 	-> decltype(std::forward<_Tp>(__t) > std::forward<_Up>(__u))
-	{ return std::forward<_Tp>(__t) > std::forward<_Up>(__u); }
+	{
+	  using _Ptrs = __and_<is_pointer<decay_t<_Tp>>, is_pointer<decay_t<_Up>>>;
+	  return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u), _Ptrs());
+	}
 
       typedef __is_transparent is_transparent;
+
+    private:
+      template<typename _Tp, typename _Up>
+	static constexpr decltype(auto)
+	_S_cmp(_Tp&& __t, _Up&& __u, false_type)
+	{ return std::forward<_Tp>(__t) > std::forward<_Up>(__u); }
+
+      template<typename _Tp, typename _Up>
+	static constexpr bool
+	_S_cmp(_Tp* __t, _Up* __u, true_type) noexcept
+	{ return __ptr_rel_ops::_S_greater(__t, __u); }
     };
 
   /// One of the @link comparison_functors comparison functors@endlink.
@@ -457,14 +546,27 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct less<void>
     {
       template <typename _Tp, typename _Up>
-	_GLIBCXX14_CONSTEXPR
-	auto
+	constexpr auto
 	operator()(_Tp&& __t, _Up&& __u) const
 	noexcept(noexcept(std::forward<_Tp>(__t) < std::forward<_Up>(__u)))
 	-> decltype(std::forward<_Tp>(__t) < std::forward<_Up>(__u))
-	{ return std::forward<_Tp>(__t) < std::forward<_Up>(__u); }
+	{
+	  using _Ptrs = __and_<is_pointer<decay_t<_Tp>>, is_pointer<decay_t<_Up>>>;
+	  return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u), _Ptrs());
+	}
 
       typedef __is_transparent is_transparent;
+
+    private:
+      template<typename _Tp, typename _Up>
+	static constexpr decltype(auto)
+	_S_cmp(_Tp&& __t, _Up&& __u, false_type)
+	{ return std::forward<_Tp>(__t) < std::forward<_Up>(__u); }
+
+      template<typename _Tp, typename _Up>
+	static constexpr bool
+	_S_cmp(_Tp* __t, _Up* __u, true_type) noexcept
+	{ return __ptr_rel_ops::_S_less(__t, __u); }
     };
 
   /// One of the @link comparison_functors comparison functors@endlink.
@@ -472,14 +574,27 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct greater_equal<void>
     {
       template <typename _Tp, typename _Up>
-	_GLIBCXX14_CONSTEXPR
-	auto
+	constexpr auto
 	operator()(_Tp&& __t, _Up&& __u) const
 	noexcept(noexcept(std::forward<_Tp>(__t) >= std::forward<_Up>(__u)))
 	-> decltype(std::forward<_Tp>(__t) >= std::forward<_Up>(__u))
-	{ return std::forward<_Tp>(__t) >= std::forward<_Up>(__u); }
+	{
+	  using _Ptrs = __and_<is_pointer<decay_t<_Tp>>, is_pointer<decay_t<_Up>>>;
+	  return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u), _Ptrs());
+	}
 
       typedef __is_transparent is_transparent;
+
+    private:
+      template<typename _Tp, typename _Up>
+	static constexpr decltype(auto)
+	_S_cmp(_Tp&& __t, _Up&& __u, false_type)
+	{ return std::forward<_Tp>(__t) >= std::forward<_Up>(__u); }
+
+      template<typename _Tp, typename _Up>
+	static constexpr bool
+	_S_cmp(_Tp* __t, _Up* __u, true_type) noexcept
+	{ return __ptr_rel_ops::_S_greater_equal(__t, __u); }
     };
 
   /// One of the @link comparison_functors comparison functors@endlink.
@@ -487,16 +602,29 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct less_equal<void>
     {
       template <typename _Tp, typename _Up>
-	_GLIBCXX14_CONSTEXPR
-	auto
+	constexpr auto
 	operator()(_Tp&& __t, _Up&& __u) const
 	noexcept(noexcept(std::forward<_Tp>(__t) <= std::forward<_Up>(__u)))
 	-> decltype(std::forward<_Tp>(__t) <= std::forward<_Up>(__u))
-	{ return std::forward<_Tp>(__t) <= std::forward<_Up>(__u); }
+	{
+	  using _Ptrs = __and_<is_pointer<decay_t<_Tp>>, is_pointer<decay_t<_Up>>>;
+	  return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u), _Ptrs());
+	}
 
       typedef __is_transparent is_transparent;
+
+    private:
+      template<typename _Tp, typename _Up>
+	static constexpr decltype(auto)
+	_S_cmp(_Tp&& __t, _Up&& __u, false_type)
+	{ return std::forward<_Tp>(__t) <= std::forward<_Up>(__u); }
+
+      template<typename _Tp, typename _Up>
+	static constexpr bool
+	_S_cmp(_Tp* __t, _Up* __u, true_type) noexcept
+	{ return __ptr_rel_ops::_S_less_equal(__t, __u); }
     };
-#endif
+#endif // C++14
   /** @}  */
 
   // 20.3.4 logical operations
diff --git a/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc b/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc
new file mode 100644
index 00000000000..317e42d8f11
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer.cc
@@ -0,0 +1,166 @@ 
+// Copyright (C) 2018 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run }
+
+#include <functional>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+int b[8];
+int a[8];
+
+void
+test01()
+{
+  auto p = a + 8;
+  std::greater<int*> gt;
+
+  std::stringstream ss;
+  ss << gt(p, b) << ' ' << gt(b, p) << ' ' << (!gt(p, b) && !gt(b, p));
+  int sum = 0, n = 0;
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+#if __cplusplus >= 201402L
+  static_assert( gt(a+1, a), "constexpr greater<int*>" );
+  static_assert( !gt(a, a+1), "constexpr greater<int*>" );
+
+  ss.str("");
+  ss.clear();
+  sum = 0;
+  auto p2 = a + 8;
+  std::greater<> gt2;
+  ss << gt2(p2, b) << ' ' << gt2(b, p2) << ' ' << (!gt2(p2, b) && !gt2(b, p2));
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+  static_assert( gt2(a+1, a), "constexpr greater<>" );
+  static_assert( !gt2(a, a+1), "constexpr greater<>" );
+#endif
+}
+
+void
+test02()
+{
+  auto p = a + 8;
+  std::less<int*> lt;
+
+  std::stringstream ss;
+  ss << lt(p, b) << ' ' << lt(b, p) << ' ' << (!lt(p, b) && !lt(b, p));
+  int sum = 0, n = 0;
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+#if __cplusplus >= 201402L
+  static_assert( lt(a, a+1), "constexpr less<int*>" );
+  static_assert( !lt(a+1, a), "constexpr less<int*>" );
+
+  ss.str("");
+  ss.clear();
+  sum = 0;
+  auto p2 = a + 8;
+  std::less<> lt2;
+  ss << lt2(p2, b) << ' ' << lt2(b, p2) << ' ' << (!lt2(p2, b) && !lt2(b, p2));
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+  static_assert( lt2(a, a+1), "constexpr less<>" );
+  static_assert( !lt2(a+1, a), "constexpr less<>" );
+#endif
+}
+
+void
+test03()
+{
+  auto p = a + 8;
+  std::greater_equal<int*> ge;
+
+  std::stringstream ss;
+  ss << !ge(p, b) << ' ' << !ge(b, p) << ' ' << (ge(p, b) && ge(b, p));
+  int sum = 0, n = 0;
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+#if __cplusplus >= 201402L
+  static_assert( !ge(a, a+1), "constexpr greater_equal<int*>" );
+  static_assert( ge(a, a), "constexpr greater_equal<int*>" );
+  static_assert( ge(a+1, a), "constexpr greater_equal<int*>" );
+
+  ss.str("");
+  ss.clear();
+  sum = 0;
+  auto p2 = a + 8;
+  std::greater_equal<> ge2;
+  ss << !ge2(p2, b) << ' ' << !ge2(b, p2) << ' ' << (ge2(p2, b) && ge2(b, p2));
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+  static_assert( !ge2(a, a+1), "constexpr greater_equal<>" );
+  static_assert( ge2(a, a), "constexpr greater_equal<>" );
+  static_assert( ge2(a+1, a), "constexpr greater_equal<>" );
+#endif
+}
+
+void
+test04()
+{
+  auto p = a + 8;
+  std::less_equal<int*> le;
+
+  std::stringstream ss;
+  ss << !le(p, b) << ' ' << !le(b, p) << ' ' << (le(p, b) && le(b, p));
+  int sum = 0, n = 0;
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+#if __cplusplus >= 201402L
+  static_assert( !le(a+1, a), "constexpr less_equal<int*>" );
+  static_assert( le(a, a), "constexpr less_equal<int*>" );
+  static_assert( le(a, a+1), "constexpr less_equal<int*>" );
+
+  ss.str("");
+  ss.clear();
+  sum = 0;
+  auto p2 = a + 8;
+  std::less_equal<> le2;
+  ss << !le2(p2, b) << ' ' << !le2(b, p2) << ' ' << (le2(p2, b) && le2(b, p2));
+  while (ss >> n)
+    sum += n;
+  VERIFY( sum == 1 );
+
+  static_assert( !le2(a+1, a), "constexpr less_equal<>" );
+  static_assert( le2(a, a), "constexpr less_equal<>" );
+  static_assert( le2(a, a+1), "constexpr less_equal<>" );
+#endif
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+}