@@ -62,19 +62,9 @@ namespace ranges
{
namespace __detail
{
- // BUILTIN-PTR-CMP(T, ==, U)
- template<typename _Tp, typename _Up>
- concept __eq_builtin_ptr_cmp
- = requires (_Tp&& __t, _Up&& __u) { { __t == __u } -> same_as<bool>; }
- && convertible_to<_Tp, const volatile void*>
- && convertible_to<_Up, const volatile void*>
- && (! requires(_Tp&& __t, _Up&& __u)
- { operator==(std::forward<_Tp>(__t), std::forward<_Up>(__u)); }
- &&
- ! requires(_Tp&& __t, _Up&& __u)
- { std::forward<_Tp>(__t).operator==(std::forward<_Up>(__u)); });
-
// BUILTIN-PTR-CMP(T, <, U)
+ // This determines whether t < u results in a call to a built-in operator<
+ // comparing pointers. It doesn't work for function pointers (PR 93628).
template<typename _Tp, typename _Up>
concept __less_builtin_ptr_cmp
= requires (_Tp&& __t, _Up&& __u) { { __t < __u } -> same_as<bool>; }
@@ -88,12 +78,14 @@ namespace ranges
// [range.cmp] Concept-constrained comparisons
+ // _GLIBCXX_RESOLVE_LIB_DEFECTS
+ // 3530 BUILTIN-PTR-MEOW should not opt the type out of syntactic checks
+
/// ranges::equal_to function object type.
struct equal_to
{
template<typename _Tp, typename _Up>
requires equality_comparable_with<_Tp, _Up>
- || __detail::__eq_builtin_ptr_cmp<_Tp, _Up>
constexpr bool
operator()(_Tp&& __t, _Up&& __u) const
noexcept(noexcept(std::declval<_Tp>() == std::declval<_Up>()))
@@ -107,7 +99,6 @@ namespace ranges
{
template<typename _Tp, typename _Up>
requires equality_comparable_with<_Tp, _Up>
- || __detail::__eq_builtin_ptr_cmp<_Tp, _Up>
constexpr bool
operator()(_Tp&& __t, _Up&& __u) const
noexcept(noexcept(std::declval<_Up>() == std::declval<_Tp>()))
@@ -121,7 +112,6 @@ namespace ranges
{
template<typename _Tp, typename _Up>
requires totally_ordered_with<_Tp, _Up>
- || __detail::__less_builtin_ptr_cmp<_Tp, _Up>
constexpr bool
operator()(_Tp&& __t, _Up&& __u) const
noexcept(noexcept(std::declval<_Tp>() < std::declval<_Up>()))
@@ -150,7 +140,6 @@ namespace ranges
{
template<typename _Tp, typename _Up>
requires totally_ordered_with<_Tp, _Up>
- || __detail::__less_builtin_ptr_cmp<_Up, _Tp>
constexpr bool
operator()(_Tp&& __t, _Up&& __u) const
noexcept(noexcept(std::declval<_Up>() < std::declval<_Tp>()))
@@ -164,7 +153,6 @@ namespace ranges
{
template<typename _Tp, typename _Up>
requires totally_ordered_with<_Tp, _Up>
- || __detail::__less_builtin_ptr_cmp<_Tp, _Up>
constexpr bool
operator()(_Tp&& __t, _Up&& __u) const
noexcept(noexcept(std::declval<_Tp>() < std::declval<_Up>()))
@@ -178,7 +166,6 @@ namespace ranges
{
template<typename _Tp, typename _Up>
requires totally_ordered_with<_Tp, _Up>
- || __detail::__less_builtin_ptr_cmp<_Up, _Tp>
constexpr bool
operator()(_Tp&& __t, _Up&& __u) const
noexcept(noexcept(std::declval<_Up>() < std::declval<_Tp>()))
@@ -479,6 +479,9 @@ namespace std
namespace __detail
{
// BUILTIN-PTR-THREE-WAY(T, U)
+ // This determines whether t <=> u results in a call to a built-in
+ // operator<=> comparing pointers. It doesn't work for function pointers
+ // (PR 93628).
template<typename _Tp, typename _Up>
concept __3way_builtin_ptr_cmp
= requires(_Tp&& __t, _Up&& __u)
@@ -491,12 +494,14 @@ namespace std
{ static_cast<_Tp&&>(__t).operator<=>(static_cast<_Up&&>(__u)); };
} // namespace __detail
+ // _GLIBCXX_RESOLVE_LIB_DEFECTS
+ // 3530 BUILTIN-PTR-MEOW should not opt the type out of syntactic checks
+
// [cmp.object], typename compare_three_way
struct compare_three_way
{
template<typename _Tp, typename _Up>
requires three_way_comparable_with<_Tp, _Up>
- || __detail::__3way_builtin_ptr_cmp<_Tp, _Up>
constexpr auto
operator()(_Tp&& __t, _Up&& __u) const
noexcept(noexcept(std::declval<_Tp>() <=> std::declval<_Up>()))
similarity index 79%
rename from libstdc++-v3/testsuite/18_support/comparisons/object/builtin-ptr-three-way.cc
rename to libstdc++-v3/testsuite/18_support/comparisons/object/lwg3530.cc
@@ -20,6 +20,11 @@
#include <compare>
+template<typename C, typename T, typename U>
+ concept comparable = requires (const C& cmp, const T& t, const U& u) {
+ cmp(t, u);
+ };
+
void
test01()
{
@@ -39,7 +44,9 @@ test01()
long l;
// But <=> is valid and resolves to a builtin operator comparing pointers:
- auto c = &l <=> x;
- // So std::compare_three_way should be usable:
- auto c2 = std::compare_three_way()(&l, x);
+ [[maybe_unused]] auto c = &l <=> x;
+
+ // But LWG 3530 says std::compare_three_way should not be usable:
+ static_assert( ! comparable<std::compare_three_way, long*, X> );
+ static_assert( ! comparable<std::compare_three_way, X, long*> );
}
new file mode 100644
@@ -0,0 +1,47 @@
+// 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 "-std=gnu++20" }
+// { dg-do compile { target c++20 } }
+
+#include <functional>
+
+struct S {
+ constexpr operator int*() const { return nullptr; }
+};
+
+void operator!=(S const&, S const&) {}
+void operator>=(S const&, S const&) {}
+
+// S can be compared via conversion to int*
+static_assert(S{} == S{});
+static_assert(S{} <= S{});
+// But concept not satisfied because operator!= returns void
+static_assert(!std::equality_comparable_with<S,S>);
+// But concept not satisfied because operator>= returns void
+static_assert(!std::totally_ordered<S>);
+
+template<typename C, typename T>
+ concept comparable = requires (const C& cmp, const T& t) { cmp(t, t); };
+
+// LWG 3530 says [range.cmp] comparisons should not work for S
+static_assert( ! comparable<std::ranges::equal_to, S> );
+static_assert( ! comparable<std::ranges::not_equal_to, S> );
+static_assert( ! comparable<std::ranges::greater, S> );
+static_assert( ! comparable<std::ranges::less, S> );
+static_assert( ! comparable<std::ranges::greater_equal, S> );
+static_assert( ! comparable<std::ranges::less_equal, S> );