diff options
author | Friedemann Kleint <[email protected]> | 2022-02-14 10:45:54 +0100 |
---|---|---|
committer | Friedemann Kleint <[email protected]> | 2022-06-21 21:14:58 +0200 |
commit | d189efa29969ce9731a2954c398c8c7eeb5f5dc2 (patch) | |
tree | e556c1051bacfa41240d0414ee9114a685eca425 | |
parent | 85fc651460f0e408548c13d7056e7e30a148a946 (diff) |
shiboken6: Implement the spaceship comparison operator of C++ 20
Synthesize all comparison operators if one is found in the code model.
Task-number: QTBUG-103757
Change-Id: I78fbcd93bc4cd172266f9dd0dbb2ebcf3a8bb7f2
Reviewed-by: Christian Tismer <[email protected]>
13 files changed, 137 insertions, 6 deletions
diff --git a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp index 824f6fc9e..1391a7b0a 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp +++ b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp @@ -282,6 +282,11 @@ void AbstractMetaBuilderPrivate::traverseOperatorFunction(const FunctionModelIte if (baseoperandClass == nullptr) return; + if (item->isSpaceshipOperator() && !item->isDeleted()) { + baseoperandClass->addSynthesizedComparisonOperators(); + return; + } + AbstractMetaFunction *metaFunction = traverseFunction(item, baseoperandClass); if (metaFunction == nullptr) return; @@ -1304,6 +1309,9 @@ AbstractMetaFunctionRawPtrList for (const FunctionModelItem &function : scopeFunctionList) { if (isNamespace && function->isOperator()) { traverseOperatorFunction(function, currentClass); + } else if (function->isSpaceshipOperator() && !function->isDeleted()) { + if (currentClass) + currentClass->addSynthesizedComparisonOperators(); } else if (auto *metaFunction = traverseFunction(function, currentClass)) { result.append(metaFunction); } else if (function->functionType() == CodeModel::Constructor) { diff --git a/sources/shiboken6/ApiExtractor/abstractmetalang.cpp b/sources/shiboken6/ApiExtractor/abstractmetalang.cpp index 21b1ba1f2..33ed659c0 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetalang.cpp +++ b/sources/shiboken6/ApiExtractor/abstractmetalang.cpp @@ -17,10 +17,14 @@ #include "namespacetypeentry.h" #include "usingmember.h" +#include "qtcompat.h" + #include <QtCore/QDebug> #include <algorithm> +using namespace Qt::StringLiterals; + bool function_sorter(const AbstractMetaFunctionCPtr &a, const AbstractMetaFunctionCPtr &b) { return a->signature() < b->signature(); @@ -819,6 +823,42 @@ AbstractMetaFunction * return f; } +static AbstractMetaType boolType() +{ + auto *boolType = TypeDatabase::instance()->findType(u"bool"_s); + Q_ASSERT(boolType); + AbstractMetaType result(boolType); + result.decideUsagePattern(); + return result; +} + +// Helper to synthesize comparison operators from a spaceship operator. Since +// shiboken also generates code for comparing to different types, this fits +// better than of handling it in the generator code. +void AbstractMetaClass::addSynthesizedComparisonOperators() +{ + static const auto returnType = boolType(); + + AbstractMetaType selfType(typeEntry()); + selfType.setConstant(true); + selfType.setReferenceType(LValueReference); + selfType.decideUsagePattern(); + AbstractMetaArgument selfArgument; + selfArgument.setType(selfType); + selfArgument.setName(u"rhs"_qs); + AbstractMetaArgumentList arguments(1, selfArgument); + + static const char *operators[] + = {"operator==", "operator!=", "operator<", "operator<=", "operator>", "operator>="}; + for (auto *op : operators) { + auto *f = AbstractMetaClassPrivate::createFunction(QLatin1StringView(op), + AbstractMetaFunction::ComparisonOperator, + Access::Public, arguments, + returnType, this); + d->addFunction(AbstractMetaFunctionCPtr(f)); + } +} + bool AbstractMetaClass::hasNonPrivateConstructor() const { return d->m_hasNonPrivateConstructor; diff --git a/sources/shiboken6/ApiExtractor/abstractmetalang.h b/sources/shiboken6/ApiExtractor/abstractmetalang.h index d17bc5c80..10543381a 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetalang.h +++ b/sources/shiboken6/ApiExtractor/abstractmetalang.h @@ -117,6 +117,8 @@ public: const TypeEntry *parentManagementEntry() const; bool hasParentManagement() const { return parentManagementEntry() != nullptr; } + void addSynthesizedComparisonOperators(); + bool generateExceptionHandling() const; CppWrapper cppWrapper() const; diff --git a/sources/shiboken6/ApiExtractor/parser/codemodel.cpp b/sources/shiboken6/ApiExtractor/parser/codemodel.cpp index 77f6d911e..4099caa2c 100644 --- a/sources/shiboken6/ApiExtractor/parser/codemodel.cpp +++ b/sources/shiboken6/ApiExtractor/parser/codemodel.cpp @@ -908,7 +908,13 @@ void _FunctionModelItem::setVariadics(bool isVariadics) bool _FunctionModelItem::isDefaultConstructor() const { return m_functionType == CodeModel::Constructor - && (m_arguments.isEmpty() || m_arguments.constFirst()->defaultValue()); + && (m_arguments.isEmpty() || m_arguments.constFirst()->defaultValue()); +} + +bool _FunctionModelItem::isSpaceshipOperator() const +{ + return m_functionType == CodeModel::ComparisonOperator + && name() == u"operator<=>"; } bool _FunctionModelItem::isNoExcept() const @@ -1101,6 +1107,7 @@ static const NameFunctionTypeHash &nameToOperatorFunction() {u"operator>=", CodeModel::ComparisonOperator}, {u"operator==", CodeModel::ComparisonOperator}, {u"operator!=", CodeModel::ComparisonOperator}, + {u"operator<=>", CodeModel::ComparisonOperator}, {u"operator!", CodeModel::LogicalOperator}, {u"operator&&", CodeModel::LogicalOperator}, {u"operator||", CodeModel::LogicalOperator}, diff --git a/sources/shiboken6/ApiExtractor/parser/codemodel.h b/sources/shiboken6/ApiExtractor/parser/codemodel.h index 3b468c55b..58c7a9d2c 100644 --- a/sources/shiboken6/ApiExtractor/parser/codemodel.h +++ b/sources/shiboken6/ApiExtractor/parser/codemodel.h @@ -514,6 +514,7 @@ public: void setVariadics(bool isVariadics); bool isDefaultConstructor() const; + bool isSpaceshipOperator() const; bool isSimilar(const FunctionModelItem &other) const; diff --git a/sources/shiboken6/ApiExtractor/tests/testreverseoperators.cpp b/sources/shiboken6/ApiExtractor/tests/testreverseoperators.cpp index 7401d64f5..90c613f96 100644 --- a/sources/shiboken6/ApiExtractor/tests/testreverseoperators.cpp +++ b/sources/shiboken6/ApiExtractor/tests/testreverseoperators.cpp @@ -7,6 +7,9 @@ #include <abstractmetafunction.h> #include <abstractmetalang.h> #include <typesystem.h> +#include <clangparser/compilersupport.h> + +#include <algorithm> void TestReverseOperators::testReverseSum() { @@ -93,7 +96,33 @@ void TestReverseOperators::testReverseSumWithAmbiguity() QCOMPARE(reverseOp->minimalSignature(), u"operator+(A,B)"); } - +void TestReverseOperators::testSpaceshipOperator() +{ + const char cppCode[] = R"( + class Test { + public: + explicit Test(int v); + int operator<=>(const Test &rhs) const = default; + };)"; + const char xmlCode[] = R"( + <typesystem package="Foo"> + <value-type name='Test'/> + </typesystem>)"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false, + {}, {}, LanguageLevel::Cpp20)); + QVERIFY(!builder.isNull()); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 1); + const AbstractMetaClass *testClass = AbstractMetaClass::findClass(classes, u"Test"); + QVERIFY(testClass); + const auto &functions = testClass->functions(); + // 6 operators should be synthesized + const auto count = std::count_if(functions.cbegin(), functions.cend(), + [](const AbstractMetaFunctionCPtr &f) { + return f->isComparisonOperator(); + }); + QCOMPARE(count, 6); +} QTEST_APPLESS_MAIN(TestReverseOperators) diff --git a/sources/shiboken6/ApiExtractor/tests/testreverseoperators.h b/sources/shiboken6/ApiExtractor/tests/testreverseoperators.h index 7cf2cf13d..fb8d97c97 100644 --- a/sources/shiboken6/ApiExtractor/tests/testreverseoperators.h +++ b/sources/shiboken6/ApiExtractor/tests/testreverseoperators.h @@ -11,6 +11,7 @@ class TestReverseOperators : public QObject private slots: void testReverseSum(); void testReverseSumWithAmbiguity(); + void testSpaceshipOperator(); }; #endif diff --git a/sources/shiboken6/ApiExtractor/tests/testutil.h b/sources/shiboken6/ApiExtractor/tests/testutil.h index 9b4ecf653..7fbc7e906 100644 --- a/sources/shiboken6/ApiExtractor/tests/testutil.h +++ b/sources/shiboken6/ApiExtractor/tests/testutil.h @@ -18,8 +18,9 @@ namespace TestUtil { static AbstractMetaBuilder *parse(const char *cppCode, const char *xmlCode, bool silent = true, - const QString &apiVersion = QString(), - const QStringList &dropTypeEntries = QStringList()) + const QString &apiVersion = {}, + const QStringList &dropTypeEntries = {}, + LanguageLevel languageLevel = LanguageLevel::Default) { ReportHandler::setSilent(silent); ReportHandler::startTimer(); @@ -51,7 +52,7 @@ namespace TestUtil auto builder = std::make_unique<AbstractMetaBuilder>(); try { - if (!builder->build(arguments)) + if (!builder->build(arguments, {}, true, languageLevel)) return nullptr; } catch (const std::exception &e) { qWarning("%s", e.what()); diff --git a/sources/shiboken6/tests/libsample/oddbool.cpp b/sources/shiboken6/tests/libsample/oddbool.cpp index 4b21fbd80..06a61f1c2 100644 --- a/sources/shiboken6/tests/libsample/oddbool.cpp +++ b/sources/shiboken6/tests/libsample/oddbool.cpp @@ -21,3 +21,7 @@ int ComparisonTester::compare(const ComparisonTester &rhs) const return 1; return 0; } + +SpaceshipComparisonTester::SpaceshipComparisonTester(int v) : m_value(v) +{ +} diff --git a/sources/shiboken6/tests/libsample/oddbool.h b/sources/shiboken6/tests/libsample/oddbool.h index 6394119db..a64a05eb3 100644 --- a/sources/shiboken6/tests/libsample/oddbool.h +++ b/sources/shiboken6/tests/libsample/oddbool.h @@ -8,6 +8,10 @@ #include <type_traits> +#if __cplusplus >= 202002 || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002) +# include <compare> +#endif + class OddBool { @@ -80,4 +84,21 @@ inline std::enable_if<std::is_assignable<ComparisonTester, int>::value, bool>::t operator!=(const ComparisonTester &c1, const ComparisonTester &c2) { return c1.compare(c2) != 0; } +class LIBSAMPLE_API SpaceshipComparisonTester +{ +public: + explicit SpaceshipComparisonTester(int v); + +#if __cplusplus >= 202002 || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002) + auto operator<=>(const SpaceshipComparisonTester &rhs) const = default; + + enum Enabled { HasSpaceshipOperator = 1 }; +#else + enum Enabled { HasSpaceshipOperator = 0 }; +#endif // C++ 20 + +private: + int m_value; +}; + #endif // ODDBOOL_H diff --git a/sources/shiboken6/tests/samplebinding/CMakeLists.txt b/sources/shiboken6/tests/samplebinding/CMakeLists.txt index 65a790627..7576f6734 100644 --- a/sources/shiboken6/tests/samplebinding/CMakeLists.txt +++ b/sources/shiboken6/tests/samplebinding/CMakeLists.txt @@ -117,6 +117,7 @@ ${CMAKE_CURRENT_BINARY_DIR}/sample/sizef_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/snakecasetest_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/snakecasederivedtest_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/sonofmderived1_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/spaceshipcomparisontester_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/str_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/strlist_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/time_wrapper.cpp diff --git a/sources/shiboken6/tests/samplebinding/oddbool_test.py b/sources/shiboken6/tests/samplebinding/oddbool_test.py index de264614f..466ddba82 100644 --- a/sources/shiboken6/tests/samplebinding/oddbool_test.py +++ b/sources/shiboken6/tests/samplebinding/oddbool_test.py @@ -13,7 +13,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from shiboken_paths import init_paths init_paths() -from sample import OddBoolUser, ComparisonTester +from sample import OddBoolUser, ComparisonTester, SpaceshipComparisonTester class DerivedOddBoolUser (OddBoolUser): def returnMyselfVirtual(self): @@ -59,6 +59,19 @@ class OddBoolTest(unittest.TestCase): t2 = ComparisonTester(42) self.assertEqual(t1, t2) + def testSpaceshipOperator(self): + if not SpaceshipComparisonTester.HasSpaceshipOperator: + print("Skipping Spaceship Operator test") + return + t1 = SpaceshipComparisonTester(42) + t2 = SpaceshipComparisonTester(42) + self.assertEqual(t1, t2) + self.assertTrue(t1 <= t2) + self.assertTrue(t1 >= t2) + t2 = SpaceshipComparisonTester(43) + self.assertTrue(t1 < t2) + self.assertFalse(t1 > t2) + if __name__ == '__main__': unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/typesystem_sample.xml b/sources/shiboken6/tests/samplebinding/typesystem_sample.xml index 706485459..7583a900a 100644 --- a/sources/shiboken6/tests/samplebinding/typesystem_sample.xml +++ b/sources/shiboken6/tests/samplebinding/typesystem_sample.xml @@ -152,6 +152,9 @@ <add-function signature="operator==(const ComparisonTester&)" return-type="bool"/> <add-function signature="operator!=(const ComparisonTester&)" return-type="bool"/> </value-type> + <value-type name="SpaceshipComparisonTester"> + <enum-type name="Enabled"/> + </value-type> <primitive-type name="PStr"> <include file-name="str.h" location="global"/> |