Author: Nerixyz
Date: 2026-07-18T10:56:56+02:00
New Revision: ccd0e982a02fa3be39a98dd98ea6cc1ccaf24d13

URL: 
https://github.com/llvm/llvm-project/commit/ccd0e982a02fa3be39a98dd98ea6cc1ccaf24d13
DIFF: 
https://github.com/llvm/llvm-project/commit/ccd0e982a02fa3be39a98dd98ea6cc1ccaf24d13.diff

LOG: [lldb][NativePDB] Fix width and signedness of enum constants (#210338)

Enumerator values aren't always encoded in the correct bit width and
signedness. This happens with both MSVC and Clang.

On MSVC, unsigned 64bit enumerators can be encoded as signed. For
example `ULONGLONG_MAX` will be encoded as `-1`.
Clang/LLVM will always encode the values as unsigned.

Example: https://godbolt.org/z/96YjGW48W.

I fixed this by setting the expected width and signedness when creating
the enumerator constant. We can't use a shell test like for the other
PDB tests, because no output shows the enum values.

Added: 
    lldb/test/API/lang/cpp/enum-limits/Makefile
    lldb/test/API/lang/cpp/enum-limits/TestCPPEnumLimits.py
    lldb/test/API/lang/cpp/enum-limits/main.cpp

Modified: 
    lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp 
b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp
index 9840b976d2713..871b053151c61 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp
@@ -331,8 +331,24 @@ Error UdtRecordCompleter::visitKnownMember(CVMemberRecord 
&cvr,
   Declaration decl;
   llvm::StringRef name = DropNameScope(enumerator.getName());
 
+  clang::EnumDecl *enum_decl = TypeSystemClang::GetAsEnumDecl(m_derived_ct);
+  if (!enum_decl)
+    return Error::success();
+
+  llvm::APSInt val = enumerator.Value;
+  clang::QualType int_ty = enum_decl->getIntegerType();
+  uint64_t n_bits = m_ast_builder.clang().getASTContext().getTypeSize(int_ty);
+  if (n_bits == 0)
+    return Error::success();
+
+  // MSVC encodes 64 Bit unsigned enum values as signed integers. For example,
+  // ULONGLONG_MAX will be encoded as -1. LLVM encodes all values as unsigned.
+  // Fix this by explicitly setting the bit width and signedness.
+  val = val.extOrTrunc(n_bits);
+  val.setIsSigned(int_ty->isSignedIntegerType());
+
   m_ast_builder.clang().AddEnumerationValueToEnumerationType(
-      m_derived_ct, decl, name.str().c_str(), enumerator.Value);
+      m_derived_ct, decl, name.str().c_str(), val);
   return Error::success();
 }
 

diff  --git a/lldb/test/API/lang/cpp/enum-limits/Makefile 
b/lldb/test/API/lang/cpp/enum-limits/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/lang/cpp/enum-limits/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules

