fix-gcc16-qcompare.patch raw

   1  From 7fccc79dd5744ea837ffe200bbfc9f2756870220 Mon Sep 17 00:00:00 2001
   2  From: Thiago Macieira <thiago.macieira@intel.com>
   3  Date: Wed, 14 Jan 2026 08:57:22 -0800
   4  Subject: [PATCH] QtCompare: adapt to GCC 16 breaking ABI for
   5   std::partial_ordering
   6  
   7  They changed[1] the stored value of the unordered constant from 2 to
   8  -SCHAR_MAX-1 (0x80). This commit hardens the check for all three
   9  std::*_ordering types and in all compilers, even though only the
  10  unordered value has been observed to be a problem.
  11  
  12  For:
  13  
  14   std::partial_ordering f(Qt::partial_ordering o) { return o; }
  15  
  16  With GCC < 16, this remains a plain bitcast. For GCC >= 16, this now
  17  generates:
  18          cmpb    $2, %dil
  19          movl    $-128, %eax
  20          cmovne  %edi, %eax
  21  
  22  Hopefully, when this is used in context, the comparisons will be elided
  23  through constant propagation.
  24  
  25  [1] https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=fcb3009a32dc33906934a2360e556dfeb98980cf
  26  
  27  Pick-to: 6.11 6.10 6.8 6.5
  28  Change-Id: I04bc40f0aeef5c0c778dfffdaea0bf68c5719462
  29  Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
  30  Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
  31  ---
  32   src/corelib/global/qcompare.cpp               |  2 +
  33   src/corelib/global/qcompare.h                 | 60 ++++++++++++++-----
  34   .../corelib/global/qcompare/tst_qcompare.cpp  |  2 +
  35   3 files changed, 50 insertions(+), 14 deletions(-)
  36  
  37  diff --git a/qtbase/src/corelib/global/qcompare.cpp b/qtbase/src/corelib/global/qcompare.cpp
  38  index 8106a84ddcd7..7f7facafbb87 100644
  39  --- a/qtbase/src/corelib/global/qcompare.cpp
  40  +++ b/qtbase/src/corelib/global/qcompare.cpp
  41  @@ -18,7 +18,9 @@ QT_BEGIN_NAMESPACE
  42       static_assert(std::bit_cast<std:: type ## _ordering>(Qt:: type ## _ordering:: flag) \
  43                     == std:: type ## _ordering :: flag) \
  44       /* end */
  45  +#if !defined(__GLIBCXX__)
  46   CHECK(partial, unordered);
  47  +#endif
  48   CHECK(partial, less);
  49   CHECK(partial, greater);
  50   CHECK(partial, equivalent);
  51  diff --git a/qtbase/src/corelib/global/qcompare.h b/qtbase/src/corelib/global/qcompare.h
  52  index d82cf5ab4a4e..7eee69db66a3 100644
  53  --- a/qtbase/src/corelib/global/qcompare.h
  54  +++ b/qtbase/src/corelib/global/qcompare.h
  55  @@ -36,6 +36,15 @@ enum class Ordering : CompareUnderlyingType
  56   
  57   enum class Uncomparable : CompareUnderlyingType
  58   {
  59  +    // We choose the value of our Uncomparable to be the same that the C++
  60  +    // Standard Library chooses for its own std::partial_ordering::unordered,
  61  +    // so we can convert from their type to ours via simple std::bit_cast.
  62  +#if 0
  63  +    // GCC 16 broke ABI, so we cannot use the std::*_ordering types
  64  +    // in our ABI until we drop support for GCC 15 and earlier. When that
  65  +    // happens and std::bit_cast is guaranteed, this can be simplified to:
  66  +    Unordered = std::bit_cast<CompareUnderlyingType>(std::partial_ordering::unordered);
  67  +#else
  68       Unordered =
  69           #if defined(_LIBCPP_VERSION) // libc++
  70                   -127
  71  @@ -44,12 +53,26 @@ enum class Uncomparable : CompareUnderlyingType
  72           #else                        // assume MSSTL
  73                   -128
  74           #endif
  75  +#endif // future Qt
  76   };
  77   
  78   } // namespace QtPrivate
  79   
  80   namespace QtOrderingPrivate {
  81   
  82  +using QtPrivate::Ordering;
  83  +using QtPrivate::Uncomparable;
  84  +
  85  +#if defined(__cpp_lib_bit_cast) && defined(__cpp_lib_three_way_comparison)
  86  +inline constexpr bool OrderingValuesAreEqual =
  87  +        std::bit_cast<Ordering>(std::weak_ordering::equivalent) == Ordering::Equivalent &&
  88  +        std::bit_cast<Ordering>(std::strong_ordering::equal) == Ordering::Equal &&
  89  +        std::bit_cast<Ordering>(std::strong_ordering::less) == Ordering::Less &&
  90  +        std::bit_cast<Ordering>(std::strong_ordering::greater) == Ordering::Greater;
  91  +inline constexpr bool UnorderedValueIsEqual =
  92  +        std::bit_cast<Uncomparable>(std::partial_ordering::unordered) == Uncomparable::Unordered;
  93  +#endif
  94  +
  95   template <typename O>
  96   constexpr O reversed(O o) noexcept
  97   {
  98  @@ -61,6 +84,9 @@ constexpr O reversed(O o) noexcept
  99   
 100   } // namespace QtOrderingPrivate
 101   
 102  +QT_WARNING_PUSH
 103  +QT_WARNING_DISABLE_MSVC(4702)   // unreachable code
 104  +
 105   namespace Qt {
 106   
 107   class weak_ordering;
 108  @@ -157,12 +183,18 @@ public:
 109       constexpr Q_IMPLICIT operator std::partial_ordering() const noexcept
 110       {
 111           static_assert(sizeof(*this) == sizeof(std::partial_ordering));
 112  -#ifdef __cpp_lib_bit_cast
 113  -        return std::bit_cast<std::partial_ordering>(*this);
 114  -#else
 115           using O = QtPrivate::Ordering;
 116           using U = QtPrivate::Uncomparable;
 117           using R = std::partial_ordering;
 118  +#ifdef __cpp_lib_bit_cast
 119  +        if constexpr (QtOrderingPrivate::OrderingValuesAreEqual) {
 120  +            if constexpr (!QtOrderingPrivate::UnorderedValueIsEqual) {
 121  +                if (m_order == qToUnderlying(U::Unordered))
 122  +                    return R::unordered;
 123  +            }
 124  +            return std::bit_cast<R>(*this);
 125  +        }
 126  +#endif // __cpp_lib_bit_cast
 127           switch (m_order) {
 128           case qToUnderlying(O::Less):        return R::less;
 129           case qToUnderlying(O::Greater):     return R::greater;
 130  @@ -170,7 +202,6 @@ public:
 131           case qToUnderlying(U::Unordered):   return R::unordered;
 132           }
 133           Q_UNREACHABLE_RETURN(R::unordered);
 134  -#endif // __cpp_lib_bit_cast
 135       }
 136   
 137       friend constexpr bool operator==(partial_ordering lhs, std::partial_ordering rhs) noexcept
 138  @@ -349,18 +380,18 @@ public:
 139       constexpr Q_IMPLICIT operator std::weak_ordering() const noexcept
 140       {
 141           static_assert(sizeof(*this) == sizeof(std::weak_ordering));
 142  -#ifdef __cpp_lib_bit_cast
 143  -        return std::bit_cast<std::weak_ordering>(*this);
 144  -#else
 145           using O = QtPrivate::Ordering;
 146           using R = std::weak_ordering;
 147  +#ifdef __cpp_lib_bit_cast
 148  +        if constexpr (QtOrderingPrivate::OrderingValuesAreEqual)
 149  +            return std::bit_cast<R>(*this);
 150  +#endif // __cpp_lib_bit_cast
 151           switch (m_order) {
 152           case qToUnderlying(O::Less):          return R::less;
 153           case qToUnderlying(O::Greater):       return R::greater;
 154           case qToUnderlying(O::Equivalent):    return R::equivalent;
 155           }
 156           Q_UNREACHABLE_RETURN(R::equivalent);
 157  -#endif // __cpp_lib_bit_cast
 158       }
 159   
 160       friend constexpr bool operator==(weak_ordering lhs, std::weak_ordering rhs) noexcept
 161  @@ -547,18 +578,18 @@ public:
 162       constexpr Q_IMPLICIT operator std::strong_ordering() const noexcept
 163       {
 164           static_assert(sizeof(*this) == sizeof(std::strong_ordering));
 165  -#ifdef __cpp_lib_bit_cast
 166  -        return std::bit_cast<std::strong_ordering>(*this);
 167  -#else
 168           using O = QtPrivate::Ordering;
 169           using R = std::strong_ordering;
 170  +#ifdef __cpp_lib_bit_cast
 171  +        if constexpr (QtOrderingPrivate::OrderingValuesAreEqual)
 172  +            return std::bit_cast<R>(*this);
 173  +#endif // __cpp_lib_bit_cast
 174           switch (m_order) {
 175           case qToUnderlying(O::Less):    return R::less;
 176           case qToUnderlying(O::Greater): return R::greater;
 177           case qToUnderlying(O::Equal):   return R::equal;
 178           }
 179           Q_UNREACHABLE_RETURN(R::equal);
 180  -#endif // __cpp_lib_bit_cast
 181       }
 182   
 183       friend constexpr bool operator==(strong_ordering lhs, std::strong_ordering rhs) noexcept
 184  @@ -625,6 +656,8 @@ inline constexpr strong_ordering strong_ordering::greater(QtPrivate::Ordering::G
 185   
 186   } // namespace Qt
 187   
 188  +QT_WARNING_POP
 189  +
 190   QT_BEGIN_INCLUDE_NAMESPACE
 191   
 192   // This is intentionally included after Qt::*_ordering types and before
 193  diff --git a/qtbase/tests/auto/corelib/global/qcompare/tst_qcompare.cpp b/qtbase/tests/auto/corelib/global/qcompare/tst_qcompare.cpp
 194  index b79a6661db33..ff5920134cc8 100644
 195  --- a/qtbase/tests/auto/corelib/global/qcompare/tst_qcompare.cpp
 196  +++ b/qtbase/tests/auto/corelib/global/qcompare/tst_qcompare.cpp
 197  @@ -185,7 +185,9 @@ void tst_QCompare::stdQtBinaryCompatibility()
 198           QCOMPARE_EQ(valueOf( Qt:: type ## _ordering :: flag), \
 199                       valueOf(std:: type ## _ordering :: flag)) \
 200           /* end */
 201  +#  if !defined(__GLIBCXX__) || QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
 202       CHECK(partial, unordered);
 203  +#  endif
 204       CHECK(partial, less);
 205       CHECK(partial, greater);
 206       CHECK(partial, equivalent);
 207