[Lldb-commits] [lldb] [lldb][NativePDB] Handle LF_CLASS2 and friends (PR #231615)

via lldb-commits lldb-commits at lists.llvm.org
Tue Oct 13 11:57:32 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: Nerixyz (Nerixyz)

<details>
<summary>Changes</summary>

In #<!-- -->228761 I added support for `LF_CLASS2`, `LF_STRUCTURE2`, `LF_INTERFACE2`, and `LF_UNION2`. They map to the same high-level types (`ClassRecord` etc.), so the migration for lldb isn't that big. Most changes are adding a case for the `2` record.

---
Full diff: https://github.com/llvm/llvm-project/pull/231615.diff


5 Files Affected:

- (modified) lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.cpp (+4) 
- (modified) lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp (+34-6) 
- (modified) lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp (+8-1) 
- (modified) lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp (+3-2) 
- (added) lldb/test/Shell/SymbolFile/NativePDB/lf-class2.cpp (+59) 


``````````diff
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.cpp
index 66a3990a54f635..c4604759fc7ea2 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.cpp
@@ -104,12 +104,16 @@ struct CreateMethodDecl : public TypeVisitorCallbacks {
 static clang::TagTypeKind TranslateUdtKind(const TagRecord &cr) {
   switch (cr.Kind) {
   case TypeRecordKind::Class:
+  case TypeRecordKind::Class2:
     return clang::TagTypeKind::Class;
   case TypeRecordKind::Struct:
+  case TypeRecordKind::Struct2:
     return clang::TagTypeKind::Struct;
   case TypeRecordKind::Union:
+  case TypeRecordKind::Union2:
     return clang::TagTypeKind::Union;
   case TypeRecordKind::Interface:
+  case TypeRecordKind::Interface2:
     return clang::TagTypeKind::Interface;
   case TypeRecordKind::Enum:
     return clang::TagTypeKind::Enum;
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
index 8f739be137eacd..c159b38f0054c6 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
@@ -198,13 +198,17 @@ CVTagRecord CVTagRecord::create(CVType type) {
   assert(IsTagRecord(type) && "type is not a tag record!");
   switch (type.kind()) {
   case LF_CLASS:
+  case LF_CLASS2:
   case LF_STRUCTURE:
-  case LF_INTERFACE: {
+  case LF_STRUCTURE2:
+  case LF_INTERFACE:
+  case LF_INTERFACE2: {
     ClassRecord cr;
     llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(type, cr));
     return CVTagRecord(std::move(cr));
   }
-  case LF_UNION: {
+  case LF_UNION:
+  case LF_UNION2: {
     UnionRecord ur;
     llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(type, ur));
     return CVTagRecord(std::move(ur));
@@ -220,8 +224,10 @@ CVTagRecord CVTagRecord::create(CVType type) {
 }
 
 CVTagRecord::CVTagRecord(ClassRecord &&c)
-    : cvclass(std::move(c)),
-      m_kind(cvclass.Kind == TypeRecordKind::Struct ? Struct : Class) {}
+    : cvclass(std::move(c)), m_kind((cvclass.Kind == TypeRecordKind::Struct ||
+                                     cvclass.Kind == TypeRecordKind::Struct2)
+                                        ? Struct
+                                        : Class) {}
 CVTagRecord::CVTagRecord(UnionRecord &&u)
     : cvunion(std::move(u)), m_kind(Union) {}
 CVTagRecord::CVTagRecord(EnumRecord &&e) : cvenum(std::move(e)), m_kind(Enum) {}
@@ -290,9 +296,13 @@ PDB_SymType lldb_private::npdb::CVTypeToPDBType(TypeLeafKind kind) {
   case LF_BINTERFACE:
     return PDB_SymType::BaseInterface;
   case LF_CLASS:
+  case LF_CLASS2:
   case LF_STRUCTURE:
+  case LF_STRUCTURE2:
   case LF_INTERFACE:
+  case LF_INTERFACE2:
   case LF_UNION:
+  case LF_UNION2:
     return PDB_SymType::UDT;
   case LF_POINTER:
     return PDB_SymType::PointerType;
@@ -499,11 +509,15 @@ bool lldb_private::npdb::IsForwardRefUdt(CVType cvt) {
   EnumRecord er;
   switch (cvt.kind()) {
   case LF_CLASS:
+  case LF_CLASS2:
   case LF_STRUCTURE:
+  case LF_STRUCTURE2:
   case LF_INTERFACE:
+  case LF_INTERFACE2:
     llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, cr));
     return cr.isForwardRef();
   case LF_UNION:
+  case LF_UNION2:
     llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, ur));
     return ur.isForwardRef();
   case LF_ENUM:
@@ -517,8 +531,11 @@ bool lldb_private::npdb::IsForwardRefUdt(CVType cvt) {
 bool lldb_private::npdb::IsTagRecord(llvm::codeview::CVType cvt) {
   switch (cvt.kind()) {
   case LF_CLASS:
+  case LF_CLASS2:
   case LF_STRUCTURE:
+  case LF_STRUCTURE2:
   case LF_UNION:
+  case LF_UNION2:
   case LF_ENUM:
     return true;
   default:
@@ -529,8 +546,11 @@ bool lldb_private::npdb::IsTagRecord(llvm::codeview::CVType cvt) {
 bool lldb_private::npdb::IsClassStructUnion(llvm::codeview::CVType cvt) {
   switch (cvt.kind()) {
   case LF_CLASS:
+  case LF_CLASS2:
   case LF_STRUCTURE:
+  case LF_STRUCTURE2:
   case LF_UNION:
+  case LF_UNION2:
     return true;
   default:
     return false;
@@ -568,13 +588,17 @@ lldb_private::npdb::TranslateMemberAccess(MemberAccess access) {
 TypeIndex lldb_private::npdb::GetFieldListIndex(CVType cvt) {
   switch (cvt.kind()) {
   case LF_CLASS:
+  case LF_CLASS2:
   case LF_STRUCTURE:
-  case LF_INTERFACE: {
+  case LF_STRUCTURE2:
+  case LF_INTERFACE:
+  case LF_INTERFACE2: {
     ClassRecord cr;
     cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, cr));
     return cr.FieldList;
   }
-  case LF_UNION: {
+  case LF_UNION:
+  case LF_UNION2: {
     UnionRecord ur;
     cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, ur));
     return ur.FieldList;
@@ -1184,10 +1208,14 @@ size_t lldb_private::npdb::GetSizeOfType(PdbTypeSymId id,
   case LF_ARRAY:
     return GetSizeOfTypeInternal<ArrayRecord>(cvt);
   case LF_CLASS:
+  case LF_CLASS2:
   case LF_STRUCTURE:
+  case LF_STRUCTURE2:
   case LF_INTERFACE:
+  case LF_INTERFACE2:
     return GetSizeOfTypeInternal<ClassRecord>(cvt);
   case LF_UNION:
+  case LF_UNION2:
     return GetSizeOfTypeInternal<UnionRecord>(cvt);
   case LF_BITFIELD: {
     BitFieldRecord record;
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
index 9ed1d327851fbe..a7ce8b8cad8043 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -298,8 +298,11 @@ static llvm::StringRef GetSimpleTypeName(SimpleTypeKind kind) {
 static bool IsClassRecord(TypeLeafKind kind) {
   switch (kind) {
   case LF_STRUCTURE:
+  case LF_STRUCTURE2:
   case LF_CLASS:
+  case LF_CLASS2:
   case LF_INTERFACE:
+  case LF_INTERFACE2:
     return true;
   default:
     return false;
@@ -928,7 +931,7 @@ TypeSP SymbolFileNativePDB::CreateType(PdbTypeSymId type_id, CompilerType ct) {
     return CreateTagType(type_id, er, ct);
   }
 
-  if (cvt.kind() == LF_UNION) {
+  if (cvt.kind() == LF_UNION || cvt.kind() == LF_UNION2) {
     UnionRecord ur;
     if (auto err = TypeDeserializer::deserializeAs<UnionRecord>(cvt, ur)) {
       LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err),
@@ -3102,11 +3105,15 @@ SymbolFileNativePDB::GetContextForType(TypeIndex ti) {
     type = m_index->tpi().getType(ti);
     switch (type.kind()) {
     case LF_CLASS:
+    case LF_CLASS2:
     case LF_STRUCTURE:
+    case LF_STRUCTURE2:
     case LF_INTERFACE:
+    case LF_INTERFACE2:
       el.kind = CompilerContextKind::ClassOrStruct;
       continue;
     case LF_UNION:
+    case LF_UNION2:
       el.kind = CompilerContextKind::Union;
       continue;
     case LF_ENUM:
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp
index 417d7630a93753..b019d95d79b5b8 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp
@@ -70,11 +70,12 @@ clang::QualType UdtRecordCompleter::AddBaseClassForTypeIndex(
   clang::QualType qt = m_ast_builder.GetOrCreateClangType(type_id);
 
   CVType udt_cvt = m_index.tpi().getType(ti);
-
+  bool base_of_class =
+      udt_cvt.kind() == LF_CLASS || udt_cvt.kind() == LF_CLASS2;
   std::unique_ptr<clang::CXXBaseSpecifier> base_spec =
       m_ast_builder.clang().CreateBaseClassSpecifier(
           qt.getAsOpaquePtr(), TranslateMemberAccess(access),
-          vtable_idx.has_value(), udt_cvt.kind() == LF_CLASS);
+          vtable_idx.has_value(), base_of_class);
   if (!base_spec)
     return {};
 
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/lf-class2.cpp b/lldb/test/Shell/SymbolFile/NativePDB/lf-class2.cpp
new file mode 100644
index 00000000000000..f5508f87068637
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/NativePDB/lf-class2.cpp
@@ -0,0 +1,59 @@
+// Test that lldb can handle the LF_CLASS2/STRUCTURE2/UNION2 records.
+
+// clang-format off
+// REQUIRES: lld, x86
+
+// Test various interesting cases for AST reconstruction.
+// RUN: %clang_cl --target=x86_64-windows-msvc -mllvm=-use-codeview-tagrecord2 \
+// RUN:   -GS- -GR- -Od -Z7 -c /Fo%t.obj -- %s
+// RUN: lld-link -debug:full -nodefaultlib -entry:main %t.obj -out:%t.exe -pdb:%t.pdb
+// RUN: llvm-pdbutil dump --types %t.pdb | FileCheck --check-prefix=PDB %s
+// RUN: lldb-test symbols --dump-ast %t.exe | FileCheck --check-prefix=STRUCT %s
+// RUN: lldb-test symbols --dump-ast %t.exe | FileCheck --check-prefix=CLASS %s
+
+struct AStruct {
+  char c1;
+  virtual ~AStruct() = default;
+  virtual int something() { return 1; }
+};
+
+class AClass : public AStruct {
+public:
+  int i;
+  union Inner {
+    short s;
+    unsigned short u;
+  };
+  Inner inner;
+};
+
+void *operator new(unsigned long long n) { return nullptr; }
+void operator delete(void *p, unsigned long long i) {}
+
+int main() {
+  AClass a;
+  return a.inner.s;
+}
+
+// Make sure that we actually check the *2 records.
+// PDB-DAG: LF_CLASS2
+// PDB-DAG: LF_STRUCTURE2
+// PDB-DAG: LF_UNION2
+
+// CLASS:      class AClass : public AStruct {
+// CLASS-NEXT:     AClass();
+// CLASS-NEXT:     ~AClass();
+// CLASS-NEXT:     union Inner {
+// CLASS-NEXT:         short s;
+// CLASS-NEXT:         unsigned short u;
+// CLASS-NEXT:     };
+// CLASS-NEXT:     int i;
+// CLASS-NEXT:     AClass::Inner inner;
+// CLASS-NEXT: };
+
+// STRUCT:      struct AStruct {
+// STRUCT-NEXT:     AStruct();
+// STRUCT-NEXT:     virtual int something();
+// STRUCT-NEXT:     virtual ~AStruct();
+// STRUCT-NEXT:     char c1;
+// STRUCT-NEXT: };

``````````

</details>


https://github.com/llvm/llvm-project/pull/231615


More information about the lldb-commits mailing list