https://gcc.gnu.org/g:29c7661c6f92d80f63a9a3cc37f3dc790a161f3f

commit r16-1707-g29c7661c6f92d80f63a9a3cc37f3dc790a161f3f
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Thu Jun 26 16:15:20 2025 +0200

    c++, libstdc++: Implement C++26 P2830R10 - Constexpr Type Ordering
    
    The following patch attempts to implement the C++26 P2830R10 - Constexpr 
Type
    Ordering paper, with a minor change that std::type_order<T, U> class 
template
    doesn't derive from integer_constant, because std::strong_ordering is not
    a structural type (except in MSVC), so instead it is just a class template
    with static constexpr strong_ordering value member and also value_type,
    type and 2 operators.
    
    The paper mostly talks about using something other than mangled names for
    the ordering, but given that the mangler is part of the GCC C++ FE, using
    the mangler seems to be the best ordering choice to me.
    
    2025-06-26  Jakub Jelinek  <ja...@redhat.com>
    
    gcc/cp/
            * cp-trait.def: Implement C++26 P2830R10 - Constexpr Type Ordering.
            (TYPE_ORDER): New.
            * method.cc (type_order_value): Define.
            * cp-tree.h (type_order_value): Declare.
            * semantics.cc (trait_expr_value): Use gcc_unreachable also
            for CPTK_TYPE_ORDER, adjust comment.
            (finish_trait_expr): Handle CPTK_TYPE_ORDER.
            * constraint.cc (diagnose_trait_expr): Likewise.
    gcc/testsuite/
            * g++.dg/cpp26/type-order1.C: New test.
            * g++.dg/cpp26/type-order2.C: New test.
            * g++.dg/cpp26/type-order3.C: New test.
    libstdc++-v3/
            * include/bits/version.def (type_order): New.
            * include/bits/version.h: Regenerate.
            * libsupc++/compare: Define __glibcxx_want_type_order before
            including bits/version.h.
            (std::type_order, std::type_order_v): New trait and template 
variable.
            * src/c++23/std.cc.in (std::type_order, std::type_order_v): Export.
            * testsuite/18_support/comparisons/type_order/1.cc: New test.

Diff:
---
 gcc/cp/constraint.cc                               |  3 +
 gcc/cp/cp-trait.def                                |  1 +
 gcc/cp/cp-tree.h                                   |  2 +
 gcc/cp/method.cc                                   | 21 +++++
 gcc/cp/semantics.cc                                | 13 ++-
 gcc/testsuite/g++.dg/cpp26/type-order1.C           | 94 +++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp26/type-order2.C           |  4 +
 gcc/testsuite/g++.dg/cpp26/type-order3.C           |  8 ++
 libstdc++-v3/include/bits/version.def              | 10 +++
 libstdc++-v3/include/bits/version.h                | 10 +++
 libstdc++-v3/libsupc++/compare                     | 23 ++++++
 libstdc++-v3/src/c++23/std.cc.in                   |  4 +
 .../18_support/comparisons/type_order/1.cc         | 95 ++++++++++++++++++++++
 13 files changed, 287 insertions(+), 1 deletion(-)

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 17d20696e874..c8eef24d585c 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3266,6 +3266,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_RANK:
       inform (loc, "  %qT cannot yield a rank", t1);
       break;
+    case CPTK_TYPE_ORDER:
+      inform (loc, "  %qT and %qT cannot be ordered", t1, t2);
+      break;
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       inform (loc, "  %qT is not a reference that binds to a temporary "
              "object of type %qT (direct-initialization)", t1, t2);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 2e3b4ca2892d..e71b28cb0a62 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -114,6 +114,7 @@ DEFTRAIT_TYPE (REMOVE_CVREF, "__remove_cvref", 1)
 DEFTRAIT_TYPE (REMOVE_EXTENT, "__remove_extent", 1)
 DEFTRAIT_TYPE (REMOVE_POINTER, "__remove_pointer", 1)
 DEFTRAIT_TYPE (REMOVE_REFERENCE, "__remove_reference", 1)
