Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package qt6-declarative for openSUSE:Factory
checked in at 2026-09-14 16:19:56
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/qt6-declarative (Old)
and /work/SRC/openSUSE:Factory/.qt6-declarative.new.1265 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "qt6-declarative"
Mon Sep 14 16:19:56 2026 rev:64 rq:1377514 version:6.11.2
Changes:
--------
--- /work/SRC/openSUSE:Factory/qt6-declarative/qt6-declarative.changes
2026-08-21 16:51:34.392241200 +0200
+++
/work/SRC/openSUSE:Factory/.qt6-declarative.new.1265/qt6-declarative.changes
2026-09-14 16:20:11.712053578 +0200
@@ -1,0 +2,6 @@
+Sat Sep 12 09:24:14 UTC 2026 - Christophe Marin <[email protected]>
+
+- Add upstream change (kde#508377)
+ * 0001-QML-engine-Correctly-compare-composites-when-multipl.patch
+
+-------------------------------------------------------------------
New:
----
0001-QML-engine-Correctly-compare-composites-when-multipl.patch
----------(New B)----------
New:- Add upstream change (kde#508377)
* 0001-QML-engine-Correctly-compare-composites-when-multipl.patch
----------(New E)----------
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ qt6-declarative.spec ++++++
--- /var/tmp/diff_new_pack.ZZlAhD/_old 2026-09-14 16:20:12.710095593 +0200
+++ /var/tmp/diff_new_pack.ZZlAhD/_new 2026-09-14 16:20:12.712095677 +0200
@@ -41,6 +41,8 @@
Source99: qt6-declarative-rpmlintrc
# PATCH-FIX-OPENSUSE
Patch0: 0001-qmlimportscanner-Include-module-versions-again.patch
+# PATCH-FIX-UPSTREAM
+Patch1: 0001-QML-engine-Correctly-compare-composites-when-multipl.patch
BuildRequires: memory-constraints
BuildRequires: pkgconfig
BuildRequires: python3-base
++++++ 0001-QML-engine-Correctly-compare-composites-when-multipl.patch ++++++
>From d0ba6d4b4acbd5c7adfb17507ca2721eb41f6750 Mon Sep 17 00:00:00 2001
From: Fabian Kosmale <[email protected]>
Date: Mon, 31 Aug 2026 16:54:58 +0200
Subject: [PATCH] QML engine: Correctly compare composites when multiple
engines are used
Since 6.11, we allow multiple types to be active for the same URL.
While we converted multiple places to handle this, we missed adjusting
the property validator. There, we just picked the last inserted property
cache, which can lead to validation failures.
Fix this by adding a fallback path which checks all potential matches
when the initial lookup would yield an error.
The same kind of check is also needed in qmlobject_can_qml_cast.
Amends 9cc23ca91c404f24fff3c36e8c9425ae674f341d.
Change compared to 6.12:
Dropped the isComposite check, which doesn't exist there. That causes
one potential extra unneeded lock acquisition, but only in a case
where'd we get an error anyway.
Fixes: QTBUG-149607
Change-Id: Ibd0df2325807e75b23056ebc0bacdfe877b002f3
Reviewed-by: Ulf Hermann <[email protected]>
(cherry picked from commit 94a76ed58629932f6d689c48ecf8d3132188923b)
Reviewed-by: Qt Cherry-pick Bot <[email protected]>
(cherry picked from commit 8c23f65ec4889f9ff7f609002a129698796de2b8)
---
src/qml/qml/qqmlglobal.cpp | 16 +++----
src/qml/qml/qqmlmetatype.cpp | 14 ++++++
src/qml/qml/qqmlmetatype_p.h | 5 ++
src/qml/qml/qqmlmetatypedata.cpp | 12 +++++
src/qml/qml/qqmlmetatypedata_p.h | 5 ++
src/qml/qml/qqmlpropertyvalidator.cpp | 28 +++++++++--
.../data/canQmlCastMultiEngine/Foo.qml | 3 ++
.../data/canQmlCastMultiEngine/Main.qml | 9 ++++
.../Consumer1.qml | 5 ++
.../Consumer2.qml | 5 ++
.../data/propertyValidatorMultiEngine/Foo.qml | 3 ++
.../qml/qqmllanguage/tst_qqmllanguage.cpp | 47 +++++++++++++++++++
12 files changed, 140 insertions(+), 12 deletions(-)
create mode 100644
tests/auto/qml/qqmllanguage/data/canQmlCastMultiEngine/Foo.qml
create mode 100644
tests/auto/qml/qqmllanguage/data/canQmlCastMultiEngine/Main.qml
create mode 100644
tests/auto/qml/qqmllanguage/data/propertyValidatorMultiEngine/Consumer1.qml
create mode 100644
tests/auto/qml/qqmllanguage/data/propertyValidatorMultiEngine/Consumer2.qml
create mode 100644
tests/auto/qml/qqmllanguage/data/propertyValidatorMultiEngine/Foo.qml
diff --git a/src/qml/qml/qqmlglobal.cpp b/src/qml/qml/qqmlglobal.cpp
index 3e11b86..e4d94e4 100644
--- a/src/qml/qml/qqmlglobal.cpp
+++ b/src/qml/qml/qqmlglobal.cpp
@@ -1112,9 +1112,9 @@ bool qmlobject_can_qml_cast(QObject *object, const
QQmlType &type)
// A non-composite type will always have a metaobject.
const QMetaObject *typeMetaObject = type.metaObject();
- const QQmlPropertyCache::ConstPtr typePropertyCache = typeMetaObject
- ? QQmlPropertyCache::ConstPtr()
- : QQmlMetaType::findPropertyCacheInCompositeTypes(type.typeId());
+ QVarLengthArray<QQmlPropertyCache::ConstPtr, 4> allPropertyCacheCandidates
=
+ typeMetaObject ? QVarLengthArray<QQmlPropertyCache::ConstPtr, 4>{}
+ :
QQmlMetaType::rawCompositePropertyCachesForType(type.typeId());
if (const QQmlData *ddata = ddata_for_cast(object)) {
for (const QQmlPropertyCache *propertyCache =
ddata->propertyCache.data(); propertyCache;
@@ -1134,17 +1134,17 @@ bool qmlobject_can_qml_cast(QObject *object, const
QQmlType &type)
// property caches to be unrelated but the types still
convertible.
// Multiple property caches can hold the same metaobject, for
example for
// versions of non-composite types.
- if (propertyCache == typePropertyCache.data())
+ if (allPropertyCacheCandidates.contains(propertyCache))
return true;
}
}
}
- // If nothing else works, we have to create the metaobjects.
+ // If nothing else works, we have to create the metaobjects (if we can).
+ if (!typeMetaObject && !allPropertyCacheCandidates.isEmpty())
+ typeMetaObject =
allPropertyCacheCandidates.first()->createMetaObject();
- return object->metaObject()->inherits(typeMetaObject
- ? typeMetaObject
- : (typePropertyCache ? typePropertyCache->createMetaObject() :
nullptr));
+ return object->metaObject()->inherits(typeMetaObject);
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index 173c21d..1703c99 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -1487,6 +1487,20 @@ QQmlPropertyCache::ConstPtr
QQmlMetaType::rawPropertyCacheForType(QMetaType meta
: QQmlPropertyCache::ConstPtr();
}
+/*!
+ * \internal
+ *
+ * Returns all candidate property caches for a composite
+ * metatype instead of only the last inserted one.
+ * compare rawPropertyCacheForType (which handles however also non-composites)
+ */
+QVarLengthArray<QQmlPropertyCache::ConstPtr, 4>
+QQmlMetaType::rawCompositePropertyCachesForType(QMetaType metaType)
+{
+ const QQmlMetaTypeDataPtr data;
+ return data->findPropertyCachesInCompositeTypes(metaType);
+}
+
/*!
* \internal
*
diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h
index 62b6cba..040a42d 100644
--- a/src/qml/qml/qqmlmetatype_p.h
+++ b/src/qml/qml/qqmlmetatype_p.h
@@ -23,6 +23,7 @@
#include <private/qtqmlglobal_p.h>
#include <QtCore/qtyperevision.h>
+#include <QtCore/qvarlengtharray.h>
QT_BEGIN_NAMESPACE
@@ -171,6 +172,10 @@ public:
static QQmlPropertyCache::ConstPtr rawPropertyCacheForType(
QMetaType metaType, QTypeRevision version);
+ // All property caches for a composite metatype, which may map to more
than one of them.
+ static QVarLengthArray<QQmlPropertyCache::ConstPtr, 4>
rawCompositePropertyCachesForType(
+ QMetaType metaType);
+
static bool canConvert(QObject *o, QMetaType metaType);
static bool canConvert(const QQmlPropertyCache::ConstPtr &from, QMetaType
metaType);
diff --git a/src/qml/qml/qqmlmetatypedata.cpp b/src/qml/qml/qqmlmetatypedata.cpp
index e72a3a9..41d7d8c 100644
--- a/src/qml/qml/qqmlmetatypedata.cpp
+++ b/src/qml/qml/qqmlmetatypedata.cpp
@@ -257,6 +257,18 @@ QQmlPropertyCache::ConstPtr
QQmlMetaTypeData::findPropertyCacheInCompositeTypes(
: propertyCacheForPotentialInlineComponentType(t, iter);
}
+QVarLengthArray<QQmlPropertyCache::ConstPtr, 4>
+QQmlMetaTypeData::findPropertyCachesInCompositeTypes(QMetaType t) const
+{
+ QVarLengthArray<QQmlPropertyCache::ConstPtr, 4> result;
+ const auto [begin, end] = compositeTypes.equal_range(t.iface());
+ for (auto iter = begin; iter != end; ++iter) {
+ if (auto cache = propertyCacheForPotentialInlineComponentType(t, iter))
+ result.append(std::move(cache));
+ }
+ return result;
+}
+
void QQmlMetaTypeData::clearCompositeTypes()
{
// Unregister all remaining composite types.
diff --git a/src/qml/qml/qqmlmetatypedata_p.h b/src/qml/qml/qqmlmetatypedata_p.h
index ce13c40..95c60a1 100644
--- a/src/qml/qml/qqmlmetatypedata_p.h
+++ b/src/qml/qml/qqmlmetatypedata_p.h
@@ -23,6 +23,7 @@
#include <private/qqmlvaluetype_p.h>
#include <QtCore/qset.h>
+#include <QtCore/qvarlengtharray.h>
#include <QtCore/qvector.h>
QT_BEGIN_NAMESPACE
@@ -119,6 +120,10 @@ struct QQmlMetaTypeData
QQmlPropertyCache::ConstPtr propertyCache(const QQmlType &type,
QTypeRevision version);
QQmlPropertyCache::ConstPtr findPropertyCacheInCompositeTypes(QMetaType t)
const;
+ // Same, but returns all matches rather than only the last inserted one.
+ QVarLengthArray<QQmlPropertyCache::ConstPtr, 4>
findPropertyCachesInCompositeTypes(
+ QMetaType t) const;
+
static QQmlPropertyCache::ConstPtr
propertyCacheForPotentialInlineComponentType(
QMetaType t, const
QQmlMetaTypeData::CompositeTypes::const_iterator &iter);
diff --git a/src/qml/qml/qqmlpropertyvalidator.cpp
b/src/qml/qml/qqmlpropertyvalidator.cpp
index c252f47..1acbb22 100644
--- a/src/qml/qml/qqmlpropertyvalidator.cpp
+++ b/src/qml/qml/qqmlpropertyvalidator.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR
GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant
+#include "qqmlmetatype_p.h"
#include "qqmlpropertyvalidator_p.h"
#include <private/qqmlcustomparser_p.h>
@@ -781,10 +782,29 @@ QQmlError
QQmlPropertyValidator::validateObjectBinding(const QQmlPropertyData *p
// Will be true if the assigned type inherits propertyMetaObject
// Determine isAssignable value
bool isAssignable = false;
- QQmlPropertyCache::ConstPtr c =
propertyCaches.at(binding->value.objectIndex);
- while (c && !isAssignable) {
- isAssignable |= c == propertyMetaObject;
- c = c->parent();
+ QQmlPropertyCache::ConstPtr source =
propertyCaches.at(binding->value.objectIndex);
+
+ const auto inheritsFrom = [&](const QQmlPropertyCache::ConstPtr
&target) {
+ for (QQmlPropertyCache::ConstPtr c = source; c; c =
c->parent()) {
+ if (c == target)
+ return true;
+ }
+ return false;
+ };
+
+ isAssignable = inheritsFrom(propertyMetaObject);
+
+ if (!isAssignable) {
+ // A single (composite) metatype can map to multiple property
caches when
+ // there are multiple engines; rawPropertyCacheForType only
returns one of
+ // them. For non-composite types this yields an empty list and
is a no-op.
+ const auto candidates =
QQmlMetaType::rawCompositePropertyCachesForType(propType);
+ for (const auto &candidate : candidates) {
+ if (inheritsFrom(candidate)) {
+ isAssignable = true;
+ break;
+ }
+ }
}
if (!isAssignable) {
diff --git a/tests/auto/qml/qqmllanguage/data/canQmlCastMultiEngine/Foo.qml
b/tests/auto/qml/qqmllanguage/data/canQmlCastMultiEngine/Foo.qml
new file mode 100644
index 0000000..8fc36a4
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/canQmlCastMultiEngine/Foo.qml
@@ -0,0 +1,3 @@
+import QtQml
+
+QtObject {}
diff --git a/tests/auto/qml/qqmllanguage/data/canQmlCastMultiEngine/Main.qml
b/tests/auto/qml/qqmllanguage/data/canQmlCastMultiEngine/Main.qml
new file mode 100644
index 0000000..1470151
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/canQmlCastMultiEngine/Main.qml
@@ -0,0 +1,9 @@
+import QtQml
+
+QtObject {
+ property Foo theFoo: Foo {}
+
+ function check(foo: Foo) : bool {
+ return foo !== null;
+ }
+}
diff --git
a/tests/auto/qml/qqmllanguage/data/propertyValidatorMultiEngine/Consumer1.qml
b/tests/auto/qml/qqmllanguage/data/propertyValidatorMultiEngine/Consumer1.qml
new file mode 100644
index 0000000..80de08e
--- /dev/null
+++
b/tests/auto/qml/qqmllanguage/data/propertyValidatorMultiEngine/Consumer1.qml
@@ -0,0 +1,5 @@
+import QtQml
+
+QtObject {
+ property Foo f: Foo {}
+}
diff --git
a/tests/auto/qml/qqmllanguage/data/propertyValidatorMultiEngine/Consumer2.qml
b/tests/auto/qml/qqmllanguage/data/propertyValidatorMultiEngine/Consumer2.qml
new file mode 100644
index 0000000..80de08e
--- /dev/null
+++
b/tests/auto/qml/qqmllanguage/data/propertyValidatorMultiEngine/Consumer2.qml
@@ -0,0 +1,5 @@
+import QtQml
+
+QtObject {
+ property Foo f: Foo {}
+}
diff --git
a/tests/auto/qml/qqmllanguage/data/propertyValidatorMultiEngine/Foo.qml
b/tests/auto/qml/qqmllanguage/data/propertyValidatorMultiEngine/Foo.qml
new file mode 100644
index 0000000..8fc36a4
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/propertyValidatorMultiEngine/Foo.qml
@@ -0,0 +1,3 @@
+import QtQml
+
+QtObject {}
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 4678949..8f28b87 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -298,6 +298,8 @@ private slots:
void instanceof();
void instanceofMultiEngine();
void instanceofMultiEngineInlineComponent();
+ void propertyValidatorMultiEngine();
+ void canQmlCastMultiEngine();
void concurrentLoadQmlDir();
@@ -6012,6 +6014,51 @@ void
tst_qqmllanguage::instanceofMultiEngineInlineComponent()
QVERIFY(result2.toBool());
}
+// verify that the property validator does not get confused when
+// multiple engines end up registering the same type, leading to
+// multiple entries in compositeTypes
+void tst_qqmllanguage::propertyValidatorMultiEngine()
+{
+ QQmlEngine engine1;
+ QQmlComponent c1(&engine1,
testFileUrl("propertyValidatorMultiEngine/Consumer1.qml"));
+ QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
+
+ QQmlEngine engine2;
+ QQmlComponent c2(&engine2,
testFileUrl("propertyValidatorMultiEngine/Consumer1.qml"));
+ QVERIFY2(c2.isReady(), qPrintable(c2.errorString()));
+
+ QQmlComponent c3(&engine1,
testFileUrl("propertyValidatorMultiEngine/Consumer2.qml"));
+ QVERIFY2(c3.isReady(), qPrintable(c3.errorString()));
+}
+
+// Verify that qmlobject_can_qml_cast() still recognizes a composite type when
+// multiple engines have loaded the same QML file
+void tst_qqmllanguage::canQmlCastMultiEngine()
+{
+ const QUrl url = testFileUrl("canQmlCastMultiEngine/Main.qml");
+
+ QQmlEngine engine1;
+ QQmlComponent c1(&engine1, url);
+ QVERIFY2(c1.isReady(), qPrintable(c1.errorString()));
+ QScopedPointer<QObject> obj1(c1.create());
+ QVERIFY(obj1);
+
+ // A second engine loads the same file and registers another compilation
unit
+ // (and thus another property cache) for Foo.
+ QQmlEngine engine2;
+ QQmlComponent c2(&engine2, url);
+ QVERIFY2(c2.isReady(), qPrintable(c2.errorString()));
+ QScopedPointer<QObject> obj2(c2.create());
+ QVERIFY(obj2);
+
+ // Call the typed function on engine1's object with engine1's Foo instance.
+ QQmlExpression expr(engine1.contextForObject(obj1.data()), obj1.data(),
+ QStringLiteral("check(theFoo)"));
+ const QVariant result = expr.evaluate();
+ QVERIFY2(!expr.hasError(), qPrintable(expr.error().description()));
+ QVERIFY(result.toBool());
+}
+
void tst_qqmllanguage::concurrentLoadQmlDir()
{
ThreadedTestHTTPServer server(dataDirectory());
--
2.55.0