Skip to content

Commit 4884d6c

Browse files
authored
[libc++] Extend __default_three_way_comparator to any types that only implements operator<=> (#157602)
This uses the new `__builtin_lt_synthesises_from_spaceship` builtin from clang to use three way comparison for arbitrary user-defined types that only provide a spaceship operator.
1 parent fae68b6 commit 4884d6c

File tree

5 files changed

+77
-11
lines changed

5 files changed

+77
-11
lines changed

libcxx/include/__type_traits/desugars_to.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define _LIBCPP___TYPE_TRAITS_DESUGARS_TO_H
1111

1212
#include <__config>
13+
#include <__type_traits/integral_constant.h>
1314

1415
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
1516
# pragma GCC system_header
@@ -64,6 +65,9 @@ template <class _CanonicalTag, class _Operation, class... _Args>
6465
inline const bool __desugars_to_v<_CanonicalTag, _Operation&&, _Args...> =
6566
__desugars_to_v<_CanonicalTag, _Operation, _Args...>;
6667

68+
template <class _CanonicalTag, class _Operation, class... _Args>
69+
struct __desugars_to : integral_constant<bool, __desugars_to_v<_CanonicalTag, _Operation, _Args...> > {};
70+
6771
_LIBCPP_END_NAMESPACE_STD
6872

6973
#endif // _LIBCPP___TYPE_TRAITS_DESUGARS_TO_H

libcxx/include/__utility/default_three_way_comparator.h

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@ _LIBCPP_BEGIN_NAMESPACE_STD
2727
template <class _LHS, class _RHS, class = void>
2828
struct __default_three_way_comparator;
2929

30-
template <class _Tp>
31-
struct __default_three_way_comparator<_Tp, _Tp, __enable_if_t<is_arithmetic<_Tp>::value> > {
32-
_LIBCPP_HIDE_FROM_ABI static int operator()(_Tp __lhs, _Tp __rhs) {
30+
template <class _LHS, class _RHS>
31+
struct __default_three_way_comparator<_LHS,
32+
_RHS,
33+
__enable_if_t<is_arithmetic<_LHS>::value && is_arithmetic<_RHS>::value> > {
34+
_LIBCPP_HIDE_FROM_ABI static int operator()(_LHS __lhs, _RHS __rhs) {
3335
if (__lhs < __rhs)
3436
return -1;
3537
if (__lhs > __rhs)
@@ -38,12 +40,30 @@ struct __default_three_way_comparator<_Tp, _Tp, __enable_if_t<is_arithmetic<_Tp>
3840
}
3941
};
4042

43+
#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_lt_synthesises_from_spaceship)
44+
template <class _LHS, class _RHS>
45+
struct __default_three_way_comparator<
46+
_LHS,
47+
_RHS,
48+
__enable_if_t<!(is_arithmetic<_LHS>::value && is_arithmetic<_RHS>::value) &&
49+
__builtin_lt_synthesises_from_spaceship(const _LHS&, const _RHS&)>> {
50+
_LIBCPP_HIDE_FROM_ABI static int operator()(const _LHS& __lhs, const _RHS& __rhs) {
51+
auto __res = __lhs <=> __rhs;
52+
if (__res < 0)
53+
return -1;
54+
if (__res > 0)
55+
return 1;
56+
return 0;
57+
}
58+
};
59+
#endif
60+
4161
template <class _LHS, class _RHS, bool = true>
42-
inline const bool __has_default_three_way_comparator_v = false;
62+
struct __has_default_three_way_comparator : false_type {};
4363

4464
template <class _LHS, class _RHS>
45-
inline const bool
46-
__has_default_three_way_comparator_v< _LHS, _RHS, sizeof(__default_three_way_comparator<_LHS, _RHS>) >= 0> = true;
65+
struct __has_default_three_way_comparator<_LHS, _RHS, sizeof(__default_three_way_comparator<_LHS, _RHS>) >= 0>
66+
: true_type {};
4767

4868
_LIBCPP_END_NAMESPACE_STD
4969

libcxx/include/__utility/lazy_synth_three_way_comparator.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define _LIBCPP___UTILITY_LAZY_SYNTH_THREE_WAY_COMPARATOR_H
1111

1212
#include <__config>
13+
#include <__type_traits/conjunction.h>
1314
#include <__type_traits/desugars_to.h>
1415
#include <__type_traits/enable_if.h>
1516
#include <__utility/default_three_way_comparator.h>
@@ -69,11 +70,12 @@ struct __eager_compare_result {
6970
};
7071

7172
template <class _Comparator, class _LHS, class _RHS>
72-
struct __lazy_synth_three_way_comparator<_Comparator,
73-
_LHS,
74-
_RHS,
75-
__enable_if_t<__desugars_to_v<__less_tag, _Comparator, _LHS, _RHS> &&
76-
__has_default_three_way_comparator_v<_LHS, _RHS> > > {
73+
struct __lazy_synth_three_way_comparator<
74+
_Comparator,
75+
_LHS,
76+
_RHS,
77+
__enable_if_t<_And<__desugars_to<__less_tag, _Comparator, _LHS, _RHS>,
78+
__has_default_three_way_comparator<_LHS, _RHS> >::value> > {
7779
// This lifetimebound annotation is technically incorrect, but other specializations actually capture the lifetime of
7880
// the comparator.
7981
_LIBCPP_HIDE_FROM_ABI __lazy_synth_three_way_comparator(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator&) {}

libcxx/include/string

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2521,6 +2521,7 @@ _LIBCPP_STRING_V1_EXTERN_TEMPLATE_LIST(_LIBCPP_DECLARE, wchar_t)
25212521
# endif
25222522
# undef _LIBCPP_DECLARE
25232523

2524+
# if _LIBCPP_STD_VER <= 17 || !__has_builtin(__builtin_lt_synthesises_from_spaceship)
25242525
template <class _CharT, class _Traits, class _Alloc>
25252526
struct __default_three_way_comparator<basic_string<_CharT, _Traits, _Alloc>, basic_string<_CharT, _Traits, _Alloc> > {
25262527
using __string_t _LIBCPP_NODEBUG = basic_string<_CharT, _Traits, _Alloc>;
@@ -2533,6 +2534,7 @@ struct __default_three_way_comparator<basic_string<_CharT, _Traits, _Alloc>, bas
25332534
return __ret;
25342535
}
25352536
};
2537+
# endif
25362538

25372539
# if _LIBCPP_STD_VER >= 17
25382540
template <class _InputIterator,
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11, c++14, c++17
10+
11+
#include <__utility/default_three_way_comparator.h>
12+
#include <string>
13+
#include <vector>
14+
15+
static_assert(std::__has_default_three_way_comparator<int, int>::value);
16+
static_assert(std::__has_default_three_way_comparator<int, long>::value);
17+
static_assert(std::__has_default_three_way_comparator<long, int>::value);
18+
static_assert(std::__has_default_three_way_comparator<long, long>::value);
19+
static_assert(std::__has_default_three_way_comparator<std::string, std::string>::value);
20+
21+
#if __has_builtin(__builtin_lt_synthesises_from_spaceship)
22+
static_assert(std::__has_default_three_way_comparator<const std::string&, const std::string&>::value);
23+
static_assert(std::__has_default_three_way_comparator<const std::string&, const std::string_view&>::value);
24+
static_assert(std::__has_default_three_way_comparator<std::string, std::string_view>::value);
25+
static_assert(std::__has_default_three_way_comparator<const std::string&, const char*>::value);
26+
static_assert(std::__has_default_three_way_comparator<std::string, const char*>::value);
27+
static_assert(!std::__has_default_three_way_comparator<const std::string&, const wchar_t*>::value);
28+
29+
static_assert(std::__has_default_three_way_comparator<const std::vector<int>&, const std::vector<int>&>::value);
30+
31+
struct MyStruct {
32+
int i;
33+
34+
friend auto operator<=>(MyStruct, MyStruct) = default;
35+
};
36+
37+
static_assert(std::__has_default_three_way_comparator<const MyStruct&, const MyStruct&>::value);
38+
#endif

0 commit comments

Comments
 (0)