+DEFTRAIT_EXPR (TYPE_ORDER, "__builtin_type_order", 2)
 DEFTRAIT_TYPE (TYPE_PACK_ELEMENT, "__type_pack_element", -1)
 DEFTRAIT_TYPE (UNDERLYING_TYPE, "__underlying_type", 1)
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 4bf02a1890f7..8f81fe034e96 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7557,6 +7557,8 @@ extern bool ctor_omit_inherited_parms             (tree);
 extern tree locate_ctor                                (tree);
 extern tree implicitly_declare_fn               (special_function_kind, tree,
                                                 bool, tree, tree);
+extern tree type_order_value                   (tree, tree);
+
 /* In module.cc  */
 class module_state; /* Forward declare.  */
 inline bool modules_p () { return flag_modules != 0; }
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 67a80a387ba7..1b9a1c8c9b1d 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -3951,5 +3951,26 @@ num_artificial_parms_for (const_tree fn)
   return count;
 }
 
+/* Return value of the __builtin_type_order trait.  */
+
+tree
+type_order_value (tree type1, tree type2)
+{
+  tree rettype = lookup_comparison_category (cc_strong_ordering);
+  if (rettype == error_mark_node)
+    return rettype;
+  int ret;
+  if (type1 == type2)
+    ret = 0;
+  else
+    {
+      const char *name1 = ASTRDUP (mangle_type_string (type1));
+      const char *name2 = mangle_type_string (type2);
+      ret = strcmp (name1, name2);
+    }
+  return lookup_comparison_result (cc_strong_ordering, rettype,
+                                  ret == 0 ? 0 : ret > 0 ? 1 : 2);
+}
+
 
 #include "gt-cp-method.h"
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 7bc346b0f294..28baf7b3172b 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -13593,8 +13593,10 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree 
type2)
     case CPTK_IS_DEDUCIBLE:
       return type_targs_deducible_from (type1, type2);
 
-    /* __array_rank is handled in finish_trait_expr. */
+    /* __array_rank and __builtin_type_order are handled in
+       finish_trait_expr.  */
     case CPTK_RANK:
+    case CPTK_TYPE_ORDER:
       gcc_unreachable ();
 
 #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
@@ -13724,6 +13726,12 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, 
tree type1, tree type2)
       tree trait_expr = make_node (TRAIT_EXPR);
       if (kind == CPTK_RANK)
        TREE_TYPE (trait_expr) = size_type_node;
+      else if (kind == CPTK_TYPE_ORDER)
+       {
+         tree val = type_order_value (type1, type1);
+         if (val != error_mark_node)
+           TREE_TYPE (trait_expr) = TREE_TYPE (val);
+       }
       else
        TREE_TYPE (trait_expr) = boolean_type_node;
       TRAIT_EXPR_TYPE1 (trait_expr) = type1;
@@ -13831,6 +13839,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, 
tree type1, tree type2)
     case CPTK_IS_UNION:
     case CPTK_IS_VOLATILE:
     case CPTK_RANK:
+    case CPTK_TYPE_ORDER:
       break;
 
     case CPTK_IS_LAYOUT_COMPATIBLE:
@@ -13870,6 +13879,8 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, 
tree type1, tree type2)
        ++rank;
       val = build_int_cst (size_type_node, rank);
     }
+  else if (kind == CPTK_TYPE_ORDER)
+    val = type_order_value (type1, type2);
   else
     val = (trait_expr_value (kind, type1, type2)
           ? boolean_true_node : boolean_false_node);
