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
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to