[PATCH] c++, libstdc++, v2: Implement C++26 P2830R10 - Constexpr Type Ordering
Jonathan Wakely
jwakely@redhat.com
Thu Jun 26 14:40:59 GMT 2025
On Thu, 26 Jun 2025 at 15:38, Jonathan Wakely <jwakely@redhat.com> wrote:
>
> On Thu, 26 Jun 2025 at 11:33, Jakub Jelinek <jakub@redhat.com> wrote:
> >
> > On Wed, Jun 25, 2025 at 10:58:59PM +0200, Maciej Cencora wrote:
> > > update of std module is missing.
> >
> > Here is an updated patch which adds the std module part and while I was
> > changing the patch, I've also added value_type/type and the 2 operators
> > to std::type_order.
> >
> > Interdiff from the last patch is:
> > --- libstdc++-v3/libsupc++/compare 2025-06-25 16:18:25.221710493 +0200
> > +++ libstdc++-v3/libsupc++/compare 2025-06-25 16:18:25.221710493 +0200
> > @@ -1271,6 +1271,10 @@
> > 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
> > --- libstdc++-v3/src/c++23/std.cc.in.jj 2025-06-12 15:50:51.400821105 +0200
> > +++ libstdc++-v3/src/c++23/std.cc.in 2025-06-26 07:37:06.902555508 +0200
> > @@ -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>
> >
> > Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
> >
> > Though, now that I look at it again, perhaps both
> > #if __glibcxx_type_order >= 202506L
> > in the patch should have been
> > #if __cpp_lib_type_order >= 202506L
> >
> > Can change that.
>
> I would leave it. Testing the internal __glibcxx_foo macro is always
> correct. Testing the standard __cpp_lib_foo macro is only correct in
> the main header that defines the __glibcxx_want_foo macro. In this
> case both are defined, because it's in the same header as the "want"
> macro, but if we decide we need std::type_order to be available in
> other headers and move it to some <bits/type_order.h> then we'd need
> to change it to test __glibcxx_type_order instead. Using the internal
> macro is a bit more robust.
Actually for std.cc.in for consistency it should be the
__cpp_lib_type_order macro (since that file is really a consumer of
the headers, not part of the headers themselves).
But in libsupc++/compare it can stay as __glibcxx_type_order.
>
> >
> > 2025-06-26 Jakub Jelinek <jakub@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.
> >
> > --- 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,28 @@ 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);
> > + 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
> >
> > --- libstdc++-v3/src/c++23/std.cc.in.jj 2025-06-12 15:50:51.400821105 +0200
> > +++ libstdc++-v3/src/c++23/std.cc.in 2025-06-26 07:37:06.902555508 +0200
> > @@ -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>
> > --- 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
> >
More information about the Libstdc++
mailing list