diff --git a/gcc/testsuite/g++.dg/cpp26/type-order1.C 
b/gcc/testsuite/g++.dg/cpp26/type-order1.C
new file mode 100644
index 000000000000..d5693570a47d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/type-order1.C
@@ -0,0 +1,94 @@
+// C++26 P2830R10 - Constexpr Type Ordering
+// { dg-do compile { target c++26 } }
+
+namespace std {
+  using type = enum _Ord { equivalent = 0, less = -1, greater = 1 };
+  struct strong_ordering {
+    type _M_value;
+    constexpr strong_ordering (_Ord x) : _M_value (x) {}
+    static const strong_ordering less;
+    static const strong_ordering equal;
+    static const strong_ordering greater;
+    constexpr bool operator== (const strong_ordering &x) const { return 
_M_value == x._M_value; }
+    constexpr bool operator!= (const strong_ordering &x) const { return 
_M_value != x._M_value; }
+  };
+  constexpr strong_ordering strong_ordering::equal (_Ord::equivalent);
+  constexpr strong_ordering strong_ordering::less (_Ord::less);
+  constexpr strong_ordering strong_ordering::greater (_Ord::greater);
+
+  template <typename T, typename U>
+  struct type_order
+  {
+    static constexpr strong_ordering value = __builtin_type_order (T, U);
+  };
+
+  template <typename T, typename U>
+  inline constexpr strong_ordering type_order_v = __builtin_type_order (T, U);
+}
+
+struct S;
+struct T;
+template <typename T>
+struct U
+{
+};
+typedef int int2;
+struct V {};
+namespace
+{
+  struct W {};
+}
+
+template <typename T, typename U>
+struct eq
+{
+  constexpr eq ()
+  {
+    static_assert (std::type_order <T, U>::value == 
std::strong_ordering::equal);
+    static_assert (std::type_order <U, T>::value == 
std::strong_ordering::equal);
+    static_assert (std::type_order_v <T, U> == std::strong_ordering::equal);
+    static_assert (std::type_order_v <U, T> == std::strong_ordering::equal);
+  }
+};
+template <typename T, typename U>
+struct ne
+{
+  constexpr ne ()
+  {
+    static_assert (std::type_order <T, U>::value != 
std::strong_ordering::equal);
+    static_assert (std::type_order <U, T>::value != 
std::strong_ordering::equal);
+    static_assert (std::type_order <T, U>::value == 
std::strong_ordering::greater
+                  ? std::type_order <U, T>::value == std::strong_ordering::less
+                  : std::type_order <U, T>::value == 
std::strong_ordering::greater);
+    static_assert (std::type_order_v <T, U> != std::strong_ordering::equal);
+    static_assert (std::type_order_v <U, T> != std::strong_ordering::equal);
+    static_assert (std::type_order_v <T, U> == std::strong_ordering::greater
+                  ? std::type_order_v <U, T> == std::strong_ordering::less
+                  : std::type_order_v <U, T> == std::strong_ordering::greater);
+  }
+};
+
+constexpr eq <void, void> a;
+constexpr eq <const void, const void> b;
+constexpr eq <int, int> c;
+constexpr eq <long int, long int> d;
+constexpr eq <const volatile unsigned, const volatile unsigned> e;
+constexpr eq <S, S> f;
+constexpr eq <U <int>, U <int>> g;
+constexpr eq <unsigned[2], unsigned[2]> h;
+constexpr eq <int, int2> i;
+constexpr eq <int (*) (int, long), int (*) (int, long)> j;
+constexpr ne <int, long> k;
+constexpr ne <const int, int> l;
+constexpr ne <S, T> m;
+constexpr ne <int &, int &&> n;
+constexpr ne <U <S>, U <T>> o;
+constexpr ne <U <short>, U <char>> p;
+static_assert (std::type_order_v <S, T> != std::strong_ordering::less
+              || std::type_order_v <T, V> != std::strong_ordering::less
+              || std::type_order_v <S, V> == std::strong_ordering::less);
+constexpr ne <int (*) (int, long), int (*) (int, int)> q;
+constexpr eq <W, W> r;
+constexpr ne <V, W> s;
+constexpr eq <U <W>, U <W>> t;
+constexpr ne <U <V>, U <W>> u;
diff --git a/gcc/testsuite/g++.dg/cpp26/type-order2.C 
b/gcc/testsuite/g++.dg/cpp26/type-order2.C
new file mode 100644
index 000000000000..8b6118ca2597
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/type-order2.C
@@ -0,0 +1,4 @@
+// C++26 P2830R10 - Constexpr Type Ordering
+// { dg-do compile { target c++26 } }
+
+constexpr auto a = __builtin_type_order (int, long);   // { dg-error 
"'strong_ordering' is not a member of 'std'" }
diff --git a/gcc/testsuite/g++.dg/cpp26/type-order3.C 
b/gcc/testsuite/g++.dg/cpp26/type-order3.C
new file mode 100644
index 000000000000..b9cd820e246d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/type-order3.C
@@ -0,0 +1,8 @@
+// C++26 P2830R10 - Constexpr Type Ordering
+// { dg-do compile { target c++26 } }
+
+namespace std {
+  struct strong_ordering {
+  };
+}
+constexpr auto a = __builtin_type_order (int, long);   // { dg-error 
"'(equal|greater|less)' is not a member of 'std::strong_ordering'" }
diff --git a/libstdc++-v3/include/bits/version.def 
b/libstdc++-v3/include/bits/version.def
index 880586e91260..caec5d114479 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -2012,6 +2012,16 @@ ftms = {
   };
 };
 