diff  --git a/lldb/test/API/lang/cpp/enum-limits/TestCPPEnumLimits.py 
b/lldb/test/API/lang/cpp/enum-limits/TestCPPEnumLimits.py
new file mode 100644
index 0000000000000..4e0c0a088c909
--- /dev/null
+++ b/lldb/test/API/lang/cpp/enum-limits/TestCPPEnumLimits.py
@@ -0,0 +1,91 @@
+"""Check that enumerator values are correct when they're near the limits of 
the underlying type."""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+import lldbsuite.test.lldbutil as lldbutil
+
+
+class CPPEnumLimitsTestCase(TestBase):
+    SHARED_BUILD_TESTCASE = False
+    TEST_WITH_PDB_DEBUG_INFO = True
+
+    def check_signed(self, ty: lldb.SBType, expected):
+        self.assertEqual(
+            {i.GetName(): i.GetValueAsSigned() for i in ty.GetEnumMembers()}, 
expected
+        )
+
+    def check_unsigned(self, ty: lldb.SBType, expected):
+        self.assertEqual(
+            {i.GetName(): i.GetValueAsUnsigned() for i in 
ty.GetEnumMembers()}, expected
+        )
+
+    def check_all(self, target: lldb.SBTarget):
+        self.check_unsigned(target.FindFirstType("U8Enum"), {"Min": 0, "Max": 
255})
+        self.check_signed(target.FindFirstType("I8Enum"), {"Min": -128, "Max": 
127})
+        self.check_unsigned(target.FindFirstType("U16Enum"), {"Min": 0, "Max": 
65535})
+        self.check_signed(
+            target.FindFirstType("I16Enum"), {"Min": -32768, "Max": 32767}
+        )
+        self.check_unsigned(
+            target.FindFirstType("U32Enum"),
+            {"Min": 0, "MaxMinusOne": 4294967294, "Max": 4294967295},
+        )
+        self.check_signed(
+            target.FindFirstType("I32Enum"), {"Min": -2147483648, "Max": 
2147483647}
+        )
+        self.check_unsigned(
+            target.FindFirstType("U64Enum"),
+            {
+                "Min": 0,
+                "MaxMinusOne": 18446744073709551614,
+                "Max": 18446744073709551615,
+            },
+        )
+        self.check_signed(
+            target.FindFirstType("I64Enum"),
+            {
+                "Min": -9223372036854775808,
+                "MinPlusOne": -9223372036854775807,
+                "MaxMinusOne": 9223372036854775806,
+                "Max": 9223372036854775807,
+            },
+        )
+
+    def test(self):
+        self.build()
+        self.check_all(self.dbg.CreateTarget(self.getBuildArtifact("a.out")))
+
+    @skipUnlessPlatform(["windows"])
+    @skipUnlessMSVC
+    @no_debug_info_test  # We only test MSVC
+    def test_msvc(self):
+        """Test that the limits work on MSVC."""
+
+        src = os.path.join(self.getSourceDir(), "main.cpp")
+        exe = os.path.join(self.getBuildDir(), "a.exe")
+
+        # FIXME: Allow MSVC to be used with the Makefile.
+        result = subprocess.run(
+            [
+                "cl.exe",
+                "/nologo",
+                "/Od",
+                "/Zi",
+                "/Fe" + exe,
+                src,
+                "/link",
+                "/nodefaultlib",
+                "/entry:main",
+            ],
+            cwd=self.getBuildDir(),
+            capture_output=True,
+            text=True,
+        )
+        self.assertEqual(
+            result.returncode,
+            0,
+            "Compilation failed:\n" + result.stdout + result.stderr,
+        )
+
+        self.check_all(self.dbg.CreateTarget(exe))

diff  --git a/lldb/test/API/lang/cpp/enum-limits/main.cpp 
b/lldb/test/API/lang/cpp/enum-limits/main.cpp
new file mode 100644
index 0000000000000..ea7036f7f7c0e
--- /dev/null
+++ b/lldb/test/API/lang/cpp/enum-limits/main.cpp
@@ -0,0 +1,48 @@
+enum class U8Enum : unsigned char {
+  Min = 0,
+  Max = 255,
+};
+enum class I8Enum : signed char {
+  Min = -128,
+  Max = 127,
+};
+enum class U16Enum : unsigned short {
+  Min = 0,
+  Max = 65535,
+};
+enum class I16Enum : short {
+  Min = -32768,
+  Max = 32767,
+};
+enum class U32Enum : unsigned {
+  Min = 0,
+  MaxMinusOne = 4294967294,
+  Max = 4294967295,
+};
+enum class I32Enum : int {
+  Min = -2147483648,
+  Max = 2147483647,
+};
+enum class U64Enum : unsigned long long {
+  Min = 0,
+  MaxMinusOne = 18446744073709551614ULL,
+  Max = 18446744073709551615ULL,
+};
+enum class I64Enum : long long {
+  Min = -9223372036854775807LL - 1,
+  MinPlusOne = -9223372036854775807LL,
+  MaxMinusOne = 9223372036854775806LL,
+  Max = 9223372036854775807LL,
+};
+
+int main() {
+  auto u8 = U8Enum::Max;
+  auto i8 = I8Enum::Max;
+  auto u16 = U16Enum::Max;
+  auto i16 = I16Enum::Max;
+  auto u32 = U32Enum::Max;
+  auto i32 = I32Enum::Max;
+  auto u64 = U64Enum::Max;
+  auto i64 = I64Enum::Max;
+  return 0;
+}


        
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to