diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp index d72ecfccf4..7e8ba71cd9 100644 --- a/src/qmlcompiler/qqmljscodegenerator.cpp +++ b/src/qmlcompiler/qqmljscodegenerator.cpp @@ -2923,20 +2923,27 @@ void QQmlJSCodeGenerator::generate_As(int lhs) // If the original output is a conversion, we're supposed to check for the contained // type and if it doesn't match, set the result to null or undefined. const QQmlJSRegisterContent originalContent = m_typeResolver->original(outputContent); + const QQmlJSScope::ConstPtr target = originalContent.storedType()->isReferenceType() + ? m_typeResolver->containedType(originalContent) + : m_typeResolver->extractNonVoidFromOptionalType(originalContent); + + if (!target) { + reject(u"type assertion to unknown type"_s); + return; + } - const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(originalContent); const bool isTrivial = m_typeResolver->inherits( - m_typeResolver->originalContainedType(inputContent), contained); + m_typeResolver->originalContainedType(inputContent), target); m_body += m_state.accumulatorVariableOut + u" = "_s; - if (contained->isReferenceType() && !isTrivial) { - const QQmlJSScope::ConstPtr genericContained = m_typeResolver->genericType(contained); + if (!isTrivial && target->isReferenceType()) { + const QQmlJSScope::ConstPtr genericContained = m_typeResolver->genericType(target); const QString inputConversion = inputContent.storedType()->isReferenceType() ? input : convertStored(inputContent.storedType(), genericContained, input); - if (contained->isComposite() && m_typeResolver->equals( + if (target->isComposite() && m_typeResolver->equals( m_state.accumulatorIn().storedType(), m_typeResolver->metaObjectType())) { m_body += conversion( genericContained, outputContent, @@ -2944,19 +2951,24 @@ void QQmlJSCodeGenerator::generate_As(int lhs) } else { m_body += conversion( genericContained, outputContent, - u'(' + metaObject(contained) + u")->cast("_s + inputConversion + u')'); + u'(' + metaObject(target) + u")->cast("_s + inputConversion + u')'); } m_body += u";\n"_s; return; } - if (m_typeResolver->equals(inputContent.storedType(), m_typeResolver->varType())) { - if (const auto target = m_typeResolver->extractNonVoidFromOptionalType(originalContent)) { - m_body += m_state.accumulatorVariableOut + u" = "_s; + if (m_typeResolver->registerIsStoredIn(inputContent, m_typeResolver->varType()) + || m_typeResolver->registerIsStoredIn(inputContent, m_typeResolver->jsPrimitiveType())) { + + const auto source = m_typeResolver->extractNonVoidFromOptionalType( + m_typeResolver->original(inputContent)); + + if (source && m_typeResolver->equals(source, target)) { m_body += input + u".metaType() == "_s + metaType(target) + u" ? " + conversion(inputContent, outputContent, input) - + u" : " + conversion(m_typeResolver->globalType(m_typeResolver->voidType()), - outputContent, QString()); + + u" : " + conversion( + m_typeResolver->globalType(m_typeResolver->voidType()), + outputContent, QString()); m_body += u";\n"_s; return; } @@ -2968,7 +2980,7 @@ void QQmlJSCodeGenerator::generate_As(int lhs) return; } - reject(u"unsupported type assertion"_s); + reject(u"non-trivial value type assertion"_s); } void QQmlJSCodeGenerator::generate_UNot() diff --git a/tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.qml b/tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.qml index 5ba8bd51ef..ee360d7142 100644 --- a/tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.qml +++ b/tests/auto/qml/qmlcppcodegen/data/getOptionalLookup.qml @@ -11,13 +11,6 @@ GOL_Object { property var v: Qt.point(5, 5) property var u: undefined - function f(input: bool) : var { - if (input) - return 0 - return Qt.point(2, 2) - } - - property int to1: root?.i property string to2: root?.s property GOL_Object to3: root?.childA @@ -27,8 +20,6 @@ GOL_Object { property int tv1: root.r?.bottom property int tv2: root.p?.y - property int tv3: (root.v as point)?.x - property var tv4: (root.u as rect)?.x property int te1: root?.e property int te2: GOL_Object?.V2 @@ -37,10 +28,7 @@ GOL_Object { property int tc1: root?.p.y property int tc2: root.r?.x - property int tc3: (root?.v as point)?.y + property var tc4: root?.childA?.s property var tc5: root.childA?.s - property var tc6: (root?.u as rect)?.height - property var tc7: (f(true) as point)?.x - property var tc8: (f(false) as point)?.x } diff --git a/tests/auto/qml/qmlcppcodegen/data/valueTypeCast.qml b/tests/auto/qml/qmlcppcodegen/data/valueTypeCast.qml index d1c9198cbd..a775773dda 100644 --- a/tests/auto/qml/qmlcppcodegen/data/valueTypeCast.qml +++ b/tests/auto/qml/qmlcppcodegen/data/valueTypeCast.qml @@ -1,9 +1,43 @@ -pragma Strict pragma ValueTypeBehavior: Addressable import QtQml QtObject { + id: root property rect r: Qt.rect(10, 20, 3, 4) property var v: r property real x: (v as rect).x + + function f(input: bool) : var { + if (input) + return 0 + return Qt.point(2, 2) + } + + property var vv: Qt.point(5, 5) + property var uu: undefined + + property int tv3: (root.vv as point)?.x + property var tv4: (root.uu as rect)?.x + property int tc3: (root?.vv as point)?.y + property var tc6: (root?.uu as rect)?.height + property var tc7: (f(true) as point)?.x + property var tc8: (f(false) as point)?.x + + property string greeting1 + property string greeting2 + + readonly property string defaultGreeting: "Default Greeting" + property QtObject o: QtObject { + id: o + property var customGreeting + function greet() : string { + return (o.customGreeting as string) ?? root.defaultGreeting + } + } + + Component.onCompleted: { + root.greeting1 = o.greet() + o.customGreeting = "Custom Greeting" + root.greeting2 = o.greet() + } } diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp index ccaffbcd72..dc062ef55e 100644 --- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp +++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp @@ -2123,7 +2123,6 @@ void tst_QmlCppCodegen::getOptionalLookup_data() // Value Types QTest::addRow("int on rect") << u"tv1"_s << QVariant(50); QTest::addRow("int on point") << u"tv2"_s << QVariant(-10); - QTest::addRow("int on variant as point") << u"tv3"_s << QVariant(5); QTest::addRow("int on undefined as point") << u"tv4"_s << QVariant(); // undefined // Enums @@ -2135,12 +2134,8 @@ void tst_QmlCppCodegen::getOptionalLookup_data() // Complex chains QTest::addRow("mixed 1") << u"tc1"_s << QVariant(-10); QTest::addRow("mixed 2") << u"tc2"_s << QVariant(0); - QTest::addRow("mixed 3") << u"tc3"_s << QVariant(5); QTest::addRow("early out 1") << u"tc4"_s << QVariant(); // undefined QTest::addRow("early out 2") << u"tc5"_s << QVariant(); // undefined - QTest::addRow("early out 3") << u"tc6"_s << QVariant(); // undefined - QTest::addRow("complex2") << u"tc7"_s << QVariant(); // undefined - QTest::addRow("complex3") << u"tc8"_s << QVariant(2); } void tst_QmlCppCodegen::getOptionalLookup() @@ -4778,6 +4773,16 @@ void tst_QmlCppCodegen::valueTypeBehavior() // If the binding throws an exception, the value doesn't change. QCOMPARE(o->property("x"), 10); + + QCOMPARE(o->property("tv3"), 5); + QCOMPARE(o->property("tc3"), 5); + QCOMPARE(o->property("tc6"), QVariant()); + QCOMPARE(o->property("tc7"), QVariant()); + QCOMPARE(o->property("tc8"), 2); + + // The default greeting is never applied because undefined can be coerced to string + QCOMPARE(o->property("greeting1"), QLatin1String("undefined")); + QCOMPARE(o->property("greeting2"), QLatin1String("Custom Greeting")); } }