Implement proper C++ type numeric promotion for QVariant comparisons

Previously, QVariant would try to convert one operand to the other's
type, which would produce unexpected results: the results would depend
in the order of the operands and whether there was data loss in the
conversion. In addition, ordering comparisons were only done with signed
values, yielding other unexpected results, like
   QVariant(LLONG_MAX / 2) < QVariant(Q_UINT64_C(0)).

Instead, try to obey the C++ standard rules for type promotion in
expressions. Our code is a little simpler than the standard would seem
to require since we know some more details from the ABI.

[ChangeLog][Important Behavior Changes][QVariant] QVariant now obeys the
C++ type promotion rules when comparing numeric types (integrals, float
and double), including the fact that unsigned comparisons are preferred
for types of the same rank (that is, now QVariant(-1) > QVariant(0U)).

Task-number: QTBUG-42722
Change-Id: Ie7b19073dcb45485354710975e561bcdb1a753f1
Reviewed-by: Jędrzej Nowacki <jedrzej.nowacki@theqtcompany.com>
This commit is contained in:
Thiago Macieira 2014-11-17 16:56:27 -08:00
parent d41834b953
commit cf475b91ae
3 changed files with 426 additions and 9 deletions

View File

@ -63,6 +63,7 @@ template <typename T>
inline Q_DECL_CONSTEXPR int qMetaTypeId();
// F is a tuple: (QMetaType::TypeName, QMetaType::TypeNameID, RealType)
// ### Qt6: reorder the types to match the C++ integral type ranking
#define QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(F)\
F(Void, 43, void) \
F(Bool, 1, bool) \

View File

