From c315583a82eac39bce6feb0f5f4d4f6036b01611 Mon Sep 17 00:00:00 2001 From: Sona Kurazyan Date: Wed, 1 Sep 2021 15:33:13 +0200 Subject: Fix compilation for recursive Qt containers The operator checks cause compilation errors when trying to check for their existence for recursive containers. This happens because of trying to check for the operators on the template parameter type(s), that inherit from the container itself, which leads to compilation errors. Introduced alternative versions of the operator checks (with _container suffix), that first check if the container is recursive, i.e. any of its template parameter types inherits from the given container, and skips the operator check, if that's the case. The fix is done for all Qt container types that had the problem, except for QVarLengthArray and QContiguousCache, which don't compile with recursive parameter types for unrelated reasons. Fixes: QTBUG-91707 Change-Id: Ia1e7240b4ce240c1c44f00ca680717d182df7550 Reviewed-by: Fabian Kosmale (cherry picked from commit 9f13842fe61541cb8ab9822174ea963e418b5537) Reviewed-by: Qt Cherry-pick Bot --- src/corelib/global/qtypeinfo.h | 17 +++ src/corelib/io/qdebug.h | 16 ++- src/corelib/serialization/qdatastream.h | 31 +++-- src/corelib/tools/qhash.h | 4 +- src/corelib/tools/qlist.h | 12 +- src/corelib/tools/qmap.h | 8 +- src/corelib/tools/qset.h | 4 +- .../corelib/tools/collections/tst_collections.cpp | 144 +++++++++++++++++++++ 8 files changed, 204 insertions(+), 32 deletions(-) diff --git a/src/corelib/global/qtypeinfo.h b/src/corelib/global/qtypeinfo.h index 58f20b7be4..2729217aab 100644 --- a/src/corelib/global/qtypeinfo.h +++ b/src/corelib/global/qtypeinfo.h @@ -335,17 +335,29 @@ struct has_operator_equal : detail::expand_operator_equal {}; template inline constexpr bool has_operator_equal_v = has_operator_equal::value; +template +using has_operator_equal_container = std::disjunction, QTypeTraits::has_operator_equal>; + template struct has_operator_less_than : detail::expand_operator_less_than {}; template inline constexpr bool has_operator_less_than_v = has_operator_less_than::value; +template +using has_operator_less_than_container = std::disjunction, QTypeTraits::has_operator_less_than>; + template using compare_eq_result = std::enable_if_t...>, bool>; +template +using compare_eq_result_container = std::enable_if_t...>, bool>; + template using compare_lt_result = std::enable_if_t...>, bool>; +template +using compare_lt_result_container = std::enable_if_t...>, bool>; + namespace detail { template @@ -363,6 +375,9 @@ struct has_ostream_operator inline constexpr bool has_ostream_operator_v = has_ostream_operator::value; +template +using has_ostream_operator_container = std::disjunction, QTypeTraits::has_ostream_operator>; + template struct has_istream_operator : std::false_type {}; template @@ -370,6 +385,8 @@ struct has_istream_operator inline constexpr bool has_istream_operator_v = has_istream_operator::value; +template +using has_istream_operator_container = std::disjunction, QTypeTraits::has_istream_operator>; template inline constexpr bool has_stream_operator_v = has_ostream_operator_v && has_istream_operator_v; diff --git a/src/corelib/io/qdebug.h b/src/corelib/io/qdebug.h index 29da386c1b..d1c2fea494 100644 --- a/src/corelib/io/qdebug.h +++ b/src/corelib/io/qdebug.h @@ -243,8 +243,12 @@ template using QDebugIfHasDebugStream = std::enable_if_t...>, QDebug>; +template +using QDebugIfHasDebugStreamContainer = + std::enable_if_t...>, QDebug>; + template -inline QDebugIfHasDebugStream operator<<(QDebug debug, const QList &vec) +inline QDebugIfHasDebugStreamContainer, T> operator<<(QDebug debug, const QList &vec) { return QtPrivate::printSequentialContainer(debug, "QList", vec); } @@ -274,25 +278,25 @@ inline QDebugIfHasDebugStream operator<<(QDebug debug, const std::multim } template -inline QDebugIfHasDebugStream operator<<(QDebug debug, const QMap &map) +inline QDebugIfHasDebugStreamContainer, Key, T> operator<<(QDebug debug, const QMap &map) { return QtPrivate::printAssociativeContainer(debug, "QMap", map); } template -inline QDebugIfHasDebugStream operator<<(QDebug debug, const QMultiMap &map) +inline QDebugIfHasDebugStreamContainer, Key, T> operator<<(QDebug debug, const QMultiMap &map) { return QtPrivate::printAssociativeContainer(debug, "QMultiMap", map); } template -inline QDebugIfHasDebugStream operator<<(QDebug debug, const QHash &hash) +inline QDebugIfHasDebugStreamContainer, Key, T> operator<<(QDebug debug, const QHash &hash) { return QtPrivate::printAssociativeContainer(debug, "QHash", hash); } template -inline QDebugIfHasDebugStream operator<<(QDebug debug, const QMultiHash &hash) +inline QDebugIfHasDebugStreamContainer, Key, T> operator<<(QDebug debug, const QMultiHash &hash) { return QtPrivate::printAssociativeContainer(debug, "QMultiHash", hash); } @@ -306,7 +310,7 @@ inline QDebugIfHasDebugStream operator<<(QDebug debug, const std::pair -inline QDebugIfHasDebugStream operator<<(QDebug debug, const QSet &set) +inline QDebugIfHasDebugStreamContainer, T> operator<<(QDebug debug, const QSet &set) { return QtPrivate::printSequentialContainer(debug, "QSet", set); } diff --git a/src/corelib/serialization/qdatastream.h b/src/corelib/serialization/qdatastream.h index 523dabfdc6..1ec9704a6a 100644 --- a/src/corelib/serialization/qdatastream.h +++ b/src/corelib/serialization/qdatastream.h @@ -351,9 +351,16 @@ QDataStream &writeAssociativeMultiContainer(QDataStream &s, const Container &c) template using QDataStreamIfHasOStreamOperators = std::enable_if_t...>, QDataStream &>; +template +using QDataStreamIfHasOStreamOperatorsContainer = + std::enable_if_t...>, QDataStream &>; + template using QDataStreamIfHasIStreamOperators = std::enable_if_t...>, QDataStream &>; +template +using QDataStreamIfHasIStreamOperatorsContainer = + std::enable_if_t...>, QDataStream &>; /***************************************************************************** QDataStream inline functions @@ -425,74 +432,74 @@ operator>>(QDataStream &s, T &t) { return s >> reinterpret_cast::type &>(t); } template -inline QDataStreamIfHasIStreamOperators operator>>(QDataStream &s, QList &v) +inline QDataStreamIfHasIStreamOperatorsContainer, T> operator>>(QDataStream &s, QList &v) { return QtPrivate::readArrayBasedContainer(s, v); } template -inline QDataStreamIfHasOStreamOperators operator<<(QDataStream &s, const QList &v) +inline QDataStreamIfHasOStreamOperatorsContainer, T> operator<<(QDataStream &s, const QList &v) { return QtPrivate::writeSequentialContainer(s, v); } template -inline QDataStreamIfHasIStreamOperators operator>>(QDataStream &s, QSet &set) +inline QDataStreamIfHasIStreamOperatorsContainer, T> operator>>(QDataStream &s, QSet &set) { return QtPrivate::readListBasedContainer(s, set); } template -inline QDataStreamIfHasOStreamOperators operator<<(QDataStream &s, const QSet &set) +inline QDataStreamIfHasOStreamOperatorsContainer, T> operator<<(QDataStream &s, const QSet &set) { return QtPrivate::writeSequentialContainer(s, set); } template -inline QDataStreamIfHasIStreamOperators operator>>(QDataStream &s, QHash &hash) +inline QDataStreamIfHasIStreamOperatorsContainer, Key, T> operator>>(QDataStream &s, QHash &hash) { return QtPrivate::readAssociativeContainer(s, hash); } template -inline QDataStreamIfHasOStreamOperators operator<<(QDataStream &s, const QHash &hash) +inline QDataStreamIfHasOStreamOperatorsContainer, Key, T> operator<<(QDataStream &s, const QHash &hash) { return QtPrivate::writeAssociativeContainer(s, hash); } template -inline QDataStreamIfHasIStreamOperators operator>>(QDataStream &s, QMultiHash &hash) +inline QDataStreamIfHasIStreamOperatorsContainer, Key, T> operator>>(QDataStream &s, QMultiHash &hash) { return QtPrivate::readAssociativeContainer(s, hash); } template -inline QDataStreamIfHasOStreamOperators operator<<(QDataStream &s, const QMultiHash &hash) +inline QDataStreamIfHasOStreamOperatorsContainer, Key, T> operator<<(QDataStream &s, const QMultiHash &hash) { return QtPrivate::writeAssociativeMultiContainer(s, hash); } template -inline QDataStreamIfHasIStreamOperators operator>>(QDataStream &s, QMap &map) +inline QDataStreamIfHasIStreamOperatorsContainer, Key, T> operator>>(QDataStream &s, QMap &map) { return QtPrivate::readAssociativeContainer(s, map); } template -inline QDataStreamIfHasOStreamOperators operator<<(QDataStream &s, const QMap &map) +inline QDataStreamIfHasOStreamOperatorsContainer, Key, T> operator<<(QDataStream &s, const QMap &map) { return QtPrivate::writeAssociativeContainer(s, map); } template -inline QDataStreamIfHasIStreamOperators operator>>(QDataStream &s, QMultiMap &map) +inline QDataStreamIfHasIStreamOperatorsContainer, Key, T> operator>>(QDataStream &s, QMultiMap &map) { return QtPrivate::readAssociativeContainer(s, map); } template -inline QDataStreamIfHasOStreamOperators operator<<(QDataStream &s, const QMultiMap &map) +inline QDataStreamIfHasOStreamOperatorsContainer, Key, T> operator<<(QDataStream &s, const QMultiMap &map) { return QtPrivate::writeAssociativeMultiContainer(s, map); } diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h index 38f23d822f..9145891faf 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -820,7 +820,7 @@ public: void swap(QHash &other) noexcept { qSwap(d, other.d); } template - QTypeTraits::compare_eq_result operator==(const QHash &other) const noexcept + QTypeTraits::compare_eq_result_container operator==(const QHash &other) const noexcept { if (d == other.d) return true; @@ -836,7 +836,7 @@ public: return true; } template - QTypeTraits::compare_eq_result operator!=(const QHash &other) const noexcept + QTypeTraits::compare_eq_result_container operator!=(const QHash &other) const noexcept { return !(*this == other); } inline qsizetype size() const noexcept { return d ? qsizetype(d->size) : 0; } diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h index ddce07bbb7..0329dd37da 100644 --- a/src/corelib/tools/qlist.h +++ b/src/corelib/tools/qlist.h @@ -290,7 +290,7 @@ public: void swap(QList &other) noexcept { qSwap(d, other.d); } template - QTypeTraits::compare_eq_result operator==(const QList &other) const + QTypeTraits::compare_eq_result_container operator==(const QList &other) const { if (size() != other.size()) return false; @@ -301,13 +301,13 @@ public: return d->compare(begin(), other.begin(), size()); } template - QTypeTraits::compare_eq_result operator!=(const QList &other) const + QTypeTraits::compare_eq_result_container operator!=(const QList &other) const { return !(*this == other); } template - QTypeTraits::compare_lt_result operator<(const QList &other) const + QTypeTraits::compare_lt_result_container operator<(const QList &other) const noexcept(noexcept(std::lexicographical_compare::const_iterator, typename QList::const_iterator>( std::declval>().begin(), std::declval>().end(), @@ -318,21 +318,21 @@ public: } template - QTypeTraits::compare_lt_result operator>(const QList &other) const + QTypeTraits::compare_lt_result_container operator>(const QList &other) const noexcept(noexcept(other < std::declval>())) { return other < *this; } template - QTypeTraits::compare_lt_result operator<=(const QList &other) const + QTypeTraits::compare_lt_result_container operator<=(const QList &other) const noexcept(noexcept(other < std::declval>())) { return !(other < *this); } template - QTypeTraits::compare_lt_result operator>=(const QList &other) const + QTypeTraits::compare_lt_result_container operator>=(const QList &other) const noexcept(noexcept(std::declval>() < other)) { return !(*this < other); diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h index d5f6b91ba8..57a964f29f 100644 --- a/src/corelib/tools/qmap.h +++ b/src/corelib/tools/qmap.h @@ -277,7 +277,7 @@ public: } template friend - QTypeTraits::compare_eq_result operator==(const QMap &lhs, const QMap &rhs) + QTypeTraits::compare_eq_result_container operator==(const QMap &lhs, const QMap &rhs) { if (lhs.d == rhs.d) return true; @@ -288,7 +288,7 @@ public: } template friend - QTypeTraits::compare_eq_result operator!=(const QMap &lhs, const QMap &rhs) + QTypeTraits::compare_eq_result_container operator!=(const QMap &lhs, const QMap &rhs) { return !(lhs == rhs); } @@ -905,7 +905,7 @@ public: } template friend - QTypeTraits::compare_eq_result operator==(const QMultiMap &lhs, const QMultiMap &rhs) + QTypeTraits::compare_eq_result_container operator==(const QMultiMap &lhs, const QMultiMap &rhs) { if (lhs.d == rhs.d) return true; @@ -916,7 +916,7 @@ public: } template friend - QTypeTraits::compare_eq_result operator!=(const QMultiMap &lhs, const QMultiMap &rhs) + QTypeTraits::compare_eq_result_container operator!=(const QMultiMap &lhs, const QMultiMap &rhs) { return !(lhs == rhs); } diff --git a/src/corelib/tools/qset.h b/src/corelib/tools/qset.h index 681ce9cbe2..565b8b5691 100644 --- a/src/corelib/tools/qset.h +++ b/src/corelib/tools/qset.h @@ -72,10 +72,10 @@ public: inline void swap(QSet &other) noexcept { q_hash.swap(other.q_hash); } template - QTypeTraits::compare_eq_result operator==(const QSet &other) const + QTypeTraits::compare_eq_result_container operator==(const QSet &other) const { return q_hash == other.q_hash; } template - QTypeTraits::compare_eq_result operator!=(const QSet &other) const + QTypeTraits::compare_eq_result_container operator!=(const QSet &other) const { return q_hash != other.q_hash; } inline qsizetype size() const { return q_hash.size(); } diff --git a/tests/auto/corelib/tools/collections/tst_collections.cpp b/tests/auto/corelib/tools/collections/tst_collections.cpp index 9dbaa9c386..0fa252427a 100644 --- a/tests/auto/corelib/tools/collections/tst_collections.cpp +++ b/tests/auto/corelib/tools/collections/tst_collections.cpp @@ -160,6 +160,150 @@ struct Pod { int i1, i2; }; +// Compile-time checks for recursive containers +struct Dummy +{ + bool operator==(const Dummy &) const { return false; } + bool operator<(const Dummy &) const { return false; } +}; + +struct RecursiveList : public QList {}; +struct RecursiveSet : public QSet {}; +struct RecursiveMapV : public QMap {}; +struct RecursiveMapK : public QMap {}; +struct RecursiveMultiMapV : public QMultiMap {}; +struct RecursiveMultiMapK : public QMultiMap {}; +struct RecursiveHashV : public QHash {}; +struct RecursiveHashK : public QHash {}; +struct RecursiveMultiHashV : public QMultiHash {}; +struct RecursiveMultiHashK : public QMultiHash {}; + +struct Empty {}; +struct NoCmpParamRecursiveMapV : public QMap {}; +struct NoCmpParamRecursiveMapK : public QMap {}; +struct NoCmpParamRecursiveMultiMapV : public QMultiMap {}; +struct NoCmpParamRecursiveMultiMapK : public QMultiMap {}; +struct NoCmpParamRecursiveHashV : public QHash {}; +struct NoCmpParamRecursiveHashK : public QHash {}; +struct NoCmpParamRecursiveMultiHashV : public QMultiHash {}; +struct NoCmpParamRecursiveMultiHashK : public QMultiHash {}; + +struct NoCmpRecursiveList : public QList +{ + bool operator==(const RecursiveList &) const = delete; + bool operator<(const RecursiveList &) const = delete; +}; +struct NoCmpRecursiveSet : public QSet +{ + bool operator==(const NoCmpRecursiveSet &) const = delete; +}; +struct NoCmpRecursiveMapV : public QMap +{ + bool operator==(const NoCmpRecursiveMapV &) const = delete; +}; +struct NoCmpRecursiveMapK : public QMap +{ + bool operator==(const NoCmpRecursiveMapK &) const = delete; +}; +struct NoCmpRecursiveMultiMapV : public QMultiMap +{ + bool operator==(const NoCmpRecursiveMultiMapV &) const = delete; +}; +struct NoCmpRecursiveMultiMapK : public QMultiMap +{ + bool operator==(const NoCmpRecursiveMultiMapK &) const = delete; +}; +struct NoCmpRecursiveHashV : public QHash +{ + bool operator==(const NoCmpRecursiveHashV &) const = delete; +}; +struct NoCmpRecursiveHashK : public QHash +{ + bool operator==(const NoCmpRecursiveHashK &) const = delete; +}; +struct NoCmpRecursiveMultiHashV : public QMultiHash +{ + bool operator==(const NoCmpRecursiveMultiHashV &) const = delete; +}; +struct NoCmpRecursiveMultiHashK : public QMultiHash +{ + bool operator==(const NoCmpRecursiveMultiHashK &) const = delete; +}; + +uint qHash(const Dummy &) { return 0; } +uint qHash(const RecursiveSet &) { return 0; } +uint qHash(const RecursiveHashK &) { return 0; } +uint qHash(const RecursiveHashV &) { return 0; } +uint qHash(const RecursiveMultiHashK &) { return 0; } +uint qHash(const RecursiveMultiHashV &) { return 0; } + +Q_DECLARE_METATYPE(RecursiveList); +Q_DECLARE_METATYPE(RecursiveSet); +Q_DECLARE_METATYPE(RecursiveMapV); +Q_DECLARE_METATYPE(RecursiveMapK); +Q_DECLARE_METATYPE(RecursiveMultiMapV); +Q_DECLARE_METATYPE(RecursiveMultiMapK); +Q_DECLARE_METATYPE(RecursiveHashV); +Q_DECLARE_METATYPE(RecursiveHashK); +Q_DECLARE_METATYPE(RecursiveMultiHashV); +Q_DECLARE_METATYPE(RecursiveMultiHashK); + +Q_DECLARE_METATYPE(NoCmpParamRecursiveMapV); +Q_DECLARE_METATYPE(NoCmpParamRecursiveMapK); +Q_DECLARE_METATYPE(NoCmpParamRecursiveMultiMapV); +Q_DECLARE_METATYPE(NoCmpParamRecursiveMultiMapK); +Q_DECLARE_METATYPE(NoCmpParamRecursiveHashK); +// TODO: fix, this requires operator== from key type (QTBUG-96256) +// Q_DECLARE_METATYPE(NoCmpParamRecursiveHashV); +Q_DECLARE_METATYPE(NoCmpParamRecursiveMultiHashK); +// TODO: fix, this requires operator== from key type (QTBUG-96256) +// Q_DECLARE_METATYPE(NoCmpParamRecursiveMultiHashK); + +Q_DECLARE_METATYPE(NoCmpRecursiveList); +// TODO: fix, this requires operator== (QTBUG-96257) +// Q_DECLARE_METATYPE(NoCmpRecursiveSet); +Q_DECLARE_METATYPE(NoCmpRecursiveMapV); +Q_DECLARE_METATYPE(NoCmpRecursiveMapK); +Q_DECLARE_METATYPE(NoCmpRecursiveMultiMapV); +Q_DECLARE_METATYPE(NoCmpRecursiveMultiMapK); +Q_DECLARE_METATYPE(NoCmpRecursiveHashV); +Q_DECLARE_METATYPE(NoCmpRecursiveHashK); +Q_DECLARE_METATYPE(NoCmpRecursiveMultiHashV); +Q_DECLARE_METATYPE(NoCmpRecursiveMultiHashK); + +static_assert(QTypeTraits::has_operator_equal_v); +static_assert(QTypeTraits::has_operator_less_than_v); +static_assert(QTypeTraits::has_operator_equal_v); +static_assert(QTypeTraits::has_operator_equal_v); +static_assert(QTypeTraits::has_operator_equal_v); +static_assert(QTypeTraits::has_operator_equal_v); +static_assert(QTypeTraits::has_operator_equal_v); +static_assert(QTypeTraits::has_operator_equal_v); +static_assert(QTypeTraits::has_operator_equal_v); +static_assert(QTypeTraits::has_operator_equal_v); +static_assert(QTypeTraits::has_operator_equal_v); + +static_assert(!QTypeTraits::has_operator_equal_v); +static_assert(!QTypeTraits::has_operator_equal_v); +static_assert(!QTypeTraits::has_operator_equal_v); +static_assert(!QTypeTraits::has_operator_equal_v); +static_assert(QTypeTraits::has_operator_equal_v); +static_assert(!QTypeTraits::has_operator_equal_v); +static_assert(QTypeTraits::has_operator_equal_v); +static_assert(!QTypeTraits::has_operator_equal_v); + +static_assert(!QTypeTraits::has_operator_equal_v); +static_assert(!QTypeTraits::has_operator_less_than_v); +static_assert(!QTypeTraits::has_operator_equal_v); +static_assert(!QTypeTraits::has_operator_equal_v); +static_assert(!QTypeTraits::has_operator_equal_v); +static_assert(!QTypeTraits::has_operator_equal_v); +static_assert(!QTypeTraits::has_operator_equal_v); +static_assert(!QTypeTraits::has_operator_equal_v); +static_assert(!QTypeTraits::has_operator_equal_v); +static_assert(!QTypeTraits::has_operator_equal_v); +static_assert(!QTypeTraits::has_operator_equal_v); + void tst_Collections::typeinfo() { QVERIFY(QTypeInfo::isPointer); -- cgit v1.2.3