[Lldb-commits] [lldb] [llvm] [CodeView] Resolve all forward referenced types from TPI stream (PR #193064)
via lldb-commits
lldb-commits at lists.llvm.org
Mon Apr 20 13:55:47 PDT 2026
https://github.com/Nerixyz updated https://github.com/llvm/llvm-project/pull/193064
>From f8eb20441023e88637f4be6b6ceaf6870a9421d3 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Mon, 20 Apr 2026 20:28:27 +0200
Subject: [PATCH] [CodeView] Resolve all forward referenced types from TPI
stream
---
.../NativePDB/PdbAstBuilderClang.cpp | 11 +-
.../Plugins/SymbolFile/NativePDB/PdbUtil.cpp | 22 ++-
.../NativePDB/SymbolFileNativePDB.cpp | 14 +-
.../llvm/DebugInfo/PDB/Native/TpiStream.h | 17 ++-
llvm/lib/DebugInfo/PDB/Native/SymbolCache.cpp | 16 ++-
llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp | 15 ++-
.../tools/llvm-pdbutil/dump-forward-refs.test | 126 ++++++++++++++++++
llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp | 36 +++--
.../llvm-pdbutil/TypeReferenceTracker.cpp | 14 +-
9 files changed, 228 insertions(+), 43 deletions(-)
create mode 100644 llvm/test/tools/llvm-pdbutil/dump-forward-refs.test
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.cpp
index b07302bc7c7bf..b39e6051c0fe9 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.cpp
@@ -904,10 +904,13 @@ clang::FunctionDecl *PdbAstBuilderClang::CreateFunctionDecl(
TagRecord tag_record = CVTagRecord::create(parent_cvt).asTag();
// If it's a forward reference, try to get the real TypeIndex.
if (tag_record.isForwardRef()) {
- llvm::Expected<TypeIndex> eti =
- index.tpi().findFullDeclForForwardRef(class_index);
- if (eti) {
- tag_record = CVTagRecord::create(index.tpi().getType(*eti)).asTag();
+ llvm::SmallVector<TypeIndex, 2> tis;
+ llvm::Error err =
+ index.tpi().findFullDeclsForForwardRef(class_index, tis);
+ if (!err && !tis.empty()) {
+ // FIXME: Find correct type if `tis` has more than one type index.
+ tag_record =
+ CVTagRecord::create(index.tpi().getType(tis.front())).asTag();
}
}
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
index 5860d90eca689..b51539c122ed1 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
@@ -1133,7 +1133,15 @@ PdbTypeSymId lldb_private::npdb::GetBestPossibleDecl(PdbTypeSymId id,
if (!IsForwardRefUdt(cvt))
return id;
- return llvm::cantFail(tpi.findFullDeclForForwardRef(id.index));
+ llvm::SmallVector<TypeIndex, 2> tis;
+ llvm::Error err = tpi.findFullDeclsForForwardRef(id.index, tis);
+ if (err)
+ llvm::consumeError(std::move(err));
+ if (tis.empty())
+ return id;
+
+ // FIXME: Find correct type if `tis` has more than one type index.
+ return tis.front();
}
template <typename RecordType> static size_t GetSizeOfTypeInternal(CVType cvt) {
@@ -1162,8 +1170,16 @@ size_t lldb_private::npdb::GetSizeOfType(PdbTypeSymId id,
}
TypeIndex index = id.index;
- if (IsForwardRefUdt(index, tpi))
- index = llvm::cantFail(tpi.findFullDeclForForwardRef(index));
+ if (IsForwardRefUdt(index, tpi)) {
+ llvm::SmallVector<TypeIndex, 2> ids;
+ llvm::Error err = tpi.findFullDeclsForForwardRef(index, ids);
+ if (err)
+ llvm::consumeError(std::move(err));
+ if (!ids.empty()) {
+ // FIXME: Find correct type if `tis` has more than one type index.
+ index = ids.front();
+ }
+ }
CVType cvt = tpi.getType(index);
switch (cvt.kind()) {
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
index 176f1f992c02d..ebe294eccadf9 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -959,12 +959,14 @@ TypeSP SymbolFileNativePDB::CreateAndCacheType(PdbTypeSymId type_id) {
// decl and just map the forward ref uid to the full decl record.
std::optional<PdbTypeSymId> full_decl_uid;
if (IsForwardRefUdt(type_id, m_index->tpi())) {
- auto expected_full_ti =
- m_index->tpi().findFullDeclForForwardRef(type_id.index);
- if (!expected_full_ti)
- llvm::consumeError(expected_full_ti.takeError());
- else if (*expected_full_ti != type_id.index) {
- full_decl_uid = PdbTypeSymId(*expected_full_ti, false);
+ llvm::SmallVector<TypeIndex, 2> full_tis;
+ llvm::Error err =
+ m_index->tpi().findFullDeclsForForwardRef(type_id.index, full_tis);
+ if (err)
+ llvm::consumeError(std::move(err));
+ else if (!full_tis.empty()) {
+ // FIXME: Find correct type if `full_tis` has more than one type index.
+ full_decl_uid = PdbTypeSymId(full_tis.front(), false);
// It's possible that a lookup would occur for the full decl causing it
// to be cached, then a second lookup would occur for the forward decl.
diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h b/llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h
index deaa313c61329..ab7f8789f3d18 100644
--- a/llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h
+++ b/llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h
@@ -61,8 +61,21 @@ class TpiStream {
codeview::LazyRandomTypeCollection &typeCollection() { return *Types; }
- LLVM_ABI Expected<codeview::TypeIndex>
- findFullDeclForForwardRef(codeview::TypeIndex ForwardRefTI) const;
+ /// Find all possible declarations this \p ForwardRefTI is a forward reference
+ /// for and add them to \p Decls.
+ ///
+ /// This method uses the TPI hash table to find the referenced types.
+ ///
+ /// In most cases, this should return at most one declaration. However, in
+ /// incrementally linked PDBs, there might be multiple possible records \p
+ /// ForwardRefTI could reference. From the TPI stream alone, it's not known
+ /// which record is the "real" one.
+ ///
+ /// If no type could be found, \p Decls will remain unmodified and success is
+ /// returned.
+ LLVM_ABI Error
+ findFullDeclsForForwardRef(codeview::TypeIndex ForwardRefTI,
+ SmallVectorImpl<codeview::TypeIndex> &Decls) const;
LLVM_ABI std::vector<codeview::TypeIndex>
findRecordsByName(StringRef Name) const;
diff --git a/llvm/lib/DebugInfo/PDB/Native/SymbolCache.cpp b/llvm/lib/DebugInfo/PDB/Native/SymbolCache.cpp
index 535893b3f1a4c..3dbbb51d948ba 100644
--- a/llvm/lib/DebugInfo/PDB/Native/SymbolCache.cpp
+++ b/llvm/lib/DebugInfo/PDB/Native/SymbolCache.cpp
@@ -179,13 +179,15 @@ SymIndexId SymbolCache::findSymbolByTypeIndex(codeview::TypeIndex Index) const {
codeview::CVType CVT = Types.getType(Index);
if (isUdtForwardRef(CVT)) {
- Expected<TypeIndex> EFD = Tpi->findFullDeclForForwardRef(Index);
-
- if (!EFD)
- consumeError(EFD.takeError());
- else if (*EFD != Index) {
- assert(!isUdtForwardRef(Types.getType(*EFD)));
- SymIndexId Result = findSymbolByTypeIndex(*EFD);
+ llvm::SmallVector<TypeIndex, 2> TIs;
+ Error Err = Tpi->findFullDeclsForForwardRef(Index, TIs);
+
+ if (Err)
+ consumeError(std::move(Err));
+ else if (!TIs.empty()) {
+ assert(!isUdtForwardRef(Types.getType(TIs.front())));
+ // FIXME: Find correct type if `TIs` has more than one type index.
+ SymIndexId Result = findSymbolByTypeIndex(TIs.front());
// Record a mapping from ForwardRef -> SymIndex of complete type so that
// we'll take the fast path next time.
assert(TypeIndexToSymbolId.count(Index) == 0);
diff --git a/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp b/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp
index 24d2f9e3360ea..a64e2bc8ab409 100644
--- a/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp
+++ b/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp
@@ -175,14 +175,14 @@ std::vector<TypeIndex> TpiStream::findRecordsByName(StringRef Name) const {
bool TpiStream::supportsTypeLookup() const { return !HashMap.empty(); }
-Expected<TypeIndex>
-TpiStream::findFullDeclForForwardRef(TypeIndex ForwardRefTI) const {
+Error TpiStream::findFullDeclsForForwardRef(
+ TypeIndex ForwardRefTI, SmallVectorImpl<TypeIndex> &Decls) const {
if (!supportsTypeLookup())
- const_cast<TpiStream*>(this)->buildHashMap();
+ const_cast<TpiStream *>(this)->buildHashMap();
CVType F = Types->getType(ForwardRefTI);
if (!isUdtForwardRef(F))
- return ForwardRefTI;
+ return Error::success();
Expected<TagRecordHash> ForwardTRH = hashTagRecord(F);
if (!ForwardTRH)
@@ -205,16 +205,17 @@ TpiStream::findFullDeclForForwardRef(TypeIndex ForwardRefTI) const {
if (!ForwardTR.hasUniqueName()) {
if (ForwardTR.getName() == FullTR.getName())
- return TI;
+ Decls.emplace_back(TI);
continue;
}
if (!FullTR.hasUniqueName())
continue;
if (ForwardTR.getUniqueName() == FullTR.getUniqueName())
- return TI;
+ Decls.emplace_back(TI);
}
- return ForwardRefTI;
+
+ return Error::success();
}
codeview::CVType TpiStream::getType(codeview::TypeIndex Index) {
diff --git a/llvm/test/tools/llvm-pdbutil/dump-forward-refs.test b/llvm/test/tools/llvm-pdbutil/dump-forward-refs.test
new file mode 100644
index 0000000000000..74a8a9c1b7344
--- /dev/null
+++ b/llvm/test/tools/llvm-pdbutil/dump-forward-refs.test
@@ -0,0 +1,126 @@
+# RUN: llvm-pdbutil yaml2pdb %s --pdb=%t.pdb
+# RUN: llvm-pdbutil dump --types %t.pdb | FileCheck %s
+
+# CHECK: 0x1000 | LF_STRUCTURE [size = 36] `Foo`
+# CHECK-NEXT: unique name: `.?AUFoo@@`
+# CHECK-NEXT: vtable: <no type>, base list: <no type>, field list: <no type>
+# CHECK-NEXT: options: forward ref (-> 0x1008, -> 0x100A) | has unique name, sizeof 0
+
+# CHECK: 0x100B | LF_STRUCTURE [size = 36] `Bar`
+# CHECK-NEXT: unique name: `.?AUBar@@`
+# CHECK-NEXT: vtable: <no type>, base list: <no type>, field list: <no type>
+# CHECK-NEXT: options: forward ref (= 0x100B) | has unique name, sizeof 0
+
+---
+TpiStream:
+ Version: VC80
+ Records:
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: Foo
+ UniqueName: '.?AUFoo@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4096
+ Attrs: 65548
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4097 ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 116
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 1
+ ArgumentList: 4098
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 116
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 0
+ ArgumentList: 4100
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4096
+ ThisType: 4097
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 0
+ ArgumentList: 4100
+ ThisPointerAdjustment: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 116
+ FieldOffset: 0
+ Name: a
+ - Kind: LF_ONEMETHOD
+ OneMethod:
+ Type: 4102
+ Attrs: 259
+ VFTableOffset: -1
+ Name: Foo
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 2
+ Options: [ None, HasConstructorOrDestructor, HasUniqueName ]
+ FieldList: 4103
+ Name: Foo
+ UniqueName: '.?AUFoo@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 4
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4097
+ FieldOffset: 0
+ Name: ptr
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 116
+ FieldOffset: 8
+ Name: a
+ - Kind: LF_ONEMETHOD
+ OneMethod:
+ Type: 4102
+ Attrs: 259
+ VFTableOffset: -1
+ Name: Foo
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 3
+ Options: [ None, HasConstructorOrDestructor, HasUniqueName ]
+ FieldList: 4105
+ Name: Foo
+ UniqueName: '.?AUFoo@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 16
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: Bar
+ UniqueName: '.?AUBar@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+...
diff --git a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp
index fc29ec46180c4..db2d2c6091ca4 100644
--- a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp
+++ b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp
@@ -41,18 +41,34 @@ static std::string formatClassOptions(uint32_t IndentLevel,
!opts::dump::DontResolveForwardRefs &&
((Options & ClassOptions::ForwardReference) != ClassOptions::None)) {
// If we're able to resolve forward references, do that.
- Expected<TypeIndex> ETI =
- Stream->findFullDeclForForwardRef(CurrentTypeIndex);
- if (!ETI) {
- consumeError(ETI.takeError());
+ SmallVector<TypeIndex, 2> TIs;
+ Error Err = Stream->findFullDeclsForForwardRef(CurrentTypeIndex, TIs);
+ if (Err) {
+ consumeError(std::move(Err));
PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref (??\?)");
- } else {
- const char *Direction = (*ETI == CurrentTypeIndex)
- ? "="
- : ((*ETI < CurrentTypeIndex) ? "<-" : "->");
- std::string Formatted =
- formatv("forward ref ({0} {1})", Direction, *ETI).str();
+ } else if (!TIs.empty()) {
+ std::string Formatted = "forward ref (";
+ bool First = true;
+ for (TypeIndex TI : TIs) {
+ if (First)
+ First = false;
+ else
+ Formatted += ", ";
+
+ if (TI > CurrentTypeIndex)
+ Formatted += "-> ";
+ else if (TI < CurrentTypeIndex)
+ Formatted += "<- ";
+ else
+ Formatted += "= ";
+
+ Formatted += formatv("{0}", TI);
+ }
+ Formatted += ')';
PUSH_FLAG(ClassOptions, ForwardReference, Options, std::move(Formatted));
+ } else {
+ PUSH_FLAG(ClassOptions, ForwardReference, Options,
+ formatv("forward ref (= {0})", CurrentTypeIndex));
}
} else {
PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref");
diff --git a/llvm/tools/llvm-pdbutil/TypeReferenceTracker.cpp b/llvm/tools/llvm-pdbutil/TypeReferenceTracker.cpp
index c7653b7c06d48..9eb3ad5fb3ce3 100644
--- a/llvm/tools/llvm-pdbutil/TypeReferenceTracker.cpp
+++ b/llvm/tools/llvm-pdbutil/TypeReferenceTracker.cpp
@@ -153,10 +153,16 @@ void TypeReferenceTracker::markReferencedTypes() {
case LF_INTERFACE:
case LF_STRUCTURE:
case LF_UNION:
- case LF_ENUM:
- addOneTypeRef(TiRefKind::TypeRef,
- cantFail(Tpi->findFullDeclForForwardRef(RefTI)));
- break;
+ case LF_ENUM: {
+ SmallVector<TypeIndex, 2> TIs;
+ cantFail(Tpi->findFullDeclsForForwardRef(RefTI, TIs));
+ if (TIs.empty()) {
+ addOneTypeRef(TiRefKind::TypeRef, RefTI);
+ } else {
+ for (TypeIndex TI : TIs)
+ addOneTypeRef(TiRefKind::TypeRef, TI);
+ }
+ } break;
}
}
}
More information about the lldb-commits
mailing list