diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h --- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h +++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h @@ -87,6 +87,7 @@ ClangASTImporter &GetClangASTImporter() { return m_importer; } void Dump(Stream &stream); + static void RequireCompleteType(CompilerType type); private: clang::Decl *TryGetDecl(PdbSymUid uid) const; diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp --- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp @@ -1170,7 +1170,9 @@ return {}; uint64_t element_count = ar.Size / element_size; - CompilerType array_ct = m_clang.CreateArrayType(ToCompilerType(element_type), + CompilerType element_ct = ToCompilerType(element_type); + RequireCompleteType(element_ct); + CompilerType array_ct = m_clang.CreateArrayType(element_ct, element_count, false); return clang::QualType::getFromOpaquePtr(array_ct.GetOpaqueQualType()); } @@ -1448,3 +1450,30 @@ void PdbAstBuilder::Dump(Stream &stream) { m_clang.Dump(stream.AsRawOstream()); } + +/// Complete a type from debug info, or mark it as forcefully completed if +/// there is no definition of the type in the current Module. Call this function +/// in contexts where the usual C++ rules require a type to be complete (base +/// class, member, etc.). +void PdbAstBuilder::RequireCompleteType(CompilerType type) { + // Technically, enums can be incomplete too, but we don't handle those as they + // are emitted even under -flimit-debug-info. + if (!TypeSystemClang::IsCXXClassType(type)) + return; + + if (type.GetCompleteType()) + return; + + // No complete definition in this module. Mark the class as complete to + // satisfy local ast invariants, but make a note of the fact that + // it is not _really_ complete so we can later search for a definition in a + // different module. + // Since we provide layout assistance, layouts of types containing this class + // will be correct even if we are not able to find the definition elsewhere. + bool started = TypeSystemClang::StartTagDeclarationDefinition(type); + lldbassert(started && "Unable to start a class type definition."); + TypeSystemClang::CompleteTagDeclarationDefinition(type); + const clang::TagDecl *td = ClangUtil::GetAsTagDecl(type); + auto &ts = llvm::cast(*type.GetTypeSystem()); + ts.GetMetadata(td)->SetIsForcefullyCompleted(); +} diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp --- a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp @@ -92,9 +92,10 @@ m_ast_builder.GetOrCreateType(PdbTypeSymId(type_idx)); if (method_qt.isNull()) return; - m_ast_builder.CompleteType(method_qt); CompilerType method_ct = m_ast_builder.ToCompilerType(method_qt); - lldb::opaque_compiler_type_t derived_opaque_ty = m_derived_ct.GetOpaqueQualType(); + PdbAstBuilder::RequireCompleteType(method_ct); + lldb::opaque_compiler_type_t derived_opaque_ty = + m_derived_ct.GetOpaqueQualType(); auto iter = m_cxx_record_map.find(derived_opaque_ty); if (iter != m_cxx_record_map.end()) { if (iter->getSecond().contains({name, method_ct})) { @@ -253,7 +254,7 @@ clang::QualType member_qt = m_ast_builder.GetOrCreateType(PdbTypeSymId(ti)); if (member_qt.isNull()) return Error::success(); - m_ast_builder.CompleteType(member_qt); + PdbAstBuilder::RequireCompleteType(m_ast_builder.ToCompilerType(member_qt)); lldb::AccessType access = TranslateMemberAccess(data_member.getAccess()); size_t field_size = bitfield_width ? bitfield_width : GetSizeOfType(ti, m_index.tpi()) * 8; @@ -310,6 +311,18 @@ bases.push_back(std::move(ib.second)); TypeSystemClang &clang = m_ast_builder.clang(); + // Make sure all base classes refer to complete types and not forward + // declarations. If we don't do this, clang will crash with an + // assertion in the call to clang_type.TransferBaseClasses() + for (const auto &base_class : bases) { + clang::TypeSourceInfo *type_source_info = + base_class->getTypeSourceInfo(); + if (type_source_info) { + PdbAstBuilder::RequireCompleteType( + clang.GetType(type_source_info->getType())); + } + } + clang.TransferBaseClasses(m_derived_ct.GetOpaqueQualType(), std::move(bases)); clang.AddMethodOverridesForCXXRecordType(m_derived_ct.GetOpaqueQualType()); diff --git a/lldb/test/Shell/SymbolFile/NativePDB/Inputs/incomplete-tag-type.cpp b/lldb/test/Shell/SymbolFile/NativePDB/Inputs/incomplete-tag-type.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/SymbolFile/NativePDB/Inputs/incomplete-tag-type.cpp @@ -0,0 +1,10 @@ +struct A { + int x; + A(); +}; +A::A() : x(47) {} + +struct C { + C(); +}; +C::C() = default; diff --git a/lldb/test/Shell/SymbolFile/NativePDB/incomplete-tag-type.cpp b/lldb/test/Shell/SymbolFile/NativePDB/incomplete-tag-type.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/SymbolFile/NativePDB/incomplete-tag-type.cpp @@ -0,0 +1,30 @@ +// clang-format off +// REQUIRES: lld, x86 + +// RUN: %clang_cl --target=x86_64-windows-msvc -c /Fo%t1.obj -- %p/Inputs/incomplete-tag-type.cpp +// RUN: %clang_cl --target=x86_64-windows-msvc /O1 /Z7 -c /Fo%t2.obj -- %s +// RUN: lld-link /debug:full /nodefaultlib /entry:main %t1.obj %t2.obj /out:%t.exe /pdb:%t.pdb +// RUN: env LLDB_USE_NATIVE_PDB_READER=1 %lldb -f %t.exe \ +// RUN: -o "p b" -o "p d" -o "exit" | FileCheck %s + +// CHECK: (lldb) p b +// CHECK: (B) $0 = {} +// CHECK: (lldb) p d +// CHECK: (D) $1 = {} + +// Complete base class. +struct A { int x; A(); }; +struct B : A {}; +B b; + +// Complete data member. +struct C { + C(); +}; + +struct D { + C c; +}; +D d; + +int main(){}