+ftms = {
+  name = type_order;
+  values = {
+    v = 202506;
+    cxxmin = 26;
+    extra_cond = "__has_builtin(__builtin_type_order) "
+    "&& __cpp_lib_three_way_comparison >= 201907L";
+  };
+};
+
 // Standard test specifications.
 stds[97] = ">= 199711L";
 stds[03] = ">= 199711L";
diff --git a/libstdc++-v3/include/bits/version.h 
b/libstdc++-v3/include/bits/version.h
index 4300adb22762..2dc21b693656 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -2253,4 +2253,14 @@
 #endif /* !defined(__cpp_lib_sstream_from_string_view) && 
defined(__glibcxx_want_sstream_from_string_view) */
 #undef __glibcxx_want_sstream_from_string_view
 
+#if !defined(__cpp_lib_type_order)
+# if (__cplusplus >  202302L) && (__has_builtin(__builtin_type_order) && 
__cpp_lib_three_way_comparison >= 201907L)
+#  define __glibcxx_type_order 202506L
+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_type_order)
+#   define __cpp_lib_type_order 202506L
+#  endif
+# endif
+#endif /* !defined(__cpp_lib_type_order) && defined(__glibcxx_want_type_order) 
*/
+#undef __glibcxx_want_type_order
+
 #undef __glibcxx_want_all
diff --git a/libstdc++-v3/libsupc++/compare b/libstdc++-v3/libsupc++/compare
index 8811bc095d0b..82b5c53139c9 100644
--- a/libstdc++-v3/libsupc++/compare
+++ b/libstdc++-v3/libsupc++/compare
@@ -35,6 +35,7 @@
 #endif
 
 #define __glibcxx_want_three_way_comparison
+#define __glibcxx_want_type_order
 #include <bits/version.h>
 
 #if __cplusplus > 201703L && __cpp_impl_three_way_comparison >= 201907L
@@ -1261,6 +1262,28 @@ namespace std _GLIBCXX_VISIBILITY(default)
                                         std::declval<_Up&>()));
   } // namespace __detail
   /// @endcond
+
+#if __glibcxx_type_order >= 202506L // C++ >= 26
+  /// Total ordering of types.
+  /// @since C++26
+
+  template<typename _Tp, typename _Up>
+    struct type_order
+    {
+      static constexpr strong_ordering value = __builtin_type_order(_Tp, _Up);
+      using value_type = strong_ordering;
+      using type = type_order<_Tp, _Up>;
+      constexpr operator value_type() const noexcept { return value; }
+      constexpr value_type operator()() const noexcept { return value; }
+    };
+
+  /// @ingroup variable_templates
+  /// @since C++26
+  template<typename _Tp, typename _Up>
+    inline constexpr strong_ordering type_order_v
+      = __builtin_type_order(_Tp, _Up);
+#endif // __glibcxx_type_order >= 202506L
+
 #endif // __cpp_lib_three_way_comparison >= 201907L
 } // namespace std
 
diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/std.cc.in
index 109f590f1d1f..36b19aec5ff3 100644
--- a/libstdc++-v3/src/c++23/std.cc.in
+++ b/libstdc++-v3/src/c++23/std.cc.in
@@ -888,6 +888,10 @@ export namespace std
   using std::partial_order;
   using std::strong_order;
   using std::weak_order;
+#if __glibcxx_type_order >= 202506L
+  using std::type_order;
+  using std::type_order_v;
+#endif
 }
 
 // 28.4 <complex>
diff --git a/libstdc++-v3/testsuite/18_support/comparisons/type_order/1.cc 
b/libstdc++-v3/testsuite/18_support/comparisons/type_order/1.cc
new file mode 100644
index 000000000000..b510494127d6
--- /dev/null
+++ b/libstdc++-v3/testsuite/18_support/comparisons/type_order/1.cc
@@ -0,0 +1,95 @@
+// Copyright (C) 2025 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 compile { target c++26 } }
+
+#include <compare>
+
+#if __cpp_lib_type_order != 202506L
+# error "__cpp_lib_type_order != 202506"
+#endif
+
+static_assert (std::is_same_v <decltype (std::type_order <int, int>::value),
+                              const std::strong_ordering>);
+static_assert (std::is_same_v <decltype (std::type_order_v <char, short>),
+                              const std::strong_ordering>);
+struct S;
+struct T;
+template <typename T>
+struct U
+{
+};
+typedef int int2;
+struct V {};
+namespace
+{
+  struct W {};
+}
+
+template <typename T, typename U>
+struct eq
+{
+  constexpr eq ()
+  {
+    static_assert (std::type_order <T, U>::value == 
std::strong_ordering::equal);
+    static_assert (std::type_order <U, T>::value == 
std::strong_ordering::equal);
+    static_assert (std::type_order_v <T, U> == std::strong_ordering::equal);
+    static_assert (std::type_order_v <U, T> == std::strong_ordering::equal);
+  }
+};
+template <typename T, typename U>
+struct ne
+{
+  constexpr ne ()
+  {
+    static_assert (std::type_order <T, U>::value != 
std::strong_ordering::equal);
+    static_assert (std::type_order <U, T>::value != 
std::strong_ordering::equal);
+    static_assert (std::type_order <T, U>::value == 
std::strong_ordering::greater
+                  ? std::type_order <U, T>::value == std::strong_ordering::less
+                  : std::type_order <U, T>::value == 
std::strong_ordering::greater);
+    static_assert (std::type_order_v <T, U> != std::strong_ordering::equal);
+    static_assert (std::type_order_v <U, T> != std::strong_ordering::equal);
+    static_assert (std::type_order_v <T, U> == std::strong_ordering::greater
+                  ? std::type_order_v <U, T> == std::strong_ordering::less
+                  : std::type_order_v <U, T> == std::strong_ordering::greater);
+  }
+};
+
+constexpr eq <void, void> a;
+constexpr eq <const void, const void> b;
+constexpr eq <int, int> c;
+constexpr eq <long int, long int> d;
+constexpr eq <const volatile unsigned, const volatile unsigned> e;
+constexpr eq <S, S> f;
+constexpr eq <U <int>, U <int>> g;
+constexpr eq <unsigned[2], unsigned[2]> h;
+constexpr eq <int, int2> i;
+constexpr eq <int (*) (int, long), int (*) (int, long)> j;
+constexpr ne <int, long> k;
+constexpr ne <const int, int> l;
+constexpr ne <S, T> m;
+constexpr ne <int &, int &&> n;
+constexpr ne <U <S>, U <T>> o;
+constexpr ne <U <short>, U <char>> p;
+static_assert (std::type_order_v <S, T> != std::strong_ordering::less
+              || std::type_order_v <T, V> != std::strong_ordering::less
+              || std::type_order_v <S, V> == std::strong_ordering::less);
+constexpr ne <int (*) (int, long), int (*) (int, int)> q;
+constexpr eq <W, W> r;
+constexpr ne <V, W> s;
+constexpr eq <U <W>, U <W>> t;
+constexpr ne <U <V>, U <W>> u;

Reply via email to