fix-gcc16-sfinae-qchar.patch raw
1 From 05f201a3d559452287b20becb960de3a50249540 Mon Sep 17 00:00:00 2001
2 From: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
3 Date: Wed, 4 Feb 2026 12:12:37 +0100
4 Subject: [PATCH] Move QChar::fromUcs4 into qchar.h
5
6 Right now fromUcs4 lives in qstringview.h because the return type has an
7 implicit conversion operator towards QStringView. This however creates
8 an inclusion loop: qchar.h includes qstringview.h, and qstringview.h
9 includes qchar.h. Only the latter should exist.
10
11 We can move the code back into qchar.h and drop the QStringView
12 dependency by making the type returned from fromUcs4 have data(). That
13 would make it a string-like range and become implicitly convertible to
14 QStringView through its ranged constructor.
15
16 In fact, add a test that checks for the *implicit* conversion; and
17 add a test that checks for a conversion towards std::u16string_view.
18
19 QMetaType was calling the conversion operator from the return type of
20 fromUcs4 to QStringView directly (...Hyrum law...), although we never
21 documented the presence of that operator, only that the conversion was
22 possible; fix it by using a conversion.
23
24 Fixes: QTBUG-143873
25 Task-number: QTBUG-143470
26 Change-Id: Id07657dd411cc2e1446fb18789e04db9cecd8ae0
27 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
28 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
29 ---
30 src/corelib/kernel/qmetatype.cpp | 2 +-
31 src/corelib/text/qchar.h | 16 +++++++++++++---
32 src/corelib/text/qstringview.h | 16 ----------------
33 tests/auto/corelib/text/qchar/tst_qchar.cpp | 10 ++++++++++
34 4 files changed, 24 insertions(+), 20 deletions(-)
35
36 diff --git a/qtbase/src/corelib/kernel/qmetatype.cpp b/qtbase/src/corelib/kernel/qmetatype.cpp
37 index e70583404a46..54a0fe671fe0 100644
38 --- a/qtbase/src/corelib/kernel/qmetatype.cpp
39 +++ b/qtbase/src/corelib/kernel/qmetatype.cpp
40 @@ -1212,7 +1212,7 @@ QT_WARNING_DISABLE_CLANG("-Wtautological-compare")
41 return true;
42 );
43 QMETATYPE_CONVERTER(QString, Char32,
44 - result = QChar::fromUcs4(source).operator QStringView().toString();
45 + result = QStringView(QChar::fromUcs4(source)).toString();
46 return true;
47 );
48 #if QT_CONFIG(datestring)
49 diff --git a/qtbase/src/corelib/text/qchar.h b/qtbase/src/corelib/text/qchar.h
50 index 4a3aad0ca0c1..f96ca590f2e2 100644
51 --- a/qtbase/src/corelib/text/qchar.h
52 +++ b/qtbase/src/corelib/text/qchar.h
53 @@ -88,7 +88,19 @@ class QT6_ONLY(Q_CORE_EXPORT) QChar {
54 #endif
55
56 static constexpr QChar fromUcs2(char16_t c) noexcept { return QChar{c}; }
57 - static constexpr inline auto fromUcs4(char32_t c) noexcept;
58 + [[nodiscard]] static constexpr inline auto fromUcs4(char32_t c) noexcept
59 + {
60 + struct R {
61 + char16_t chars[2];
62 + [[nodiscard]] constexpr qsizetype size() const noexcept { return chars[1] ? 2 : 1; }
63 + [[nodiscard]] constexpr const char16_t *data() const noexcept { return chars; }
64 + [[nodiscard]] constexpr const char16_t *begin() const noexcept { return chars; }
65 + [[nodiscard]] constexpr const char16_t *end() const noexcept { return begin() + size(); }
66 + };
67 + return requiresSurrogates(c) ? R{{QChar::highSurrogate(c),
68 + QChar::lowSurrogate(c)}} :
69 + R{{char16_t(c), u'\0'}} ;
70 + }
71
72 // Unicode information
73
74 @@ -666,5 +678,3 @@ struct hash<QT_PREPEND_NAMESPACE(QChar)>
75 } // namespace std
76
77 #endif // QCHAR_H
78 -
79 -#include <QtCore/qstringview.h> // for QChar::fromUcs4() definition
80 diff --git a/qtbase/src/corelib/text/qstringview.h b/qtbase/src/corelib/text/qstringview.h
81 index 6d5edfc06d7d..d5897af6da2d 100644
82 --- a/qtbase/src/corelib/text/qstringview.h
83 +++ b/qtbase/src/corelib/text/qstringview.h
84 @@ -472,22 +472,6 @@ template <typename QStringLike, typename std::enable_if<
85 inline QStringView qToStringViewIgnoringNull(const QStringLike &s) noexcept
86 { return QStringView(s.begin(), s.size()); }
87
88 -// QChar inline functions:
89 -
90 -[[nodiscard]] constexpr auto QChar::fromUcs4(char32_t c) noexcept
91 -{
92 - struct R {
93 - char16_t chars[2];
94 - [[nodiscard]] constexpr operator QStringView() const noexcept { return {begin(), end()}; }
95 - [[nodiscard]] constexpr qsizetype size() const noexcept { return chars[1] ? 2 : 1; }
96 - [[nodiscard]] constexpr const char16_t *begin() const noexcept { return chars; }
97 - [[nodiscard]] constexpr const char16_t *end() const noexcept { return begin() + size(); }
98 - };
99 - return requiresSurrogates(c) ? R{{QChar::highSurrogate(c),
100 - QChar::lowSurrogate(c)}} :
101 - R{{char16_t(c), u'\0'}} ;
102 -}
103 -
104 qsizetype QtPrivate::findString(QStringView str, qsizetype from, QChar ch, Qt::CaseSensitivity cs) noexcept
105 {
106 if (from < -str.size()) // from < 0 && abs(from) > str.size(), avoiding overflow
107 diff --git a/qtbase/tests/auto/corelib/text/qchar/tst_qchar.cpp b/qtbase/tests/auto/corelib/text/qchar/tst_qchar.cpp
108 index 6701f0e33f3d..5781d36e458b 100644
109 --- a/qtbase/tests/auto/corelib/text/qchar/tst_qchar.cpp
110 +++ b/qtbase/tests/auto/corelib/text/qchar/tst_qchar.cpp
111 @@ -142,10 +142,20 @@ void tst_QChar::fromUcs4()
112 QCOMPARE(result.chars[0], QChar::highSurrogate(ucs4));
113 QCOMPARE(result.chars[1], QChar::lowSurrogate(ucs4));
114 QCOMPARE(QStringView{result}.size(), 2);
115 + QStringView v = result;
116 + QCOMPARE(v.size(), 2);
117 +#if __cplusplus >= 202302L // no FTM for the ranged constructor of basic_string_view
118 + QCOMPARE(std::u16string_view{result}.size(), 2);
119 +#endif
120 } else {
121 QCOMPARE(result.chars[0], ucs4);
122 QCOMPARE(result.chars[1], 0u);
123 QCOMPARE(QStringView{result}.size(), 1);
124 + QStringView v = result;
125 + QCOMPARE(v.size(), 1);
126 +#if __cplusplus >= 202302L // no FTM for the ranged constructor of basic_string_view
127 + QCOMPARE(std::u16string_view{result}.size(), 1);
128 +#endif
129 }
130 }
131
132