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 +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::V will show up + // in the global namespace. + template + struct V { + Param I = 8; + Param J = 9; + + using W = T::NestedTypedef; + using X = U; + }; + + struct W { + Param M = 12; + Param N = 13; + }; + Param K = 10; + Param L = 11; + using Y = V; + using Z = V; +}; + +constexpr S GlobalA; +constexpr S::NestedStruct GlobalB; +constexpr T GlobalC; +constexpr T::NestedStruct GlobalD; +constexpr T::U GlobalE; +constexpr U GlobalF; +constexpr U::V GlobalG; +constexpr U::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) GlobalF = { +// CHECK: (int) K = 10 +// CHECK: (int) L = 11 +// CHECK: } +// CHECK: (lldb) target variable -T GlobalG +// CHECK: (const U::V) 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 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::V definition +// CHECK: | |-FieldDecl {{.*}} I 'int' +// CHECK: | `-FieldDecl {{.*}} J 'int' 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 +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 &parents) - : index(index), parents(parents), parent(parent) {} + : index(index), parents(parents), parent(parent), + parent_cvt(parent_cvt) {} PdbIndex &index; llvm::DenseMap &parents; TypeIndex parent; + const CVTagRecord &parent_cvt; llvm::Error visitKnownMember(CVMemberRecord &CVR, NestedTypeRecord &Record) override { + llvm::Optional 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 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 scopes) { + for (llvm::ms_demangle::Node *n : scopes) { + auto *idn = static_cast(n); + if (idn->TemplateParams) + return true; + } + return false; +} + std::pair 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(scope); std::string str = RenderDemanglerNode(nii); Index: llvm/trunk/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp +++ llvm/trunk/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp @@ -89,6 +89,8 @@ } CVType LazyRandomTypeCollection::getType(TypeIndex Index) { + assert(!Index.isSimple()); + auto EC = ensureTypeExists(Index); error(std::move(EC)); assert(contains(Index)); @@ -97,6 +99,9 @@ } Optional 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); 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); }