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