[PDB] Add native reading support for UDT / class types.
authorZachary Turner <zturner@google.com>
Fri, 21 Sep 2018 22:36:04 +0000 (22:36 +0000)
committerZachary Turner <zturner@google.com>
Fri, 21 Sep 2018 22:36:04 +0000 (22:36 +0000)
This allows the native reader to find records of class/struct/
union type and dump them.  This behavior is tested by using the
diadump subcommand against golden output produced by actual DIA
SDK on the same PDB file, and again using pretty -native to
confirm that we actually dump the classes.  We don't find class
members or anything like that yet, for now it's just the class
itself.

llvm-svn: 342779

19 files changed:
llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h
llvm/include/llvm/DebugInfo/CodeView/TypeRecordHelpers.h [new file with mode: 0644]
llvm/include/llvm/DebugInfo/PDB/IPDBEnumChildren.h
llvm/include/llvm/DebugInfo/PDB/Native/NativeEnumTypes.h
llvm/include/llvm/DebugInfo/PDB/Native/NativeTypeEnum.h
llvm/include/llvm/DebugInfo/PDB/Native/NativeTypeUDT.h [new file with mode: 0644]
llvm/include/llvm/DebugInfo/PDB/Native/SymbolCache.h
llvm/lib/DebugInfo/CodeView/CMakeLists.txt
llvm/lib/DebugInfo/CodeView/TypeRecordHelpers.cpp [new file with mode: 0644]
llvm/lib/DebugInfo/PDB/CMakeLists.txt
llvm/lib/DebugInfo/PDB/Native/NativeEnumTypes.cpp
llvm/lib/DebugInfo/PDB/Native/NativeExeSymbol.cpp
llvm/lib/DebugInfo/PDB/Native/NativeRawSymbol.cpp
llvm/lib/DebugInfo/PDB/Native/NativeTypeEnum.cpp
llvm/lib/DebugInfo/PDB/Native/NativeTypeUDT.cpp [new file with mode: 0644]
llvm/lib/DebugInfo/PDB/Native/SymbolCache.cpp
llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp
llvm/test/DebugInfo/PDB/Native/pdb-native-udts.test [new file with mode: 0644]
llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp

index 61ebdf8..ee6f538 100644 (file)
@@ -923,6 +923,7 @@ public:
 
   uint32_t Signature;
 };
+
 } // end namespace codeview
 } // end namespace llvm
 
diff --git a/llvm/include/llvm/DebugInfo/CodeView/TypeRecordHelpers.h b/llvm/include/llvm/DebugInfo/CodeView/TypeRecordHelpers.h
new file mode 100644 (file)
index 0000000..389472e
--- /dev/null
@@ -0,0 +1,28 @@
+//===- TypeRecordHelpers.h --------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_DEBUGINFO_CODEVIEW_TYPERECORDHELPERS_H
+#define LLVM_DEBUGINFO_CODEVIEW_TYPERECORDHELPERS_H
+
+#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+
+namespace llvm {
+  namespace codeview {
+    /// Given an arbitrary codeview type, determine if it is an LF_STRUCTURE,
+    /// LF_CLASS, LF_INTERFACE, LF_UNION, or LF_ENUM with the forward ref class
+    /// option.
+    bool isUdtForwardRef(CVType CVT);
+
+    /// Given a CVType which is assumed to be an LF_MODIFIER, return the
+    /// TypeIndex of the type that the LF_MODIFIER modifies.
+    TypeIndex getModifiedType(const CVType &CVT);
+  }
+}
+
+#endif
index 14f1373..7017f26 100644 (file)
@@ -35,11 +35,9 @@ class NullEnumerator : public IPDBEnumChildren<ChildType> {
   virtual uint32_t getChildCount() const override { return 0; }
   virtual std::unique_ptr<ChildType>
   getChildAtIndex(uint32_t Index) const override {
-    assert(false);
     return nullptr;
   }
   virtual std::unique_ptr<ChildType> getNext() override {
-    assert(false);
     return nullptr;
   }
   virtual void reset() override {}
index 59d4361..492caf7 100644 (file)
@@ -26,7 +26,7 @@ class NativeEnumTypes : public IPDBEnumChildren<PDBSymbol> {
 public:
   NativeEnumTypes(NativeSession &Session,
                   codeview::LazyRandomTypeCollection &TypeCollection,
-                  codeview::TypeLeafKind Kind);
+                  std::vector<codeview::TypeLeafKind> Kinds);
 
   uint32_t getChildCount() const override;
   std::unique_ptr<PDBSymbol> getChildAtIndex(uint32_t Index) const override;
@@ -34,10 +34,6 @@ public:
   void reset() override;
 
 private:
-  NativeEnumTypes(NativeSession &Session,
-                  const std::vector<codeview::TypeIndex> &Matches,
-                  codeview::TypeLeafKind Kind);
-
   std::vector<codeview::TypeIndex> Matches;
   uint32_t Index;
   NativeSession &Session;
index 3c6915e..a5cbefc 100644 (file)
@@ -10,6 +10,7 @@
 #ifndef LLVM_DEBUGINFO_PDB_NATIVE_NATIVETYPEENUM_H
 #define LLVM_DEBUGINFO_PDB_NATIVE_NATIVETYPEENUM_H
 
+#include "llvm/ADT/Optional.h"
 #include "llvm/DebugInfo/CodeView/CodeView.h"
 #include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
 #include "llvm/DebugInfo/PDB/Native/NativeRawSymbol.h"
@@ -26,9 +27,8 @@ public:
                  codeview::EnumRecord Record);
 
   NativeTypeEnum(NativeSession &Session, SymIndexId Id,
-                 codeview::TypeIndex ModifierTI,
-                 codeview::ModifierRecord Modifier,
-                 codeview::EnumRecord EnumRecord);
+                 NativeTypeEnum &UnmodifiedType,
+                 codeview::ModifierRecord Modifier);
   ~NativeTypeEnum() override;
 
   void dump(raw_ostream &OS, int Indent, PdbSymbolIdField ShowIdFields,
@@ -60,10 +60,12 @@ public:
   bool isInterfaceUdt() const override;
 
   const NativeTypeBuiltin &getUnderlyingBuiltinType() const;
+  const codeview::EnumRecord &getEnumRecord() const { return *Record; }
 
 protected:
   codeview::TypeIndex Index;
-  codeview::EnumRecord Record;
+  Optional<codeview::EnumRecord> Record;
+  NativeTypeEnum *UnmodifiedType = nullptr;
   Optional<codeview::ModifierRecord> Modifiers;
 };
 
diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/NativeTypeUDT.h b/llvm/include/llvm/DebugInfo/PDB/Native/NativeTypeUDT.h
new file mode 100644 (file)
index 0000000..84821d8
--- /dev/null
@@ -0,0 +1,74 @@
+//===- NativeTypeUDT.h - info about class/struct type ------------*- C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_DEBUGINFO_PDB_NATIVE_NATIVETYPEUDT_H
+#define LLVM_DEBUGINFO_PDB_NATIVE_NATIVETYPEUDT_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/DebugInfo/CodeView/CodeView.h"
+#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/DebugInfo/PDB/Native/NativeRawSymbol.h"
+#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
+
+namespace llvm {
+namespace pdb {
+
+class NativeTypeUDT : public NativeRawSymbol {
+public:
+  NativeTypeUDT(NativeSession &Session, SymIndexId Id, codeview::TypeIndex TI,
+                codeview::ClassRecord Class);
+
+  NativeTypeUDT(NativeSession &Session, SymIndexId Id, codeview::TypeIndex TI,
+                codeview::UnionRecord Union);
+
+  NativeTypeUDT(NativeSession &Session, SymIndexId Id,
+                NativeTypeUDT &UnmodifiedType,
+                codeview::ModifierRecord Modifier);
+
+  ~NativeTypeUDT() override;
+
+  void dump(raw_ostream &OS, int Indent, PdbSymbolIdField ShowIdFields,
+            PdbSymbolIdField RecurseIdFields) const override;
+
+  std::string getName() const override;
+  SymIndexId getLexicalParentId() const override;
+  SymIndexId getUnmodifiedTypeId() const override;
+  SymIndexId getVirtualTableShapeId() const override;
+  uint64_t getLength() const override;
+  PDB_UdtType getUdtKind() const override;
+  bool hasConstructor() const override;
+  bool isConstType() const override;
+  bool hasAssignmentOperator() const override;
+  bool hasCastOperator() const override;
+  bool hasNestedTypes() const override;
+  bool hasOverloadedOperator() const override;
+  bool isInterfaceUdt() const override;
+  bool isIntrinsic() const override;
+  bool isNested() const override;
+  bool isPacked() const override;
+  bool isRefUdt() const override;
+  bool isScoped() const override;
+  bool isValueUdt() const override;
+  bool isUnalignedType() const override;
+  bool isVolatileType() const override;
+
+protected:
+  codeview::TypeIndex Index;
+
+  Optional<codeview::ClassRecord> Class;
+  Optional<codeview::UnionRecord> Union;
+  NativeTypeUDT *UnmodifiedType = nullptr;
+  codeview::TagRecord *Tag = nullptr;
+  Optional<codeview::ModifierRecord> Modifiers;
+};
+
+} // namespace pdb
+} // namespace llvm
+
+#endif // LLVM_DEBUGINFO_PDB_NATIVE_NATIVETYPEUDT_H
\ No newline at end of file
index 3ef01c4..c0ffff7 100644 (file)
@@ -77,6 +77,9 @@ public:
   std::unique_ptr<IPDBEnumSymbols>
   createTypeEnumerator(codeview::TypeLeafKind Kind);
 
