1// -*- C++ -*- operator<=> three-way comparison support.
2
3// Copyright (C) 2019-2020 Free Software Foundation, Inc.
4//
5// This file is part of GCC.
6//
7// GCC is free software; you can redistribute it and/or modify
8// it under the terms of the GNU General Public License as published by
9// the Free Software Foundation; either version 3, or (at your option)
10// any later version.
11//
12// GCC is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15// GNU General Public License for more details.
16//
17// Under Section 7 of GPL version 3, you are granted additional
18// permissions described in the GCC Runtime Library Exception, version
19// 3.1, as published by the Free Software Foundation.
20
21// You should have received a copy of the GNU General Public License and
22// a copy of the GCC Runtime Library Exception along with this program;
23// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
24// <http://www.gnu.org/licenses/>.
25
26/** @file compare
27 *  This is a Standard C++ Library header.
28 */
29
30#ifndef _COMPARE
31#define _COMPARE
32
33#pragma GCC system_header
34
35#if __cplusplus > 201703L && __cpp_impl_three_way_comparison >= 201907L
36
37#pragma GCC visibility push(default)
38
39#include <concepts>
40
41#if __cpp_lib_concepts
42# define __cpp_lib_three_way_comparison 201907L
43#endif
44
45namespace std
46{
47  // [cmp.categories], comparison category types
48
49  namespace __cmp_cat
50  {
51    using type = signed char;
52
53    enum class _Ord : type { equivalent = 0, less = -1, greater = 1 };
54
55    enum class _Ncmp : type { _Unordered = 2 };
56
57    struct __unspec
58    {
59      constexpr __unspec(__unspec*) noexcept { }
60    };
61  }
62
63  class partial_ordering
64  {
65    // less=0xff, equiv=0x00, greater=0x01, unordered=0x02
66    __cmp_cat::type _M_value;
67
68    constexpr explicit
69    partial_ordering(__cmp_cat::_Ord __v) noexcept
70    : _M_value(__cmp_cat::type(__v))
71    { }
72
73    constexpr explicit
74    partial_ordering(__cmp_cat::_Ncmp __v) noexcept
75    : _M_value(__cmp_cat::type(__v))
76    { }
77
78    friend class weak_ordering;
79    friend class strong_ordering;
80
81  public:
82    // valid values
83    static const partial_ordering less;
84    static const partial_ordering equivalent;
85    static const partial_ordering greater;
86    static const partial_ordering unordered;
87
88    // comparisons
89    friend constexpr bool
90    operator==(partial_ordering __v, __cmp_cat::__unspec) noexcept
91    { return __v._M_value == 0; }
92
93    friend constexpr bool
94    operator==(partial_ordering, partial_ordering) noexcept = default;
95
96    friend constexpr bool
97    operator< (partial_ordering __v, __cmp_cat::__unspec) noexcept
98    { return __v._M_value == -1; }
99
100    friend constexpr bool
101    operator> (partial_ordering __v, __cmp_cat::__unspec) noexcept
102    { return __v._M_value == 1; }
103
104    friend constexpr bool
105    operator<=(partial_ordering __v, __cmp_cat::__unspec) noexcept
106    { return __v._M_value <= 0; }
107
108    friend constexpr bool
109    operator>=(partial_ordering __v, __cmp_cat::__unspec) noexcept
110    { return __cmp_cat::type(__v._M_value & 1) == __v._M_value; }
111
112    friend constexpr bool
113    operator< (__cmp_cat::__unspec, partial_ordering __v) noexcept
114    { return __v._M_value == 1; }
115
116    friend constexpr bool
117    operator> (__cmp_cat::__unspec, partial_ordering __v) noexcept
118    { return __v._M_value == -1; }
119
120    friend constexpr bool
121    operator<=(__cmp_cat::__unspec, partial_ordering __v) noexcept
122    { return __cmp_cat::type(__v._M_value & 1) == __v._M_value; }
123
124    friend constexpr bool
125    operator>=(__cmp_cat::__unspec, partial_ordering __v) noexcept
126    { return 0 >= __v._M_value; }
127
128    friend constexpr partial_ordering
129    operator<=>(partial_ordering __v, __cmp_cat::__unspec) noexcept
130    { return __v; }
131
132    friend constexpr partial_ordering
133    operator<=>(__cmp_cat::__unspec, partial_ordering __v) noexcept
134    {
135      if (__v._M_value & 1)
136	return partial_ordering(__cmp_cat::_Ord(-__v._M_value));
137      else
138	return __v;
139    }
140  };
141
142  // valid values' definitions
143  inline constexpr partial_ordering
144  partial_ordering::less(__cmp_cat::_Ord::less);
145
146  inline constexpr partial_ordering
147  partial_ordering::equivalent(__cmp_cat::_Ord::equivalent);
148
149  inline constexpr partial_ordering
150  partial_ordering::greater(__cmp_cat::_Ord::greater);
151
152  inline constexpr partial_ordering
153  partial_ordering::unordered(__cmp_cat::_Ncmp::_Unordered);
154
155  class weak_ordering
156  {
157    __cmp_cat::type _M_value;
158
159    constexpr explicit
160    weak_ordering(__cmp_cat::_Ord __v) noexcept : _M_value(__cmp_cat::type(__v))
161    { }
162
163    friend class strong_ordering;
164
165  public:
166    // valid values
167    static const weak_ordering less;
168    static const weak_ordering equivalent;
169    static const weak_ordering greater;
170
171    constexpr operator partial_ordering() const noexcept
172    { return partial_ordering(__cmp_cat::_Ord(_M_value)); }
173
174    // comparisons
175    friend constexpr bool
176    operator==(weak_ordering __v, __cmp_cat::__unspec) noexcept
177    { return __v._M_value == 0; }
178
179    friend constexpr bool
180    operator==(weak_ordering, weak_ordering) noexcept = default;
181
182    friend constexpr bool
183    operator< (weak_ordering __v, __cmp_cat::__unspec) noexcept
184    { return __v._M_value < 0; }
185
186    friend constexpr bool
187    operator> (weak_ordering __v, __cmp_cat::__unspec) noexcept
188    { return __v._M_value > 0; }
189
190    friend constexpr bool
191    operator<=(weak_ordering __v, __cmp_cat::__unspec) noexcept
192    { return __v._M_value <= 0; }
193
194    friend constexpr bool
195    operator>=(weak_ordering __v, __cmp_cat::__unspec) noexcept
196    { return __v._M_value >= 0; }
197
198    friend constexpr bool
199    operator< (__cmp_cat::__unspec, weak_ordering __v) noexcept
200    { return 0 < __v._M_value; }
201
202    friend constexpr bool
203    operator> (__cmp_cat::__unspec, weak_ordering __v) noexcept
204    { return 0 > __v._M_value; }
205
206    friend constexpr bool
207    operator<=(__cmp_cat::__unspec, weak_ordering __v) noexcept
208    { return 0 <= __v._M_value; }
209
210    friend constexpr bool
211    operator>=(__cmp_cat::__unspec, weak_ordering __v) noexcept
212    { return 0 >= __v._M_value; }
213
214    friend constexpr weak_ordering
215    operator<=>(weak_ordering __v, __cmp_cat::__unspec) noexcept
216    { return __v; }
217
218    friend constexpr weak_ordering
219    operator<=>(__cmp_cat::__unspec, weak_ordering __v) noexcept
220    { return weak_ordering(__cmp_cat::_Ord(-__v._M_value)); }
221  };
222
223  // valid values' definitions
224  inline constexpr weak_ordering
225  weak_ordering::less(__cmp_cat::_Ord::less);
226
227  inline constexpr weak_ordering
228  weak_ordering::equivalent(__cmp_cat::_Ord::equivalent);
229
230  inline constexpr weak_ordering
231  weak_ordering::greater(__cmp_cat::_Ord::greater);
232
233  class strong_ordering
234  {
235    __cmp_cat::type _M_value;
236
237    constexpr explicit
238    strong_ordering(__cmp_cat::_Ord __v) noexcept
239    : _M_value(__cmp_cat::type(__v))
240    { }
241
242  public:
243    // valid values
244    static const strong_ordering less;
245    static const strong_ordering equal;
246    static const strong_ordering equivalent;
247    static const strong_ordering greater;
248
249    constexpr operator partial_ordering() const noexcept
250    { return partial_ordering(__cmp_cat::_Ord(_M_value)); }
251
252    constexpr operator weak_ordering() const noexcept
253    { return weak_ordering(__cmp_cat::_Ord(_M_value)); }
254
255    // comparisons
256    friend constexpr bool
257    operator==(strong_ordering __v, __cmp_cat::__unspec) noexcept
258    { return __v._M_value == 0; }
259
260    friend constexpr bool
261    operator==(strong_ordering, strong_ordering) noexcept = default;
262
263    friend constexpr bool
264    operator< (strong_ordering __v, __cmp_cat::__unspec) noexcept
265    { return __v._M_value < 0; }
266
267    friend constexpr bool
268    operator> (strong_ordering __v, __cmp_cat::__unspec) noexcept
269    { return __v._M_value > 0; }
270
271    friend constexpr bool
272    operator<=(strong_ordering __v, __cmp_cat::__unspec) noexcept
273    { return __v._M_value <= 0; }
274
275    friend constexpr bool
276    operator>=(strong_ordering __v, __cmp_cat::__unspec) noexcept
277    { return __v._M_value >= 0; }
278
279    friend constexpr bool
280    operator< (__cmp_cat::__unspec, strong_ordering __v) noexcept
281    { return 0 < __v._M_value; }
282
283    friend constexpr bool
284    operator> (__cmp_cat::__unspec, strong_ordering __v) noexcept
285    { return 0 > __v._M_value; }
286
287    friend constexpr bool
288    operator<=(__cmp_cat::__unspec, strong_ordering __v) noexcept
289    { return 0 <= __v._M_value; }
290
291    friend constexpr bool
292    operator>=(__cmp_cat::__unspec, strong_ordering __v) noexcept
293    { return 0 >= __v._M_value; }
294
295    friend constexpr strong_ordering
296    operator<=>(strong_ordering __v, __cmp_cat::__unspec) noexcept
297    { return __v; }
298
299    friend constexpr strong_ordering
300    operator<=>(__cmp_cat::__unspec, strong_ordering __v) noexcept
301    { return strong_ordering(__cmp_cat::_Ord(-__v._M_value)); }
302  };
303
304  // valid values' definitions
305  inline constexpr strong_ordering
306  strong_ordering::less(__cmp_cat::_Ord::less);
307
308  inline constexpr strong_ordering
309  strong_ordering::equal(__cmp_cat::_Ord::equivalent);
310
311  inline constexpr strong_ordering
312  strong_ordering::equivalent(__cmp_cat::_Ord::equivalent);
313
314  inline constexpr strong_ordering
315  strong_ordering::greater(__cmp_cat::_Ord::greater);
316
317
318  // named comparison functions
319  constexpr bool
320  is_eq(partial_ordering __cmp) noexcept
321  { return __cmp == 0; }
322
323  constexpr bool
324  is_neq(partial_ordering __cmp) noexcept
325  { return __cmp != 0; }
326
327  constexpr bool
328  is_lt  (partial_ordering __cmp) noexcept
329  { return __cmp < 0; }
330
331  constexpr bool
332  is_lteq(partial_ordering __cmp) noexcept
333  { return __cmp <= 0; }
334
335  constexpr bool
336  is_gt  (partial_ordering __cmp) noexcept
337  { return __cmp > 0; }
338
339  constexpr bool
340  is_gteq(partial_ordering __cmp) noexcept
341  { return __cmp >= 0; }
342
343  namespace __detail
344  {
345    template<typename _Tp>
346      inline constexpr unsigned __cmp_cat_id = 1;
347    template<>
348      inline constexpr unsigned __cmp_cat_id<partial_ordering> = 2;
349    template<>
350      inline constexpr unsigned __cmp_cat_id<weak_ordering> = 4;
351    template<>
352      inline constexpr unsigned __cmp_cat_id<strong_ordering> = 8;
353
354    template<typename... _Ts>
355      constexpr auto __common_cmp_cat()
356      {
357	constexpr unsigned __cats = (__cmp_cat_id<_Ts> | ...);
358	// If any Ti is not a comparison category type, U is void.
359	if constexpr (__cats & 1)
360	  return;
361	// Otherwise, if at least one Ti is std::partial_ordering,
362	// U is std::partial_ordering.
363	else if constexpr (bool(__cats & __cmp_cat_id<partial_ordering>))
364	  return partial_ordering::equivalent;
365	// Otherwise, if at least one Ti is std::weak_ordering,
366	// U is std::weak_ordering.
367	else if constexpr (bool(__cats & __cmp_cat_id<weak_ordering>))
368	  return weak_ordering::equivalent;
369	// Otherwise, U is std::strong_ordering.
370	else
371	  return strong_ordering::equivalent;
372      }
373  } // namespace __detail
374
375  // [cmp.common], common comparison category type
376  template<typename... _Ts>
377    struct common_comparison_category
378    {
379      using type = decltype(__detail::__common_cmp_cat<_Ts...>());
380    };
381
382  // Partial specializations for one and zero argument cases.
383
384  template<typename _Tp>
385    struct common_comparison_category<_Tp>
386    { using type = void; };
387
388  template<>
389    struct common_comparison_category<partial_ordering>
390    { using type = partial_ordering; };
391
392  template<>
393    struct common_comparison_category<weak_ordering>
394    { using type = weak_ordering; };
395
396  template<>
397    struct common_comparison_category<strong_ordering>
398    { using type = strong_ordering; };
399
400  template<>
401    struct common_comparison_category<>
402    { using type = strong_ordering; };
403
404  template<typename... _Ts>
405    using common_comparison_category_t
406      = typename common_comparison_category<_Ts...>::type;
407
408#if __cpp_lib_concepts
409  namespace __detail
410  {
411    template<typename _Tp, typename _Cat>
412      concept __compares_as
413	= same_as<common_comparison_category_t<_Tp, _Cat>, _Cat>;
414  } // namespace __detail
415
416  // [cmp.concept], concept three_way_comparable
417  template<typename _Tp, typename _Cat = partial_ordering>
418    concept three_way_comparable
419      = __detail::__weakly_eq_cmp_with<_Tp, _Tp>
420      && __detail::__partially_ordered_with<_Tp, _Tp>
421      && requires(const remove_reference_t<_Tp>& __a,
422		  const remove_reference_t<_Tp>& __b)
423      {
424	{ __a <=> __b } -> __detail::__compares_as<_Cat>;
425      };
426
427  template<typename _Tp, typename _Up, typename _Cat = partial_ordering>
428    concept three_way_comparable_with
429      = three_way_comparable<_Tp, _Cat>
430      && three_way_comparable<_Up, _Cat>
431      && common_reference_with<const remove_reference_t<_Tp>&,
432			       const remove_reference_t<_Up>&>
433      && three_way_comparable<
434	  common_reference_t<const remove_reference_t<_Tp>&,
435			     const remove_reference_t<_Up>&>, _Cat>
436      && __detail::__weakly_eq_cmp_with<_Tp, _Up>
437      && __detail::__partially_ordered_with<_Tp, _Up>
438      && requires(const remove_reference_t<_Tp>& __t,
439		  const remove_reference_t<_Up>& __u)
440      {
441	{ __t <=> __u } -> __detail::__compares_as<_Cat>;
442	{ __u <=> __t } -> __detail::__compares_as<_Cat>;
443      };
444
445  namespace __detail
446  {
447    template<typename _Tp, typename _Up>
448      using __cmp3way_res_t
449	= decltype(std::declval<_Tp>() <=> std::declval<_Up>());
450
451    // Implementation of std::compare_three_way_result.
452    // It is undefined for a program to add specializations of
453    // std::compare_three_way_result, so the std::compare_three_way_result_t
454    // alias ignores std::compare_three_way_result and uses
455    // __detail::__cmp3way_res_impl directly instead.
456    template<typename _Tp, typename _Up>
457      struct __cmp3way_res_impl
458      { };
459
460    template<typename _Tp, typename _Up>
461      requires requires { typename __cmp3way_res_t<__cref<_Tp>, __cref<_Up>>; }
462      struct __cmp3way_res_impl<_Tp, _Up>
463      {
464	using type = __cmp3way_res_t<__cref<_Tp>, __cref<_Up>>;
465      };
466  } // namespace __detail
467
468  /// [cmp.result], result of three-way comparison
469  template<typename _Tp, typename _Up = _Tp>
470    struct compare_three_way_result
471    : __detail::__cmp3way_res_impl<_Tp, _Up>
472    { };
473
474  /// [cmp.result], result of three-way comparison
475  template<typename _Tp, typename _Up = _Tp>
476    using compare_three_way_result_t
477      = typename __detail::__cmp3way_res_impl<_Tp, _Up>::type;
478
479  namespace __detail
480  {
481    // BUILTIN-PTR-THREE-WAY(T, U)
482    // This determines whether t <=> u results in a call to a built-in
483    // operator<=> comparing pointers. It doesn't work for function pointers
484    // (PR 93628).
485    template<typename _Tp, typename _Up>
486      concept __3way_builtin_ptr_cmp
487	= requires(_Tp&& __t, _Up&& __u)
488	  { static_cast<_Tp&&>(__t) <=> static_cast<_Up&&>(__u); }
489	  && convertible_to<_Tp, const volatile void*>
490	  && convertible_to<_Up, const volatile void*>
491	  && ! requires(_Tp&& __t, _Up&& __u)
492	  { operator<=>(static_cast<_Tp&&>(__t), static_cast<_Up&&>(__u)); }
493	  && ! requires(_Tp&& __t, _Up&& __u)
494	  { static_cast<_Tp&&>(__t).operator<=>(static_cast<_Up&&>(__u)); };
495  } // namespace __detail
496
497  // _GLIBCXX_RESOLVE_LIB_DEFECTS
498  // 3530 BUILTIN-PTR-MEOW should not opt the type out of syntactic checks
499
500  // [cmp.object], typename compare_three_way
501  struct compare_three_way
502  {
503    template<typename _Tp, typename _Up>
504      requires three_way_comparable_with<_Tp, _Up>
505      constexpr auto
506      operator()(_Tp&& __t, _Up&& __u) const
507      noexcept(noexcept(std::declval<_Tp>() <=> std::declval<_Up>()))
508      {
509	if constexpr (__detail::__3way_builtin_ptr_cmp<_Tp, _Up>)
510	  {
511	    auto __pt = static_cast<const volatile void*>(__t);
512	    auto __pu = static_cast<const volatile void*>(__u);
513	    if (__builtin_is_constant_evaluated())
514	      return __pt <=> __pu;
515	    auto __it = reinterpret_cast<__UINTPTR_TYPE__>(__pt);
516	    auto __iu = reinterpret_cast<__UINTPTR_TYPE__>(__pu);
517	    return __it <=> __iu;
518	  }
519	else
520	  return static_cast<_Tp&&>(__t) <=> static_cast<_Up&&>(__u);
521      }
522
523    using is_transparent = void;
524  };
525
526  namespace __cmp_cust
527  {
528    template<floating_point _Tp>
529      constexpr weak_ordering
530      __fp_weak_ordering(_Tp __e, _Tp __f)
531      {
532	// Returns an integer with the same sign as the argument, and magnitude
533	// indicating the classification: zero=1 subnorm=2 norm=3 inf=4 nan=5
534	auto __cat = [](_Tp __fp) -> int {
535	  const int __sign = __builtin_signbit(__fp) ? -1 : 1;
536	  if (__builtin_isnormal(__fp))
537	    return (__fp == 0 ? 1 : 3) * __sign;
538	  if (__builtin_isnan(__fp))
539	    return 5 * __sign;
540	  if (int __inf = __builtin_isinf_sign(__fp))
541	    return 4 * __inf;
542	  return 2 * __sign;
543	};
544
545	auto __po = __e <=> __f;
546	if (is_lt(__po))
547	  return weak_ordering::less;
548	else if (is_gt(__po))
549	  return weak_ordering::greater;
550	else if (__po == partial_ordering::equivalent)
551	  return weak_ordering::equivalent;
552	else  // unordered, at least one argument is NaN
553	  {
554	    // return -1 for negative nan, +1 for positive nan, 0 otherwise.
555	    auto __isnan_sign = [](_Tp __fp) -> int {
556	      return __builtin_isnan(__fp)
557		? __builtin_signbit(__fp) ? -1 : 1
558		: 0;
559	    };
560	    auto __ord = __isnan_sign(__e) <=> __isnan_sign(__f);
561	    if (is_eq(__ord))
562	      return weak_ordering::equivalent;
563	    else if (is_lt(__ord))
564	      return weak_ordering::less;
565	    else
566	      return weak_ordering::greater;
567	  }
568      }
569
570    template<typename _Tp, typename _Up>
571      concept __adl_strong = requires(_Tp&& __t, _Up&& __u)
572	{
573	  strong_ordering(strong_order(static_cast<_Tp&&>(__t),
574				       static_cast<_Up&&>(__u)));
575	};
576
577    template<typename _Tp, typename _Up>
578      concept __adl_weak = requires(_Tp&& __t, _Up&& __u)
579	{
580	  weak_ordering(weak_order(static_cast<_Tp&&>(__t),
581				   static_cast<_Up&&>(__u)));
582	};
583
584    template<typename _Tp, typename _Up>
585      concept __adl_partial = requires(_Tp&& __t, _Up&& __u)
586	{
587	  partial_ordering(partial_order(static_cast<_Tp&&>(__t),
588					 static_cast<_Up&&>(__u)));
589	};
590
591    template<typename _Ord, typename _Tp, typename _Up>
592      concept __cmp3way = requires(_Tp&& __t, _Up&& __u, compare_three_way __c)
593	{
594	  _Ord(__c(static_cast<_Tp&&>(__t), static_cast<_Up&&>(__u)));
595	};
596
597    template<typename _Tp, typename _Up>
598      concept __strongly_ordered
599	= __adl_strong<_Tp, _Up>
600	  // FIXME: || floating_point<remove_reference_t<_Tp>>
601	  || __cmp3way<strong_ordering, _Tp, _Up>;
602
603    template<typename _Tp, typename _Up>
604      concept __decayed_same_as = same_as<decay_t<_Tp>, decay_t<_Up>>;
605
606    class _Strong_order
607    {
608      template<typename _Tp, typename _Up>
609	static constexpr bool
610	_S_noexcept()
611	{
612	  if constexpr (floating_point<decay_t<_Tp>>)
613	    return true;
614	  else if constexpr (__adl_strong<_Tp, _Up>)
615	    return noexcept(strong_ordering(strong_order(std::declval<_Tp>(),
616							 std::declval<_Up>())));
617	  else if constexpr (__cmp3way<strong_ordering, _Tp, _Up>)
618	    return noexcept(compare_three_way()(std::declval<_Tp>(),
619						std::declval<_Up>()));
620	}
621
622      friend class _Weak_order;
623      friend class _Strong_fallback;
624
625    public:
626      template<typename _Tp, __decayed_same_as<_Tp> _Up>
627	requires __strongly_ordered<_Tp, _Up>
628	constexpr strong_ordering
629	operator()(_Tp&& __e, _Up&& __f) const
630	noexcept(_S_noexcept<_Tp, _Up>())
631	{
632	  /* FIXME:
633	  if constexpr (floating_point<decay_t<_Tp>>)
634	    return __cmp_cust::__fp_strong_order(__e, __f);
635	  else */ if constexpr (__adl_strong<_Tp, _Up>)
636	    return strong_ordering(strong_order(static_cast<_Tp&&>(__e),
637						static_cast<_Up&&>(__f)));
638	  else if constexpr (__cmp3way<strong_ordering, _Tp, _Up>)
639	    return compare_three_way()(static_cast<_Tp&&>(__e),
640				       static_cast<_Up&&>(__f));
641	}
642    };
643
644    template<typename _Tp, typename _Up>
645      concept __weakly_ordered
646	= floating_point<remove_reference_t<_Tp>>
647	  || __adl_weak<_Tp, _Up>
648	  || __cmp3way<weak_ordering, _Tp, _Up>
649	  || __strongly_ordered<_Tp, _Up>;
650
651    class _Weak_order
652    {
653      template<typename _Tp, typename _Up>
654	static constexpr bool
655	_S_noexcept()
656	{
657	  if constexpr (floating_point<decay_t<_Tp>>)
658	    return true;
659	  else if constexpr (__adl_weak<_Tp, _Up>)
660	    return noexcept(weak_ordering(weak_order(std::declval<_Tp>(),
661						     std::declval<_Up>())));
662	  else if constexpr (__cmp3way<weak_ordering, _Tp, _Up>)
663	    return noexcept(compare_three_way()(std::declval<_Tp>(),
664						std::declval<_Up>()));
665	  else if constexpr (__strongly_ordered<_Tp, _Up>)
666	    return _Strong_order::_S_noexcept<_Tp, _Up>();
667	}
668
669      friend class _Partial_order;
670      friend class _Weak_fallback;
671
672    public:
673      template<typename _Tp, __decayed_same_as<_Tp> _Up>
674	requires __weakly_ordered<_Tp, _Up>
675	constexpr weak_ordering
676	operator()(_Tp&& __e, _Up&& __f) const
677	noexcept(_S_noexcept<_Tp, _Up>())
678	{
679	  if constexpr (floating_point<decay_t<_Tp>>)
680	    return __cmp_cust::__fp_weak_ordering(__e, __f);
681	  else if constexpr (__adl_weak<_Tp, _Up>)
682	    return weak_ordering(weak_order(static_cast<_Tp&&>(__e),
683					    static_cast<_Up&&>(__f)));
684	  else if constexpr (__cmp3way<weak_ordering, _Tp, _Up>)
685	    return compare_three_way()(static_cast<_Tp&&>(__e),
686				       static_cast<_Up&&>(__f));
687	  else if constexpr (__strongly_ordered<_Tp, _Up>)
688	    return _Strong_order{}(static_cast<_Tp&&>(__e),
689				   static_cast<_Up&&>(__f));
690	}
691    };
692
693    template<typename _Tp, typename _Up>
694      concept __partially_ordered
695	= __adl_partial<_Tp, _Up>
696	|| __cmp3way<partial_ordering, _Tp, _Up>
697	|| __weakly_ordered<_Tp, _Up>;
698
699    class _Partial_order
700    {
701      template<typename _Tp, typename _Up>
702	static constexpr bool
703	_S_noexcept()
704	{
705	  if constexpr (__adl_partial<_Tp, _Up>)
706	    return noexcept(partial_ordering(partial_order(std::declval<_Tp>(),
707							 std::declval<_Up>())));
708	  else if constexpr (__cmp3way<partial_ordering, _Tp, _Up>)
709	    return noexcept(compare_three_way()(std::declval<_Tp>(),
710						std::declval<_Up>()));
711	  else if constexpr (__weakly_ordered<_Tp, _Up>)
712	    return _Weak_order::_S_noexcept<_Tp, _Up>();
713	}
714
715      friend class _Partial_fallback;
716
717    public:
718      template<typename _Tp, __decayed_same_as<_Tp> _Up>
719	requires __partially_ordered<_Tp, _Up>
720	constexpr partial_ordering
721	operator()(_Tp&& __e, _Up&& __f) const
722	noexcept(_S_noexcept<_Tp, _Up>())
723	{
724	  if constexpr (__adl_partial<_Tp, _Up>)
725	    return partial_ordering(partial_order(static_cast<_Tp&&>(__e),
726						  static_cast<_Up&&>(__f)));
727	  else if constexpr (__cmp3way<partial_ordering, _Tp, _Up>)
728	    return compare_three_way()(static_cast<_Tp&&>(__e),
729				       static_cast<_Up&&>(__f));
730	  else if constexpr (__weakly_ordered<_Tp, _Up>)
731	    return _Weak_order{}(static_cast<_Tp&&>(__e),
732				 static_cast<_Up&&>(__f));
733	}
734    };
735
736    template<typename _Tp, typename _Up>
737      concept __op_eq_lt = requires(_Tp&& __t, _Up&& __u)
738	{
739	  { static_cast<_Tp&&>(__t) == static_cast<_Up&&>(__u) }
740	    -> convertible_to<bool>;
741	  { static_cast<_Tp&&>(__t) < static_cast<_Up&&>(__u) }
742	    -> convertible_to<bool>;
743	};
744
745    class _Strong_fallback
746    {
747      template<typename _Tp, typename _Up>
748	static constexpr bool
749	_S_noexcept()
750	{
751	  if constexpr (__strongly_ordered<_Tp, _Up>)
752	    return _Strong_order::_S_noexcept<_Tp, _Up>();
753	  else
754	    return noexcept(bool(std::declval<_Tp>() == std::declval<_Up>()))
755	      && noexcept(bool(std::declval<_Tp>() < std::declval<_Up>()));
756	}
757
758    public:
759      template<typename _Tp, __decayed_same_as<_Tp> _Up>
760	requires __strongly_ordered<_Tp, _Up> || __op_eq_lt<_Tp, _Up>
761	constexpr strong_ordering
762	operator()(_Tp&& __e, _Up&& __f) const
763	noexcept(_S_noexcept<_Tp, _Up>())
764	{
765	  if constexpr (__strongly_ordered<_Tp, _Up>)
766	    return _Strong_order{}(static_cast<_Tp&&>(__e),
767				   static_cast<_Up&&>(__f));
768	  else // __op_eq_lt<_Tp, _Up>
769	    return static_cast<_Tp&&>(__e) == static_cast<_Up&&>(__f)
770	      ? strong_ordering::equal
771	      : static_cast<_Tp&&>(__e) < static_cast<_Up&&>(__f)
772	      ? strong_ordering::less
773	      : strong_ordering::greater;
774	}
775    };
776
777    class _Weak_fallback
778    {
779      template<typename _Tp, typename _Up>
780	static constexpr bool
781	_S_noexcept()
782	{
783	  if constexpr (__weakly_ordered<_Tp, _Up>)
784	    return _Weak_order::_S_noexcept<_Tp, _Up>();
785	  else
786	    return noexcept(bool(std::declval<_Tp>() == std::declval<_Up>()))
787	      && noexcept(bool(std::declval<_Tp>() < std::declval<_Up>()));
788	}
789
790    public:
791      template<typename _Tp, __decayed_same_as<_Tp> _Up>
792	requires __weakly_ordered<_Tp, _Up> || __op_eq_lt<_Tp, _Up>
793	constexpr weak_ordering
794	operator()(_Tp&& __e, _Up&& __f) const
795	noexcept(_S_noexcept<_Tp, _Up>())
796	{
797	  if constexpr (__weakly_ordered<_Tp, _Up>)
798	    return _Weak_order{}(static_cast<_Tp&&>(__e),
799				 static_cast<_Up&&>(__f));
800	  else // __op_eq_lt<_Tp, _Up>
801	    return static_cast<_Tp&&>(__e) == static_cast<_Up&&>(__f)
802	      ? weak_ordering::equivalent
803	      : static_cast<_Tp&&>(__e) < static_cast<_Up&&>(__f)
804	      ? weak_ordering::less
805	      : weak_ordering::greater;
806	}
807    };
808
809    // _GLIBCXX_RESOLVE_LIB_DEFECTS
810    // 3465. compare_partial_order_fallback requires F < E
811    template<typename _Tp, typename _Up>
812      concept __op_eq_lt_lt = __op_eq_lt<_Tp, _Up>
813	&& requires(_Tp&& __t, _Up&& __u)
814	{
815	  { static_cast<_Up&&>(__u) < static_cast<_Tp&&>(__t) }
816	    -> convertible_to<bool>;
817	};
818
819    class _Partial_fallback
820    {
821      template<typename _Tp, typename _Up>
822	static constexpr bool
823	_S_noexcept()
824	{
825	  if constexpr (__partially_ordered<_Tp, _Up>)
826	    return _Partial_order::_S_noexcept<_Tp, _Up>();
827	  else
828	    return noexcept(bool(std::declval<_Tp>() == std::declval<_Up>()))
829	      && noexcept(bool(std::declval<_Tp>() < std::declval<_Up>()));
830	}
831
832    public:
833      template<typename _Tp, __decayed_same_as<_Tp> _Up>
834	requires __partially_ordered<_Tp, _Up> || __op_eq_lt_lt<_Tp, _Up>
835	constexpr partial_ordering
836	operator()(_Tp&& __e, _Up&& __f) const
837	noexcept(_S_noexcept<_Tp, _Up>())
838	{
839	  if constexpr (__partially_ordered<_Tp, _Up>)
840	    return _Partial_order{}(static_cast<_Tp&&>(__e),
841				    static_cast<_Up&&>(__f));
842	  else // __op_eq_lt_lt<_Tp, _Up>
843	    return static_cast<_Tp&&>(__e) == static_cast<_Up&&>(__f)
844	      ? partial_ordering::equivalent
845	      : static_cast<_Tp&&>(__e) < static_cast<_Up&&>(__f)
846	      ? partial_ordering::less
847	      : static_cast<_Up&&>(__f) < static_cast<_Tp&&>(__e)
848	      ? partial_ordering::greater
849	      : partial_ordering::unordered;
850	}
851    };
852  } // namespace __cmp_cust
853
854  // [cmp.alg], comparison algorithms
855  inline namespace __cmp_alg
856  {
857    inline constexpr __cmp_cust::_Strong_order strong_order{};
858
859    inline constexpr __cmp_cust::_Weak_order weak_order{};
860
861    inline constexpr __cmp_cust::_Partial_order partial_order{};
862
863    inline constexpr __cmp_cust::_Strong_fallback
864    compare_strong_order_fallback{};
865
866    inline constexpr __cmp_cust::_Weak_fallback
867    compare_weak_order_fallback{};
868
869    inline constexpr __cmp_cust::_Partial_fallback
870    compare_partial_order_fallback{};
871  }
872
873  namespace __detail
874  {
875    // [expos.only.func] synth-three-way
876    inline constexpr struct _Synth3way
877    {
878      template<typename _Tp, typename _Up>
879	static constexpr bool
880	_S_noexcept(const _Tp* __t = nullptr, const _Up* __u = nullptr)
881	{
882	  if constexpr (three_way_comparable_with<_Tp, _Up>)
883	    return noexcept(*__t <=> *__u);
884	  else
885	    return noexcept(*__t < *__u) && noexcept(*__u < *__t);
886	}
887
888      template<typename _Tp, typename _Up>
889	constexpr auto
890	operator()(const _Tp& __t, const _Up& __u) const
891	noexcept(_S_noexcept<_Tp, _Up>())
892	requires requires
893	{
894	  { __t < __u } -> __boolean_testable;
895	  { __u < __t } -> __boolean_testable;
896	}
897	{
898	  if constexpr (three_way_comparable_with<_Tp, _Up>)
899	    return __t <=> __u;
900	  else
901	    {
902	      if (__t < __u)
903		return weak_ordering::less;
904	      else if (__u < __t)
905		return weak_ordering::greater;
906	      else
907		return weak_ordering::equivalent;
908	    }
909	}
910    } __synth3way = {};
911
912    // [expos.only.func] synth-three-way-result
913    template<typename _Tp, typename _Up = _Tp>
914      using __synth3way_t
915	= decltype(__detail::__synth3way(std::declval<_Tp&>(),
916					 std::declval<_Up&>()));
917  } // namespace __detail
918#endif // concepts
919} // namespace std
920
921#pragma GCC visibility pop
922
923#endif // C++20
924
925#endif // _COMPARE
926