QmlCompiler: Fix conditions around as casts

We can only generate an as-cast from an optional value type if we know
that the optional type is actually the requested one. Otherwise we have
to reject for now. We might add more logic here in a further iteration,
and process more complicated type assertions. However, we should pick
such logic back to 6.6.

Amends commit 05f56d7c78.

Pick-to: 6.7 6.6
Change-Id: I37fc1b6018bfb0663e5ce4fd80084c7d13c6d3e3
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io>
This commit is contained in:
Ulf Hermann 2024-02-16 15:08:20 +01:00
parent 49dc279bdc
commit e2611e8ee9
4 changed files with 70 additions and 31 deletions

View File

@ -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()

View File

@ -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
}

View File

@ -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()
}
}

View File

@ -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"));
}
}