@ -3150,6 +3150,92 @@ static bool qIsFloatingPoint(uint tp)
return tp == QVariant::Double || tp == QMetaType::Float;
}
static int normalizeLowerRanks(uint tp)
{
if (tp == QVariant::Bool
|| tp == QVariant::Char || tp == QMetaType::SChar || tp == QMetaType::UChar
|| tp == QMetaType::Short || tp == QMetaType::UShort)
return QVariant::Int;
return tp;
}
static int normalizeLong(uint tp)
{
const uint IntType = sizeof(long) == sizeof(int) ? QVariant::Int : QVariant::LongLong;
const uint UIntType = sizeof(ulong) == sizeof(uint) ? QVariant::UInt : QVariant::ULongLong;
return tp == QMetaType::Long ? IntType :
tp == QMetaType::ULong ? UIntType : tp;
}
static int numericTypePromotion(uint t1, uint t2)
{
Q_ASSERT(qIsNumericType(t1));
Q_ASSERT(qIsNumericType(t2));
// C++ integral ranks: (4.13 Integer conversion rank [conv.rank])
// bool < signed char < short < int < long < long long
// unsigneds have the same rank as their signed counterparts
// C++ integral promotion rules (4.5 Integral Promotions [conv.prom])
// - any type with rank less than int can be converted to int or unsigned int
// 5 Expressions [expr] paragraph 9:
// - if either operand is double, the other shall be converted to double
// - " " float, " " " float
// - if both operands have the same type, no further conversion is needed.
// - if both are signed or if both are unsigned, convert to the one with highest rank
// - if the unsigned has higher or same rank, convert the signed to the unsigned one
// - if the signed can represent all values of the unsigned, convert to the signed
// - otherwise, convert to the unsigned corresponding to the rank of the signed
// floating point: we deviate from the C++ standard by always using qreal
if (qIsFloatingPoint(t1) || qIsFloatingPoint(t2))
return QMetaType::QReal;
// integral rules:
// for all platforms we support, int can always hold the values of lower-ranked types
t1 = normalizeLowerRanks(t1);
t2 = normalizeLowerRanks(t2);
// normalize long / ulong: in all platforms we run, they're either the same as int or as long long
t1 = normalizeLong(t1);
t2 = normalizeLong(t2);
// implement the other rules
// the four possibilities are Int, UInt, LongLong and ULongLong
// if any of the two is ULongLong, then it wins (highest rank, unsigned)
// otherwise, if one of the two is LongLong, then the other is either LongLong too or lower-ranked
// otherwise, if one of the two is UInt, then the other is either UInt too or Int
if (t1 == QVariant::ULongLong || t2 == QVariant::ULongLong)
return QVariant::ULongLong;
if (t1 == QVariant::LongLong || t2 == QVariant::LongLong)
return QVariant::LongLong;
if (t1 == QVariant::UInt || t2 == QVariant::UInt)
return QVariant::UInt;
return QVariant::Int;
}
static int integralCompare(uint promotedType, const QVariant::Private *d1, const QVariant::Private *d2)
{
// use toLongLong to retrieve the data, it gets us all the bits
bool ok;
qlonglong l1 = qConvertToNumber(d1, &ok);
Q_ASSERT(ok);
qlonglong l2 = qConvertToNumber(d2, &ok);
Q_ASSERT(ok);
if (promotedType == QVariant::Int)
return int(l1) < int(l2) ? -1 : int(l1) == int(l2) ? 0 : 1;
if (promotedType == QVariant::UInt)
return uint(l1) < uint(l2) ? -1 : uint(l1) == uint(l2) ? 0 : 1;
if (promotedType == QVariant::LongLong)
return l1 < l2 ? -1 : l1 == l2 ? 0 : 1;
if (promotedType == QVariant::ULongLong)
return qulonglong(l1) < qulonglong(l2) ? -1 : qulonglong(l1) == qulonglong(l2) ? 0 : 1;
Q_UNREACHABLE();
return 0;
}
/*!
\internal
*/
@ -3159,10 +3245,11 @@ bool QVariant::cmp(const QVariant &v) const
QVariant v2 = v;
if (d.type != v2.d.type) {
if (qIsNumericType(d.type) && qIsNumericType(v.d.type)) {
if (qIsFloatingPoint(d.type) || qIsFloatingPoint(v.d.type))
uint promotedType = numericTypePromotion(v1.d.type, v2.d.type);
if (promotedType == QMetaType::QReal)
return qFuzzyCompare(toReal(), v.toReal());
else
return toLongLong() == v.toLongLong();
return integralCompare(promotedType, &v1.d, &v2.d) == 0;
}
if (v2.canConvert(v1.d.type)) {
if (!v2.convert(v1.d.type))
@ -3191,6 +3278,16 @@ int QVariant::compare(const QVariant &v) const
return 0;
QVariant v1 = *this;
QVariant v2 = v;
// try numerics first, with C++ type promotion rules (no conversion)
if (qIsNumericType(v1.d.type) && qIsNumericType(v2.d.type)) {
uint promotedType = numericTypePromotion(v1.d.type, v2.d.type);
if (promotedType == QMetaType::QReal)
return v1.toReal() < v2.toReal() ? -1 : 1;
else
return integralCompare(promotedType, &v1.d, &v2.d);
}
if (v1.d.type != v2.d.type) {
// if both types differ, try to convert
if (v2.canConvert(v1.d.type)) {
@ -3212,18 +3309,16 @@ int QVariant::compare(const QVariant &v) const
}
return r;
}
// did we end up with two numerics? If so, restart
if (qIsNumericType(v1.d.type) && qIsNumericType(v2.d.type))
return v1.compare(v2);
}
if (v1.d.type >= QMetaType::User) {
int result;
if (QMetaType::compare(QT_PREPEND_NAMESPACE(constData(d)), QT_PREPEND_NAMESPACE(constData(v2.d)), d.type, &result))
return result;
}
if (qIsNumericType(v1.d.type)) {
if (qIsFloatingPoint(v1.d.type))
return v1.toReal() < v2.toReal() ? -1 : 1;
else
return v1.toLongLong() < v2.toLongLong() ? -1 : 1;
}
switch (v1.d.type) {
case QVariant::Date:
return v1.toDate() < v2.toDate() ? -1 : 1;

View File

@ -170,8 +170,9 @@ private slots:
void operator_eq_eq_data();
void operator_eq_eq();
void operator_eq_eq_rhs();
void compareNumbers_data() const;
void compareNumbers() const;
void typeName_data();
void typeName();
@ -1591,6 +1592,326 @@ void tst_QVariant::operator_eq_eq_rhs()
#endif
}
void tst_QVariant::compareNumbers_data() const
{
typedef signed char schar;
QTest::addColumn<QVariant>("v1");
QTest::addColumn<QVariant>("v2");
QTest::addColumn<int>("expected");
// sanity checking: same types
QTest::newRow("bool1") << QVariant(false) << QVariant(false) << 0;
QTest::newRow("bool2") << QVariant(true) << QVariant(true) << 0;
QTest::newRow("bool3") << QVariant(false) << QVariant(true) << -1;
QTest::newRow("bool4") << QVariant(true) << QVariant(false) << +1;
QTest::newRow("char1") << qVariantFromValue(char(0)) << qVariantFromValue(char(0)) << 0;
QTest::newRow("char2") << qVariantFromValue(CHAR_MAX) << qVariantFromValue(CHAR_MAX) << 0;
QTest::newRow("char3") << qVariantFromValue(CHAR_MIN) << qVariantFromValue(CHAR_MIN) << 0;
QTest::newRow("char4") << qVariantFromValue(CHAR_MIN) << qVariantFromValue(CHAR_MAX) << -1;
QTest::newRow("char5") << qVariantFromValue(CHAR_MAX) << qVariantFromValue(CHAR_MIN) << +1;
QTest::newRow("schar1") << qVariantFromValue(schar(0)) << qVariantFromValue(schar(0)) << 0;
QTest::newRow("schar2") << qVariantFromValue(SCHAR_MAX) << qVariantFromValue(SCHAR_MAX) << 0;
QTest::newRow("schar3") << qVariantFromValue(SCHAR_MIN) << qVariantFromValue(SCHAR_MIN) << 0;
QTest::newRow("schar4") << qVariantFromValue(SCHAR_MIN) << qVariantFromValue(SCHAR_MAX) << -1;
QTest::newRow("schar5") << qVariantFromValue(SCHAR_MAX) << qVariantFromValue(SCHAR_MIN) << +1;
QTest::newRow("uchar1") << qVariantFromValue(uchar(0)) << qVariantFromValue(uchar(0)) << 0;
QTest::newRow("uchar2") << qVariantFromValue(UCHAR_MAX) << qVariantFromValue(UCHAR_MAX) << 0;
QTest::newRow("uchar3") << qVariantFromValue(uchar(0)) << qVariantFromValue(UCHAR_MAX) << -1;
QTest::newRow("uchar4") << qVariantFromValue(UCHAR_MAX) << qVariantFromValue(uchar(0)) << +1;
QTest::newRow("short1") << qVariantFromValue(short(0)) << qVariantFromValue(short(0)) << 0;
QTest::newRow("short2") << qVariantFromValue(SHRT_MAX) << qVariantFromValue(SHRT_MAX) << 0;
QTest::newRow("short3") << qVariantFromValue(SHRT_MIN) << qVariantFromValue(SHRT_MIN) << 0;
QTest::newRow("short4") << qVariantFromValue(SHRT_MIN) << qVariantFromValue(SHRT_MAX) << -1;
QTest::newRow("short5") << qVariantFromValue(SHRT_MAX) << qVariantFromValue(SHRT_MIN) << +1;
QTest::newRow("ushort1") << qVariantFromValue(ushort(0)) << qVariantFromValue(ushort(0)) << 0;
QTest::newRow("ushort2") << qVariantFromValue(USHRT_MAX) << qVariantFromValue(USHRT_MAX) << 0;
QTest::newRow("ushort3") << qVariantFromValue(ushort(0)) << qVariantFromValue(USHRT_MAX) << -1;
QTest::newRow("ushort4") << qVariantFromValue(USHRT_MAX) << qVariantFromValue(ushort(0)) << +1;
QTest::newRow("int1") << qVariantFromValue(int(0)) << qVariantFromValue(int(0)) << 0;
QTest::newRow("int2") << qVariantFromValue(INT_MAX) << qVariantFromValue(INT_MAX) << 0;
QTest::newRow("int3") << qVariantFromValue(INT_MIN) << qVariantFromValue(INT_MIN) << 0;
QTest::newRow("int4") << qVariantFromValue(INT_MIN) << qVariantFromValue(INT_MAX) << -1;
QTest::newRow("int5") << qVariantFromValue(INT_MAX) << qVariantFromValue(INT_MIN) << +1;
QTest::newRow("uint1") << qVariantFromValue(uint(0)) << qVariantFromValue(uint(0)) << 0;
QTest::newRow("uint2") << qVariantFromValue(UINT_MAX) << qVariantFromValue(UINT_MAX) << 0;
QTest::newRow("uint3") << qVariantFromValue(uint(0)) << qVariantFromValue(UINT_MAX) << -1;
QTest::newRow("uint4") << qVariantFromValue(UINT_MAX) << qVariantFromValue(uint(0)) << +1;
QTest::newRow("long1") << qVariantFromValue(long(0)) << qVariantFromValue(long(0)) << 0;
QTest::newRow("long2") << qVariantFromValue(LONG_MAX) << qVariantFromValue(LONG_MAX) << 0;
QTest::newRow("long3") << qVariantFromValue(LONG_MIN) << qVariantFromValue(LONG_MIN) << 0;
QTest::newRow("long4") << qVariantFromValue(LONG_MIN) << qVariantFromValue(LONG_MAX) << -1;
QTest::newRow("long5") << qVariantFromValue(LONG_MAX) << qVariantFromValue(LONG_MIN) << +1;
QTest::newRow("ulong1") << qVariantFromValue(ulong(0)) << qVariantFromValue(ulong(0)) << 0;
QTest::newRow("ulong2") << qVariantFromValue(ULONG_MAX) << qVariantFromValue(ULONG_MAX) << 0;
QTest::newRow("ulong3") << qVariantFromValue(ulong(0)) << qVariantFromValue(ULONG_MAX) << -1;
QTest::newRow("ulong4") << qVariantFromValue(ULONG_MAX) << qVariantFromValue(ulong(0)) << +1;
QTest::newRow("llong1") << qVariantFromValue(qlonglong(0)) << qVariantFromValue(qlonglong(0)) << 0;
QTest::newRow("llong2") << qVariantFromValue(LLONG_MAX) << qVariantFromValue(LLONG_MAX) << 0;
QTest::newRow("llong3") << qVariantFromValue(LLONG_MIN) << qVariantFromValue(LLONG_MIN) << 0;
QTest::newRow("llong4") << qVariantFromValue(LLONG_MIN) << qVariantFromValue(LLONG_MAX) << -1;
QTest::newRow("llong5") << qVariantFromValue(LLONG_MAX) << qVariantFromValue(LLONG_MIN) << +1;
QTest::newRow("ullong1") << qVariantFromValue(qulonglong(0)) << qVariantFromValue(qulonglong(0)) << 0;
QTest::newRow("ullong2") << qVariantFromValue(ULLONG_MAX) << qVariantFromValue(ULLONG_MAX) << 0;
QTest::newRow("ullong3") << qVariantFromValue(qulonglong(0)) << qVariantFromValue(ULLONG_MAX) << -1;
QTest::newRow("ullong4") << qVariantFromValue(ULLONG_MAX) << qVariantFromValue(qulonglong(0)) << +1;
QTest::newRow("float1") << qVariantFromValue(0.f) << qVariantFromValue(0.f) << 0;
QTest::newRow("float2") << qVariantFromValue(-1.f) << qVariantFromValue(0.f) << -1;
QTest::newRow("float3") << qVariantFromValue(0.f) << qVariantFromValue(-1.f) << +1;
QTest::newRow("float4") << qVariantFromValue(-float(qInf())) << qVariantFromValue(0.f) << -1;
QTest::newRow("float5") << qVariantFromValue(0.f) << qVariantFromValue(-float(qInf())) << +1;
QTest::newRow("double1") << qVariantFromValue(0.) << qVariantFromValue(0.) << 0;
QTest::newRow("double2") << qVariantFromValue(-1.) << qVariantFromValue(0.) << -1;
QTest::newRow("double3") << qVariantFromValue(0.) << qVariantFromValue(-1.) << +1;
QTest::newRow("double4") << qVariantFromValue(-qInf()) << qVariantFromValue(0.) << -1;
QTest::newRow("double5") << qVariantFromValue(0.) << qVariantFromValue(-qInf()) << +1;
// mixed comparisons
// fp + fp
QTest::newRow("float+double1") << qVariantFromValue(0.f) << qVariantFromValue(0.) << 0;
QTest::newRow("float+double2") << qVariantFromValue(-1.f) << qVariantFromValue(0.) << -1;
QTest::newRow("float+double3") << qVariantFromValue(0.f) << qVariantFromValue(-1.) << +1;
QTest::newRow("float+double4") << qVariantFromValue(-float(qInf())) << qVariantFromValue(0.) << -1;
QTest::newRow("float+double5") << qVariantFromValue(0.f) << qVariantFromValue(-qInf()) << +1;
// fp + int
QTest::newRow("float+int1") << qVariantFromValue(0.f) << qVariantFromValue(0) << 0;
QTest::newRow("double+int1") << qVariantFromValue(0.) << qVariantFromValue(0) << 0;
QTest::newRow("float+int2") << qVariantFromValue(-1.f) << qVariantFromValue(0) << -1;
QTest::newRow("double+int2") << qVariantFromValue(-1.) << qVariantFromValue(0) << -1;
QTest::newRow("float+int3") << qVariantFromValue(0.f) << qVariantFromValue(-1) << +1;
QTest::newRow("double+int3") << qVariantFromValue(0.) << qVariantFromValue(-1) << +1;
QTest::newRow("float+int4") << qVariantFromValue(1.5f) << qVariantFromValue(1) << +1;
QTest::newRow("double+int4") << qVariantFromValue(1.5) << qVariantFromValue(1) << +1;
// fp + uint
QTest::newRow("float+uint1") << qVariantFromValue(0.f) << qVariantFromValue(0U) << 0;
QTest::newRow("double+uint1") << qVariantFromValue(0.) << qVariantFromValue(0U) << 0;
QTest::newRow("float+uint2") << qVariantFromValue(-1.f) << qVariantFromValue(0U) << -1;
QTest::newRow("double+uint2") << qVariantFromValue(-1.) << qVariantFromValue(0U) << -1;
QTest::newRow("float+uint3") << qVariantFromValue(0.f) << qVariantFromValue(1U) << -1;
QTest::newRow("double+uint3") << qVariantFromValue(0.) << qVariantFromValue(1U) << -1;
QTest::newRow("float+uint4") << qVariantFromValue(1.5f) << qVariantFromValue(1U) << +1;
QTest::newRow("double+uint4") << qVariantFromValue(1.5) << qVariantFromValue(1U) << +1;
// lower ranked + int
QTest::newRow("bool+int1") << qVariantFromValue(false) << qVariantFromValue(0) << 0;
QTest::newRow("bool+int2") << qVariantFromValue(false) << qVariantFromValue(1) << -1;
QTest::newRow("bool+int3") << qVariantFromValue(true) << qVariantFromValue(0) << +1;
QTest::newRow("bool+int4") << qVariantFromValue(true) << qVariantFromValue(1) << 0;
QTest::newRow("bool+int5") << qVariantFromValue(true) << qVariantFromValue(2) << -1;
QTest::newRow("char+int1") << qVariantFromValue(char(0)) << qVariantFromValue(0) << 0;
QTest::newRow("char+int2") << qVariantFromValue(char(0)) << qVariantFromValue(1) << -1;
QTest::newRow("char+int3") << qVariantFromValue(char(1)) << qVariantFromValue(0) << +1;
QTest::newRow("char+int4") << qVariantFromValue(char(1)) << qVariantFromValue(1) << 0;
if (std::numeric_limits<char>::is_signed) {
QTest::newRow("char+int5") << qVariantFromValue(char(-1)) << qVariantFromValue(0) << -1;
QTest::newRow("char+int6") << qVariantFromValue(char(-1)) << qVariantFromValue(-1) << 0;
}
QTest::newRow("schar+int1") << qVariantFromValue(schar(0)) << qVariantFromValue(0) << 0;
QTest::newRow("schar+int2") << qVariantFromValue(schar(0)) << qVariantFromValue(1) << -1;
QTest::newRow("schar+int3") << qVariantFromValue(schar(1)) << qVariantFromValue(0) << +1;
QTest::newRow("schar+int4") << qVariantFromValue(schar(1)) << qVariantFromValue(1) << 0;
QTest::newRow("schar+int5") << qVariantFromValue(schar(-1)) << qVariantFromValue(0) << -1;
QTest::newRow("schar+int6") << qVariantFromValue(schar(-1)) << qVariantFromValue(-1) << 0;
QTest::newRow("uchar+int1") << qVariantFromValue(uchar(0)) << qVariantFromValue(0) << 0;
QTest::newRow("uchar+int2") << qVariantFromValue(uchar(0)) << qVariantFromValue(1) << -1;
QTest::newRow("uchar+int3") << qVariantFromValue(uchar(1)) << qVariantFromValue(0) << +1;
QTest::newRow("uchar+int4") << qVariantFromValue(uchar(1)) << qVariantFromValue(1) << 0;
QTest::newRow("short+int1") << qVariantFromValue(short(0)) << qVariantFromValue(0) << 0;
QTest::newRow("short+int2") << qVariantFromValue(short(0)) << qVariantFromValue(1) << -1;
QTest::newRow("short+int3") << qVariantFromValue(short(1)) << qVariantFromValue(0) << +1;
QTest::newRow("short+int4") << qVariantFromValue(short(1)) << qVariantFromValue(1) << 0;
QTest::newRow("short+int5") << qVariantFromValue(short(-1)) << qVariantFromValue(0) << -1;
QTest::newRow("short+int6") << qVariantFromValue(short(-1)) << qVariantFromValue(-1) << 0;
QTest::newRow("ushort+int1") << qVariantFromValue(ushort(0)) << qVariantFromValue(0) << 0;
QTest::newRow("ushort+int2") << qVariantFromValue(ushort(0)) << qVariantFromValue(1) << -1;
QTest::newRow("ushort+int3") << qVariantFromValue(ushort(1)) << qVariantFromValue(0) << +1;
QTest::newRow("ushort+int4") << qVariantFromValue(ushort(1)) << qVariantFromValue(1) << 0;
// lower ranked + uint (without sign change)
QTest::newRow("bool+uint1") << qVariantFromValue(false) << qVariantFromValue(0U) << 0;
QTest::newRow("bool+uint2") << qVariantFromValue(false) << qVariantFromValue(1U) << -1;
QTest::newRow("bool+uint3") << qVariantFromValue(true) << qVariantFromValue(0U) << +1;
QTest::newRow("bool+uint4") << qVariantFromValue(true) << qVariantFromValue(1U) << 0;
QTest::newRow("bool+uint5") << qVariantFromValue(true) << qVariantFromValue(2U) << -1;
QTest::newRow("char+uint1") << qVariantFromValue(char(0)) << qVariantFromValue(0U) << 0;
QTest::newRow("char+uint2") << qVariantFromValue(char(0)) << qVariantFromValue(1U) << -1;
QTest::newRow("char+uint3") << qVariantFromValue(char(1)) << qVariantFromValue(0U) << +1;
QTest::newRow("char+uint4") << qVariantFromValue(char(1)) << qVariantFromValue(1U) << 0;
QTest::newRow("schar+uint1") << qVariantFromValue(schar(0)) << qVariantFromValue(0U) << 0;
QTest::newRow("schar+uint2") << qVariantFromValue(schar(0)) << qVariantFromValue(1U) << -1;
QTest::newRow("schar+uint3") << qVariantFromValue(schar(1)) << qVariantFromValue(0U) << +1;
QTest::newRow("schar+uint4") << qVariantFromValue(schar(1)) << qVariantFromValue(1U) << 0;
QTest::newRow("uchar+uint1") << qVariantFromValue(uchar(0)) << qVariantFromValue(0U) << 0;
QTest::newRow("uchar+uint2") << qVariantFromValue(uchar(0)) << qVariantFromValue(1U) << -1;
QTest::newRow("uchar+uint3") << qVariantFromValue(uchar(1)) << qVariantFromValue(0U) << +1;
QTest::newRow("uchar+uint4") << qVariantFromValue(uchar(1)) << qVariantFromValue(1U) << 0;
QTest::newRow("short+uint1") << qVariantFromValue(short(0)) << qVariantFromValue(0U) << 0;
QTest::newRow("short+uint2") << qVariantFromValue(short(0)) << qVariantFromValue(1U) << -1;
QTest::newRow("short+uint3") << qVariantFromValue(short(1)) << qVariantFromValue(0U) << +1;
QTest::newRow("short+uint4") << qVariantFromValue(short(1)) << qVariantFromValue(1U) << 0;
QTest::newRow("ushort+uint1") << qVariantFromValue(ushort(0)) << qVariantFromValue(0U) << 0;
QTest::newRow("ushort+uint2") << qVariantFromValue(ushort(0)) << qVariantFromValue(1U) << -1;
QTest::newRow("ushort+uint3") << qVariantFromValue(ushort(1)) << qVariantFromValue(0U) << +1;
QTest::newRow("ushort+uint4") << qVariantFromValue(ushort(1)) << qVariantFromValue(1U) << 0;
// int + qlonglong
QTest::newRow("int+qlonglong1") << qVariantFromValue(0) << qVariantFromValue(Q_INT64_C(0)) << 0;
QTest::newRow("int+qlonglong2") << qVariantFromValue(1) << qVariantFromValue(Q_INT64_C(0)) << +1;
QTest::newRow("int+qlonglong3") << qVariantFromValue(0) << qVariantFromValue(Q_INT64_C(1)) << -1;
QTest::newRow("int+qlonglong4") << qVariantFromValue(1) << qVariantFromValue(Q_INT64_C(1)) << 0;
QTest::newRow("int+qlonglong5") << qVariantFromValue(0) << qVariantFromValue(Q_INT64_C(-1)) << +1;
QTest::newRow("int+qlonglong6") << qVariantFromValue(-1) << qVariantFromValue(Q_INT64_C(0)) << -1;
QTest::newRow("int+qlonglong7") << qVariantFromValue(-1) << qVariantFromValue(Q_INT64_C(-1)) << 0;
// uint + qulonglong
QTest::newRow("uint+qulonglong1") << qVariantFromValue(0U) << qVariantFromValue(Q_UINT64_C(0)) << 0;
QTest::newRow("uint+qulonglong2") << qVariantFromValue(1U) << qVariantFromValue(Q_UINT64_C(0)) << +1;
QTest::newRow("uint+qulonglong3") << qVariantFromValue(0U) << qVariantFromValue(Q_UINT64_C(1)) << -1;
QTest::newRow("uint+qulonglong4") << qVariantFromValue(1U) << qVariantFromValue(Q_UINT64_C(1)) << 0;
// int + uint (without sign change)
QTest::newRow("int+uint1") << qVariantFromValue(0) << qVariantFromValue(0U) << 0;
QTest::newRow("int+uint2") << qVariantFromValue(1) << qVariantFromValue(0U) << +1;
QTest::newRow("int+uint3") << qVariantFromValue(0) << qVariantFromValue(1U) << -1;
QTest::newRow("int+uint4") << qVariantFromValue(1) << qVariantFromValue(1U) << 0;
// uint + qlonglong
QTest::newRow("uint+qlonglong1") << qVariantFromValue(0U) << qVariantFromValue(Q_INT64_C(0)) << 0;
QTest::newRow("uint+qlonglong2") << qVariantFromValue(1U) << qVariantFromValue(Q_INT64_C(0)) << +1;
QTest::newRow("uint+qlonglong3") << qVariantFromValue(0U) << qVariantFromValue(Q_INT64_C(1)) << -1;
QTest::newRow("uint+qlonglong4") << qVariantFromValue(1U) << qVariantFromValue(Q_INT64_C(1)) << 0;
QTest::newRow("uint+qlonglong5") << qVariantFromValue(0U) << qVariantFromValue(Q_INT64_C(-1)) << +1;
// boundary conditions
QTest::newRow("charmax+intmax") << qVariantFromValue(CHAR_MAX) << qVariantFromValue(INT_MAX) << -1;
QTest::newRow("charmax+uintmax") << qVariantFromValue(CHAR_MAX) << qVariantFromValue(UINT_MAX) << -1;
QTest::newRow("scharmax+intmax") << qVariantFromValue(SCHAR_MAX) << qVariantFromValue(INT_MAX) << -1;
QTest::newRow("scharmax+uintmax") << qVariantFromValue(SCHAR_MAX) << qVariantFromValue(UINT_MAX) << -1;
QTest::newRow("ucharmax+intmax") << qVariantFromValue(UCHAR_MAX) << qVariantFromValue(INT_MAX) << -1;
QTest::newRow("ucharmax+uintmax") << qVariantFromValue(UCHAR_MAX) << qVariantFromValue(UINT_MAX) << -1;
QTest::newRow("shortmax+intmax") << qVariantFromValue(SHRT_MAX) << qVariantFromValue(INT_MAX) << -1;
QTest::newRow("shortmax+uintmax") << qVariantFromValue(SHRT_MAX) << qVariantFromValue(UINT_MAX) << -1;
QTest::newRow("ushortmax+intmax") << qVariantFromValue(USHRT_MAX) << qVariantFromValue(INT_MAX) << -1;
QTest::newRow("ushortmax+uintmax") << qVariantFromValue(USHRT_MAX) << qVariantFromValue(UINT_MAX) << -1;
QTest::newRow("intmin+qlonglongmin") << qVariantFromValue(INT_MIN) << qVariantFromValue(LLONG_MIN) << +1;
QTest::newRow("intmax+uintmax") << qVariantFromValue(INT_MAX) << qVariantFromValue(UINT_MAX) << -1;
QTest::newRow("intmax+qlonglongmax") << qVariantFromValue(INT_MAX) << qVariantFromValue(LLONG_MAX) << -1;
QTest::newRow("uintmax+qlonglongmax") << qVariantFromValue(UINT_MAX) << qVariantFromValue(LLONG_MAX) << -1;
QTest::newRow("intmax+qulonglongmax") << qVariantFromValue(INT_MAX) << qVariantFromValue(ULLONG_MAX) << -1;
QTest::newRow("qlonglongmax+qulonglongmax") << qVariantFromValue(LLONG_MAX) << qVariantFromValue(ULLONG_MAX) << -1;
QTest::newRow("uintmax+qlonglongmin") << qVariantFromValue(UINT_MAX) << qVariantFromValue(LLONG_MIN) << +1;
// check for no sign-extension issues
QTest::newRow("ushortmax+intzero") << qVariantFromValue(USHRT_MAX) << qVariantFromValue(0) << +1;
QTest::newRow("ushortmax+qlonglongzero") << qVariantFromValue(USHRT_MAX) << qVariantFromValue(Q_INT64_C(0)) << +1;
QTest::newRow("uintmax+qlonglongzero") << qVariantFromValue(UINT_MAX) << qVariantFromValue(Q_INT64_C(0)) << +1;
// sign changes
// the tests below check that a signed negative number sign-changes to a non-zero unsigned number and that
// signed -1 sign-changes to unsigned maximum (all bits set, ~0). This works on two's complement machines
// (all that Qt supports), and would also work on one's complement.
if (std::numeric_limits<char>::is_signed) {
QTest::newRow("signchange-char+uint") << qVariantFromValue(char(-1)) << qVariantFromValue(0U) << +1;
QTest::newRow("signchange-char+uintmax") << qVariantFromValue(char(-1)) << qVariantFromValue(UINT_MAX) << 0;
QTest::newRow("signchange-charmin+uint") << qVariantFromValue(CHAR_MIN) << qVariantFromValue(0U) << +1;
QTest::newRow("signchange-char+qulonglong") << qVariantFromValue(char(-1)) << qVariantFromValue(Q_UINT64_C(0)) << +1;
QTest::newRow("signchange-char+qulonglongmax") << qVariantFromValue(char(-1)) << qVariantFromValue(ULLONG_MAX) << 0;
QTest::newRow("signchange-charmin+qulonglong") << qVariantFromValue(CHAR_MIN) << qVariantFromValue(Q_UINT64_C(0)) << +1;
}
QTest::newRow("signchange-schar+uint") << qVariantFromValue(schar(-1)) << qVariantFromValue(0U) << +1;
QTest::newRow("signchange-schar+uintmax") << qVariantFromValue(schar(-1)) << qVariantFromValue(UINT_MAX) << 0;
QTest::newRow("signchange-scharmin+uint") << qVariantFromValue(SCHAR_MIN) << qVariantFromValue(0U) << +1;
QTest::newRow("signchange-schar+qulonglong") << qVariantFromValue(schar(-1)) << qVariantFromValue(Q_UINT64_C(0)) << +1;
QTest::newRow("signchange-schar+qulonglongmax") << qVariantFromValue(schar(-1)) << qVariantFromValue(ULLONG_MAX) << 0;
QTest::newRow("signchange-scharmin+qulonglong") << qVariantFromValue(SCHAR_MIN) << qVariantFromValue(Q_UINT64_C(0)) << +1;
QTest::newRow("signchange-short+uint") << qVariantFromValue(short(-1)) << qVariantFromValue(0U) << +1;
QTest::newRow("signchange-short+uintmax") << qVariantFromValue(short(-1)) << qVariantFromValue(UINT_MAX) << 0;
QTest::newRow("signchange-shortmin+uint") << qVariantFromValue(SHRT_MIN) << qVariantFromValue(0U) << +1;
QTest::newRow("signchange-short+qulonglong") << qVariantFromValue(short(-1)) << qVariantFromValue(Q_UINT64_C(0)) << +1;
QTest::newRow("signchange-short+qulonglongmax") << qVariantFromValue(short(-1)) << qVariantFromValue(ULLONG_MAX) << 0;
QTest::newRow("signchange-shortmin+qulonglong") << qVariantFromValue(SHRT_MIN) << qVariantFromValue(Q_UINT64_C(0)) << +1;
QTest::newRow("signchange-int+uint") << qVariantFromValue(-1) << qVariantFromValue(0U) << +1;
QTest::newRow("signchange-int+uintmax") << qVariantFromValue(-1) << qVariantFromValue(UINT_MAX) << 0;
QTest::newRow("signchange-intmin+uint") << qVariantFromValue(INT_MIN) << qVariantFromValue(0U) << +1;
QTest::newRow("signchange-int+qulonglong") << qVariantFromValue(-1) << qVariantFromValue(Q_UINT64_C(0)) << +1;
QTest::newRow("signchange-int+qulonglongmax") << qVariantFromValue(-1) << qVariantFromValue(ULLONG_MAX) << 0;
QTest::newRow("signchange-intmin+qulonglong") << qVariantFromValue(INT_MIN) << qVariantFromValue(Q_UINT64_C(0)) << +1;
// no qlonglong+uint, since that should promote to qlonglong and then the comparison is signed (tested above)
QTest::newRow("signchange-qlonglong+qulonglong") << qVariantFromValue(Q_INT64_C(-1)) << qVariantFromValue(Q_UINT64_C(0)) << +1;
QTest::newRow("signchange-qlonglong+qulonglongmax") << qVariantFromValue(Q_INT64_C(-1)) << qVariantFromValue(ULLONG_MAX) << 0;
QTest::newRow("signchange-qlonglongmin+qulonglong") << qVariantFromValue(LLONG_MIN) << qVariantFromValue(Q_UINT64_C(0)) << +1;
}
void tst_QVariant::compareNumbers() const
{
QFETCH(QVariant, v1);
QFETCH(QVariant, v2);
QFETCH(int, expected);
if (expected == -1) {
QVERIFY(v1 < v2);
QVERIFY(v1 <= v2);
QVERIFY(!(v1 == v2));
QVERIFY(!(v1 > v2));
QVERIFY(!(v1 >= v2));
QVERIFY(!(v2 < v1));
QVERIFY(!(v2 <= v1));
QVERIFY(!(v2 == v1));
QVERIFY(v2 >= v1);
QVERIFY(v2 > v1);
} else if (expected == 0) {
QVERIFY(!(v1 < v2));
QVERIFY(v1 <= v2);
QCOMPARE(v1, v2);
QVERIFY(!(v1 > v2));
QVERIFY(v1 >= v2);
QVERIFY(!(v2 < v1));
QVERIFY(v2 <= v1);
QCOMPARE(v2, v1);
QVERIFY(v2 >= v1);
QVERIFY(!(v2 > v1));
} else {
QVERIFY(!(v1 < v2));
QVERIFY(!(v1 <= v2));
QVERIFY(!(v1 == v2));
QVERIFY(v1 > v2);
QVERIFY(v1 >= v2);
QVERIFY(v2 < v1);
QVERIFY(v2 <= v1);
QVERIFY(!(v2 == v1));
QVERIFY(!(v2 >= v1));
QVERIFY(!(v2 > v1));
}
}
void tst_QVariant::typeName_data()
{
QTest::addColumn<int>("type");