moc: add support for storing 64-bit enums and flags

The metatype format is extended by storing the high parts of all
enumerations after the (name, low part) section, for a total of 12 bytes
per enumeration instead of 8. This allows 32-bit flags and enums to
remain unchanged. In fact, older meta object parsers remain unchanged,
aside from not knowing what this new bit in the flags field means.

Because this is entirely done in the C++ constexpr side, moc knows
nothing about it so it can't update the JSON output with a new field.

Task-number: QTBUG-111926
Change-Id: I8a96935cf6c742259c9dfffd17e9512e4f4add19
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Thiago Macieira 2024-08-18 13:33:04 -07:00
parent d40a2265ea
commit 0d26dc6727
4 changed files with 51 additions and 18 deletions

View File

@ -29,7 +29,7 @@ namespace QtMocConstants {
// and metamethods store a flag stating whether they are const // and metamethods store a flag stating whether they are const
// revision 11 is Qt 6.5: The metatype for void is stored in the metatypes array // revision 11 is Qt 6.5: The metatype for void is stored in the metatypes array
// revision 12 is Qt 6.6: It adds the metatype for enums // revision 12 is Qt 6.6: It adds the metatype for enums
// revision 13 is Qt 6.9: It moves the location of the meta method revisions // revision 13 is Qt 6.9: Adds support for 64-bit QFlags and moves the method revision
enum { OutputRevision = 13 }; // Used by moc, qmetaobjectbuilder and qdbus enum { OutputRevision = 13 }; // Used by moc, qmetaobjectbuilder and qdbus
enum PropertyFlags : uint { enum PropertyFlags : uint {
@ -88,6 +88,7 @@ enum MetaDataFlags : uint {
enum EnumFlags : uint { enum EnumFlags : uint {
EnumIsFlag = 0x1, EnumIsFlag = 0x1,
EnumIsScoped = 0x2, EnumIsScoped = 0x2,
EnumIs64Bit = 0x40,
}; };
} // namespace QtMocConstants } // namespace QtMocConstants

View File

@ -83,6 +83,16 @@ template <uint... Nx> constexpr auto stringData(const char (&...strings)[Nx])
struct NoType {}; struct NoType {};
namespace detail { namespace detail {
template<typename Enum> constexpr int payloadSizeForEnum()
{
// How many uint blocks do we need to store the values of this enum and the
// string indices for the enumeration labels? We only support 8- 16-, 32-
// and 64-bit enums at the time of this writing, so this code is extra
// pedantic allowing for 48-, 96-, 128-bit, etc.
int n = int(sizeof(Enum) + sizeof(uint)) - 1;
return 1 + n / sizeof(uint);
}
template <uint H, uint P> struct UintDataBlock template <uint H, uint P> struct UintDataBlock
{ {
static constexpr uint headerSize() { return H; } static constexpr uint headerSize() { return H; }
@ -207,10 +217,10 @@ struct PropertyData : detail::UintDataBlock<5, 0>
}; };
template <typename Enum, int N = 0> template <typename Enum, int N = 0>
struct EnumData : detail::UintDataBlock<5, 2 * N> struct EnumData : detail::UintDataBlock<5, N * detail::payloadSizeForEnum<Enum>()>
{ {
private: private:
static_assert(sizeof(Enum) <= sizeof(uint), "Cannot store enumeration of this size"); static_assert(sizeof(Enum) <= 2 * sizeof(uint), "Cannot store enumeration of this size");
template <typename T> struct RealEnum { using Type = T; }; template <typename T> struct RealEnum { using Type = T; };
template <typename T> struct RealEnum<QFlags<T>> { using Type = T; }; template <typename T> struct RealEnum<QFlags<T>> { using Type = T; };
public: public:
@ -219,7 +229,6 @@ public:
typename RealEnum<Enum>::Type value; typename RealEnum<Enum>::Type value;
}; };
constexpr EnumData(uint nameOffset, uint aliasOffset, uint flags) constexpr EnumData(uint nameOffset, uint aliasOffset, uint flags)
{ {
this->header[0] = nameOffset; this->header[0] = nameOffset;
@ -234,7 +243,7 @@ public:
this->header[2] |= QtMocConstants::EnumIsScoped; this->header[2] |= QtMocConstants::EnumIsScoped;
} }
template <int Added> constexpr auto add(const EnumEntry (&entries)[Added]) template <int Added> constexpr auto add(const EnumEntry (&entries)[Added]) const
{ {
EnumData<Enum, N + Added> result(this->header[0], this->header[1], this->header[2]); EnumData<Enum, N + Added> result(this->header[0], this->header[1], this->header[2]);
@ -245,6 +254,15 @@ public:
auto value = qToUnderlying(entry.value); auto value = qToUnderlying(entry.value);
result.payload[o++] = uint(value); result.payload[o++] = uint(value);
} }
if constexpr (sizeof(Enum) > sizeof(uint)) {
static_assert(N == 0, "Unimplemented: merging with non-empty EnumData");
result.header[2] |= QtMocConstants::EnumIs64Bit;
for (auto entry : entries) {
auto value = qToUnderlying(entry.value);
result.payload[o++] = uint(value >> 32);
}
}
return result; return result;
} }

View File

@ -152,12 +152,12 @@ public:
enum MetaEnumTest_Enum0 { MetaEnumTest_Enum0_dummy = 2, MetaEnumTest_Enum0_value = 42, MetaEnsureSignedEnum0 = -1 }; enum MetaEnumTest_Enum0 { MetaEnumTest_Enum0_dummy = 2, MetaEnumTest_Enum0_value = 42, MetaEnsureSignedEnum0 = -1 };
Q_ENUM(MetaEnumTest_Enum0) Q_ENUM(MetaEnumTest_Enum0)
enum MetaEnumTest_Enum1 : qint64 { MetaEnumTest_Enum1_value = 42, MetaEnumTest_Enum1_bigValue = (Q_INT64_C(1) << 33) + 50 }; enum MetaEnumTest_Enum1 : qint64 { MetaEnumTest_Enum1_value = 42, MetaEnumTest_Enum1_bigValue = (Q_INT64_C(1) << 33) + 50 };
// Q_ENUM(MetaEnumTest_Enum1) Q_ENUM(MetaEnumTest_Enum1)
enum MetaEnumTest_Enum3 : qint64 { MetaEnumTest_Enum3_value = -47, MetaEnumTest_Enum3_bigValue = (Q_INT64_C(1) << 56) + 5, MetaEnumTest_Enum3_bigNegValue = -(Q_INT64_C(1) << 56) - 3 }; enum MetaEnumTest_Enum3 : qint64 { MetaEnumTest_Enum3_value = -47, MetaEnumTest_Enum3_bigValue = (Q_INT64_C(1) << 56) + 5, MetaEnumTest_Enum3_bigNegValue = -(Q_INT64_C(1) << 56) - 3 };
// Q_ENUM(MetaEnumTest_Enum3) Q_ENUM(MetaEnumTest_Enum3)
enum MetaEnumTest_Enum4 : quint64 { MetaEnumTest_Enum4_value = 47, MetaEnumTest_Enum4_bigValue = (Q_INT64_C(1) << 52) + 45 }; enum MetaEnumTest_Enum4 : quint64 { MetaEnumTest_Enum4_value = 47, MetaEnumTest_Enum4_bigValue = (Q_INT64_C(1) << 52) + 45 };
// Q_ENUM(MetaEnumTest_Enum4) Q_ENUM(MetaEnumTest_Enum4)
enum MetaEnumTest_Enum5 : uint { MetaEnumTest_Enum5_value = 47 }; enum MetaEnumTest_Enum5 : uint { MetaEnumTest_Enum5_value = 47 };
Q_ENUM(MetaEnumTest_Enum5) Q_ENUM(MetaEnumTest_Enum5)
enum MetaEnumTest_Enum6 : uchar { MetaEnumTest_Enum6_value = 47 }; enum MetaEnumTest_Enum6 : uchar { MetaEnumTest_Enum6_value = 47 };
@ -5390,13 +5390,13 @@ void tst_QVariant::metaEnums_data()
QTest::newRow(#Value) << &testVariantMetaEnum<decltype(Value), Value> << #Value; QTest::newRow(#Value) << &testVariantMetaEnum<decltype(Value), Value> << #Value;
METAENUMS_TEST(MetaEnumTest_Enum0_value); METAENUMS_TEST(MetaEnumTest_Enum0_value);
// METAENUMS_TEST(MetaEnumTest_Enum1_value); METAENUMS_TEST(MetaEnumTest_Enum1_value);
// METAENUMS_TEST(MetaEnumTest_Enum1_bigValue); METAENUMS_TEST(MetaEnumTest_Enum1_bigValue);
// METAENUMS_TEST(MetaEnumTest_Enum3_value); METAENUMS_TEST(MetaEnumTest_Enum3_value);
// METAENUMS_TEST(MetaEnumTest_Enum3_bigValue); METAENUMS_TEST(MetaEnumTest_Enum3_bigValue);
// METAENUMS_TEST(MetaEnumTest_Enum3_bigNegValue); METAENUMS_TEST(MetaEnumTest_Enum3_bigNegValue);
// METAENUMS_TEST(MetaEnumTest_Enum4_value); METAENUMS_TEST(MetaEnumTest_Enum4_value);
// METAENUMS_TEST(MetaEnumTest_Enum4_bigValue); METAENUMS_TEST(MetaEnumTest_Enum4_bigValue);
METAENUMS_TEST(MetaEnumTest_Enum5_value); METAENUMS_TEST(MetaEnumTest_Enum5_value);
METAENUMS_TEST(MetaEnumTest_Enum6_value); METAENUMS_TEST(MetaEnumTest_Enum6_value);
METAENUMS_TEST(MetaEnumTest_Enum8_value); METAENUMS_TEST(MetaEnumTest_Enum8_value);

View File

@ -123,11 +123,17 @@ template <typename E, int N> void enumUintData_check(const E (&values)[N])
QCOMPARE(result.payload[2 * i + 0], uint(namesAndOffsets[i].nameIndex)); QCOMPARE(result.payload[2 * i + 0], uint(namesAndOffsets[i].nameIndex));
QCOMPARE(result.payload[2 * i + 1], uint(values[i])); QCOMPARE(result.payload[2 * i + 1], uint(values[i]));
} }
if constexpr (sizeof(E) > sizeof(uint)) {
using U = std::underlying_type_t<E>;
for (uint i = 0; i < std::size(values); ++i)
QCOMPARE(result.payload[2 * N + i], uint(U(values[i]) >> 32));
}
} }
enum E1 { AnEnumValue }; enum E1 { AnEnumValue };
enum class E2 { V0 = INT_MAX, V1 = INT_MIN }; enum class E2 { V0 = INT_MAX, V1 = INT_MIN };
enum class E3 : int { V = 0x1111'2222, V2 = -V }; enum class E3 : qint64 { V = 0x1111'2222'3333'4444, V2 = -V };
void tst_MocHelpers::enumUintData() void tst_MocHelpers::enumUintData()
{ {
using namespace QtMocHelpers; using namespace QtMocHelpers;
@ -171,12 +177,14 @@ void tst_MocHelpers::enumUintData()
.add({ { 2, E3::V }, {3, E3::V2 }, }); .add({ { 2, E3::V }, {3, E3::V2 }, });
QCOMPARE(result.header[0], 1U); QCOMPARE(result.header[0], 1U);
QCOMPARE(result.header[1], 1U); QCOMPARE(result.header[1], 1U);
QCOMPARE(result.header[2], EnumIsScoped); QCOMPARE(result.header[2], EnumIsScoped | EnumIs64Bit);
QCOMPARE(result.header[3], 2U); QCOMPARE(result.header[3], 2U);
QCOMPARE(result.payload[0], 2U); QCOMPARE(result.payload[0], 2U);
QCOMPARE(result.payload[1], uint(E3::V)); QCOMPARE(result.payload[1], uint(E3::V));
QCOMPARE(result.payload[2], 3U); QCOMPARE(result.payload[2], 3U);
QCOMPARE(result.payload[3], uint(E3::V2)); QCOMPARE(result.payload[3], uint(E3::V2));
QCOMPARE(result.payload[4], uint(quint64(E3::V) >> 32));
QCOMPARE(result.payload[5], uint(quint64(E3::V2) >> 32));
} }
QTest::setThrowOnFail(true); QTest::setThrowOnFail(true);
@ -184,6 +192,10 @@ void tst_MocHelpers::enumUintData()
enum E { E0, E1 = -1, E2 = 123, E3 = INT_MIN }; enum E { E0, E1 = -1, E2 = 123, E3 = INT_MIN };
enumUintData_check({E0, E1, E2, E3}); enumUintData_check({E0, E1, E2, E3});
} }
{
enum E : quint64 { E0, E1 = quint64(INT_MIN), E2 = 0x1'0000'0000, E3 = quint64(LLONG_MIN) };
enumUintData_check({E0, E1, E2, E3});
}
} }
template <typename Data> void testUintData(const Data &data) template <typename Data> void testUintData(const Data &data)
@ -223,7 +235,7 @@ template <size_t N> static void checkEnums(const std::array<uint, N> &data)
// E3: // E3:
QCOMPARE(header[5 + 0], 4U); QCOMPARE(header[5 + 0], 4U);
QCOMPARE(header[5 + 1], 5U); QCOMPARE(header[5 + 1], 5U);
QCOMPARE(header[5 + 2], EnumIsFlag | EnumIsScoped); QCOMPARE(header[5 + 2], EnumIsFlag | EnumIsScoped | EnumIs64Bit);
QCOMPARE(header[5 + 3], 2U); QCOMPARE(header[5 + 3], 2U);
QCOMPARE_GE(header[5 + 4], 14U); QCOMPARE_GE(header[5 + 4], 14U);
payload = data.data() + header[5 + 4]; payload = data.data() + header[5 + 4];
@ -231,6 +243,8 @@ template <size_t N> static void checkEnums(const std::array<uint, N> &data)
QCOMPARE(payload[1], uint(E3::V)); QCOMPARE(payload[1], uint(E3::V));
QCOMPARE(payload[2], 8U); QCOMPARE(payload[2], 8U);
QCOMPARE(payload[3], uint(E3::V2)); QCOMPARE(payload[3], uint(E3::V2));
QCOMPARE(payload[4], uint(quint64(E3::V) >> 32));
QCOMPARE(payload[5], uint(quint64(E3::V2) >> 32));
// E2: // E2:
QCOMPARE(header[10 + 0], 7U); QCOMPARE(header[10 + 0], 7U);