+  std::unique_ptr<IPDBEnumSymbols>
+  createTypeEnumerator(std::vector<codeview::TypeLeafKind> Kinds);
+
   SymIndexId findSymbolByTypeIndex(codeview::TypeIndex TI);
 
   template <typename ConcreteSymbolT, typename... Args>
index 0515788..e1bdffd 100644 (file)
@@ -34,6 +34,7 @@ add_llvm_library(LLVMDebugInfoCodeView
   TypeIndex.cpp
   TypeIndexDiscovery.cpp
   TypeHashing.cpp
+  TypeRecordHelpers.cpp
   TypeRecordMapping.cpp
   TypeStreamMerger.cpp
   TypeTableCollection.cpp
diff --git a/llvm/lib/DebugInfo/CodeView/TypeRecordHelpers.cpp b/llvm/lib/DebugInfo/CodeView/TypeRecordHelpers.cpp
new file mode 100644 (file)
index 0000000..2a66474
--- /dev/null
@@ -0,0 +1,53 @@
+//===- TypeRecordHelpers.cpp ------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h"
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
+#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+
+using namespace llvm;
+using namespace llvm::codeview;
+
+template <typename RecordT> static ClassOptions getUdtOptions(CVType CVT) {
+  RecordT Record;
+  if (auto EC = TypeDeserializer::deserializeAs<RecordT>(CVT, Record)) {
+    consumeError(std::move(EC));
+    return ClassOptions::None;
+  }
+  return Record.getOptions();
+}
+
+bool llvm::codeview::isUdtForwardRef(CVType CVT) {
+  ClassOptions UdtOptions = ClassOptions::None;
+  switch (CVT.kind()) {
+  case LF_STRUCTURE:
+  case LF_CLASS:
+  case LF_INTERFACE:
+    UdtOptions = getUdtOptions<ClassRecord>(std::move(CVT));
+    break;
+  case LF_ENUM:
+    UdtOptions = getUdtOptions<EnumRecord>(std::move(CVT));
+    break;
+  case LF_UNION:
+    UdtOptions = getUdtOptions<UnionRecord>(std::move(CVT));
+    break;
+  default:
+    return false;
+  }
+  return (UdtOptions & ClassOptions::ForwardReference) != ClassOptions::None;
+}
+
+TypeIndex llvm::codeview::getModifiedType(const CVType &CVT) {
+  assert(CVT.kind() == LF_MODIFIER);
+  SmallVector<TypeIndex, 1> Refs;
+  discoverTypeIndices(CVT, Refs);
+  return Refs.front();
+}
index 8bf16c5..db39ba3 100644 (file)
@@ -55,6 +55,7 @@ add_pdb_impl_folder(Native
   Native/NativeTypeBuiltin.cpp
   Native/NativeTypeEnum.cpp
   Native/NativeTypePointer.cpp
+  Native/NativeTypeUDT.cpp
   Native/NamedStreamMap.cpp
   Native/NativeSession.cpp
   Native/PDBFile.cpp
index 97348a5..f16b776 100644 (file)
@@ -10,6 +10,7 @@
 #include "llvm/DebugInfo/PDB/Native/NativeEnumTypes.h"
 
 #include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h"
 #include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
 #include "llvm/DebugInfo/PDB/Native/NativeSession.h"
 #include "llvm/DebugInfo/PDB/Native/NativeTypeEnum.h"
@@ -22,21 +23,24 @@ using namespace llvm::pdb;
 
 NativeEnumTypes::NativeEnumTypes(NativeSession &PDBSession,
                                  LazyRandomTypeCollection &Types,
-                                 TypeLeafKind Kind)
+                                 std::vector<codeview::TypeLeafKind> Kinds)
     : Matches(), Index(0), Session(PDBSession) {
   Optional<TypeIndex> TI = Types.getFirst();
   while (TI) {
     CVType CVT = Types.getType(*TI);
     TypeLeafKind K = CVT.kind();
-    if (K == Kind)
-      Matches.push_back(*TI);
-    else if (K == TypeLeafKind::LF_MODIFIER) {
-      ModifierRecord MR;
-      if (auto EC = TypeDeserializer::deserializeAs<ModifierRecord>(CVT, MR)) {
-        consumeError(std::move(EC));
-      } else if (!MR.ModifiedType.isSimple()) {
-        CVType UnmodifiedCVT = Types.getType(MR.ModifiedType);
-        if (UnmodifiedCVT.kind() == Kind)
+    if (llvm::is_contained(Kinds, K)) {
+      // Don't add forward refs, we'll find those later while enumerating.
+      if (!isUdtForwardRef(CVT))
+        Matches.push_back(*TI);
+    } else if (K == TypeLeafKind::LF_MODIFIER) {
+      TypeIndex ModifiedTI = getModifiedType(CVT);
+      if (!ModifiedTI.isSimple()) {
+        CVType UnmodifiedCVT = Types.getType(ModifiedTI);
+        // LF_MODIFIERs point to forward refs, but don't worry about that
+        // here.  We're pushing the TypeIndex of the LF_MODIFIER itself,
+        // so we'll worry about resolving forward refs later.
+        if (llvm::is_contained(Kinds, UnmodifiedCVT.kind()))
           Matches.push_back(*TI);
       }
     }
@@ -44,11 +48,6 @@ NativeEnumTypes::NativeEnumTypes(NativeSession &PDBSession,
   }
 }
 
-NativeEnumTypes::NativeEnumTypes(NativeSession &PDBSession,
-                                 const std::vector<TypeIndex> &Matches,
-                                 TypeLeafKind Kind)
-    : Matches(Matches), Index(0), Session(PDBSession) {}
-
 uint32_t NativeEnumTypes::getChildCount() const {
   return static_cast<uint32_t>(Matches.size());
 }
index 97d20d4..ca17380 100644 (file)
@@ -45,6 +45,10 @@ NativeExeSymbol::findChildren(PDB_SymType Type) const {
     return Session.getSymbolCache().createTypeEnumerator(codeview::LF_ENUM);
   case PDB_SymType::PointerType:
     return Session.getSymbolCache().createTypeEnumerator(codeview::LF_POINTER);
+  case PDB_SymType::UDT:
+    return Session.getSymbolCache().createTypeEnumerator(
+        {codeview::LF_STRUCTURE, codeview::LF_CLASS, codeview::LF_UNION,
+         codeview::LF_INTERFACE});
   default:
     break;
   }
index 1a8c75d..62950cb 100644 (file)
@@ -31,68 +31,68 @@ void NativeRawSymbol::dump(raw_ostream &OS, int Indent,
 
 std::unique_ptr<IPDBEnumSymbols>
 NativeRawSymbol::findChildren(PDB_SymType Type) const {
-  return nullptr;
+  return llvm::make_unique<NullEnumerator<PDBSymbol>>();
 }
 
 std::unique_ptr<IPDBEnumSymbols>
 NativeRawSymbol::findChildren(PDB_SymType Type, StringRef Name,
     PDB_NameSearchFlags Flags) const {
-  return nullptr;
+  return llvm::make_unique<NullEnumerator<PDBSymbol>>();
 }
 
 std::unique_ptr<IPDBEnumSymbols>
 NativeRawSymbol::findChildrenByAddr(PDB_SymType Type, StringRef Name,
     PDB_NameSearchFlags Flags, uint32_t Section, uint32_t Offset) const {
-  return nullptr;
+  return llvm::make_unique<NullEnumerator<PDBSymbol>>();
 }
 
 std::unique_ptr<IPDBEnumSymbols>
 NativeRawSymbol::findChildrenByVA(PDB_SymType Type, StringRef Name,
    PDB_NameSearchFlags Flags, uint64_t VA) const {
-  return nullptr;
+  return llvm::make_unique<NullEnumerator<PDBSymbol>>();
 }
 
 std::unique_ptr<IPDBEnumSymbols>
 NativeRawSymbol::findChildrenByRVA(PDB_SymType Type, StringRef Name,
     PDB_NameSearchFlags Flags, uint32_t RVA) const {
-  return nullptr;
+  return llvm::make_unique<NullEnumerator<PDBSymbol>>();
 }
 
 std::unique_ptr<IPDBEnumSymbols>
 NativeRawSymbol::findInlineFramesByAddr(uint32_t Section,
                                         uint32_t Offset) const {
-  return nullptr;
+  return llvm::make_unique<NullEnumerator<PDBSymbol>>();
 }
 
 std::unique_ptr<IPDBEnumSymbols>
 NativeRawSymbol::findInlineFramesByRVA(uint32_t RVA) const {
-  return nullptr;
+  return llvm::make_unique<NullEnumerator<PDBSymbol>>();
 }
 
 std::unique_ptr<IPDBEnumSymbols>
 NativeRawSymbol::findInlineFramesByVA(uint64_t VA) const {
-  return nullptr;
+  return llvm::make_unique<NullEnumerator<PDBSymbol>>();
 }
 
 std::unique_ptr<IPDBEnumLineNumbers>
 NativeRawSymbol::findInlineeLines() const {
-  return nullptr;
+  return llvm::make_unique<NullEnumerator<IPDBLineNumber>>();
 }
 
 std::unique_ptr<IPDBEnumLineNumbers>
 NativeRawSymbol::findInlineeLinesByAddr(uint32_t Section, uint32_t Offset,
                                         uint32_t Length) const {
-  return nullptr;
+  return llvm::make_unique<NullEnumerator<IPDBLineNumber>>();
 }
 
 std::unique_ptr<IPDBEnumLineNumbers>
 NativeRawSymbol::findInlineeLinesByRVA(uint32_t RVA, uint32_t Length) const {
-  return nullptr;
+  return llvm::make_unique<NullEnumerator<IPDBLineNumber>>();
 }
 
 std::unique_ptr<IPDBEnumLineNumbers>
 NativeRawSymbol::findInlineeLinesByVA(uint64_t VA, uint32_t Length) const {
-  return nullptr;
+  return llvm::make_unique<NullEnumerator<IPDBLineNumber>>();
 }
 
 void NativeRawSymbol::getDataBytes(SmallVector<uint8_t, 32> &bytes) const {
index 0994be4..37176fe 100644 (file)
@@ -40,8 +40,7 @@ namespace {
 class NativeEnumEnumEnumerators : public IPDBEnumSymbols, TypeVisitorCallbacks {
 public:
   NativeEnumEnumEnumerators(NativeSession &Session,
-                            const NativeTypeEnum &ClassParent,
-                            const codeview::EnumRecord &CVEnum);
+                            const NativeTypeEnum &ClassParent);
 
   uint32_t getChildCount() const override;
   std::unique_ptr<PDBSymbol> getChildAtIndex(uint32_t Index) const override;
@@ -56,7 +55,6 @@ private:
 
   NativeSession &Session;
   const NativeTypeEnum &ClassParent;
-  const codeview::EnumRecord &CVEnum;
   std::vector<EnumeratorRecord> Enumerators;
   Optional<TypeIndex> ContinuationIndex;
   uint32_t Index = 0;
@@ -64,13 +62,12 @@ private:
 } // namespace
 
 NativeEnumEnumEnumerators::NativeEnumEnumEnumerators(
-    NativeSession &Session, const NativeTypeEnum &ClassParent,
-    const codeview::EnumRecord &CVEnum)
-    : Session(Session), ClassParent(ClassParent), CVEnum(CVEnum) {
+    NativeSession &Session, const NativeTypeEnum &ClassParent)
+    : Session(Session), ClassParent(ClassParent) {
   TpiStream &Tpi = cantFail(Session.getPDBFile().getPDBTpiStream());
   LazyRandomTypeCollection &Types = Tpi.typeCollection();
 
-  ContinuationIndex = CVEnum.FieldList;
+  ContinuationIndex = ClassParent.getEnumRecord().FieldList;
   while (ContinuationIndex) {
     CVType FieldList = Types.getType(*ContinuationIndex);
     assert(FieldList.kind() == LF_FIELDLIST);
@@ -100,10 +97,10 @@ NativeEnumEnumEnumerators::getChildAtIndex(uint32_t Index) const {
   if (Index >= getChildCount())
     return nullptr;
 
-  SymIndexId Id =
-      Session.getSymbolCache()
-          .getOrCreateFieldListMember<NativeSymbolEnumerator>(
-              CVEnum.FieldList, Index, ClassParent, Enumerators[Index]);
+  SymIndexId Id = Session.getSymbolCache()
+                      .getOrCreateFieldListMember<NativeSymbolEnumerator>(
+                          ClassParent.getEnumRecord().FieldList, Index,
+                          ClassParent, Enumerators[Index]);
   return Session.getSymbolCache().getSymbolById(Id);
 }
 
@@ -122,11 +119,10 @@ NativeTypeEnum::NativeTypeEnum(NativeSession &Session, SymIndexId Id,
       Record(std::move(Record)) {}
 
 NativeTypeEnum::NativeTypeEnum(NativeSession &Session, SymIndexId Id,
-                               codeview::TypeIndex ModifierTI,
-                               codeview::ModifierRecord Modifier,
-                               codeview::EnumRecord EnumRecord)
-    : NativeRawSymbol(Session, PDB_SymType::Enum, Id), Index(ModifierTI),
-      Record(std::move(EnumRecord)), Modifiers(std::move(Modifier)) {}
+                               NativeTypeEnum &UnmodifiedType,
+                               codeview::ModifierRecord Modifier)
+    : NativeRawSymbol(Session, PDB_SymType::Enum, Id),
+      UnmodifiedType(&UnmodifiedType), Modifiers(std::move(Modifier)) {}
 
 NativeTypeEnum::~NativeTypeEnum() {}
 
@@ -173,22 +169,20 @@ NativeTypeEnum::findChildren(PDB_SymType Type) const {
   const NativeTypeEnum *ClassParent = nullptr;
   if (!Modifiers)
     ClassParent = this;
-  else {
-    NativeRawSymbol &NRS =
-        Session.getSymbolCache().getNativeSymbolById(getUnmodifiedTypeId());
-    assert(NRS.getSymTag() == PDB_SymType::Enum);
-    ClassParent = static_cast<NativeTypeEnum *>(&NRS);
-  }
-  return llvm::make_unique<NativeEnumEnumEnumerators>(Session, *ClassParent,
-                                                      Record);
+  else
+    ClassParent = UnmodifiedType;
+  return llvm::make_unique<NativeEnumEnumEnumerators>(Session, *ClassParent);
 }
 
 PDB_SymType NativeTypeEnum::getSymTag() const { return PDB_SymType::Enum; }
 
 PDB_BuiltinType NativeTypeEnum::getBuiltinType() const {
-  Session.getSymbolCache().findSymbolByTypeIndex(Record.getUnderlyingType());
+  if (UnmodifiedType)
+    return UnmodifiedType->getBuiltinType();
+
+  Session.getSymbolCache().findSymbolByTypeIndex(Record->getUnderlyingType());
 
-  codeview::TypeIndex Underlying = Record.getUnderlyingType();
+  codeview::TypeIndex Underlying = Record->getUnderlyingType();
 
   // This indicates a corrupt record.
   if (!Underlying.isSimple() ||
@@ -255,67 +249,101 @@ PDB_BuiltinType NativeTypeEnum::getBuiltinType() const {
 }
 
 SymIndexId NativeTypeEnum::getUnmodifiedTypeId() const {
-  if (!Modifiers)
-    return 0;
-
-  return Session.getSymbolCache().findSymbolByTypeIndex(
-      Modifiers->ModifiedType);
+  return UnmodifiedType ? UnmodifiedType->getSymIndexId() : 0;
 }
 
 bool NativeTypeEnum::hasConstructor() const {
-  return bool(Record.getOptions() &
+  if (UnmodifiedType)
+    return UnmodifiedType->hasConstructor();
+
+  return bool(Record->getOptions() &
               codeview::ClassOptions::HasConstructorOrDestructor);
 }
 
 bool NativeTypeEnum::hasAssignmentOperator() const {
-  return bool(Record.getOptions() &
+  if (UnmodifiedType)
+    return UnmodifiedType->hasAssignmentOperator();
+
+  return bool(Record->getOptions() &
               codeview::ClassOptions::HasOverloadedAssignmentOperator);
 }
 
 bool NativeTypeEnum::hasNestedTypes() const {
-  return bool(Record.getOptions() &
+  if (UnmodifiedType)
+    return UnmodifiedType->hasNestedTypes();
+
+  return bool(Record->getOptions() &
               codeview::ClassOptions::ContainsNestedClass);
 }
 
 bool NativeTypeEnum::isIntrinsic() const {
-  return bool(Record.getOptions() & codeview::ClassOptions::Intrinsic);
+  if (UnmodifiedType)
+    return UnmodifiedType->isIntrinsic();
+
+  return bool(Record->getOptions() & codeview::ClassOptions::Intrinsic);
 }
 
 bool NativeTypeEnum::hasCastOperator() const {
-  return bool(Record.getOptions() &
+  if (UnmodifiedType)
+    return UnmodifiedType->hasCastOperator();
+
+  return bool(Record->getOptions() &
               codeview::ClassOptions::HasConversionOperator);
 }
 
 uint64_t NativeTypeEnum::getLength() const {
+  if (UnmodifiedType)
+    return UnmodifiedType->getLength();
+
   const auto Id = Session.getSymbolCache().findSymbolByTypeIndex(
-      Record.getUnderlyingType());
+      Record->getUnderlyingType());
   const auto UnderlyingType =
       Session.getConcreteSymbolById<PDBSymbolTypeBuiltin>(Id);
   return UnderlyingType ? UnderlyingType->getLength() : 0;
 }
 
-std::string NativeTypeEnum::getName() const { return Record.getName(); }
+std::string NativeTypeEnum::getName() const {
+  if (UnmodifiedType)
+    return UnmodifiedType->getName();
+
+  return Record->getName();
+}
 
 bool NativeTypeEnum::isNested() const {
-  return bool(Record.getOptions() & codeview::ClassOptions::Nested);
+  if (UnmodifiedType)
+    return UnmodifiedType->isNested();
+
+  return bool(Record->getOptions() & codeview::ClassOptions::Nested);
 }
 
 bool NativeTypeEnum::hasOverloadedOperator() const {
-  return bool(Record.getOptions() &
+  if (UnmodifiedType)
+    return UnmodifiedType->hasOverloadedOperator();
+
+  return bool(Record->getOptions() &
               codeview::ClassOptions::HasOverloadedOperator);
 }
 
 bool NativeTypeEnum::isPacked() const {
-  return bool(Record.getOptions() & codeview::ClassOptions::Packed);
+  if (UnmodifiedType)
+    return UnmodifiedType->isPacked();
+
+  return bool(Record->getOptions() & codeview::ClassOptions::Packed);
 }
 
 bool NativeTypeEnum::isScoped() const {
-  return bool(Record.getOptions() & codeview::ClassOptions::Scoped);
+  if (UnmodifiedType)
+    return UnmodifiedType->isScoped();
+
+  return bool(Record->getOptions() & codeview::ClassOptions::Scoped);
 }
 
 SymIndexId NativeTypeEnum::getTypeId() const {
+  if (UnmodifiedType)
+    return UnmodifiedType->getTypeId();
+
   return Session.getSymbolCache().findSymbolByTypeIndex(
-      Record.getUnderlyingType());
+      Record->getUnderlyingType());
 }
 
 bool NativeTypeEnum::isRefUdt() const { return false; }
@@ -346,6 +374,9 @@ bool NativeTypeEnum::isUnalignedType() const {
 }
 
 const NativeTypeBuiltin &NativeTypeEnum::getUnderlyingBuiltinType() const {
+  if (UnmodifiedType)
+    return UnmodifiedType->getUnderlyingBuiltinType();
+
   return Session.getSymbolCache().getNativeSymbolById<NativeTypeBuiltin>(
       getTypeId());
 }
diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeTypeUDT.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeTypeUDT.cpp
new file mode 100644 (file)
index 0000000..3abf91d
--- /dev/null
@@ -0,0 +1,221 @@
+//===- NativeTypeUDT.cpp - info about class/struct type ---------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/PDB/Native/NativeTypeUDT.h"
+
+#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+
+#include <cassert>
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+NativeTypeUDT::NativeTypeUDT(NativeSession &Session, SymIndexId Id,
+                             codeview::TypeIndex TI, codeview::ClassRecord CR)
+    : NativeRawSymbol(Session, PDB_SymType::UDT, Id), Index(TI),
+      Class(std::move(CR)), Tag(Class.getPointer()) {}
+
+NativeTypeUDT::NativeTypeUDT(NativeSession &Session, SymIndexId Id,
+                             codeview::TypeIndex TI, codeview::UnionRecord UR)
+    : NativeRawSymbol(Session, PDB_SymType::UDT, Id), Index(TI),
+      Union(std::move(UR)), Tag(Union.getPointer()) {}
+
+NativeTypeUDT::NativeTypeUDT(NativeSession &Session, SymIndexId Id,
+                             NativeTypeUDT &UnmodifiedType,
+                             codeview::ModifierRecord Modifier)
+    : NativeRawSymbol(Session, PDB_SymType::UDT, Id),
+      UnmodifiedType(&UnmodifiedType), Modifiers(std::move(Modifier)) {}
+
+NativeTypeUDT::~NativeTypeUDT() {}
+
+void NativeTypeUDT::dump(raw_ostream &OS, int Indent,
+                         PdbSymbolIdField ShowIdFields,
+                         PdbSymbolIdField RecurseIdFields) const {
+
+  NativeRawSymbol::dump(OS, Indent, ShowIdFields, RecurseIdFields);
+
+  dumpSymbolField(OS, "name", getName(), Indent);
+  dumpSymbolIdField(OS, "lexicalParentId", 0, Indent, Session,
+                    PdbSymbolIdField::LexicalParent, ShowIdFields,
+                    RecurseIdFields);
+  if (Modifiers.hasValue())
+    dumpSymbolIdField(OS, "unmodifiedTypeId", getUnmodifiedTypeId(), Indent,
+                      Session, PdbSymbolIdField::UnmodifiedType, ShowIdFields,
+                      RecurseIdFields);
+  if (getUdtKind() != PDB_UdtType::Union)
+    dumpSymbolField(OS, "virtualTableShapeId", getVirtualTableShapeId(),
+                    Indent);
+  dumpSymbolField(OS, "length", getLength(), Indent);
+  dumpSymbolField(OS, "udtKind", getUdtKind(), Indent);
+  dumpSymbolField(OS, "constructor", hasConstructor(), Indent);
+  dumpSymbolField(OS, "constType", isConstType(), Indent);
+  dumpSymbolField(OS, "hasAssignmentOperator", hasAssignmentOperator(), Indent);
+  dumpSymbolField(OS, "hasCastOperator", hasCastOperator(), Indent);
+  dumpSymbolField(OS, "hasNestedTypes", hasNestedTypes(), Indent);
+  dumpSymbolField(OS, "overloadedOperator", hasOverloadedOperator(), Indent);
+  dumpSymbolField(OS, "isInterfaceUdt", isInterfaceUdt(), Indent);
+  dumpSymbolField(OS, "intrinsic", isIntrinsic(), Indent);
+  dumpSymbolField(OS, "nested", isNested(), Indent);
+  dumpSymbolField(OS, "packed", isPacked(), Indent);
+  dumpSymbolField(OS, "isRefUdt", isRefUdt(), Indent);
+  dumpSymbolField(OS, "scoped", isScoped(), Indent);
+  dumpSymbolField(OS, "unalignedType", isUnalignedType(), Indent);
+  dumpSymbolField(OS, "isValueUdt", isValueUdt(), Indent);
+  dumpSymbolField(OS, "volatileType", isVolatileType(), Indent);
+}
+
+std::string NativeTypeUDT::getName() const {
+  if (UnmodifiedType)
+    return UnmodifiedType->getName();
+
+  return Tag->getName();
+}
+
+SymIndexId NativeTypeUDT::getLexicalParentId() const { return 0; }
+
+SymIndexId NativeTypeUDT::getUnmodifiedTypeId() const {
+  if (UnmodifiedType)
+    return UnmodifiedType->getSymIndexId();
+
+  return 0;
+}
+
+SymIndexId NativeTypeUDT::getVirtualTableShapeId() const {
+  if (UnmodifiedType)
+    return UnmodifiedType->getVirtualTableShapeId();
+
+  if (Class)
+    return Session.getSymbolCache().findSymbolByTypeIndex(Class->VTableShape);
+
+  return 0;
+}
+
+uint64_t NativeTypeUDT::getLength() const {
+  if (UnmodifiedType)
+    return UnmodifiedType->getLength();
+
+  if (Class)
+    return Class->getSize();
+
+  return Union->getSize();
+}
+
+PDB_UdtType NativeTypeUDT::getUdtKind() const {
+  if (UnmodifiedType)
+    return UnmodifiedType->getUdtKind();
+
+  switch (Tag->Kind) {
+  case TypeRecordKind::Class:
+    return PDB_UdtType::Class;
+  case TypeRecordKind::Union:
+    return PDB_UdtType::Union;
+  case TypeRecordKind::Struct:
+    return PDB_UdtType::Struct;
+  case TypeRecordKind::Interface:
+    return PDB_UdtType::Interface;
+  default:
+    llvm_unreachable("Unexected udt kind");
+  }
+}
+
+bool NativeTypeUDT::hasConstructor() const {
+  if (UnmodifiedType)
+    return UnmodifiedType->hasConstructor();
+
+  return (Tag->Options & ClassOptions::HasConstructorOrDestructor) !=
+         ClassOptions::None;
+}
+
+bool NativeTypeUDT::isConstType() const {
+  if (!Modifiers)
+    return false;
+  return (Modifiers->Modifiers & ModifierOptions::Const) !=
+         ModifierOptions::None;
+}
+
+bool NativeTypeUDT::hasAssignmentOperator() const {
+  if (UnmodifiedType)
+    return UnmodifiedType->hasAssignmentOperator();
+
+  return (Tag->Options & ClassOptions::HasOverloadedAssignmentOperator) !=
+         ClassOptions::None;
+}
+
+bool NativeTypeUDT::hasCastOperator() const {
+  if (UnmodifiedType)
+    return UnmodifiedType->hasCastOperator();
+
+  return (Tag->Options & ClassOptions::HasConversionOperator) !=
+         ClassOptions::None;
+}
+
+bool NativeTypeUDT::hasNestedTypes() const {
+  if (UnmodifiedType)
+    return UnmodifiedType->hasNestedTypes();
+
+  return (Tag->Options & ClassOptions::ContainsNestedClass) !=
+         ClassOptions::None;
+}
+
+bool NativeTypeUDT::hasOverloadedOperator() const {
+  if (UnmodifiedType)
+    return UnmodifiedType->hasOverloadedOperator();
+
+  return (Tag->Options & ClassOptions::HasOverloadedOperator) !=
+         ClassOptions::None;
+}
+
+bool NativeTypeUDT::isInterfaceUdt() const { return false; }
+
+bool NativeTypeUDT::isIntrinsic() const {
+  if (UnmodifiedType)
+    return UnmodifiedType->isIntrinsic();
+
+  return (Tag->Options & ClassOptions::Intrinsic) != ClassOptions::None;
+}
+
+bool NativeTypeUDT::isNested() const {
+  if (UnmodifiedType)
+    return UnmodifiedType->isNested();
+
+  return (Tag->Options & ClassOptions::Nested) != ClassOptions::None;
+}
+
+bool NativeTypeUDT::isPacked() const {
+  if (UnmodifiedType)
+    return UnmodifiedType->isPacked();
+
+  return (Tag->Options & ClassOptions::Packed) != ClassOptions::None;
+}
+
+bool NativeTypeUDT::isRefUdt() const { return false; }
+
+bool NativeTypeUDT::isScoped() const {
+  if (UnmodifiedType)
+    return UnmodifiedType->isScoped();
+
+  return (Tag->Options & ClassOptions::Scoped) != ClassOptions::None;
+}
+
+bool NativeTypeUDT::isValueUdt() const { return false; }
+
+bool NativeTypeUDT::isUnalignedType() const {
+  if (!Modifiers)
+    return false;
+  return (Modifiers->Modifiers & ModifierOptions::Unaligned) !=
+         ModifierOptions::None;
+}
+
+bool NativeTypeUDT::isVolatileType() const {
+  if (!Modifiers)
+    return false;
+  return (Modifiers->Modifiers & ModifierOptions::Volatile) !=
+         ModifierOptions::None;
+}
index d714177..4fd7adf 100644 (file)
@@ -1,6 +1,7 @@
 #include "llvm/DebugInfo/PDB/Native/SymbolCache.h"
 
 #include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h"
 #include "llvm/DebugInfo/PDB/Native/DbiStream.h"
 #include "llvm/DebugInfo/PDB/Native/NativeCompilandSymbol.h"
 #include "llvm/DebugInfo/PDB/Native/NativeEnumTypes.h"
@@ -9,6 +10,7 @@
 #include "llvm/DebugInfo/PDB/Native/NativeTypeBuiltin.h"
 #include "llvm/DebugInfo/PDB/Native/NativeTypeEnum.h"
 #include "llvm/DebugInfo/PDB/Native/NativeTypePointer.h"
+#include "llvm/DebugInfo/PDB/Native/NativeTypeUDT.h"
 #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
 #include "llvm/DebugInfo/PDB/Native/TpiStream.h"
 #include "llvm/DebugInfo/PDB/PDBSymbol.h"
@@ -50,10 +52,18 @@ SymbolCache::SymbolCache(NativeSession &Session, DbiStream *Dbi)
 
   if (Dbi)
     Compilands.resize(Dbi->modules().getModuleCount());
+
+  auto &Tpi = cantFail(Session.getPDBFile().getPDBTpiStream());
+  Tpi.buildHashMap();
+}
+
+std::unique_ptr<IPDBEnumSymbols>
+SymbolCache::createTypeEnumerator(TypeLeafKind Kind) {
+  return createTypeEnumerator(std::vector<TypeLeafKind>{Kind});
 }
 
 std::unique_ptr<IPDBEnumSymbols>
-SymbolCache::createTypeEnumerator(codeview::TypeLeafKind Kind) {
+SymbolCache::createTypeEnumerator(std::vector<TypeLeafKind> Kinds) {
   auto Tpi = Session.getPDBFile().getPDBTpiStream();
   if (!Tpi) {
     consumeError(Tpi.takeError());
@@ -61,7 +71,7 @@ SymbolCache::createTypeEnumerator(codeview::TypeLeafKind Kind) {
   }
   auto &Types = Tpi->typeCollection();
   return std::unique_ptr<IPDBEnumSymbols>(
-      new NativeEnumTypes(Session, Types, Kind));
+      new NativeEnumTypes(Session, Types, std::move(Kinds)));
 }
 
 SymIndexId SymbolCache::createSimpleType(TypeIndex Index,
@@ -98,38 +108,24 @@ SymbolCache::createSymbolForModifiedType(codeview::TypeIndex ModifierTI,
   if (Record.ModifiedType.isSimple())
     return createSimpleType(Record.ModifiedType, Record.Modifiers);
 
-  auto Tpi = Session.getPDBFile().getPDBTpiStream();
-  if (!Tpi) {
-    consumeError(Tpi.takeError());
-    return 0;
-  }
-  codeview::LazyRandomTypeCollection &Types = Tpi->typeCollection();
-
-  codeview::CVType UnmodifiedType = Types.getType(Record.ModifiedType);
-
-  switch (UnmodifiedType.kind()) {
-  case LF_ENUM: {
-    EnumRecord ER;
-    if (auto EC =
-            TypeDeserializer::deserializeAs<EnumRecord>(UnmodifiedType, ER)) {
-      consumeError(std::move(EC));
-      return 0;
-    }
-    return createSymbol<NativeTypeEnum>(Record.ModifiedType, std::move(Record),
-                                        std::move(ER));
-  }
-  case LF_STRUCTURE:
-  case LF_UNION:
-  case LF_CLASS:
-    // FIXME: Handle these
-    break;
+  // Make sure we create and cache a record for the unmodified type.
+  SymIndexId UnmodifiedId = findSymbolByTypeIndex(Record.ModifiedType);
+  NativeRawSymbol &UnmodifiedNRS = *Cache[UnmodifiedId];
+
+  switch (UnmodifiedNRS.getSymTag()) {
+  case PDB_SymType::Enum:
+    return createSymbol<NativeTypeEnum>(
+        static_cast<NativeTypeEnum &>(UnmodifiedNRS), std::move(Record));
+  case PDB_SymType::UDT:
+    return createSymbol<NativeTypeUDT>(
+        static_cast<NativeTypeUDT &>(UnmodifiedNRS), std::move(Record));
   default:
     // No other types can be modified.  (LF_POINTER, for example, records
     // its modifiers a different way.
     assert(false && "Invalid LF_MODIFIER record");
     break;
   }
-  return createSymbolPlaceholder();
+  return 0;
 }
 
 SymIndexId SymbolCache::findSymbolByTypeIndex(codeview::TypeIndex Index) {
@@ -150,13 +146,37 @@ SymIndexId SymbolCache::findSymbolByTypeIndex(codeview::TypeIndex Index) {
   }
   codeview::LazyRandomTypeCollection &Types = Tpi->typeCollection();
   codeview::CVType CVT = Types.getType(Index);
-  // TODO(amccarth):  Make this handle all types.
-  SymIndexId Id = 0;
 
+  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);
+      // Record a mapping from ForwardRef -> SymIndex of complete type so that
+      // we'll take the fast path next time.
+      TypeIndexToSymbolId[Index] = Result;
+      return Result;
+    }
+  }
+
+  // At this point if we still have a forward ref udt it means the full decl was
+  // not in the PDB.  We just have to deal with it and use the forward ref.
+  SymIndexId Id = 0;
   switch (CVT.kind()) {
   case codeview::LF_ENUM:
     Id = createSymbolForType<NativeTypeEnum, EnumRecord>(Index, std::move(CVT));
     break;
+  case codeview::LF_CLASS:
+  case codeview::LF_STRUCTURE:
+  case codeview::LF_INTERFACE:
+    Id = createSymbolForType<NativeTypeUDT, ClassRecord>(Index, std::move(CVT));
+    break;
+  case codeview::LF_UNION:
+    Id = createSymbolForType<NativeTypeUDT, UnionRecord>(Index, std::move(CVT));
+    break;
   case codeview::LF_POINTER:
     Id = createSymbolForType<NativeTypePointer, PointerRecord>(Index,
                                                                std::move(CVT));
index 311c385..96221f7 100644 (file)
@@ -11,9 +11,8 @@
 
 #include "llvm/ADT/iterator_range.h"
 #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
-#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
-#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
 #include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h"
 #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
 #include "llvm/DebugInfo/PDB/Native/Hash.h"
 #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
@@ -161,35 +160,6 @@ void TpiStream::buildHashMap() {
 
 bool TpiStream::supportsTypeLookup() const { return !HashMap.empty(); }
 
-template <typename RecordT> static ClassOptions getUdtOptions(CVType CVT) {
-  RecordT Record;
-  if (auto EC = TypeDeserializer::deserializeAs<RecordT>(CVT, Record)) {
-    consumeError(std::move(EC));
-    return ClassOptions::None;
-  }
-  return Record.getOptions();
-}
-
-static bool isUdtForwardRef(CVType CVT) {
-  ClassOptions UdtOptions = ClassOptions::None;
-  switch (CVT.kind()) {
-  case LF_STRUCTURE:
-  case LF_CLASS:
-  case LF_INTERFACE:
-    UdtOptions = getUdtOptions<ClassRecord>(std::move(CVT));
-    break;
-  case LF_ENUM:
-    UdtOptions = getUdtOptions<EnumRecord>(std::move(CVT));
-    break;
-  case LF_UNION:
-    UdtOptions = getUdtOptions<UnionRecord>(std::move(CVT));
-    break;
-  default:
-    return false;
-  }
-  return (UdtOptions & ClassOptions::ForwardReference) != ClassOptions::None;
-}
-
 Expected<TypeIndex>
 TpiStream::findFullDeclForForwardRef(TypeIndex ForwardRefTI) const {
   CVType F = Types->getType(ForwardRefTI);
diff --git a/llvm/test/DebugInfo/PDB/Native/pdb-native-udts.test b/llvm/test/DebugInfo/PDB/Native/pdb-native-udts.test
new file mode 100644 (file)
index 0000000..f9f2358
--- /dev/null
@@ -0,0 +1,539 @@
+; RUN: llvm-pdbutil pretty -native -classes %p/../Inputs/every-class.pdb \
+; RUN:     | FileCheck -check-prefix=PRETTY %s
+
+; RUN: llvm-pdbutil diadump -native -udts %p/../Inputs/every-class.pdb \
+; RUN:     | FileCheck -check-prefix=DUMP %s
+
+
+PRETTY: struct main::__l2::<unnamed-type-Anonymous> [sizeof = 1]
+PRETTY: struct main::__l2::Scoped [sizeof = 1]
+PRETTY: struct __vc_attributes::event_sourceAttribute [sizeof = 12]
+PRETTY: struct __vc_attributes::helper_attributes::v1_alttypeAttribute [sizeof = 4]
+PRETTY: struct __vc_attributes::helper_attributes::usageAttribute [sizeof = 4]
+PRETTY: struct __vc_attributes::threadingAttribute [sizeof = 4]
+PRETTY: struct __vc_attributes::aggregatableAttribute [sizeof = 4]
+PRETTY: struct __vc_attributes::event_receiverAttribute [sizeof = 8]
+PRETTY: struct __vc_attributes::moduleAttribute [sizeof = 96]
+PRETTY: struct Nested [sizeof = 1]
+PRETTY: struct Nested::F [sizeof = 1]
+PRETTY: struct Constructor [sizeof = 1]
+PRETTY: class Class [sizeof = 1]
+PRETTY: union Union [sizeof = 1]
+PRETTY: struct Operator [sizeof = 1]
+PRETTY: struct Cast [sizeof = 1]
+PRETTY: struct Nothing [sizeof = 1]
+PRETTY: struct Assignment [sizeof = 1]
+PRETTY: const struct Nothing
+PRETTY: volatile struct Nothing
+PRETTY: const volatile struct Nothing
+PRETTY: unaligned struct Nothing
+
+; DUMP:      {
+; DUMP-NEXT:   symIndexId: 2
+; DUMP-NEXT:   symTag: UDT
+; DUMP-NEXT:   name: main::__l2::<unnamed-type-Anonymous>
+; DUMP-NEXT:   virtualTableShapeId: 3
+; DUMP-NEXT:   length: 1
+; DUMP-NEXT:   udtKind: struct
+; DUMP-NEXT:   constructor: 0
+; DUMP-NEXT:   constType: 0
+; DUMP-NEXT:   hasAssignmentOperator: 0
+; DUMP-NEXT:   hasCastOperator: 0
+; DUMP-NEXT:   hasNestedTypes: 0
+; DUMP-NEXT:   overloadedOperator: 0
+; DUMP-NEXT:   isInterfaceUdt: 0
+; DUMP-NEXT:   intrinsic: 0
+; DUMP-NEXT:   nested: 0
+; DUMP-NEXT:   packed: 0
+; DUMP-NEXT:   isRefUdt: 0
+; DUMP-NEXT:   scoped: 1
+; DUMP-NEXT:   unalignedType: 0
+; DUMP-NEXT:   isValueUdt: 0
+; DUMP-NEXT:   volatileType: 0
+; DUMP-NEXT: }
+; DUMP-NEXT: {
+; DUMP-NEXT:   symIndexId: 4
+; DUMP-NEXT:   symTag: UDT
+; DUMP-NEXT:   name: main::__l2::Scoped
+; DUMP-NEXT:   virtualTableShapeId: 3
+; DUMP-NEXT:   length: 1
+; DUMP-NEXT:   udtKind: struct
+; DUMP-NEXT:   constructor: 0
+; DUMP-NEXT:   constType: 0
+; DUMP-NEXT:   hasAssignmentOperator: 0
+; DUMP-NEXT:   hasCastOperator: 0
+; DUMP-NEXT:   hasNestedTypes: 0
+; DUMP-NEXT:   overloadedOperator: 0
+; DUMP-NEXT:   isInterfaceUdt: 0
+; DUMP-NEXT:   intrinsic: 0
+; DUMP-NEXT:   nested: 0
+; DUMP-NEXT:   packed: 0
+; DUMP-NEXT:   isRefUdt: 0
+; DUMP-NEXT:   scoped: 1
+; DUMP-NEXT:   unalignedType: 0
+; DUMP-NEXT:   isValueUdt: 0
+; DUMP-NEXT:   volatileType: 0
+; DUMP-NEXT: }
+; DUMP-NEXT: {
+; DUMP-NEXT:   symIndexId: 5
+; DUMP-NEXT:   symTag: UDT
+; DUMP-NEXT:   name: __vc_attributes::event_sourceAttribute
+; DUMP-NEXT:   virtualTableShapeId: 3
+; DUMP-NEXT:   length: 12
+; DUMP-NEXT:   udtKind: struct
+; DUMP-NEXT:   constructor: 1
+; DUMP-NEXT:   constType: 0
+; DUMP-NEXT:   hasAssignmentOperator: 0
+; DUMP-NEXT:   hasCastOperator: 0
+; DUMP-NEXT:   hasNestedTypes: 1
+; DUMP-NEXT:   overloadedOperator: 0
+; DUMP-NEXT:   isInterfaceUdt: 0
+; DUMP-NEXT:   intrinsic: 0
+; DUMP-NEXT:   nested: 0
+; DUMP-NEXT:   packed: 0
+; DUMP-NEXT:   isRefUdt: 0
+; DUMP-NEXT:   scoped: 0
+; DUMP-NEXT:   unalignedType: 0
+; DUMP-NEXT:   isValueUdt: 0
+; DUMP-NEXT:   volatileType: 0
+; DUMP-NEXT: }
+; DUMP-NEXT: {
+; DUMP-NEXT:   symIndexId: 6
+; DUMP-NEXT:   symTag: UDT
+; DUMP-NEXT:   name: __vc_attributes::helper_attributes::v1_alttypeAttribute
+; DUMP-NEXT:   virtualTableShapeId: 3
+; DUMP-NEXT:   length: 4
+; DUMP-NEXT:   udtKind: struct
+; DUMP-NEXT:   constructor: 1
+; DUMP-NEXT:   constType: 0
+; DUMP-NEXT:   hasAssignmentOperator: 0
+; DUMP-NEXT:   hasCastOperator: 0
+; DUMP-NEXT:   hasNestedTypes: 1
+; DUMP-NEXT:   overloadedOperator: 0
+; DUMP-NEXT:   isInterfaceUdt: 0
+; DUMP-NEXT:   intrinsic: 0
+; DUMP-NEXT:   nested: 0
+; DUMP-NEXT:   packed: 0
+; DUMP-NEXT:   isRefUdt: 0
+; DUMP-NEXT:   scoped: 0
+; DUMP-NEXT:   unalignedType: 0
+; DUMP-NEXT:   isValueUdt: 0
+; DUMP-NEXT:   volatileType: 0
+; DUMP-NEXT: }
+; DUMP-NEXT: {
+; DUMP-NEXT:   symIndexId: 7
+; DUMP-NEXT:   symTag: UDT
+; DUMP-NEXT:   name: __vc_attributes::helper_attributes::usageAttribute
+; DUMP-NEXT:   virtualTableShapeId: 3
+; DUMP-NEXT:   length: 4
+; DUMP-NEXT:   udtKind: struct
+; DUMP-NEXT:   constructor: 1
+; DUMP-NEXT:   constType: 0
+; DUMP-NEXT:   hasAssignmentOperator: 0
+; DUMP-NEXT:   hasCastOperator: 0
+; DUMP-NEXT:   hasNestedTypes: 1
+; DUMP-NEXT:   overloadedOperator: 0
+; DUMP-NEXT:   isInterfaceUdt: 0
+; DUMP-NEXT:   intrinsic: 0
+; DUMP-NEXT:   nested: 0
+; DUMP-NEXT:   packed: 0
+; DUMP-NEXT:   isRefUdt: 0
+; DUMP-NEXT:   scoped: 0
+; DUMP-NEXT:   unalignedType: 0
+; DUMP-NEXT:   isValueUdt: 0
+; DUMP-NEXT:   volatileType: 0
+; DUMP-NEXT: }
+; DUMP-NEXT: {
+; DUMP-NEXT:   symIndexId: 8
+; DUMP-NEXT:   symTag: UDT
+; DUMP-NEXT:   name: __vc_attributes::threadingAttribute
+; DUMP-NEXT:   virtualTableShapeId: 3
+; DUMP-NEXT:   length: 4
+; DUMP-NEXT:   udtKind: struct
+; DUMP-NEXT:   constructor: 1
+; DUMP-NEXT:   constType: 0
+; DUMP-NEXT:   hasAssignmentOperator: 0
+; DUMP-NEXT:   hasCastOperator: 0
+; DUMP-NEXT:   hasNestedTypes: 1
+; DUMP-NEXT:   overloadedOperator: 0
+; DUMP-NEXT:   isInterfaceUdt: 0
+; DUMP-NEXT:   intrinsic: 0
+; DUMP-NEXT:   nested: 0
+; DUMP-NEXT:   packed: 0
+; DUMP-NEXT:   isRefUdt: 0
+; DUMP-NEXT:   scoped: 0
+; DUMP-NEXT:   unalignedType: 0
+; DUMP-NEXT:   isValueUdt: 0
+; DUMP-NEXT:   volatileType: 0
+; DUMP-NEXT: }
+; DUMP-NEXT: {
+; DUMP-NEXT:   symIndexId: 9
+; DUMP-NEXT:   symTag: UDT
+; DUMP-NEXT:   name: __vc_attributes::aggregatableAttribute
+; DUMP-NEXT:   virtualTableShapeId: 3
+; DUMP-NEXT:   length: 4
+; DUMP-NEXT:   udtKind: struct
+; DUMP-NEXT:   constructor: 1
+; DUMP-NEXT:   constType: 0
+; DUMP-NEXT:   hasAssignmentOperator: 0
+; DUMP-NEXT:   hasCastOperator: 0
+; DUMP-NEXT:   hasNestedTypes: 1
+; DUMP-NEXT:   overloadedOperator: 0
+; DUMP-NEXT:   isInterfaceUdt: 0
+; DUMP-NEXT:   intrinsic: 0
+; DUMP-NEXT:   nested: 0
+; DUMP-NEXT:   packed: 0
+; DUMP-NEXT:   isRefUdt: 0
+; DUMP-NEXT:   scoped: 0
+; DUMP-NEXT:   unalignedType: 0
+; DUMP-NEXT:   isValueUdt: 0
+; DUMP-NEXT:   volatileType: 0
+; DUMP-NEXT: }
+; DUMP-NEXT: {
+; DUMP-NEXT:   symIndexId: 10
+; DUMP-NEXT:   symTag: UDT
+; DUMP-NEXT:   name: __vc_attributes::event_receiverAttribute
+; DUMP-NEXT:   virtualTableShapeId: 3
+; DUMP-NEXT:   length: 8
+; DUMP-NEXT:   udtKind: struct
+; DUMP-NEXT:   constructor: 1
+; DUMP-NEXT:   constType: 0
+; DUMP-NEXT:   hasAssignmentOperator: 0
+; DUMP-NEXT:   hasCastOperator: 0
+; DUMP-NEXT:   hasNestedTypes: 1
+; DUMP-NEXT:   overloadedOperator: 0
+; DUMP-NEXT:   isInterfaceUdt: 0
+; DUMP-NEXT:   intrinsic: 0
+; DUMP-NEXT:   nested: 0
+; DUMP-NEXT:   packed: 0
+; DUMP-NEXT:   isRefUdt: 0
+; DUMP-NEXT:   scoped: 0
+; DUMP-NEXT:   unalignedType: 0
+; DUMP-NEXT:   isValueUdt: 0
+; DUMP-NEXT:   volatileType: 0
+; DUMP-NEXT: }
+; DUMP-NEXT: {
+; DUMP-NEXT:   symIndexId: 11
+; DUMP-NEXT:   symTag: UDT
+; DUMP-NEXT:   name: __vc_attributes::moduleAttribute
+; DUMP-NEXT:   virtualTableShapeId: 3
+; DUMP-NEXT:   length: 96
+; DUMP-NEXT:   udtKind: struct
+; DUMP-NEXT:   constructor: 1
+; DUMP-NEXT:   constType: 0
+; DUMP-NEXT:   hasAssignmentOperator: 0
+; DUMP-NEXT:   hasCastOperator: 0
+; DUMP-NEXT:   hasNestedTypes: 1
+; DUMP-NEXT:   overloadedOperator: 0
+; DUMP-NEXT:   isInterfaceUdt: 0
+; DUMP-NEXT:   intrinsic: 0
+; DUMP-NEXT:   nested: 0
+; DUMP-NEXT:   packed: 0
+; DUMP-NEXT:   isRefUdt: 0
+; DUMP-NEXT:   scoped: 0
+; DUMP-NEXT:   unalignedType: 0
+; DUMP-NEXT:   isValueUdt: 0
+; DUMP-NEXT:   volatileType: 0
+; DUMP-NEXT: }
+; DUMP-NEXT: {
+; DUMP-NEXT:   symIndexId: 12
+; DUMP-NEXT:   symTag: UDT
+; DUMP-NEXT:   name: Nested
+; DUMP-NEXT:   virtualTableShapeId: 3
+; DUMP-NEXT:   length: 1
+; DUMP-NEXT:   udtKind: struct
+; DUMP-NEXT:   constructor: 0
+; DUMP-NEXT:   constType: 0
+; DUMP-NEXT:   hasAssignmentOperator: 0
+; DUMP-NEXT:   hasCastOperator: 0
+; DUMP-NEXT:   hasNestedTypes: 1
+; DUMP-NEXT:   overloadedOperator: 0
+; DUMP-NEXT:   isInterfaceUdt: 0
+; DUMP-NEXT:   intrinsic: 0
+; DUMP-NEXT:   nested: 0
+; DUMP-NEXT:   packed: 0
+; DUMP-NEXT:   isRefUdt: 0
+; DUMP-NEXT:   scoped: 0
+; DUMP-NEXT:   unalignedType: 0
+; DUMP-NEXT:   isValueUdt: 0
+; DUMP-NEXT:   volatileType: 0
+; DUMP-NEXT: }
+; DUMP-NEXT: {
+; DUMP-NEXT:   symIndexId: 13
+; DUMP-NEXT:   symTag: UDT
+; DUMP-NEXT:   name: Nested::F
+; DUMP-NEXT:   virtualTableShapeId: 3
+; DUMP-NEXT:   length: 1
+; DUMP-NEXT:   udtKind: struct
+; DUMP-NEXT:   constructor: 0
+; DUMP-NEXT:   constType: 0
+; DUMP-NEXT:   hasAssignmentOperator: 0
+; DUMP-NEXT:   hasCastOperator: 0
+; DUMP-NEXT:   hasNestedTypes: 0
+; DUMP-NEXT:   overloadedOperator: 0
+; DUMP-NEXT:   isInterfaceUdt: 0
+; DUMP-NEXT:   intrinsic: 0
+; DUMP-NEXT:   nested: 1
+; DUMP-NEXT:   packed: 0
+; DUMP-NEXT:   isRefUdt: 0
+; DUMP-NEXT:   scoped: 0
+; DUMP-NEXT:   unalignedType: 0
+; DUMP-NEXT:   isValueUdt: 0
+; DUMP-NEXT:   volatileType: 0
+; DUMP-NEXT: }
+; DUMP-NEXT: {
+; DUMP-NEXT:   symIndexId: 14
+; DUMP-NEXT:   symTag: UDT
+; DUMP-NEXT:   name: Constructor
+; DUMP-NEXT:   virtualTableShapeId: 3
+; DUMP-NEXT:   length: 1
+; DUMP-NEXT:   udtKind: struct
+; DUMP-NEXT:   constructor: 1
+; DUMP-NEXT:   constType: 0
+; DUMP-NEXT:   hasAssignmentOperator: 0
+; DUMP-NEXT:   hasCastOperator: 0
+; DUMP-NEXT:   hasNestedTypes: 0
+; DUMP-NEXT:   overloadedOperator: 0
+; DUMP-NEXT:   isInterfaceUdt: 0
+; DUMP-NEXT:   intrinsic: 0
+; DUMP-NEXT:   nested: 0
+; DUMP-NEXT:   packed: 0
+; DUMP-NEXT:   isRefUdt: 0
+; DUMP-NEXT:   scoped: 0
+; DUMP-NEXT:   unalignedType: 0
+; DUMP-NEXT:   isValueUdt: 0
+; DUMP-NEXT:   volatileType: 0
+; DUMP-NEXT: }
+; DUMP-NEXT: {
+; DUMP-NEXT:   symIndexId: 15
+; DUMP-NEXT:   symTag: UDT
+; DUMP-NEXT:   name: Class
+; DUMP-NEXT:   virtualTableShapeId: 3
+; DUMP-NEXT:   length: 1
+; DUMP-NEXT:   udtKind: class
+; DUMP-NEXT:   constructor: 0
+; DUMP-NEXT:   constType: 0
+; DUMP-NEXT:   hasAssignmentOperator: 0
+; DUMP-NEXT:   hasCastOperator: 0
+; DUMP-NEXT:   hasNestedTypes: 0
+; DUMP-NEXT:   overloadedOperator: 0
+; DUMP-NEXT:   isInterfaceUdt: 0
+; DUMP-NEXT:   intrinsic: 0
+; DUMP-NEXT:   nested: 0
+; DUMP-NEXT:   packed: 0
+; DUMP-NEXT:   isRefUdt: 0
+; DUMP-NEXT:   scoped: 0
+; DUMP-NEXT:   unalignedType: 0
+; DUMP-NEXT:   isValueUdt: 0
+; DUMP-NEXT:   volatileType: 0
+; DUMP-NEXT: }
+; DUMP-NEXT: {
+; DUMP-NEXT:   symIndexId: 16
+; DUMP-NEXT:   symTag: UDT
+; DUMP-NEXT:   name: Union
+; DUMP-NEXT:   length: 1
+; DUMP-NEXT:   udtKind: union
+; DUMP-NEXT:   constructor: 0
+; DUMP-NEXT:   constType: 0
+; DUMP-NEXT:   hasAssignmentOperator: 0
+; DUMP-NEXT:   hasCastOperator: 0
+; DUMP-NEXT:   hasNestedTypes: 0
+; DUMP-NEXT:   overloadedOperator: 0
+; DUMP-NEXT:   isInterfaceUdt: 0
+; DUMP-NEXT:   intrinsic: 0
+; DUMP-NEXT:   nested: 0
+; DUMP-NEXT:   packed: 0
+; DUMP-NEXT:   isRefUdt: 0
+; DUMP-NEXT:   scoped: 0
+; DUMP-NEXT:   unalignedType: 0
+; DUMP-NEXT:   isValueUdt: 0
+; DUMP-NEXT:   volatileType: 0
+; DUMP-NEXT: }
+; DUMP-NEXT: {
+; DUMP-NEXT:   symIndexId: 17
+; DUMP-NEXT:   symTag: UDT
+; DUMP-NEXT:   name: Operator
+; DUMP-NEXT:   virtualTableShapeId: 3
+; DUMP-NEXT:   length: 1
+; DUMP-NEXT:   udtKind: struct
+; DUMP-NEXT:   constructor: 0
+; DUMP-NEXT:   constType: 0
+; DUMP-NEXT:   hasAssignmentOperator: 0
+; DUMP-NEXT:   hasCastOperator: 0
+; DUMP-NEXT:   hasNestedTypes: 0
+; DUMP-NEXT:   overloadedOperator: 1
+; DUMP-NEXT:   isInterfaceUdt: 0
+; DUMP-NEXT:   intrinsic: 0
+; DUMP-NEXT:   nested: 0
+; DUMP-NEXT:   packed: 0
+; DUMP-NEXT:   isRefUdt: 0
+; DUMP-NEXT:   scoped: 0
+; DUMP-NEXT:   unalignedType: 0
+; DUMP-NEXT:   isValueUdt: 0
+; DUMP-NEXT:   volatileType: 0
+; DUMP-NEXT: }
+; DUMP-NEXT: {
+; DUMP-NEXT:   symIndexId: 18
+; DUMP-NEXT:   symTag: UDT
+; DUMP-NEXT:   name: Cast
+; DUMP-NEXT:   virtualTableShapeId: 3
+; DUMP-NEXT:   length: 1
+; DUMP-NEXT:   udtKind: struct
+; DUMP-NEXT:   constructor: 0
+; DUMP-NEXT:   constType: 0
+; DUMP-NEXT:   hasAssignmentOperator: 0
+; DUMP-NEXT:   hasCastOperator: 1
+; DUMP-NEXT:   hasNestedTypes: 0
+; DUMP-NEXT:   overloadedOperator: 1
+; DUMP-NEXT:   isInterfaceUdt: 0
+; DUMP-NEXT:   intrinsic: 0
+; DUMP-NEXT:   nested: 0
+; DUMP-NEXT:   packed: 0
+; DUMP-NEXT:   isRefUdt: 0
+; DUMP-NEXT:   scoped: 0
+; DUMP-NEXT:   unalignedType: 0
+; DUMP-NEXT:   isValueUdt: 0
+; DUMP-NEXT:   volatileType: 0
+; DUMP-NEXT: }
+; DUMP-NEXT: {
+; DUMP-NEXT:   symIndexId: 19
+; DUMP-NEXT:   symTag: UDT
+; DUMP-NEXT:   name: Nothing
+; DUMP-NEXT:   virtualTableShapeId: 3
+; DUMP-NEXT:   length: 1
+; DUMP-NEXT:   udtKind: struct
+; DUMP-NEXT:   constructor: 0
+; DUMP-NEXT:   constType: 0
+; DUMP-NEXT:   hasAssignmentOperator: 0
+; DUMP-NEXT:   hasCastOperator: 0
+; DUMP-NEXT:   hasNestedTypes: 0
+; DUMP-NEXT:   overloadedOperator: 0
+; DUMP-NEXT:   isInterfaceUdt: 0
+; DUMP-NEXT:   intrinsic: 0
+; DUMP-NEXT:   nested: 0
+; DUMP-NEXT:   packed: 0
+; DUMP-NEXT:   isRefUdt: 0
+; DUMP-NEXT:   scoped: 0
+; DUMP-NEXT:   unalignedType: 0
+; DUMP-NEXT:   isValueUdt: 0
+; DUMP-NEXT:   volatileType: 0
+; DUMP-NEXT: }
+; DUMP-NEXT: {
+; DUMP-NEXT:   symIndexId: 20
+; DUMP-NEXT:   symTag: UDT
+; DUMP-NEXT:   name: Assignment
+; DUMP-NEXT:   virtualTableShapeId: 3
+; DUMP-NEXT:   length: 1
+; DUMP-NEXT:   udtKind: struct
+; DUMP-NEXT:   constructor: 0
+; DUMP-NEXT:   constType: 0
+; DUMP-NEXT:   hasAssignmentOperator: 1
+; DUMP-NEXT:   hasCastOperator: 0
+; DUMP-NEXT:   hasNestedTypes: 0
+; DUMP-NEXT:   overloadedOperator: 1
+; DUMP-NEXT:   isInterfaceUdt: 0
+; DUMP-NEXT:   intrinsic: 0
+; DUMP-NEXT:   nested: 0
+; DUMP-NEXT:   packed: 0
+; DUMP-NEXT:   isRefUdt: 0
+; DUMP-NEXT:   scoped: 0
+; DUMP-NEXT:   unalignedType: 0
+; DUMP-NEXT:   isValueUdt: 0
+; DUMP-NEXT:   volatileType: 0
+; DUMP-NEXT: }
+; DUMP-NEXT: {
+; DUMP-NEXT:   symIndexId: 21
+; DUMP-NEXT:   symTag: UDT
+; DUMP-NEXT:   name: Nothing
+; DUMP-NEXT:   unmodifiedTypeId: 19
+; DUMP-NEXT:   virtualTableShapeId: 3
+; DUMP-NEXT:   length: 1
+; DUMP-NEXT:   udtKind: struct
+; DUMP-NEXT:   constructor: 0
+; DUMP-NEXT:   constType: 1
+; DUMP-NEXT:   hasAssignmentOperator: 0
+; DUMP-NEXT:   hasCastOperator: 0
+; DUMP-NEXT:   hasNestedTypes: 0
+; DUMP-NEXT:   overloadedOperator: 0
+; DUMP-NEXT:   isInterfaceUdt: 0
+; DUMP-NEXT:   intrinsic: 0
+; DUMP-NEXT:   nested: 0
+; DUMP-NEXT:   packed: 0
+; DUMP-NEXT:   isRefUdt: 0
+; DUMP-NEXT:   scoped: 0
+; DUMP-NEXT:   unalignedType: 0
+; DUMP-NEXT:   isValueUdt: 0
+; DUMP-NEXT:   volatileType: 0
+; DUMP-NEXT: }
+; DUMP-NEXT: {
+; DUMP-NEXT:   symIndexId: 22
+; DUMP-NEXT:   symTag: UDT
+; DUMP-NEXT:   name: Nothing
+; DUMP-NEXT:   unmodifiedTypeId: 19
+; DUMP-NEXT:   virtualTableShapeId: 3
+; DUMP-NEXT:   length: 1
+; DUMP-NEXT:   udtKind: struct
+; DUMP-NEXT:   constructor: 0
+; DUMP-NEXT:   constType: 0
+; DUMP-NEXT:   hasAssignmentOperator: 0
+; DUMP-NEXT:   hasCastOperator: 0
+; DUMP-NEXT:   hasNestedTypes: 0
+; DUMP-NEXT:   overloadedOperator: 0
+; DUMP-NEXT:   isInterfaceUdt: 0
+; DUMP-NEXT:   intrinsic: 0
+; DUMP-NEXT:   nested: 0
+; DUMP-NEXT:   packed: 0
+; DUMP-NEXT:   isRefUdt: 0
+; DUMP-NEXT:   scoped: 0
+; DUMP-NEXT:   unalignedType: 0
+; DUMP-NEXT:   isValueUdt: 0
+; DUMP-NEXT:   volatileType: 1
+; DUMP-NEXT: }
+; DUMP-NEXT: {
+; DUMP-NEXT:   symIndexId: 23
+; DUMP-NEXT:   symTag: UDT
+; DUMP-NEXT:   name: Nothing
+; DUMP-NEXT:   unmodifiedTypeId: 19
+; DUMP-NEXT:   virtualTableShapeId: 3
+; DUMP-NEXT:   length: 1
+; DUMP-NEXT:   udtKind: struct
+; DUMP-NEXT:   constructor: 0
+; DUMP-NEXT:   constType: 1
+; DUMP-NEXT:   hasAssignmentOperator: 0
+; DUMP-NEXT:   hasCastOperator: 0
+; DUMP-NEXT:   hasNestedTypes: 0
+; DUMP-NEXT:   overloadedOperator: 0
+; DUMP-NEXT:   isInterfaceUdt: 0
+; DUMP-NEXT:   intrinsic: 0
+; DUMP-NEXT:   nested: 0
+; DUMP-NEXT:   packed: 0
+; DUMP-NEXT:   isRefUdt: 0
+; DUMP-NEXT:   scoped: 0
+; DUMP-NEXT:   unalignedType: 0
+; DUMP-NEXT:   isValueUdt: 0
+; DUMP-NEXT:   volatileType: 1
+; DUMP-NEXT: }
+; DUMP-NEXT: {
+; DUMP-NEXT:   symIndexId: 24
+; DUMP-NEXT:   symTag: UDT
+; DUMP-NEXT:   name: Nothing
+; DUMP-NEXT:   unmodifiedTypeId: 19
+; DUMP-NEXT:   virtualTableShapeId: 3
+; DUMP-NEXT:   length: 1
+; DUMP-NEXT:   udtKind: struct
+; DUMP-NEXT:   constructor: 0
+; DUMP-NEXT:   constType: 0
+; DUMP-NEXT:   hasAssignmentOperator: 0
+; DUMP-NEXT:   hasCastOperator: 0
+; DUMP-NEXT:   hasNestedTypes: 0
+; DUMP-NEXT:   overloadedOperator: 0
+; DUMP-NEXT:   isInterfaceUdt: 0
+; DUMP-NEXT:   intrinsic: 0
+; DUMP-NEXT:   nested: 0
+; DUMP-NEXT:   packed: 0
+; DUMP-NEXT:   isRefUdt: 0
+; DUMP-NEXT:   scoped: 0
+; DUMP-NEXT:   unalignedType: 1
+; DUMP-NEXT:   isValueUdt: 0
+; DUMP-NEXT:   volatileType: 0
+; DUMP-NEXT: }
index ab9b459..4ce1922 100644 (file)
@@ -986,6 +986,8 @@ static void dumpDia(StringRef Path) {
     SymTypes.push_back(PDB_SymType::Enum);
   if (opts::diadump::Pointers)
     SymTypes.push_back(PDB_SymType::PointerType);
+  if (opts::diadump::UDTs)
+    SymTypes.push_back(PDB_SymType::UDT);
 
   PdbSymbolIdField Ids = opts::diadump::NoSymIndexIds ? PdbSymbolIdField::None
                                                       : PdbSymbolIdField::All;
@@ -1011,6 +1013,8 @@ static void dumpDia(StringRef Path) {
       outs() << "\n}\n";
     }
   }
+  auto Child = Session->getSymbolById(3);
+  Child->defaultDump(outs(), 2, PdbSymbolIdField::All, PdbSymbolIdField::None);
 }
 
 static void dumpPretty(StringRef Path) {