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
>

Reply via email to