This revision was automatically updated to reflect the committed changes.
Closed by commit rL346786: [NativePDB] Improved support for nested type
reconstruction. (authored by zturner, committed by ).
Herald added a subscriber: llvm-commits.
Changed prior to commit:
https://reviews.llvm.org/D54357?vs=173447&id=173902#toc
Repository:
rL LLVM
https://reviews.llvm.org/D54357
Files:
lldb/trunk/lit/SymbolFile/NativePDB/Inputs/nested-types.lldbinit
lldb/trunk/lit/SymbolFile/NativePDB/nested-types.cpp
lldb/trunk/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
llvm/trunk/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp
llvm/trunk/lib/DebugInfo/PDB/Native/TpiStream.cpp
Index: lldb/trunk/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
===================================================================
--- lldb/trunk/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ lldb/trunk/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -535,6 +535,49 @@
lldbassert(m_clang);
}
+static llvm::Optional<CVTagRecord>
+GetNestedTagRecord(const NestedTypeRecord &Record, const CVTagRecord &parent,
+ TpiStream &tpi) {
+ // An LF_NESTTYPE is essentially a nested typedef / using declaration, but it
+ // is also used to indicate the primary definition of a nested class. That is
+ // to say, if you have:
+ // struct A {
+ // struct B {};
+ // using C = B;
+ // };
+ // Then in the debug info, this will appear as:
+ // LF_STRUCTURE `A::B` [type index = N]
+ // LF_STRUCTURE `A`
+ // LF_NESTTYPE [name = `B`, index = N]
+ // LF_NESTTYPE [name = `C`, index = N]
+ // In order to accurately reconstruct the decl context hierarchy, we need to
+ // know which ones are actual definitions and which ones are just aliases.
+
+ // If it's a simple type, then this is something like `using foo = int`.
+ if (Record.Type.isSimple())
+ return llvm::None;
+
+ // If it's an inner definition, then treat whatever name we have here as a
+ // single component of a mangled name. So we can inject it into the parent's
+ // mangled name to see if it matches.
+ CVTagRecord child = CVTagRecord::create(tpi.getType(Record.Type));
+ std::string qname = parent.asTag().getUniqueName();
+ if (qname.size() < 4 || child.asTag().getUniqueName().size() < 4)
+ return llvm::None;
+
+ // qname[3] is the tag type identifier (struct, class, union, etc). Since the
+ // inner tag type is not necessarily the same as the outer tag type, re-write
+ // it to match the inner tag type.
+ qname[3] = child.asTag().getUniqueName()[3];
+ std::string piece = Record.Name;
+ piece.push_back('@');
+ qname.insert(4, std::move(piece));
+ if (qname != child.asTag().UniqueName)
+ return llvm::None;
+
+ return std::move(child);
+}
+
void SymbolFileNativePDB::PreprocessTpiStream() {
LazyRandomTypeCollection &types = m_index->tpi().typeCollection();
@@ -552,19 +595,27 @@
struct ProcessTpiStream : public TypeVisitorCallbacks {
ProcessTpiStream(PdbIndex &index, TypeIndex parent,
+ const CVTagRecord &parent_cvt,
llvm::DenseMap<TypeIndex, TypeIndex> &parents)
- : index(index), parents(parents), parent(parent) {}
+ : index(index), parents(parents), parent(parent),
+ parent_cvt(parent_cvt) {}
PdbIndex &index;
llvm::DenseMap<TypeIndex, TypeIndex> &parents;
TypeIndex parent;
+ const CVTagRecord &parent_cvt;
llvm::Error visitKnownMember(CVMemberRecord &CVR,
NestedTypeRecord &Record) override {
+ llvm::Optional<CVTagRecord> tag =
+ GetNestedTagRecord(Record, parent_cvt, index.tpi());
+ if (!tag)
+ return llvm::ErrorSuccess();
+
parents[Record.Type] = parent;
- CVType child = index.tpi().getType(Record.Type);
- if (!IsForwardRefUdt(child))
+ if (!tag->asTag().isForwardRef())
return llvm::ErrorSuccess();
+
llvm::Expected<TypeIndex> full_decl =
index.tpi().findFullDeclForForwardRef(Record.Type);
if (!full_decl) {
@@ -577,7 +628,7 @@
};
CVType field_list = m_index->tpi().getType(tag.asTag().FieldList);
- ProcessTpiStream process(*m_index, *ti, m_parent_types);
+ ProcessTpiStream process(*m_index, *ti, tag, m_parent_types);
llvm::Error error = visitMemberRecordStream(field_list.data(), process);
if (error)
llvm::consumeError(std::move(error));
@@ -792,6 +843,16 @@
return {OS.getBuffer()};
}
+static bool
+AnyScopesHaveTemplateParams(llvm::ArrayRef<llvm::ms_demangle::Node *> scopes) {
+ for (llvm::ms_demangle::Node *n : scopes) {
+ auto *idn = static_cast<llvm::ms_demangle::IdentifierNode *>(n);
+ if (idn->TemplateParams)
+ return true;
+ }
+ return false;
+}
+
std::pair<clang::DeclContext *, std::string>
SymbolFileNativePDB::CreateDeclInfoForType(const TagRecord &record,
TypeIndex ti) {
@@ -817,6 +878,14 @@
if (scopes.empty())
return {context, uname};
+ // If there is no parent in the debug info, but some of the scopes have
+ // template params, then this is a case of bad debug info. See, for
+ // example, llvm.org/pr39607. We don't want to create an ambiguity between
+ // a NamespaceDecl and a CXXRecordDecl, so instead we create a class at
+ // global scope with the fully qualified name.
+ if (AnyScopesHaveTemplateParams(scopes))
+ return {context, record.Name};
+
for (llvm::ms_demangle::Node *scope : scopes) {
auto *nii = static_cast<llvm::ms_demangle::NamedIdentifierNode *>(scope);
std::string str = RenderDemanglerNode(nii);
Index: lldb/trunk/lit/SymbolFile/NativePDB/Inputs/nested-types.lldbinit
===================================================================
--- lldb/trunk/lit/SymbolFile/NativePDB/Inputs/nested-types.lldbinit
+++ lldb/trunk/lit/SymbolFile/NativePDB/Inputs/nested-types.lldbinit
@@ -0,0 +1,12 @@
+settings set auto-one-line-summaries false
+
+target variable -T GlobalA
+target variable -T GlobalB
+target variable -T GlobalC
+target variable -T GlobalD
+target variable -T GlobalE
+target variable -T GlobalF
+target variable -T GlobalG
+target variable -T GlobalH
+
+target modules dump ast
Index: lldb/trunk/lit/SymbolFile/NativePDB/nested-types.cpp
===================================================================
--- lldb/trunk/lit/SymbolFile/NativePDB/nested-types.cpp
+++ lldb/trunk/lit/SymbolFile/NativePDB/nested-types.cpp
@@ -0,0 +1,139 @@
+// clang-format off
+// REQUIRES: lld
+
+// Test various interesting cases for AST reconstruction.
+// RUN: clang-cl /Z7 /GS- /GR- -Xclang -fkeep-static-consts /c /Fo%t.obj -- %s
+// RUN: lld-link /DEBUG /nodefaultlib /entry:main /OUT:%t.exe /PDB:%t.pdb -- %t.obj
+// RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb -f %t.exe -s \
+// RUN: %p/Inputs/nested-types.lldbinit 2>&1 | FileCheck %s
+
+struct S {
+ struct NestedStruct {
+ int A = 0;
+ int B = 1;
+ };
+ int C = 2;
+ int D = 3;
+};
+struct T {
+ using NestedTypedef = int;
+ using NestedTypedef2 = S;
+
+ struct NestedStruct {
+ int E = 4;
+ int F = 5;
+ };
+
+ using NestedStructAlias = NestedStruct;
+ using NST = S::NestedStruct;
+
+ NestedTypedef NT = 4;
+
+ using U = struct {
+ int G = 6;
+ int H = 7;
+ };
+};
+
+template<typename Param>
+class U {
+public:
+ // See llvm.org/pr39607. clang-cl currently doesn't emit an important debug
+ // info record for nested template instantiations, so we can't reconstruct
+ // a proper DeclContext hierarchy for these. As such, U<X>::V<Y> will show up
+ // in the global namespace.
+ template<typename Param>
+ struct V {
+ Param I = 8;
+ Param J = 9;
+
+ using W = T::NestedTypedef;
+ using X = U<int>;
+ };
+
+ struct W {
+ Param M = 12;
+ Param N = 13;
+ };
+ Param K = 10;
+ Param L = 11;
+ using Y = V<int>;
+ using Z = V<T>;
+};
+
+constexpr S GlobalA;
+constexpr S::NestedStruct GlobalB;
+constexpr T GlobalC;
+constexpr T::NestedStruct GlobalD;
+constexpr T::U GlobalE;
+constexpr U<int> GlobalF;
+constexpr U<int>::V<int> GlobalG;
+constexpr U<int>::W GlobalH;
+
+
+int main(int argc, char **argv) {
+ return 0;
+}
+
+
+
+// CHECK: (lldb) target variable -T GlobalA
+// CHECK: (const S) GlobalA = {
+// CHECK: (int) C = 2
+// CHECK: (int) D = 3
+// CHECK: }
+// CHECK: (lldb) target variable -T GlobalB
+// CHECK: (const S::NestedStruct) GlobalB = {
+// CHECK: (int) A = 0
+// CHECK: (int) B = 1
+// CHECK: }
+// CHECK: (lldb) target variable -T GlobalC
+// CHECK: (const T) GlobalC = {
+// CHECK: (int) NT = 4
+// CHECK: }
+// CHECK: (lldb) target variable -T GlobalD
+// CHECK: (const T::NestedStruct) GlobalD = {
+// CHECK: (int) E = 4
+// CHECK: (int) F = 5
+// CHECK: }
+// CHECK: (lldb) target variable -T GlobalE
+// CHECK: (const T::U) GlobalE = {
+// CHECK: (int) G = 6
+// CHECK: (int) H = 7
+// CHECK: }
+// CHECK: (lldb) target variable -T GlobalF
+// CHECK: (const U<int>) GlobalF = {
+// CHECK: (int) K = 10
+// CHECK: (int) L = 11
+// CHECK: }
+// CHECK: (lldb) target variable -T GlobalG
+// CHECK: (const U<int>::V<int>) GlobalG = {
+// CHECK: (int) I = 8
+// CHECK: (int) J = 9
+// CHECK: }
+// CHECK: (lldb) target modules dump ast
+// CHECK: Dumping clang ast for 1 modules.
+// CHECK: TranslationUnitDecl {{.*}}
+// CHECK: |-CXXRecordDecl {{.*}} struct S definition
+// CHECK: | |-FieldDecl {{.*}} C 'int'
+// CHECK: | |-FieldDecl {{.*}} D 'int'
+// CHECK: | `-CXXRecordDecl {{.*}} struct NestedStruct definition
+// CHECK: | |-FieldDecl {{.*}} A 'int'
+// CHECK: | `-FieldDecl {{.*}} B 'int'
+// CHECK: |-CXXRecordDecl {{.*}} struct T definition
+// CHECK: | |-FieldDecl {{.*}} NT 'int'
+// CHECK: | |-CXXRecordDecl {{.*}} struct NestedStruct definition
+// CHECK: | | |-FieldDecl {{.*}} E 'int'
+// CHECK: | | `-FieldDecl {{.*}} F 'int'
+// CHECK: | `-CXXRecordDecl {{.*}} struct U definition
+// CHECK: | |-FieldDecl {{.*}} G 'int'
+// CHECK: | `-FieldDecl {{.*}} H 'int'
+// CHECK: |-CXXRecordDecl {{.*}} class U<int> definition
+// CHECK: | |-FieldDecl {{.*}} K 'int'
+// CHECK: | |-FieldDecl {{.*}} L 'int'
+// CHECK: | `-CXXRecordDecl {{.*}} struct W definition
+// CHECK: | |-FieldDecl {{.*}} M 'int'
+// CHECK: | `-FieldDecl {{.*}} N 'int'
+// CHECK: |-CXXRecordDecl {{.*}} struct U<int>::V<int> definition
+// CHECK: | |-FieldDecl {{.*}} I 'int'
+// CHECK: | `-FieldDecl {{.*}} J 'int'
Index: llvm/trunk/lib/DebugInfo/PDB/Native/TpiStream.cpp
===================================================================
--- llvm/trunk/lib/DebugInfo/PDB/Native/TpiStream.cpp
+++ llvm/trunk/lib/DebugInfo/PDB/Native/TpiStream.cpp
@@ -215,6 +215,7 @@
}
codeview::CVType TpiStream::getType(codeview::TypeIndex Index) {
+ assert(!Index.isSimple());
return Types->getType(Index);
}
Index: llvm/trunk/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp
===================================================================
--- llvm/trunk/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp
+++ llvm/trunk/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp
@@ -89,14 +89,19 @@
}
CVType LazyRandomTypeCollection::getType(TypeIndex Index) {
+ assert(!Index.isSimple());
+
auto EC = ensureTypeExists(Index);
error(std::move(EC));
assert(contains(Index));
return Records[Index.toArrayIndex()].Type;
}
Optional<CVType> LazyRandomTypeCollection::tryGetType(TypeIndex Index) {
+ if (Index.isSimple())
+ return None;
+
if (auto EC = ensureTypeExists(Index)) {
consumeError(std::move(EC));
return None;
@@ -151,6 +156,7 @@
}
void LazyRandomTypeCollection::ensureCapacityFor(TypeIndex Index) {
+ assert(!Index.isSimple());
uint32_t MinSize = Index.toArrayIndex() + 1;
if (MinSize <= capacity())
@@ -163,6 +169,7 @@
}
Error LazyRandomTypeCollection::visitRangeForType(TypeIndex TI) {
+ assert(!TI.isSimple());
if (PartialOffsets.empty())
return fullScanForType(TI);
@@ -217,6 +224,7 @@
}
Error LazyRandomTypeCollection::fullScanForType(TypeIndex TI) {
+ assert(!TI.isSimple());
assert(PartialOffsets.empty());
TypeIndex CurrentTI = TypeIndex::fromArrayIndex(0);
_______________________________________________
lldb-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits