From 14f494bb8ef71a0287ec22c26814e9d40e21fd6b Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Wed, 19 Aug 2026 21:29:39 +0200 Subject: [PATCH] Return enum constant values as their int64_t bit pattern GetEnumConstantValue returns size_t and calls APSInt::getExtValue unguarded, which asserts above INT64_MAX. Round-tripping large values through std::stoul (#970) throws where unsigned long is 32 bit (LLP64 Windows, wasm32), and a size_t return truncates there regardless. ROOT's Windows CI hit this as an uncaught out_of_range in MetaClingTests test_GH_20925. Return the 64-bit pattern as int64_t everywhere: getExtValue when representable, the zero-extended value as two's complement otherwise. --- lib/CppInterOp/CppInterOp.cpp | 8 ++++++-- lib/CppInterOp/CppInterOp.td | 7 ++++--- unittests/CppInterOp/EnumReflectionTest.cpp | 11 ++++++++++- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/lib/CppInterOp/CppInterOp.cpp b/lib/CppInterOp/CppInterOp.cpp index 6ad4d8d7e..fb93e5dc0 100644 --- a/lib/CppInterOp/CppInterOp.cpp +++ b/lib/CppInterOp/CppInterOp.cpp @@ -878,12 +878,16 @@ TypeRef GetEnumConstantType(ConstDeclRef DRef) { return INTEROP_RETURN(nullptr); } -size_t GetEnumConstantValue(ConstDeclRef DRef) { +int64_t GetEnumConstantValue(ConstDeclRef DRef) { INTEROP_TRACE(DRef); const auto* D = unwrap(DRef); if (const auto* ECD = llvm::dyn_cast_or_null(D)) { const llvm::APSInt& Val = ECD->getInitVal(); - return INTEROP_RETURN(Val.getExtValue()); + if (Val.isRepresentableByInt64()) + return INTEROP_RETURN(Val.getExtValue()); + // Do not round-trip through a string and std::stoul: unsigned long is + // 32 bit on LLP64/ILP32 and throws out_of_range for these values. + return INTEROP_RETURN((int64_t)Val.getZExtValue()); } return INTEROP_RETURN(0); } diff --git a/lib/CppInterOp/CppInterOp.td b/lib/CppInterOp/CppInterOp.td index be9cd1e86..4f4c45e86 100644 --- a/lib/CppInterOp/CppInterOp.td +++ b/lib/CppInterOp/CppInterOp.td @@ -1059,10 +1059,11 @@ def GetEnumConstantType : CppInterOpAPI { } def GetEnumConstantValue : CppInterOpAPI { - let Doc = [{Gets the index value (0,1,2, etcetera) of the enum constant -that was passed into this function.}]; + let Doc = [{Gets the value of the enum constant that was passed into this +function, as its 64-bit bit pattern (values above INT64_MAX come back as +their two's complement representation).}]; - let ReturnType = "size_t"; + let ReturnType = "int64_t"; let Args = [ Arg<"ConstDeclRef", "DRef"> ]; diff --git a/unittests/CppInterOp/EnumReflectionTest.cpp b/unittests/CppInterOp/EnumReflectionTest.cpp index 0e5d56025..0696cd71d 100644 --- a/unittests/CppInterOp/EnumReflectionTest.cpp +++ b/unittests/CppInterOp/EnumReflectionTest.cpp @@ -237,6 +237,10 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, EnumReflection_GetEnumConstantValue) { MinusTen = -10, MinusNine }; + enum Huge : unsigned long long { + Big = ((unsigned long long)1) << 63, + Max = 0xFFFFFFFFFFFFFFFFULL + }; int a = 10; )"; @@ -250,7 +254,12 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, EnumReflection_GetEnumConstantValue) { EXPECT_EQ(Cpp::GetEnumConstantValue(EnumConstants[4]), 54); EXPECT_EQ(Cpp::GetEnumConstantValue(EnumConstants[5]), -10); EXPECT_EQ(Cpp::GetEnumConstantValue(EnumConstants[6]), -9); - EXPECT_EQ(Cpp::GetEnumConstantValue(Decls[1]), 0); // Checking value of non enum constant + EXPECT_EQ(Cpp::GetEnumConstantValue(Decls[2]), 0); // Checking value of non enum constant + + auto HugeConstants = Cpp::GetEnumConstants(Decls[1]); + EXPECT_EQ(Cpp::GetEnumConstantValue(HugeConstants[0]), + (int64_t)((uint64_t)1 << 63)); + EXPECT_EQ(Cpp::GetEnumConstantValue(HugeConstants[1]), (int64_t)-1); } TYPED_TEST(CPPINTEROP_TEST_MODE, EnumReflection_GetEnums) {