diff options
| author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2025-11-27 12:35:22 +0100 |
|---|---|---|
| committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2025-12-16 14:55:39 +0100 |
| commit | 425f2470e47d9ddfa776434111ef0f7ae3d4ccca (patch) | |
| tree | 6f017ca88dde08eb4614c8ab1609c3cf4a3da97c /sources | |
| parent | 4cce05488f04b279e14e2830b39a2c243ea4c793 (diff) | |
When encountering an operator==() in C++ 20 and the scope
does not have an equivalent operator!=(), synthesize
operator!=() using the functions added for operator<=>()
[ChangeLog][shiboken6] The support for synthesizing comparison
operators in C++ 20 has been reimplemented and improved.
Task-number: PYSIDE-3245
Change-Id: I4f9829c9a67e1c02e5beb02b8f36f847784359a4
Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
Diffstat (limited to 'sources')
9 files changed, 112 insertions, 6 deletions
diff --git a/sources/shiboken6/tests/libsample/spaceship.cpp b/sources/shiboken6/tests/libsample/spaceship.cpp index 294ad58f8..c883f7c2f 100644 --- a/sources/shiboken6/tests/libsample/spaceship.cpp +++ b/sources/shiboken6/tests/libsample/spaceship.cpp @@ -32,3 +32,18 @@ std::strong_ordering operator<=>(FreeSpaceshipComparisonTester lhs, return lhs.value() <=> rhs.value(); } #endif // C++ 20 + +NonEqualityComparisonTester::NonEqualityComparisonTester(int v) noexcept + : m_value(v) +{ +} + +int NonEqualityComparisonTester::value() const +{ + return m_value; +} + +bool NonEqualityComparisonTester::operator==(NonEqualityComparisonTester rhs) const +{ + return m_value == rhs.m_value; +} diff --git a/sources/shiboken6/tests/libsample/spaceship.h b/sources/shiboken6/tests/libsample/spaceship.h index 1bdd1a3e5..26f636009 100644 --- a/sources/shiboken6/tests/libsample/spaceship.h +++ b/sources/shiboken6/tests/libsample/spaceship.h @@ -45,4 +45,18 @@ LIBSAMPLE_API std::strong_ordering operator<=>(FreeSpaceshipComparisonTester lhs FreeSpaceshipComparisonTester rhs); #endif // C++ 20 + +class LIBSAMPLE_API NonEqualityComparisonTester +{ +public: + explicit NonEqualityComparisonTester(int v) noexcept; + + int value() const; + + bool operator==(NonEqualityComparisonTester rhs) const; + +private: + int m_value; +}; + #endif // SPACESHIP_H diff --git a/sources/shiboken6/tests/samplebinding/CMakeLists.txt b/sources/shiboken6/tests/samplebinding/CMakeLists.txt index c9e4f601f..01f51fc2d 100644 --- a/sources/shiboken6/tests/samplebinding/CMakeLists.txt +++ b/sources/shiboken6/tests/samplebinding/CMakeLists.txt @@ -64,6 +64,7 @@ ${CMAKE_CURRENT_BINARY_DIR}/sample/moveonly_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/moveonlyhandler_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/noimplicitconversion_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/nondefaultctor_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/nonequalitycomparisontester_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/objectmodel_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/objecttype_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/objecttypebyvalue_wrapper.cpp diff --git a/sources/shiboken6/tests/samplebinding/spaceship_test.py b/sources/shiboken6/tests/samplebinding/spaceship_test.py index 1b07511f8..95d2506ce 100644 --- a/sources/shiboken6/tests/samplebinding/spaceship_test.py +++ b/sources/shiboken6/tests/samplebinding/spaceship_test.py @@ -14,7 +14,8 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from shiboken_paths import init_paths init_paths() -from sample import FreeSpaceshipComparisonTester, SpaceshipComparisonTester +from sample import (FreeSpaceshipComparisonTester, SpaceshipComparisonTester, + NonEqualityComparisonTester) class SpaceshipTest(unittest.TestCase): @@ -45,6 +46,15 @@ class SpaceshipTest(unittest.TestCase): self.assertTrue(t1 < t2) self.assertFalse(t1 > t2) + @unittest.skipUnless(SpaceshipComparisonTester.Enabled.HasSpaceshipOperator, "< C++ 20") + def testNonEqualSynthetization(self): + ne_a = NonEqualityComparisonTester(1) + ne_b = NonEqualityComparisonTester(1) + self.assertTrue(ne_a == ne_b) + # Verify that different instances with same value are not reported as "not equal", + # (fooling the FallbackRichCompare() function which is generated for missing operators). + self.assertFalse(ne_a != ne_b) + if __name__ == '__main__': unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/typesystem_sample.xml b/sources/shiboken6/tests/samplebinding/typesystem_sample.xml index fed84ba0d..05798a6ce 100644 --- a/sources/shiboken6/tests/samplebinding/typesystem_sample.xml +++ b/sources/shiboken6/tests/samplebinding/typesystem_sample.xml @@ -160,6 +160,7 @@ <enum-type name="Enabled"/> </value-type> <value-type name="FreeSpaceshipComparisonTester"/> + <value-type name="NonEqualityComparisonTester"/> <primitive-type name="PStr"> <include file-name="str.h" location="global"/> diff --git a/sources/shiboken6_generator/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken6_generator/ApiExtractor/abstractmetabuilder.cpp index 7c5d34315..bf8d3246c 100644 --- a/sources/shiboken6_generator/ApiExtractor/abstractmetabuilder.cpp +++ b/sources/shiboken6_generator/ApiExtractor/abstractmetabuilder.cpp @@ -278,6 +278,15 @@ void AbstractMetaBuilderPrivate::registerToStringCapability(const FunctionModelI } } +// Find "operator!=" matching an "operator==" in a scope. +static bool hasOperatorNotEqual(const ScopeModelItem &scopeItem, const FunctionModelItem &operatorEqual) +{ + auto pred = [&operatorEqual](const FunctionModelItem &f) { + return f->isOperatorNotEqual() && operatorEqual->hasEquivalentArguments(*f); + }; + return std::any_of(scopeItem->functions().cbegin(), scopeItem->functions().cend(), pred); +} + static ComparisonOperators synthesizedSpaceshipComparison(const AbstractMetaClassCPtr ¤tClass, const FunctionModelItem &item) { @@ -297,6 +306,7 @@ static ComparisonOperators synthesizedSpaceshipComparison(const AbstractMetaClas // Traverse free operator functions (global/namespace) void AbstractMetaBuilderPrivate::traverseFreeOperatorFunction(const FunctionModelItem &item, + const ScopeModelItem &scope, const AbstractMetaClassPtr ¤tClass) { Q_ASSERT(!currentClass || currentClass->isNamespace()); @@ -380,6 +390,17 @@ void AbstractMetaBuilderPrivate::traverseFreeOperatorFunction(const FunctionMode ops, flags); return; } + + // C++20: Synthesize "!=" from "==" + if (clang::emulatedCompilerLanguageLevel() >= LanguageLevel::Cpp20 + && item->isOperatorEqual() + && !item->hasPointerArguments() && !hasOperatorNotEqual(scope, item)) { + AbstractMetaClass::addSynthesizedComparisonOperators( + baseoperandClass, metaFunction->arguments(), + ComparisonOperatorType::OperatorNotEqual, + flags | InternalFunctionFlag::OperatorCpp20NonEquality); + } + AbstractMetaClass::addFunction(baseoperandClass, metaFunction); ReportHandler::addGeneralMessage(msgSynthesizedFunction(metaFunction, item)); if (!metaFunction->arguments().isEmpty()) { @@ -697,11 +718,11 @@ void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom, case CodeModel::ArithmeticOperator: case CodeModel::BitwiseOperator: case CodeModel::LogicalOperator: - traverseFreeOperatorFunction(func, {}); + traverseFreeOperatorFunction(func, dom, {}); break; case CodeModel::ShiftOperator: if (!traverseStreamOperator(func, {})) - traverseFreeOperatorFunction(func, {}); + traverseFreeOperatorFunction(func, dom, {}); default: break; } @@ -1486,7 +1507,7 @@ void AbstractMetaBuilderPrivate::traverseNameSpaceFunctions(const ScopeModelItem functions.reserve(scopeFunctionList.size()); for (const FunctionModelItem &function : scopeFunctionList) { if (function->isOperator()) { - traverseFreeOperatorFunction(function, currentClass); + traverseFreeOperatorFunction(function, scopeItem, currentClass); } else if (auto metaFunction = traverseFunction(function, currentClass)) { metaFunction->setCppAttribute(FunctionAttribute::Static); functions.append(metaFunction); @@ -1569,7 +1590,6 @@ void AbstractMetaBuilderPrivate::traverseClassFunction(const ScopeModelItem& sco const AbstractMetaFunctionPtr &metaFunction, const AbstractMetaClassPtr &metaClass) const { - Q_UNUSED(scopeItem) if (function->isSpaceshipOperator()) { // For spaceship, the traverse mechanism is only used to handle rejections // and get the argument type. @@ -1582,6 +1602,15 @@ void AbstractMetaBuilderPrivate::traverseClassFunction(const ScopeModelItem& sco return; } + // C++20: Synthesize "!=" from "==" + if (clang::emulatedCompilerLanguageLevel() >= LanguageLevel::Cpp20 + && function->isOperatorEqual() && !hasOperatorNotEqual(scopeItem, function)) { + AbstractMetaClass::addSynthesizedComparisonOperators( + metaClass, metaFunction->arguments(), + ComparisonOperatorType::OperatorNotEqual, + InternalFunctionFlag::OperatorCpp20NonEquality); + } + traverseClassFunction(metaFunction, metaClass); } diff --git a/sources/shiboken6_generator/ApiExtractor/abstractmetabuilder_p.h b/sources/shiboken6_generator/ApiExtractor/abstractmetabuilder_p.h index b503f4b33..0890eb752 100644 --- a/sources/shiboken6_generator/ApiExtractor/abstractmetabuilder_p.h +++ b/sources/shiboken6_generator/ApiExtractor/abstractmetabuilder_p.h @@ -116,7 +116,7 @@ public: void traverseFields(const ScopeModelItem &item, const AbstractMetaClassPtr &parent); bool traverseStreamOperator(const FunctionModelItem &functionItem, const AbstractMetaClassPtr ¤tClass); - void traverseFreeOperatorFunction(const FunctionModelItem &item, + void traverseFreeOperatorFunction(const FunctionModelItem &item, const ScopeModelItem &scope, const AbstractMetaClassPtr ¤tClass); AbstractMetaFunctionPtr traverseAddedFunctionHelper(const AddedFunctionPtr &addedFunc, diff --git a/sources/shiboken6_generator/ApiExtractor/parser/codemodel.cpp b/sources/shiboken6_generator/ApiExtractor/parser/codemodel.cpp index 8cbca7bc9..bfccacbac 100644 --- a/sources/shiboken6_generator/ApiExtractor/parser/codemodel.cpp +++ b/sources/shiboken6_generator/ApiExtractor/parser/codemodel.cpp @@ -846,6 +846,13 @@ void _ArgumentModelItem::setScopeResolution(bool v) m_scopeResolution = v; } +bool _ArgumentModelItem::isEquivalent(const _ArgumentModelItem &rhs) const +{ + return m_scopeResolution == rhs.m_scopeResolution && m_defaultValue == rhs.m_defaultValue + && m_defaultValueExpression == rhs.m_defaultValueExpression + && m_type == rhs.m_type; +} + #ifndef QT_NO_DEBUG_STREAM void _ArgumentModelItem::formatDebug(QDebug &d) const { @@ -991,6 +998,16 @@ bool _FunctionModelItem::isOperator() const return result; } +static bool isPointerArgument(const ArgumentModelItem &a) +{ + return !a->type().indirectionsV().isEmpty(); +} + +bool _FunctionModelItem::hasPointerArguments() const +{ + return std::any_of(m_arguments.cbegin(), m_arguments.cend(), isPointerArgument); +} + ExceptionSpecification _FunctionModelItem::exceptionSpecification() const { return m_exceptionSpecification; @@ -1075,6 +1092,19 @@ QString _FunctionModelItem::typeSystemSignature() const // For dumping out type return result; } +static inline bool equivalentArguments(const ArgumentModelItem &lhs, + const ArgumentModelItem &rhs) +{ + return lhs->isEquivalent(*rhs); +} + +bool _FunctionModelItem::hasEquivalentArguments(const _FunctionModelItem &rhs) const +{ + return m_arguments.size() == rhs.m_arguments.size() + && std::equal(m_arguments.cbegin(), m_arguments.cend(), rhs.m_arguments.cbegin(), rhs.m_arguments.cend(), + equivalentArguments); +} + using NameFunctionTypeHash = QHash<QStringView, CodeModel::FunctionType>; static const NameFunctionTypeHash &nameToOperatorFunction() diff --git a/sources/shiboken6_generator/ApiExtractor/parser/codemodel.h b/sources/shiboken6_generator/ApiExtractor/parser/codemodel.h index 8d757e635..7ff0a88e2 100644 --- a/sources/shiboken6_generator/ApiExtractor/parser/codemodel.h +++ b/sources/shiboken6_generator/ApiExtractor/parser/codemodel.h @@ -380,6 +380,8 @@ public: bool scopeResolution() const; void setScopeResolution(bool v); + bool isEquivalent(const _ArgumentModelItem &rhs) const; // Compare all except name + #ifndef QT_NO_DEBUG_STREAM void formatDebug(QDebug &d) const override; #endif @@ -502,6 +504,7 @@ public: bool isSpaceshipOperator() const; bool isOperatorEqual() const; bool isOperatorNotEqual() const; + bool hasPointerArguments() const; bool isSimilar(const FunctionModelItem &other) const; @@ -515,6 +518,9 @@ public: QString classQualifiedSignature() const; QString typeSystemSignature() const; // For dumping out type system files + // Compare all except names + bool hasEquivalentArguments(const _FunctionModelItem &rhs) const; + // Private, for usage by the clang builder. void _determineType(); |
