On Wed, 25 Jun 2025 at 19:51, Jakub Jelinek <ja...@redhat.com> wrote: > > Hi! > > 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. > > 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. > > Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
The library parts are OK, thanks! (LWG seems to be leaning towards adding all the type_order::type, type_order::value_type, type_order::operator() etc. members, but we can add those later.) > > 2025-06-25 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. > * testsuite/18_support/comparisons/type_order/1.cc: New test. > > --- gcc/cp/method.cc.jj 2025-06-25 16:04:51.611158952 +0200 > +++ gcc/cp/method.cc 2025-06-25 16:09:32.017556551 +0200 > @@ -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" > --- gcc/cp/cp-tree.h.jj 2025-06-25 16:04:51.610158965 +0200 > +++ gcc/cp/cp-tree.h 2025-06-25 16:09:32.019556525 +0200 > @@ -7557,6 +7557,8 @@ extern bool ctor_omit_inherited_parms ( > 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; } > --- gcc/cp/semantics.cc.jj 2025-06-25 16:04:51.633158669 +0200 > +++ gcc/cp/semantics.cc 2025-06-25 16:09:32.021556500 +0200 > @@ -13593,8 +13593,10 @@ trait_expr_value (cp_trait_kind kind, tr > 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_tr > 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_tr > 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_tr > ++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); > --- gcc/cp/constraint.cc.jj 2025-06-18 17:24:03.973867379 +0200 > +++ gcc/cp/constraint.cc 2025-06-25 17:20:58.458202011 +0200 > @@ -3266,6 +3266,9 @@ diagnose_trait_expr (tree expr, tree arg > 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); > --- gcc/cp/cp-trait.def.jj 2025-06-25 16:04:51.609158978 +0200 > +++ gcc/cp/cp-trait.def 2025-06-25 16:09:32.021556500 +0200 > @@ -114,6 +114,7 @@ DEFTRAIT_TYPE (REMOVE_CVREF, "__remove_c > 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) > > --- gcc/testsuite/g++.dg/cpp26/type-order1.C.jj 2025-06-25 16:09:32.021556500 > +0200 > +++ gcc/testsuite/g++.dg/cpp26/type-order1.C 2025-06-25 16:10:16.909979822 > +0200 > @@ -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; > --- gcc/testsuite/g++.dg/cpp26/type-order2.C.jj 2025-06-25 16:09:32.021556500 > +0200 > +++ gcc/testsuite/g++.dg/cpp26/type-order2.C 2025-06-25 16:09:32.021556500 > +0200 > @@ -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'" } > --- gcc/testsuite/g++.dg/cpp26/type-order3.C.jj 2025-06-25 16:09:32.021556500 > +0200 > +++ gcc/testsuite/g++.dg/cpp26/type-order3.C 2025-06-25 16:09:32.021556500 > +0200 > @@ -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'" } > --- libstdc++-v3/include/bits/version.def.jj 2025-06-25 16:04:51.679158078 > +0200 > +++ libstdc++-v3/include/bits/version.def 2025-06-25 16:18:51.690372255 > +0200 > @@ -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"; > --- libstdc++-v3/include/bits/version.h.jj 2025-06-25 16:04:51.679158078 > +0200 > +++ libstdc++-v3/include/bits/version.h 2025-06-25 16:18:57.480477517 +0200 > @@ -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 > --- libstdc++-v3/libsupc++/compare.jj 2025-06-24 18:53:13.759807726 +0200 > +++ libstdc++-v3/libsupc++/compare 2025-06-25 16:18:25.221710493 +0200 > @@ -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,24 @@ namespace std _GLIBCXX_VISIBILITY(defaul > 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); > + }; > + > + /// @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 > > --- libstdc++-v3/testsuite/18_support/comparisons/type_order/1.cc.jj > 2025-06-25 16:09:32.036556306 +0200 > +++ libstdc++-v3/testsuite/18_support/comparisons/type_order/1.cc > 2025-06-25 16:34:13.070598266 +0200 > @@ -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; > > Jakub >