diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h b/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h index da2b1a15f7461..bc950cc7a05b9 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h @@ -10,6 +10,8 @@ #define LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CLANG_ASTUTILS_H #include "clang/Basic/ASTSourceDescriptor.h" +#include "Plugins/TypeSystem/Clang/ImporterBackedASTSource.h" +#include "clang/Basic/Module.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/MultiplexExternalSemaSource.h" #include "clang/Sema/Sema.h" @@ -26,7 +28,7 @@ namespace lldb_private { /// Wraps an ExternalASTSource into an ExternalSemaSource. Doesn't take /// ownership of the provided source. -class ExternalASTSourceWrapper : public clang::ExternalSemaSource { +class ExternalASTSourceWrapper : public ImporterBackedASTSource { ExternalASTSource *m_Source; public: @@ -246,7 +248,7 @@ class ASTConsumerForwarder : public clang::SemaConsumer { /// provide more accurate replies to the requests, but might not be able to /// answer all requests. The debug information will be used as a fallback then /// to provide information that is not in the C++ module. -class SemaSourceWithPriorities : public clang::ExternalSemaSource { +class SemaSourceWithPriorities : public ImporterBackedASTSource { private: /// The sources ordered in decreasing priority. @@ -279,9 +281,16 @@ class SemaSourceWithPriorities : public clang::ExternalSemaSource { return nullptr; } + /// Call ExternalASTSource::CompleteRedeclChain(D) + /// on each AST source. Returns as soon as we got + /// a definition for D. void CompleteRedeclChain(const clang::Decl *D) override { - for (size_t i = 0; i < Sources.size(); ++i) + for (size_t i = 0; i < Sources.size(); ++i) { Sources[i]->CompleteRedeclChain(D); + if (auto *td = llvm::dyn_cast(D)) + if (td->getDefinition()) + return; + } } clang::Selector GetExternalSelector(uint32_t ID) override { diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp index 44071d1ea71c7..722df69615368 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "Plugins/TypeSystem/Clang/ImporterBackedASTSource.h" #include "lldb/Core/Module.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/LLDBLog.h" @@ -17,6 +18,8 @@ #include "clang/AST/RecordLayout.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/Sema.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include "Plugins/ExpressionParser/Clang/ClangASTImporter.h" @@ -251,32 +254,6 @@ class CompleteTagDeclsScope : public ClangASTImporter::NewDeclListener { while (!m_decls_to_complete.empty()) { NamedDecl *decl = m_decls_to_complete.pop_back_val(); m_decls_already_completed.insert(decl); - - // The decl that should be completed has to be imported into the target - // context from some other context. - assert(to_context_md->hasOrigin(decl)); - // We should only complete decls coming from the source context. - assert(to_context_md->getOrigin(decl).ctx == m_src_ctx); - - Decl *original_decl = to_context_md->getOrigin(decl).decl; - - // Complete the decl now. - TypeSystemClang::GetCompleteDecl(m_src_ctx, original_decl); - if (auto *tag_decl = dyn_cast(decl)) { - if (auto *original_tag_decl = dyn_cast(original_decl)) { - if (original_tag_decl->isCompleteDefinition()) { - m_delegate->ImportDefinitionTo(tag_decl, original_tag_decl); - tag_decl->setCompleteDefinition(true); - } - } - - tag_decl->setHasExternalLexicalStorage(false); - tag_decl->setHasExternalVisibleStorage(false); - } else if (auto *container_decl = dyn_cast(decl)) { - container_decl->setHasExternalLexicalStorage(false); - container_decl->setHasExternalVisibleStorage(false); - } - to_context_md->removeOrigin(decl); } @@ -289,6 +266,9 @@ class CompleteTagDeclsScope : public ClangASTImporter::NewDeclListener { // Filter out decls that we can't complete later. if (!isa(to) && !isa(to)) return; + + to = ClangUtil::GetFirstDecl(to); + RecordDecl *from_record_decl = dyn_cast(from); // We don't need to complete injected class name decls. if (from_record_decl && from_record_decl->isInjectedClassName()) @@ -359,6 +339,16 @@ clang::Decl *ClangASTImporter::DeportDecl(clang::ASTContext *dst_ctx, return result; } +bool ClangASTImporter::CanImport(const Decl *d) { + if (!d) + return false; + if (isa(d)) + return GetDeclOrigin(d).Valid(); + if (isa(d)) + return GetDeclOrigin(d).Valid(); + return false; +} + bool ClangASTImporter::CanImport(const CompilerType &type) { if (!ClangUtil::IsClangType(type)) return false; @@ -368,24 +358,10 @@ bool ClangASTImporter::CanImport(const CompilerType &type) { const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { - case clang::Type::Record: { - const clang::CXXRecordDecl *cxx_record_decl = - qual_type->getAsCXXRecordDecl(); - if (cxx_record_decl) { - if (GetDeclOrigin(cxx_record_decl).Valid()) - return true; - } - } break; - - case clang::Type::Enum: { - clang::EnumDecl *enum_decl = - llvm::cast(qual_type)->getDecl(); - if (enum_decl) { - if (GetDeclOrigin(enum_decl).Valid()) - return true; - } - } break; - + case clang::Type::Record: + return CanImport(qual_type->getAsRecordDecl()); + case clang::Type::Enum: + return CanImport(llvm::cast(qual_type)->getDecl()); case clang::Type::ObjCObject: case clang::Type::ObjCInterface: { const clang::ObjCObjectType *objc_class_type = @@ -395,10 +371,7 @@ bool ClangASTImporter::CanImport(const CompilerType &type) { objc_class_type->getInterface(); // We currently can't complete objective C types through the newly added // ASTContext because it only supports TagDecl objects right now... - if (class_interface_decl) { - if (GetDeclOrigin(class_interface_decl).Valid()) - return true; - } + return CanImport(class_interface_decl); } } break; @@ -510,14 +483,7 @@ bool ClangASTImporter::CompleteType(const CompilerType &compiler_type) { if (!CanImport(compiler_type)) return false; - if (Import(compiler_type)) { - TypeSystemClang::CompleteTagDeclarationDefinition(compiler_type); - return true; - } - - TypeSystemClang::SetHasExternalStorage(compiler_type.GetOpaqueQualType(), - false); - return false; + return Import(compiler_type); } /// Copy layout information from \ref source_map to the \ref destination_map. @@ -666,8 +632,8 @@ bool ClangASTImporter::importRecordLayoutFromOrigin( int field_idx = 0, field_count = record_layout.getFieldCount(); - for (RecordDecl::field_iterator fi = origin_record->field_begin(), - fe = origin_record->field_end(); + for (RecordDecl::field_iterator fi = definition->field_begin(), + fe = definition->field_end(); fi != fe; ++fi) { if (field_idx >= field_count) return false; // Layout didn't go well. Bail out. @@ -758,6 +724,9 @@ bool ClangASTImporter::LayoutRecordType( &base_offsets, llvm::DenseMap &vbase_offsets) { + + record_decl = llvm::cast(record_decl->getFirstDecl()); + RecordDeclToLayoutMap::iterator pos = m_record_decl_to_layout_map.find(record_decl); base_offsets.clear(); @@ -768,7 +737,6 @@ bool ClangASTImporter::LayoutRecordType( field_offsets.swap(pos->second.field_offsets); base_offsets.swap(pos->second.base_offsets); vbase_offsets.swap(pos->second.vbase_offsets); - m_record_decl_to_layout_map.erase(pos); return true; } @@ -790,16 +758,24 @@ bool ClangASTImporter::LayoutRecordType( void ClangASTImporter::SetRecordLayout(clang::RecordDecl *decl, const LayoutInfo &layout) { + decl = llvm::cast(decl->getFirstDecl()); + assert(!HasRecordLayout(decl) && "Trying to overwrite layout?"); m_record_decl_to_layout_map.insert(std::make_pair(decl, layout)); } +bool ClangASTImporter::HasRecordLayout(const RecordDecl *decl) const { + decl = llvm::cast(decl->getFirstDecl()); + return m_record_decl_to_layout_map.count(decl) > 0; +} + bool ClangASTImporter::CompleteTagDecl(clang::TagDecl *decl) { DeclOrigin decl_origin = GetDeclOrigin(decl); if (!decl_origin.Valid()) return false; - if (!TypeSystemClang::GetCompleteDecl(decl_origin.ctx, decl_origin.decl)) + auto *origin_def = llvm::cast(decl_origin.decl)->getDefinition(); + if (!origin_def) return false; ImporterDelegateSP delegate_sp( @@ -807,29 +783,36 @@ bool ClangASTImporter::CompleteTagDecl(clang::TagDecl *decl) { ASTImporterDelegate::CxxModuleScope std_scope(*delegate_sp, &decl->getASTContext()); - if (delegate_sp) - delegate_sp->ImportDefinitionTo(decl, decl_origin.decl); + + // This is expected to pull in a definition for result_decl (if in redecl + // completion mode) + llvm::Expected result = delegate_sp->Import(origin_def); + if (!result) { + llvm::handleAllErrors(result.takeError(), + [](const clang::ASTImportError &e) { + llvm::errs() << "ERR: " << e.toString() << "\n"; + }); + return false; + } + + // Create redeclaration chain with the 'to' decls. + // Only need to do this if the 'result_decl' is a definition outside + // of any redeclaration chain and the input 'decl' was a forward declaration + TagDecl *result_decl = llvm::cast(*result); + if (!decl->isThisDeclarationADefinition() && result_decl != decl) + if (result_decl->getPreviousDecl() == nullptr) + result_decl->setPreviousDecl(decl); return true; } bool ClangASTImporter::CompleteTagDeclWithOrigin(clang::TagDecl *decl, clang::TagDecl *origin_decl) { - clang::ASTContext *origin_ast_ctx = &origin_decl->getASTContext(); - - if (!TypeSystemClang::GetCompleteDecl(origin_ast_ctx, origin_decl)) + if (!origin_decl->getDefinition()) return false; - ImporterDelegateSP delegate_sp( - GetDelegate(&decl->getASTContext(), origin_ast_ctx)); - - if (delegate_sp) - delegate_sp->ImportDefinitionTo(decl, origin_decl); - - ASTContextMetadataSP context_md = GetContextMetadata(&decl->getASTContext()); - - context_md->setOrigin(decl, DeclOrigin(origin_ast_ctx, origin_decl)); - return true; + SetDeclOrigin(decl, origin_decl->getFirstDecl()); + return CompleteTagDecl(decl); } bool ClangASTImporter::CompleteObjCInterfaceDecl( @@ -839,89 +822,29 @@ bool ClangASTImporter::CompleteObjCInterfaceDecl( if (!decl_origin.Valid()) return false; - if (!TypeSystemClang::GetCompleteDecl(decl_origin.ctx, decl_origin.decl)) + ObjCInterfaceDecl *origin_decl = + llvm::cast(decl_origin.decl); + + origin_decl = origin_decl->getDefinition(); + if (!origin_decl) return false; ImporterDelegateSP delegate_sp( GetDelegate(&interface_decl->getASTContext(), decl_origin.ctx)); - if (delegate_sp) - delegate_sp->ImportDefinitionTo(interface_decl, decl_origin.decl); + llvm::Expected result = delegate_sp->Import(origin_decl); + if (result) + return true; - if (ObjCInterfaceDecl *super_class = interface_decl->getSuperClass()) - RequireCompleteType(clang::QualType(super_class->getTypeForDecl(), 0)); + llvm::handleAllErrors(result.takeError(), [](const clang::ASTImportError &e) { + llvm::errs() << "ERR: " << e.toString() << "\n"; + }); - return true; + return false; } bool ClangASTImporter::CompleteAndFetchChildren(clang::QualType type) { - if (!RequireCompleteType(type)) - return false; - - Log *log = GetLog(LLDBLog::Expressions); - - if (const TagType *tag_type = type->getAs()) { - TagDecl *tag_decl = tag_type->getDecl(); - - DeclOrigin decl_origin = GetDeclOrigin(tag_decl); - - if (!decl_origin.Valid()) - return false; - - ImporterDelegateSP delegate_sp( - GetDelegate(&tag_decl->getASTContext(), decl_origin.ctx)); - - ASTImporterDelegate::CxxModuleScope std_scope(*delegate_sp, - &tag_decl->getASTContext()); - - TagDecl *origin_tag_decl = llvm::dyn_cast(decl_origin.decl); - - for (Decl *origin_child_decl : origin_tag_decl->decls()) { - llvm::Expected imported_or_err = - delegate_sp->Import(origin_child_decl); - if (!imported_or_err) { - LLDB_LOG_ERROR(log, imported_or_err.takeError(), - "Couldn't import decl: {0}"); - return false; - } - } - - if (RecordDecl *record_decl = dyn_cast(origin_tag_decl)) - record_decl->setHasLoadedFieldsFromExternalStorage(true); - - return true; - } - - if (const ObjCObjectType *objc_object_type = type->getAs()) { - if (ObjCInterfaceDecl *objc_interface_decl = - objc_object_type->getInterface()) { - DeclOrigin decl_origin = GetDeclOrigin(objc_interface_decl); - - if (!decl_origin.Valid()) - return false; - - ImporterDelegateSP delegate_sp( - GetDelegate(&objc_interface_decl->getASTContext(), decl_origin.ctx)); - - ObjCInterfaceDecl *origin_interface_decl = - llvm::dyn_cast(decl_origin.decl); - - for (Decl *origin_child_decl : origin_interface_decl->decls()) { - llvm::Expected imported_or_err = - delegate_sp->Import(origin_child_decl); - if (!imported_or_err) { - LLDB_LOG_ERROR(log, imported_or_err.takeError(), - "Couldn't import decl: {0}"); - return false; - } - } - - return true; - } - return false; - } - - return true; + return RequireCompleteType(type); } bool ClangASTImporter::RequireCompleteType(clang::QualType type) { @@ -1103,6 +1026,20 @@ ClangASTImporter::ASTImporterDelegate::ImportImpl(Decl *From) { } } + if (auto *source = llvm::dyn_cast( + getToContext().getExternalSource())) { + // We added a new declaration (which is not a definition) into the + // destination AST context, so bump the declaration chain generation + // counter. + if (clang::TagDecl *td = dyn_cast(From)) + if (!td->isThisDeclarationADefinition()) + source->MarkRedeclChainsAsOutOfDate(getToContext()); + + if (clang::ObjCInterfaceDecl *td = dyn_cast(From)) + if (!td->isThisDeclarationADefinition()) + source->MarkRedeclChainsAsOutOfDate(getToContext()); + } + // If we have a forcefully completed type, try to find an actual definition // for it in other modules. const ClangASTMetadata *md = m_main.GetDeclMetadata(From); @@ -1123,6 +1060,12 @@ ClangASTImporter::ASTImporterDelegate::ImportImpl(Decl *From) { DeclContext::lookup_result lr = dc->lookup(*dn_or_err); for (clang::Decl *candidate : lr) { if (candidate->getKind() == From->getKind()) { + // If we're dealing with redecl chains, we want to find the definition, + // so skip if the decl is actually just a forwad decl. + if (auto *tag_decl = llvm::dyn_cast(candidate); + !tag_decl || !tag_decl->getDefinition()) + continue; + RegisterImportedDecl(From, candidate); m_decls_to_ignore.insert(candidate); return candidate; @@ -1224,27 +1167,6 @@ void ClangASTImporter::ASTImporterDelegate::ImportDefinitionTo( } } -/// Takes a CXXMethodDecl and completes the return type if necessary. This -/// is currently only necessary for virtual functions with covariant return -/// types where Clang's CodeGen expects that the underlying records are already -/// completed. -static void MaybeCompleteReturnType(ClangASTImporter &importer, - CXXMethodDecl *to_method) { - if (!to_method->isVirtual()) - return; - QualType return_type = to_method->getReturnType(); - if (!return_type->isPointerType() && !return_type->isReferenceType()) - return; - - clang::RecordDecl *rd = return_type->getPointeeType()->getAsRecordDecl(); - if (!rd) - return; - if (rd->getDefinition()) - return; - - importer.CompleteTagDecl(rd); -} - /// Recreate a module with its parents in \p to_source and return its id. static OptionalClangModuleID RemapModule(OptionalClangModuleID from_id, @@ -1362,54 +1284,39 @@ void ClangASTImporter::ASTImporterDelegate::Imported(clang::Decl *from, from, m_source_ctx, &to->getASTContext()); } - if (auto *to_tag_decl = dyn_cast(to)) { - to_tag_decl->setHasExternalLexicalStorage(); - to_tag_decl->getPrimaryContext()->setMustBuildLookupTable(); - auto from_tag_decl = cast(from); - - LLDB_LOG( - log, - " [ClangASTImporter] To is a TagDecl - attributes {0}{1} [{2}->{3}]", - (to_tag_decl->hasExternalLexicalStorage() ? " Lexical" : ""), - (to_tag_decl->hasExternalVisibleStorage() ? " Visible" : ""), - (from_tag_decl->isCompleteDefinition() ? "complete" : "incomplete"), - (to_tag_decl->isCompleteDefinition() ? "complete" : "incomplete")); - } - if (auto *to_namespace_decl = dyn_cast(to)) { m_main.BuildNamespaceMap(to_namespace_decl); to_namespace_decl->setHasExternalVisibleStorage(); } - if (auto *to_container_decl = dyn_cast(to)) { - to_container_decl->setHasExternalLexicalStorage(); - to_container_decl->setHasExternalVisibleStorage(); - - if (log) { - if (ObjCInterfaceDecl *to_interface_decl = - llvm::dyn_cast(to_container_decl)) { - LLDB_LOG( - log, - " [ClangASTImporter] To is an ObjCInterfaceDecl - attributes " - "{0}{1}{2}", - (to_interface_decl->hasExternalLexicalStorage() ? " Lexical" : ""), - (to_interface_decl->hasExternalVisibleStorage() ? " Visible" : ""), - (to_interface_decl->hasDefinition() ? " HasDefinition" : "")); - } else { - LLDB_LOG( - log, " [ClangASTImporter] To is an {0}Decl - attributes {1}{2}", - ((Decl *)to_container_decl)->getDeclKindName(), - (to_container_decl->hasExternalLexicalStorage() ? " Lexical" : ""), - (to_container_decl->hasExternalVisibleStorage() ? " Visible" : "")); - } - } + if (clang::ObjCInterfaceDecl *td = dyn_cast(to)) { + if (clang::ExternalASTSource *s = getToContext().getExternalSource()) + if (td->isThisDeclarationADefinition()) + s->CompleteRedeclChain(td); + td->setHasExternalVisibleStorage(); } - - if (clang::CXXMethodDecl *to_method = dyn_cast(to)) - MaybeCompleteReturnType(m_main, to_method); } clang::Decl * ClangASTImporter::ASTImporterDelegate::GetOriginalDecl(clang::Decl *To) { return m_main.GetDeclOrigin(To).decl; } + +ClangASTImporter::ASTImporterDelegate::ASTImporterDelegate( + ClangASTImporter &main, clang::ASTContext *target_ctx, + clang::ASTContext *source_ctx) + : clang::ASTImporter(*target_ctx, main.m_file_manager, *source_ctx, + main.m_file_manager, true /*minimal*/), + m_main(main), m_source_ctx(source_ctx) { + // Target and source ASTContext shouldn't be identical. Importing AST + // nodes within the same AST doesn't make any sense as the whole idea + // is to import them to a different AST. + lldbassert(target_ctx != source_ctx && "Can't import into itself"); + // This is always doing a minimal import of any declarations. This means + // that there has to be an ExternalASTSource in the target ASTContext + // (that should implement the callbacks that complete any declarations + // on demand). Without an ExternalASTSource, this ASTImporter will just + // do a minimal import and the imported declarations won't be completed. + assert(target_ctx->getExternalSource() && "Missing ExternalSource"); + setODRHandling(clang::ASTImporter::ODRHandlingType::Liberal); +} diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h index bc962e544d2f1..7da82d804cd7e 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h @@ -119,6 +119,8 @@ class ClangASTImporter { /// \param layout The layout for the record. void SetRecordLayout(clang::RecordDecl *decl, const LayoutInfo &layout); + bool HasRecordLayout(const clang::RecordDecl *decl) const; + bool LayoutRecordType( const clang::RecordDecl *record_decl, uint64_t &bit_size, uint64_t &alignment, @@ -157,6 +159,8 @@ class ClangASTImporter { /// \see ClangASTImporter::Import bool CanImport(const CompilerType &type); + bool CanImport(const clang::Decl *d); + /// If the given type was copied from another TypeSystemClang then copy over /// all missing information (e.g., the definition of a 'class' type). /// @@ -171,6 +175,8 @@ class ClangASTImporter { bool CompleteTagDecl(clang::TagDecl *decl); + /// This function assumes origin has been completed (i.e., has a valid + /// definition). bool CompleteTagDeclWithOrigin(clang::TagDecl *decl, clang::TagDecl *origin); bool CompleteObjCInterfaceDecl(clang::ObjCInterfaceDecl *interface_decl); @@ -282,22 +288,7 @@ class ClangASTImporter { /// their counterpart from a C++ module. struct ASTImporterDelegate : public clang::ASTImporter { ASTImporterDelegate(ClangASTImporter &main, clang::ASTContext *target_ctx, - clang::ASTContext *source_ctx) - : clang::ASTImporter(*target_ctx, main.m_file_manager, *source_ctx, - main.m_file_manager, true /*minimal*/), - m_main(main), m_source_ctx(source_ctx) { - // Target and source ASTContext shouldn't be identical. Importing AST - // nodes within the same AST doesn't make any sense as the whole idea - // is to import them to a different AST. - lldbassert(target_ctx != source_ctx && "Can't import into itself"); - // This is always doing a minimal import of any declarations. This means - // that there has to be an ExternalASTSource in the target ASTContext - // (that should implement the callbacks that complete any declarations - // on demand). Without an ExternalASTSource, this ASTImporter will just - // do a minimal import and the imported declarations won't be completed. - assert(target_ctx->getExternalSource() && "Missing ExternalSource"); - setODRHandling(clang::ASTImporter::ODRHandlingType::Liberal); - } + clang::ASTContext *source_ctx); /// Scope guard that attaches a CxxModuleHandler to an ASTImporterDelegate /// and deattaches it at the end of the scope. Supports being used multiple @@ -346,6 +337,8 @@ class ClangASTImporter { llvm::Expected ImportImpl(clang::Decl *From) override; private: + void MarkDeclImported(clang::Decl *from, clang::Decl *to); + /// Decls we should ignore when mapping decls back to their original /// ASTContext. Used by the CxxModuleHandler to mark declarations that /// were created from the 'std' C++ module to prevent that the Importer diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp index 1fdd272dcbece..b88c2f0385713 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp @@ -281,8 +281,11 @@ void ClangASTSource::CompleteType(TagDecl *tag_decl) { if (!m_ast_importer_sp->CompleteTagDecl(tag_decl)) { // We couldn't complete the type. Maybe there's a definition somewhere // else that can be completed. - if (TagDecl *alternate = FindCompleteType(tag_decl)) + if (TagDecl *alternate = FindCompleteType(tag_decl)) { + assert(alternate->getDefinition() != nullptr && + "Trying to complete a TagDecl from an incomplete origin"); m_ast_importer_sp->CompleteTagDeclWithOrigin(tag_decl, alternate); + } } LLDB_LOG(log, " [CTD] After:\n{0}", ClangUtil::DumpDecl(tag_decl)); @@ -323,6 +326,42 @@ void ClangASTSource::CompleteType(clang::ObjCInterfaceDecl *interface_decl) { LLDB_LOG(log, " [COID] {0}", ClangUtil::DumpDecl(interface_decl)); } +void ClangASTSource::CompleteRedeclChain(const Decl *d) { + if (const clang::TagDecl *td = llvm::dyn_cast(d)) { + if (td->isBeingDefined()) + return; + + if (td->getDefinition()) + return; + + m_ast_importer_sp->CompleteTagDecl(const_cast(td)); + if (!td->getDefinition() && m_ast_importer_sp->GetDeclOrigin(td).Valid()) { + if (TagDecl *alternate = FindCompleteType(td)) + m_ast_importer_sp->CompleteTagDeclWithOrigin( + const_cast(td), alternate); + } + } + if (const auto *od = llvm::dyn_cast(d)) { + ClangASTImporter::DeclOrigin original = + m_ast_importer_sp->GetDeclOrigin(od); + if (ObjCInterfaceDecl *orig = + dyn_cast_or_null(original.decl)) { + if (ObjCInterfaceDecl *i = GetCompleteObjCInterface(orig)) { + if (i != orig) { + m_ast_importer_sp->SetDeclOrigin(d, i); + m_ast_importer_sp->CompleteObjCInterfaceDecl( + const_cast(od)); + return; + } + } + } + if (od->getDefinition()) + return; + m_ast_importer_sp->CompleteObjCInterfaceDecl( + const_cast(od)); + } +} + clang::ObjCInterfaceDecl *ClangASTSource::GetCompleteObjCInterface( const clang::ObjCInterfaceDecl *interface_decl) { lldb::ProcessSP process(m_target->GetProcessSP()); @@ -360,8 +399,7 @@ clang::ObjCInterfaceDecl *ClangASTSource::GetCompleteObjCInterface( return nullptr; ObjCInterfaceDecl *complete_iface_decl(complete_interface_type->getDecl()); - - return complete_iface_decl; + return complete_iface_decl->getDefinition(); } void ClangASTSource::FindExternalLexicalDecls( diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h index 83c910477acc8..08103ff11228a 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h @@ -13,6 +13,7 @@ #include "Plugins/ExpressionParser/Clang/ClangASTImporter.h" #include "Plugins/ExpressionParser/Clang/NameSearchContext.h" +#include "Plugins/TypeSystem/Clang/ImporterBackedASTSource.h" #include "lldb/Symbol/CompilerType.h" #include "lldb/Target/Target.h" #include "clang/AST/ExternalASTSource.h" @@ -30,7 +31,7 @@ namespace lldb_private { /// knows the name it is looking for, but nothing else. The ExternalSemaSource /// class provides Decls (VarDecl, FunDecl, TypeDecl) to Clang for these /// names, consulting the ClangExpressionDeclMap to do the actual lookups. -class ClangASTSource : public clang::ExternalASTSource, +class ClangASTSource : public ImporterBackedASTSource, public ClangASTImporter::MapCompleter { public: /// Constructor @@ -153,6 +154,19 @@ class ClangASTSource : public clang::ExternalASTSource, /// The Decl to be completed in place. void CompleteType(clang::ObjCInterfaceDecl *Class) override; + /// Implements ExternalASTSource::CompleteRedeclChain. + /// + /// This function will simply complete \ref D, using + /// its origin to copy the definition if necessary. + /// If the definition for \ref D is on a different + /// Decl, this function will link the two into a + /// declaration chain (this can happen if we found + /// the definition on the origin). + /// + /// \param[in] D + /// The Decl to complete. + void CompleteRedeclChain(clang::Decl const *D) override; + /// Called on entering a translation unit. Tells Clang by calling /// setHasExternalVisibleStorage() and setHasExternalLexicalStorage() that /// this object has something to say about undefined names. @@ -231,6 +245,10 @@ class ClangASTSource : public clang::ExternalASTSource, return m_original.CompleteType(Class); } + void CompleteRedeclChain(clang::Decl const *D) override { + return m_original.CompleteRedeclChain(D); + } + bool layoutRecordType( const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment, llvm::DenseMap &FieldOffsets, diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExternalASTSourceCallbacks.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExternalASTSourceCallbacks.cpp index e746e6afe39be..515559006f36e 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExternalASTSourceCallbacks.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExternalASTSourceCallbacks.cpp @@ -27,6 +27,22 @@ void ClangExternalASTSourceCallbacks::CompleteType( m_ast.CompleteObjCInterfaceDecl(objc_decl); } +void ClangExternalASTSourceCallbacks::CompleteRedeclChain( + const clang::Decl *d) { + if (const clang::TagDecl *td = llvm::dyn_cast(d)) { + if (td->isBeingDefined()) + return; + if (td->getDefinition()) + return; + m_ast.CompleteTagDecl(const_cast(td)); + } + if (const auto *od = llvm::dyn_cast(d)) { + if (od->getDefinition()) + return; + m_ast.CompleteObjCInterfaceDecl(const_cast(od)); + } +} + bool ClangExternalASTSourceCallbacks::layoutRecordType( const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment, llvm::DenseMap &FieldOffsets, diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExternalASTSourceCallbacks.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangExternalASTSourceCallbacks.h index 6bd18186a567d..f73904d998e48 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExternalASTSourceCallbacks.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExternalASTSourceCallbacks.h @@ -9,6 +9,7 @@ #ifndef LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CLANG_CLANGEXTERNALASTSOURCECALLBACKS_H #define LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CLANG_CLANGEXTERNALASTSOURCECALLBACKS_H +#include "Plugins/TypeSystem/Clang/ImporterBackedASTSource.h" #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" #include "clang/Basic/ASTSourceDescriptor.h" #include @@ -21,13 +22,15 @@ class Module; namespace lldb_private { -class ClangExternalASTSourceCallbacks : public clang::ExternalASTSource { +class ClangExternalASTSourceCallbacks : public ImporterBackedASTSource { /// LLVM RTTI support. static char ID; public: /// LLVM RTTI support. - bool isA(const void *ClassID) const override { return ClassID == &ID; } + bool isA(const void *ClassID) const override { + return ClassID == &ID || ImporterBackedASTSource::isA(ClassID); + } static bool classof(const clang::ExternalASTSource *s) { return s->isA(&ID); } ClangExternalASTSourceCallbacks(TypeSystemClang &ast) : m_ast(ast) {} @@ -44,6 +47,8 @@ class ClangExternalASTSourceCallbacks : public clang::ExternalASTSource { void CompleteType(clang::ObjCInterfaceDecl *objc_decl) override; + void CompleteRedeclChain(clang::Decl const *D) override; + bool layoutRecordType( const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment, llvm::DenseMap &FieldOffsets, diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.cpp index 2e0bb318cb507..a604779fa8e17 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.cpp @@ -10,6 +10,7 @@ #include "Plugins/ExpressionParser/Clang/ClangUtil.h" #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "clang/AST/DeclObjC.h" using namespace clang; using namespace lldb_private; @@ -65,6 +66,28 @@ clang::TagDecl *ClangUtil::GetAsTagDecl(const CompilerType &type) { return qual_type->getAsTagDecl(); } +clang::Decl *ClangUtil::GetFirstDecl(clang::Decl *d) { + if (!d) + return nullptr; + if (auto *td = llvm::dyn_cast(d)) + return td->getFirstDecl(); + if (auto *od = llvm::dyn_cast(d)) + return od->getFirstDecl(); + return d; +} + +clang::ObjCInterfaceDecl *ClangUtil::GetAsObjCDecl(const CompilerType &type) { + clang::QualType qual_type = ClangUtil::GetCanonicalQualType(type); + if (qual_type.isNull()) + return nullptr; + + if (const auto *ot = qual_type->getAsObjCInterfaceType()) + return ot->getInterface(); + if (const auto *ot = qual_type->getAsObjCInterfacePointerType()) + return ot->getInterfaceDecl(); + return nullptr; +} + std::string ClangUtil::DumpDecl(const clang::Decl *d) { if (!d) return "nullptr"; diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.h index 50cae42bc7c21..199c6639b68fb 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.h @@ -36,6 +36,17 @@ struct ClangUtil { static clang::TagDecl *GetAsTagDecl(const CompilerType &type); + /// If the given Decl is redeclarable, return the first declaration in its + /// redeclaration chain. Otherwise return d itself. + static clang::Decl *GetFirstDecl(clang::Decl *d); + + /// \see ClangUtil::GetFirstDecl + static const clang::Decl *GetFirstDecl(const clang::Decl *d) { + return GetFirstDecl(const_cast(d)); + } + + static clang::ObjCInterfaceDecl *GetAsObjCDecl(const CompilerType &type); + /// Returns a textual representation of the given Decl's AST. Does not /// deserialize any child nodes. static std::string DumpDecl(const clang::Decl *d); diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp index 6894cdccaf95a..5665252fb6c03 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp @@ -11,6 +11,8 @@ #include "Plugins/ExpressionParser/Clang/ClangASTMetadata.h" #include "Plugins/ExpressionParser/Clang/ClangUtil.h" #include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" +#include "Plugins/TypeSystem/Clang/ImporterBackedASTSource.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" #include "lldb/Core/Module.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" @@ -24,49 +26,11 @@ using namespace lldb_private; class lldb_private::AppleObjCExternalASTSource - : public clang::ExternalASTSource { + : public ImporterBackedASTSource { public: AppleObjCExternalASTSource(AppleObjCDeclVendor &decl_vendor) : m_decl_vendor(decl_vendor) {} - bool FindExternalVisibleDeclsByName(const clang::DeclContext *decl_ctx, - clang::DeclarationName name) override { - - Log *log(GetLog( - LLDBLog::Expressions)); // FIXME - a more appropriate log channel? - - if (log) { - LLDB_LOGF(log, - "AppleObjCExternalASTSource::FindExternalVisibleDeclsByName" - " on (ASTContext*)%p Looking for %s in (%sDecl*)%p", - static_cast(&decl_ctx->getParentASTContext()), - name.getAsString().c_str(), decl_ctx->getDeclKindName(), - static_cast(decl_ctx)); - } - - do { - const clang::ObjCInterfaceDecl *interface_decl = - llvm::dyn_cast(decl_ctx); - - if (!interface_decl) - break; - - clang::ObjCInterfaceDecl *non_const_interface_decl = - const_cast(interface_decl); - - if (!m_decl_vendor.FinishDecl(non_const_interface_decl)) - break; - - clang::DeclContext::lookup_result result = - non_const_interface_decl->lookup(name); - - return (!result.empty()); - } while (false); - - SetNoExternalVisibleDeclsForName(decl_ctx, name); - return false; - } - void CompleteType(clang::TagDecl *tag_decl) override { Log *log(GetLog( @@ -108,6 +72,15 @@ class lldb_private::AppleObjCExternalASTSource } } + void CompleteRedeclChain(const clang::Decl *d) override { + using namespace clang; + auto *const_interface = llvm::dyn_cast(d); + if (!const_interface) + return; + auto *interface = const_cast(const_interface); + m_decl_vendor.FinishDecl(interface); + } + bool layoutRecordType( const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment, llvm::DenseMap &FieldOffsets, @@ -169,8 +142,8 @@ AppleObjCDeclVendor::GetDeclForISA(ObjCLanguageRuntime::ObjCISA isa) { meta_data.SetISAPtr(isa); m_ast_ctx->SetMetadata(new_iface_decl, meta_data); - new_iface_decl->setHasExternalVisibleStorage(); - new_iface_decl->setHasExternalLexicalStorage(); + m_interface_to_isa[new_iface_decl] = isa; + m_external_source->MarkRedeclChainsAsOutOfDate(m_ast_ctx->getASTContext()); ast_ctx.getTranslationUnitDecl()->addDecl(new_iface_decl); @@ -398,6 +371,19 @@ bool AppleObjCDeclVendor::FinishDecl(clang::ObjCInterfaceDecl *interface_decl) { Log *log( GetLog(LLDBLog::Expressions)); // FIXME - a more appropriate log channel? + clang::ObjCInterfaceDecl *iface_def = nullptr; + // Already completed. + if (interface_decl->hasDefinition()) + return true; + + clang::QualType qt(interface_decl->getTypeForDecl(), 0U); + CompilerType type(m_ast_ctx, qt.getAsOpaquePtr()); + CompilerType def_type = m_ast_ctx->CreateRedeclaration(type); + iface_def = ClangUtil::GetAsObjCDecl(def_type); + + auto isa = m_interface_to_isa[interface_decl]; + m_isa_to_interface[isa] = iface_def; + ClangASTMetadata *metadata = m_ast_ctx->GetMetadata(interface_decl); ObjCLanguageRuntime::ObjCISA objc_isa = 0; if (metadata) @@ -406,14 +392,12 @@ bool AppleObjCDeclVendor::FinishDecl(clang::ObjCInterfaceDecl *interface_decl) { if (!objc_isa) return false; - if (!interface_decl->hasExternalVisibleStorage()) + // Could've completed during CreateRedeclaration above + if (interface_decl->hasDefinition()) return true; interface_decl->startDefinition(); - interface_decl->setHasExternalVisibleStorage(false); - interface_decl->setHasExternalLexicalStorage(false); - ObjCLanguageRuntime::ClassDescriptorSP descriptor = m_runtime.GetClassDescriptorFromISA(objc_isa); diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.h b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.h index 3bb0f77f6bde4..586b3095af244 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.h +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.h @@ -46,6 +46,10 @@ class AppleObjCDeclVendor : public ClangDeclVendor { ISAToInterfaceMap; ISAToInterfaceMap m_isa_to_interface; + + using InterfaceToISAMap = + llvm::DenseMap; + InterfaceToISAMap m_interface_to_isa; }; } // namespace lldb_private diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 52f4d765cbbd4..4eb3a96faaa59 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -13,6 +13,8 @@ #include "DWARFDebugInfo.h" #include "DWARFDeclContext.h" #include "DWARFDefines.h" +#include "Plugins/TypeSystem/Clang/ImporterBackedASTSource.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" #include "SymbolFileDWARF.h" #include "SymbolFileDWARFDebugMap.h" #include "SymbolFileDWARFDwo.h" @@ -27,11 +29,13 @@ #include "lldb/Core/Value.h" #include "lldb/Host/Host.h" #include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/CompilerType.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/TypeList.h" #include "lldb/Symbol/TypeMap.h" +#include "lldb/Symbol/TypeSystem.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/Language.h" #include "lldb/Utility/LLDBAssert.h" @@ -44,6 +48,7 @@ #include "clang/AST/DeclTemplate.h" #include "clang/AST/Type.h" #include "llvm/Demangle/Demangle.h" +#include "llvm/Support/Casting.h" #include #include @@ -64,6 +69,18 @@ using namespace lldb_private; using namespace lldb_private::dwarf; using namespace lldb_private::plugin::dwarf; +/// Types that we want to complete directly (instead of +/// relying on CompleteRedeclChain): +/// - Anonymous structures +/// - Function-local classes +static bool DirectlyCompleteType(clang::DeclContext *decl_ctx, + const ParsedDWARFTypeAttributes &attrs) { + assert(decl_ctx); + if (decl_ctx->isFunctionOrMethod()) + return true; + return attrs.name.IsEmpty() && !attrs.is_forward_declaration; +} + DWARFASTParserClang::DWARFASTParserClang(TypeSystemClang &ast) : DWARFASTParser(Kind::DWARFASTParserClang), m_ast(ast), m_die_to_decl_ctx(), m_decl_ctx_to_die() {} @@ -260,7 +277,8 @@ static void PrepareContextToReceiveMembers(TypeSystemClang &ast, // We have already completed the type, or we have found its definition and are // ready to complete it later (cf. ParseStructureLikeDIE). - if (tag_decl_ctx->isCompleteDefinition() || tag_decl_ctx->isBeingDefined()) + if (tag_decl_ctx->getDefinition() != nullptr || + tag_decl_ctx->isBeingDefined()) return; // We reach this point of the tag was present in the debug info as a @@ -268,8 +286,8 @@ static void PrepareContextToReceiveMembers(TypeSystemClang &ast, // gmodules case), we can complete the type by doing a full import. // If this type was not imported from an external AST, there's nothing to do. - CompilerType type = ast.GetTypeForDecl(tag_decl_ctx); - if (type && ast_importer.CanImport(type)) { + if (ast_importer.CanImport(tag_decl_ctx)) { + CompilerType type = ast.GetTypeForDecl(tag_decl_ctx); auto qual_type = ClangUtil::GetQualType(type); if (ast_importer.RequireCompleteType(qual_type)) return; @@ -281,7 +299,18 @@ static void PrepareContextToReceiveMembers(TypeSystemClang &ast, // We don't have a type definition and/or the import failed. We must // forcefully complete the type to avoid crashes. - ForcefullyCompleteType(type); + ForcefullyCompleteType(ast.GetTypeForDecl(tag_decl_ctx)); +} + +void DWARFASTParserClang::RegisterDIE(DWARFDebugInfoEntry *die, + CompilerType type) { + if (clang::TagDecl *td = ClangUtil::GetAsTagDecl(type)) { + m_die_to_record_map[die] = td; + } else if (auto *od = ClangUtil::GetAsObjCDecl(type)) + m_die_to_objc_interface_map[die] = od; + else { + assert(false && "Unknown Decl kind?"); + } } ParsedDWARFTypeAttributes::ParsedDWARFTypeAttributes(const DWARFDIE &die) { @@ -458,13 +487,8 @@ TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, } // Set a bit that lets us know that we are currently parsing this - if (auto [it, inserted] = - dwarf->GetDIEToType().try_emplace(die.GetDIE(), DIE_IS_BEING_PARSED); - !inserted) { - if (it->getSecond() == nullptr || it->getSecond() == DIE_IS_BEING_PARSED) - return nullptr; - return it->getSecond()->shared_from_this(); - } + if (auto *type_ptr = dwarf->GetDIEToType().lookup(die.GetDIE())) + return type_ptr->shared_from_this(); ParsedDWARFTypeAttributes attrs(die); @@ -524,6 +548,13 @@ TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, break; } UpdateSymbolContextScopeForType(sc, die, type_sp); + + while (!m_to_complete.empty()) { + TypeToComplete to_complete = m_to_complete.back(); + m_to_complete.pop_back(); + CompleteRecordType(to_complete.die, to_complete.type.get(), + to_complete.clang_type); + } } if (type_sp) { dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); @@ -1086,85 +1117,51 @@ std::pair DWARFASTParserClang::ParseCXXMethod( if (!TypeSystemClang::IsCXXClassType(class_opaque_type)) return {}; - if (class_opaque_type.IsBeingDefined()) { - // We have a C++ member function with no children (this - // pointer!) and clang will get mad if we try and make - // a function that isn't well formed in the DWARF, so - // we will just skip it... - if (!is_static && !die.HasChildren()) - return {true, nullptr}; - - const bool is_attr_used = false; - // Neither GCC 4.2 nor clang++ currently set a valid - // accessibility in the DWARF for C++ methods... - // Default to public for now... - const auto accessibility = attrs.accessibility == eAccessNone - ? eAccessPublic - : attrs.accessibility; - - clang::CXXMethodDecl *cxx_method_decl = m_ast.AddMethodToCXXRecordType( - class_opaque_type.GetOpaqueQualType(), attrs.name.GetCString(), - attrs.mangled_name, clang_type, accessibility, attrs.is_virtual, - is_static, attrs.is_inline, attrs.is_explicit, is_attr_used, - attrs.is_artificial); - - if (cxx_method_decl) { - LinkDeclContextToDIE(cxx_method_decl, die); - - ClangASTMetadata metadata; - metadata.SetUserID(die.GetID()); - - char const *object_pointer_name = - attrs.object_pointer ? attrs.object_pointer.GetName() : nullptr; - if (object_pointer_name) { - metadata.SetObjectPtrName(object_pointer_name); - LLDB_LOGF(log, - "Setting object pointer name: %s on method " - "object %p.\n", - object_pointer_name, static_cast(cxx_method_decl)); - } - m_ast.SetMetadata(cxx_method_decl, metadata); - } else { - ignore_containing_context = true; - } + // We have a C++ member function with no children (this + // pointer!) and clang will get mad if we try and make + // a function that isn't well formed in the DWARF, so + // we will just skip it... + if (!is_static && !die.HasChildren()) + return {true, nullptr}; - // Artificial methods are always handled even when we - // don't create a new declaration for them. - const bool type_handled = cxx_method_decl != nullptr || attrs.is_artificial; + const bool is_attr_used = false; + // Neither GCC 4.2 nor clang++ currently set a valid + // accessibility in the DWARF for C++ methods... + // Default to public for now... + const auto accessibility = + attrs.accessibility == eAccessNone ? eAccessPublic : attrs.accessibility; - return {type_handled, nullptr}; - } + clang::CXXMethodDecl *cxx_method_decl = m_ast.AddMethodToCXXRecordType( + class_opaque_type.GetOpaqueQualType(), attrs.name.GetCString(), + attrs.mangled_name, clang_type, accessibility, attrs.is_virtual, + is_static, attrs.is_inline, attrs.is_explicit, is_attr_used, + attrs.is_artificial); - // We were asked to parse the type for a method in a - // class, yet the class hasn't been asked to complete - // itself through the clang::ExternalASTSource protocol, - // so we need to just have the class complete itself and - // do things the right way, then our - // DIE should then have an entry in the - // dwarf->GetDIEToType() map. First - // we need to modify the dwarf->GetDIEToType() so it - // doesn't think we are trying to parse this DIE - // anymore... - dwarf->GetDIEToType().erase(die.GetDIE()); + if (cxx_method_decl) { + LinkDeclContextToDIE(cxx_method_decl, die); - // Now we get the full type to force our class type to - // complete itself using the clang::ExternalASTSource - // protocol which will parse all base classes and all - // methods (including the method for this DIE). - class_type->GetFullCompilerType(); + ClangASTMetadata metadata; + metadata.SetUserID(die.GetID()); + + char const *object_pointer_name = + attrs.object_pointer ? attrs.object_pointer.GetName() : nullptr; + if (object_pointer_name) { + metadata.SetObjectPtrName(object_pointer_name); + LLDB_LOGF(log, + "Setting object pointer name: %s on method " + "object %p.\n", + object_pointer_name, static_cast(cxx_method_decl)); + } + m_ast.SetMetadata(cxx_method_decl, metadata); + } else { + ignore_containing_context = true; + } - // The type for this DIE should have been filled in the - // function call above. - Type *type_ptr = dwarf->GetDIEToType().lookup(die.GetDIE()); - if (type_ptr && type_ptr != DIE_IS_BEING_PARSED) - return {true, type_ptr->shared_from_this()}; + // Artificial methods are always handled even when we + // don't create a new declaration for them. + const bool type_handled = cxx_method_decl != nullptr || attrs.is_artificial; - // The previous comment isn't actually true if the class wasn't - // resolved using the current method's parent DIE as source - // data. We need to ensure that we look up the method correctly - // in the class and then link the method's DIE to the unique - // CXXMethodDecl appropriately. - return {true, nullptr}; + return {type_handled, nullptr}; } TypeSP @@ -1206,6 +1203,10 @@ DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die, const clang::Decl::Kind containing_decl_kind = containing_decl_ctx->getDeclKind(); + PrepareContextToReceiveMembers(m_ast, GetClangASTImporter(), + containing_decl_ctx, die, + attrs.name.GetCString()); + bool is_cxx_method = DeclKindIsCXXClass(containing_decl_kind); // Start off static. This will be set to false in // ParseChildParameters(...) if we find a "this" parameters as the @@ -1633,6 +1634,30 @@ DWARFASTParserClang::GetCPlusPlusQualifiedName(const DWARFDIE &die) { return qualified_name; } +static void adjustArgPassing(TypeSystemClang &ast, + ParsedDWARFTypeAttributes const &attrs, + CompilerType const &clang_type) { + // If we made a clang type, set the trivial abi if applicable: We only + // do this for pass by value - which implies the Trivial ABI. There + // isn't a way to assert that something that would normally be pass by + // value is pass by reference, so we ignore that attribute if set. + if (attrs.calling_convention == llvm::dwarf::DW_CC_pass_by_value) { + clang::CXXRecordDecl *record_decl = + ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType()); + if (record_decl && record_decl->getDefinition()) { + record_decl->setHasTrivialSpecialMemberForCall(); + } + } + + if (attrs.calling_convention == llvm::dwarf::DW_CC_pass_by_reference) { + clang::CXXRecordDecl *record_decl = + ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType()); + if (record_decl) + record_decl->setArgPassingRestrictions( + clang::RecordArgPassingKind::CannotPassInRegs); + } +} + TypeSP DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, const DWARFDIE &die, @@ -1644,6 +1669,9 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, LanguageType cu_language = SymbolFileDWARF::GetLanguage(*die.GetCU()); Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups); + clang::DeclContext *decl_ctx = GetClangDeclContextContainingDIE(die, nullptr); + + const bool should_directly_complete = DirectlyCompleteType(decl_ctx, attrs); // UniqueDWARFASTType is large, so don't create a local variables on the // stack, put it on the heap. This function is often called recursively and // clang isn't good at sharing the stack space for variables in different @@ -1806,7 +1834,6 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, assert(tag_decl_kind != -1); UNUSED_IF_ASSERT_DISABLED(tag_decl_kind); bool clang_type_was_created = false; - clang::DeclContext *decl_ctx = GetClangDeclContextContainingDIE(die, nullptr); PrepareContextToReceiveMembers(m_ast, GetClangASTImporter(), decl_ctx, die, attrs.name.GetCString()); @@ -1851,6 +1878,11 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, m_ast.SetMetadata(class_template_decl, metadata); m_ast.SetMetadata(class_specialization_decl, metadata); + + RegisterDIE(die.GetDIE(), clang_type); + if (auto *source = llvm::dyn_cast_or_null( + m_ast.getASTContext().getExternalSource())) + source->MarkRedeclChainsAsOutOfDate(m_ast.getASTContext()); } if (!clang_type_was_created) { @@ -1859,12 +1891,18 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, decl_ctx, GetOwningClangModule(die), attrs.accessibility, attrs.name.GetCString(), tag_decl_kind, attrs.class_language, &metadata, attrs.exports_symbols); + RegisterDIE(die.GetDIE(), clang_type); + if (!should_directly_complete) + if (auto *source = llvm::dyn_cast_or_null( + m_ast.getASTContext().getExternalSource())) + source->MarkRedeclChainsAsOutOfDate(m_ast.getASTContext()); } // Store a forward declaration to this class type in case any // parameters in any class methods need it for the clang types for // function prototypes. - LinkDeclContextToDIE(m_ast.GetDeclContextForType(clang_type), die); + LinkDeclContextToDIE(GetClangDeclContextContainingDIE(die, nullptr), die); + type_sp = dwarf->MakeType( die.GetID(), attrs.name, attrs.byte_size, nullptr, LLDB_INVALID_UID, Type::eEncodingIsUID, &attrs.decl, clang_type, @@ -1882,10 +1920,6 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, *unique_ast_entry_up); if (!attrs.is_forward_declaration) { - // Always start the definition for a class type so that if the class - // has child classes or types that require the class to be created - // for use as their decl contexts the class will be ready to accept - // these child definitions. if (!die.HasChildren()) { // No children for this struct/union/class, lets finish it if (TypeSystemClang::StartTagDeclarationDefinition(clang_type)) { @@ -1918,57 +1952,29 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, GetClangASTImporter().SetRecordLayout(record_decl, layout); } } - } else if (clang_type_was_created) { - // Start the definition if the class is not objective C since the - // underlying decls respond to isCompleteDefinition(). Objective - // C decls don't respond to isCompleteDefinition() so we can't - // start the declaration definition right away. For C++ - // class/union/structs we want to start the definition in case the - // class is needed as the declaration context for a contained class - // or type without the need to complete that type.. - + } else if (clang_type_was_created && !should_directly_complete) { if (attrs.class_language != eLanguageTypeObjC && attrs.class_language != eLanguageTypeObjC_plus_plus) - TypeSystemClang::StartTagDeclarationDefinition(clang_type); - - // Leave this as a forward declaration until we need to know the - // details of the type. lldb_private::Type will automatically call - // the SymbolFile virtual function - // "SymbolFileDWARF::CompleteType(Type *)" When the definition - // needs to be defined. - assert(!dwarf->GetForwardDeclCompilerTypeToDIE().count( - ClangUtil::RemoveFastQualifiers(clang_type) - .GetOpaqueQualType()) && - "Type already in the forward declaration map!"); + // Leave this as a forward declaration until we need to know the + // details of the type. lldb_private::Type will automatically call + // the SymbolFile virtual function + // "SymbolFileDWARF::CompleteType(Type *)" When the definition + // needs to be defined. + assert(!dwarf->GetForwardDeclCompilerTypeToDIE().count( + ClangUtil::RemoveFastQualifiers(clang_type) + .GetOpaqueQualType()) && + "Type already in the forward declaration map!"); // Can't assume m_ast.GetSymbolFile() is actually a // SymbolFileDWARF, it can be a SymbolFileDWARFDebugMap for Apple // binaries. dwarf->GetForwardDeclCompilerTypeToDIE().try_emplace( ClangUtil::RemoveFastQualifiers(clang_type).GetOpaqueQualType(), *die.GetDIERef()); - m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), true); } } - // If we made a clang type, set the trivial abi if applicable: We only - // do this for pass by value - which implies the Trivial ABI. There - // isn't a way to assert that something that would normally be pass by - // value is pass by reference, so we ignore that attribute if set. - if (attrs.calling_convention == llvm::dwarf::DW_CC_pass_by_value) { - clang::CXXRecordDecl *record_decl = - m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType()); - if (record_decl && record_decl->getDefinition()) { - record_decl->setHasTrivialSpecialMemberForCall(); - } - } + m_to_complete.push_back({clang_type, die, type_sp}); - if (attrs.calling_convention == llvm::dwarf::DW_CC_pass_by_reference) { - clang::CXXRecordDecl *record_decl = - m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType()); - if (record_decl) - record_decl->setArgPassingRestrictions( - clang::RecordArgPassingKind::CannotPassInRegs); - } return type_sp; } @@ -2175,79 +2181,86 @@ bool DWARFASTParserClang::ParseTemplateParameterInfos( bool DWARFASTParserClang::CompleteRecordType(const DWARFDIE &die, lldb_private::Type *type, CompilerType &clang_type) { + if (!m_currently_parsed_record_dies.insert(die.GetDIE()).second) + return true; + const dw_tag_t tag = die.Tag(); SymbolFileDWARF *dwarf = die.GetDWARF(); ClangASTImporter::LayoutInfo layout_info; std::vector contained_type_dies; - if (die.HasChildren()) { - const bool type_is_objc_object_or_interface = - TypeSystemClang::IsObjCObjectOrInterfaceType(clang_type); - if (type_is_objc_object_or_interface) { - // For objective C we don't start the definition when the class is - // created. - TypeSystemClang::StartTagDeclarationDefinition(clang_type); - } - - AccessType default_accessibility = eAccessNone; - if (tag == DW_TAG_structure_type) { - default_accessibility = eAccessPublic; - } else if (tag == DW_TAG_union_type) { - default_accessibility = eAccessPublic; - } else if (tag == DW_TAG_class_type) { - default_accessibility = eAccessPrivate; - } - - std::vector> bases; - // Parse members and base classes first - std::vector member_function_dies; - - DelayedPropertyList delayed_properties; - ParseChildMembers(die, clang_type, bases, member_function_dies, - contained_type_dies, delayed_properties, - default_accessibility, layout_info); - - // Now parse any methods if there were any... - for (const DWARFDIE &die : member_function_dies) - dwarf->ResolveType(die); - - if (type_is_objc_object_or_interface) { - ConstString class_name(clang_type.GetTypeName()); - if (class_name) { - dwarf->GetObjCMethods(class_name, [&](DWARFDIE method_die) { - method_die.ResolveType(); - return true; - }); + ParsedDWARFTypeAttributes attrs(die); + if (attrs.is_forward_declaration) + return true; - for (DelayedAddObjCClassProperty &property : delayed_properties) - property.Finalize(); - } - } + clang::DeclContext *decl_ctx = GetClangDeclContextContainingDIE(die, nullptr); - if (!bases.empty()) { - // 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) - TypeSystemClang::RequireCompleteType( - m_ast.GetType(type_source_info->getType())); - } + if (!DirectlyCompleteType(decl_ctx, attrs)) { + clang_type = m_ast.CreateRedeclaration(clang_type); + RegisterDIE(die.GetDIE(), clang_type); + } + + TypeSystemClang::StartTagDeclarationDefinition(clang_type); + + AccessType default_accessibility = eAccessNone; + if (tag == DW_TAG_structure_type) { + default_accessibility = eAccessPublic; + } else if (tag == DW_TAG_union_type) { + default_accessibility = eAccessPublic; + } else if (tag == DW_TAG_class_type) { + default_accessibility = eAccessPrivate; + } + + std::vector> bases; + // Parse members and base classes first + std::vector member_function_dies; - m_ast.TransferBaseClasses(clang_type.GetOpaqueQualType(), - std::move(bases)); + DelayedPropertyList delayed_properties; + ParseChildMembers(die, clang_type, bases, member_function_dies, + contained_type_dies, delayed_properties, + default_accessibility, layout_info); + + // Now parse any methods if there were any... + for (const DWARFDIE &die : member_function_dies) + dwarf->ResolveType(die); + + if (TypeSystemClang::IsObjCObjectOrInterfaceType(clang_type)) { + ConstString class_name(clang_type.GetTypeName()); + if (class_name) { + dwarf->GetObjCMethods(class_name, [&](DWARFDIE method_die) { + method_die.ResolveType(); + return true; + }); + + for (DelayedAddObjCClassProperty &property : delayed_properties) + property.Finalize(); + } + } + + if (!bases.empty()) { + // 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) + TypeSystemClang::RequireCompleteType( + m_ast.GetType(type_source_info->getType())); } + + m_ast.TransferBaseClasses(clang_type.GetOpaqueQualType(), std::move(bases)); } + adjustArgPassing(m_ast, attrs, clang_type); + m_ast.AddMethodOverridesForCXXRecordType(clang_type.GetOpaqueQualType()); TypeSystemClang::BuildIndirectFields(clang_type); TypeSystemClang::CompleteTagDeclarationDefinition(clang_type); - if (!layout_info.field_offsets.empty() || !layout_info.base_offsets.empty() || - !layout_info.vbase_offsets.empty()) { + clang::CXXRecordDecl *record_decl = + m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType()); + if (record_decl && !GetClangASTImporter().HasRecordLayout(record_decl)) { if (type) layout_info.bit_size = type->GetByteSize(nullptr).value_or(0) * 8; if (layout_info.bit_size == 0) @@ -2257,10 +2270,7 @@ bool DWARFASTParserClang::CompleteRecordType(const DWARFDIE &die, layout_info.alignment = die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_alignment, 0) * 8; - clang::CXXRecordDecl *record_decl = - m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType()); - if (record_decl) - GetClangASTImporter().SetRecordLayout(record_decl, layout_info); + GetClangASTImporter().SetRecordLayout(record_decl, layout_info); } // Now parse all contained types inside of the class. We make forward // declarations to all classes, but we need the CXXRecordDecl to have decls @@ -2295,10 +2305,6 @@ bool DWARFASTParserClang::CompleteTypeFromDWARF(const DWARFDIE &die, std::lock_guard guard( dwarf->GetObjectFile()->GetModule()->GetMutex()); - // Disable external storage for this type so we don't get anymore - // clang::ExternalASTSource queries for this type. - m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), false); - if (!die) return false; @@ -3675,6 +3681,13 @@ clang::DeclContext *DWARFASTParserClang::GetClangDeclContextContainingDIE( clang::DeclContext * DWARFASTParserClang::GetCachedClangDeclContextForDIE(const DWARFDIE &die) { if (die) { + DIEToRecordMap::iterator pos2 = m_die_to_record_map.find(die.GetDIE()); + if (pos2 != m_die_to_record_map.end()) + return pos2->second; + DIEToObjCInterfaceMap::iterator pos3 = + m_die_to_objc_interface_map.find(die.GetDIE()); + if (pos3 != m_die_to_objc_interface_map.end()) + return pos3->second; DIEToDeclContextMap::iterator pos = m_die_to_decl_ctx.find(die.GetDIE()); if (pos != m_die_to_decl_ctx.end()) return pos->second; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h index 7b5ddbaa2a6b5..50366bdd0e99e 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h @@ -130,12 +130,30 @@ class DWARFASTParserClang : public lldb_private::plugin::dwarf::DWARFASTParser { const lldb_private::plugin::dwarf::DWARFDebugInfoEntry *, clang::Decl *> DIEToDeclMap; + using DIEToRecordMap = + llvm::DenseMap; + using DIEToObjCInterfaceMap = + llvm::DenseMap; + lldb_private::TypeSystemClang &m_ast; DIEToDeclMap m_die_to_decl; DIEToDeclContextMap m_die_to_decl_ctx; DeclContextToDIEMap m_decl_ctx_to_die; DIEToModuleMap m_die_to_module; + DIEToRecordMap m_die_to_record_map; + DIEToObjCInterfaceMap m_die_to_objc_interface_map; std::unique_ptr m_clang_ast_importer_up; + + struct TypeToComplete { + lldb_private::CompilerType clang_type; + lldb_private::plugin::dwarf::DWARFDIE die; + lldb::TypeSP type; + }; + std::vector m_to_complete; + llvm::DenseSet + m_currently_parsed_record_dies; /// @} clang::DeclContext * @@ -228,6 +246,9 @@ class DWARFASTParserClang : public lldb_private::plugin::dwarf::DWARFASTParser { void LinkDeclToDIE(clang::Decl *decl, const lldb_private::plugin::dwarf::DWARFDIE &die); + void RegisterDIE(lldb_private::plugin::dwarf::DWARFDebugInfoEntry *die, + lldb_private::CompilerType type); + /// If \p type_sp is valid, calculate and set its symbol context scope, and /// update the type list for its backing symbol file. /// diff --git a/lldb/source/Plugins/TypeSystem/Clang/CMakeLists.txt b/lldb/source/Plugins/TypeSystem/Clang/CMakeLists.txt index 37a3142da7274..4b33789479478 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/CMakeLists.txt +++ b/lldb/source/Plugins/TypeSystem/Clang/CMakeLists.txt @@ -1,4 +1,5 @@ add_lldb_library(lldbPluginTypeSystemClang PLUGIN + ImporterBackedASTSource.cpp TypeSystemClang.cpp LINK_LIBS diff --git a/lldb/source/Plugins/TypeSystem/Clang/ImporterBackedASTSource.cpp b/lldb/source/Plugins/TypeSystem/Clang/ImporterBackedASTSource.cpp new file mode 100644 index 0000000000000..f9445b4548cc7 --- /dev/null +++ b/lldb/source/Plugins/TypeSystem/Clang/ImporterBackedASTSource.cpp @@ -0,0 +1,13 @@ +//===-- ImporterBackedASTSource.cpp ---------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Plugins/TypeSystem/Clang/ImporterBackedASTSource.h" + +using namespace lldb_private; + +char ImporterBackedASTSource::ID; diff --git a/lldb/source/Plugins/TypeSystem/Clang/ImporterBackedASTSource.h b/lldb/source/Plugins/TypeSystem/Clang/ImporterBackedASTSource.h new file mode 100644 index 0000000000000..00d1a578a0596 --- /dev/null +++ b/lldb/source/Plugins/TypeSystem/Clang/ImporterBackedASTSource.h @@ -0,0 +1,46 @@ +//===-- ImporterBackedASTSource.h -------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_TYPESYSTEM_CLANG_IMPORTERBACKEDASTSOURCE +#define LLDB_SOURCE_PLUGINS_TYPESYSTEM_CLANG_IMPORTERBACKEDASTSOURCE + +#include "clang/AST/ASTContext.h" +#include "clang/Sema/ExternalSemaSource.h" + +namespace lldb_private { + +/// The base class of all ExternalASTSources in LLDB that use the +/// ClangASTImporter to move declarations from other ASTs to the ASTContext they +/// are attached to. +class ImporterBackedASTSource : public clang::ExternalSemaSource { + /// LLVM RTTI support. + static char ID; + +public: + /// LLVM RTTI support. + bool isA(const void *ClassID) const override { + return ClassID == &ID || ExternalSemaSource::isA(ClassID); + } + static bool classof(const clang::ExternalASTSource *s) { return s->isA(&ID); } + + /// This marks all redeclaration chains in the ASTContext as out-of-date and + /// that this ExternalASTSource should be consulted to get the complete + /// redeclaration chain. + /// + /// \see ExternalASTSource::CompleteRedeclChain + void MarkRedeclChainsAsOutOfDate(clang::ASTContext &c) { + // This invalidates redeclaration chains but also other things such as + // identifiers. There isn't a more precise way at the moment that only + // affects redecl chains. + incrementGeneration(c); + } +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_TYPESYSTEM_CLANG_IMPORTERBACKEDASTSOURCE diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index cd1c500d9aa29..c88854f275d48 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -10,6 +10,7 @@ #include "clang/AST/DeclBase.h" #include "clang/AST/ExprCXX.h" +#include "llvm/ADT/STLForwardCompat.h" #include "llvm/Support/Casting.h" #include "llvm/Support/FormatAdapters.h" #include "llvm/Support/FormatVariadic.h" @@ -1159,11 +1160,18 @@ CompilerType TypeSystemClang::GetTypeForDecl(clang::NamedDecl *decl) { } CompilerType TypeSystemClang::GetTypeForDecl(TagDecl *decl) { - return GetType(getASTContext().getTagDeclType(decl)); + // Create the type for the TagDecl. Pass the previous decl to make sure that + // all redeclarations share the same type. + return GetType( + getASTContext().getTypeDeclType(decl, decl->getPreviousDecl())); } -CompilerType TypeSystemClang::GetTypeForDecl(ObjCInterfaceDecl *decl) { - return GetType(getASTContext().getObjCInterfaceType(decl)); +CompilerType TypeSystemClang::GetTypeForDecl(const ObjCInterfaceDecl *decl) { + // FIXME: getObjCInterfaceType second parameter could be const. + // Create the type for the ObjCInterfaceDecl. Pass the previous decl to make + // sure that all redeclarations share the same type. + return GetType(getASTContext().getObjCInterfaceType( + decl, const_cast(decl->getPreviousDecl()))); } CompilerType TypeSystemClang::GetTypeForDecl(clang::ValueDecl *value_decl) { @@ -1218,6 +1226,17 @@ TypeSystemClang::GetOrCreateClangModule(llvm::StringRef name, } CompilerType TypeSystemClang::CreateRecordType( + clang::DeclContext *decl_ctx, OptionalClangModuleID owning_module, + lldb::AccessType access_type, llvm::StringRef name, int kind, + lldb::LanguageType language, ClangASTMetadata *metadata, + bool exports_symbols) { + clang::NamedDecl *d = + CreateRecordDecl(decl_ctx, owning_module, access_type, name, kind, + language, metadata, exports_symbols); + return GetTypeForDecl(d); +} + +clang::NamedDecl *TypeSystemClang::CreateRecordDecl( clang::DeclContext *decl_ctx, OptionalClangModuleID owning_module, AccessType access_type, llvm::StringRef name, int kind, LanguageType language, ClangASTMetadata *metadata, bool exports_symbols) { @@ -1225,13 +1244,14 @@ CompilerType TypeSystemClang::CreateRecordType( if (decl_ctx == nullptr) decl_ctx = ast.getTranslationUnitDecl(); + decl_ctx = decl_ctx->getPrimaryContext(); if (language == eLanguageTypeObjC || language == eLanguageTypeObjC_plus_plus) { bool isForwardDecl = true; bool isInternal = false; - return CreateObjCClass(name, decl_ctx, owning_module, isForwardDecl, - isInternal, metadata); + return CreateObjCDecl(name, decl_ctx, owning_module, isForwardDecl, + isInternal, metadata); } // NOTE: Eventually CXXRecordDecl will be merged back into RecordDecl and @@ -1286,7 +1306,7 @@ CompilerType TypeSystemClang::CreateRecordType( if (decl_ctx) decl_ctx->addDecl(decl); - return GetType(ast.getTagDeclType(decl)); + return decl; } namespace { @@ -1548,6 +1568,7 @@ ClassTemplateDecl *TypeSystemClang::CreateClassTemplateDecl( ClassTemplateDecl *class_template_decl = nullptr; if (decl_ctx == nullptr) decl_ctx = ast.getTranslationUnitDecl(); + decl_ctx = decl_ctx->getPrimaryContext(); IdentifierInfo &identifier_info = ast.Idents.get(class_name); DeclarationName decl_name(&identifier_info); @@ -1602,6 +1623,9 @@ ClassTemplateDecl *TypeSystemClang::CreateClassTemplateDecl( class_template_decl->init(template_cxx_decl); template_cxx_decl->setDescribedClassTemplate(class_template_decl); SetOwningModule(class_template_decl, owning_module); + ast.getInjectedClassNameType( + template_cxx_decl, + class_template_decl->getInjectedClassNameSpecialization()); if (access_type != eAccessNone) class_template_decl->setAccess( @@ -1643,6 +1667,8 @@ TypeSystemClang::CreateClassTemplateSpecializationDecl( DeclContext *decl_ctx, OptionalClangModuleID owning_module, ClassTemplateDecl *class_template_decl, int kind, const TemplateParameterInfos &template_param_infos) { + decl_ctx = decl_ctx->getPrimaryContext(); + ASTContext &ast = getASTContext(); llvm::SmallVector args( template_param_infos.Size() + @@ -1660,9 +1686,9 @@ TypeSystemClang::CreateClassTemplateSpecializationDecl( static_cast(kind)); class_template_specialization_decl->setDeclContext(decl_ctx); class_template_specialization_decl->setInstantiationOf(class_template_decl); + ast.getTypeDeclType(class_template_specialization_decl, nullptr); class_template_specialization_decl->setTemplateArgs( TemplateArgumentList::CreateCopy(ast, args)); - ast.getTypeDeclType(class_template_specialization_decl, nullptr); class_template_specialization_decl->setDeclName( class_template_decl->getDeclName()); SetOwningModule(class_template_specialization_decl, owning_module); @@ -1671,6 +1697,13 @@ TypeSystemClang::CreateClassTemplateSpecializationDecl( class_template_specialization_decl->setSpecializationKind( TSK_ExplicitSpecialization); + // Store the information that is needed to later redeclare this exact + // template specialization. + ClassTemplateRedeclInfo redecl_info; + redecl_info.m_template_args = template_param_infos; + m_class_template_redecl_infos[class_template_specialization_decl] = + redecl_info; + return class_template_specialization_decl; } @@ -1796,7 +1829,7 @@ bool TypeSystemClang::RecordHasFields(const RecordDecl *record_decl) { #pragma mark Objective-C Classes -CompilerType TypeSystemClang::CreateObjCClass( +clang::ObjCInterfaceDecl *TypeSystemClang::CreateObjCDecl( llvm::StringRef name, clang::DeclContext *decl_ctx, OptionalClangModuleID owning_module, bool isForwardDecl, bool isInternal, ClangASTMetadata *metadata) { @@ -1816,7 +1849,7 @@ CompilerType TypeSystemClang::CreateObjCClass( if (metadata) SetMetadata(decl, *metadata); - return GetType(ast.getObjCInterfaceType(decl)); + return decl; } bool TypeSystemClang::BaseSpecifierIsEmpty(const CXXBaseSpecifier *b) { @@ -2299,7 +2332,7 @@ CompilerType TypeSystemClang::GetOrCreateStructForIdentifier( #pragma mark Enumeration Types -CompilerType TypeSystemClang::CreateEnumerationType( +clang::EnumDecl *TypeSystemClang::CreateEnumerationDecl( llvm::StringRef name, clang::DeclContext *decl_ctx, OptionalClangModuleID owning_module, const Declaration &decl, const CompilerType &integer_clang_type, bool is_scoped) { @@ -2325,7 +2358,7 @@ CompilerType TypeSystemClang::CreateEnumerationType( enum_decl->setAccess(AS_public); // TODO respect what's in the debug info - return GetType(ast.getTagDeclType(enum_decl)); + return enum_decl; } CompilerType TypeSystemClang::GetIntTypeFromBitSize(size_t bit_size, @@ -2427,23 +2460,22 @@ bool TypeSystemClang::GetCompleteDecl(clang::ASTContext *ast, if (tag_decl->isCompleteDefinition()) return true; - if (!tag_decl->hasExternalLexicalStorage()) - return false; - ast_source->CompleteType(tag_decl); - return !tag_decl->getTypeForDecl()->isIncompleteType(); + tag_decl = tag_decl->getDefinition(); + + return tag_decl && !tag_decl->getTypeForDecl()->isIncompleteType(); } else if (clang::ObjCInterfaceDecl *objc_interface_decl = llvm::dyn_cast(decl)) { if (objc_interface_decl->getDefinition()) return true; - if (!objc_interface_decl->hasExternalLexicalStorage()) - return false; - ast_source->CompleteType(objc_interface_decl); - return !objc_interface_decl->getTypeForDecl()->isIncompleteType(); + objc_interface_decl = objc_interface_decl->getDefinition(); + + return objc_interface_decl && + !objc_interface_decl->getTypeForDecl()->isIncompleteType(); } else { return false; } @@ -2463,9 +2495,17 @@ void TypeSystemClang::SetMetadataAsUserID(const clang::Type *type, SetMetadata(type, meta_data); } +/// Returns the Decl in a redeclaration chain that is used to store the +/// the ClangASTMetadata in the metadata map. +static const clang::Decl *GetDeclForMetadataStorage(const clang::Decl *d) { + // Only the first Decl never changes and never requires any loading from + // the ExternalASTSource, so it can be a stable key for the map. + return ClangUtil::GetFirstDecl(d); +} + void TypeSystemClang::SetMetadata(const clang::Decl *object, ClangASTMetadata &metadata) { - m_decl_metadata[object] = metadata; + m_decl_metadata[GetDeclForMetadataStorage(object)] = metadata; } void TypeSystemClang::SetMetadata(const clang::Type *object, @@ -2474,7 +2514,7 @@ void TypeSystemClang::SetMetadata(const clang::Type *object, } ClangASTMetadata *TypeSystemClang::GetMetadata(const clang::Decl *object) { - auto It = m_decl_metadata.find(object); + auto It = m_decl_metadata.find(GetDeclForMetadataStorage(object)); if (It != m_decl_metadata.end()) return &It->second; return nullptr; @@ -2589,35 +2629,14 @@ static const clang::RecordType *GetCompleteRecordType(clang::ASTContext *ast, // RecordType with no way of completing it, return the plain // TagType. - if (!cxx_record_decl || !cxx_record_decl->hasExternalLexicalStorage()) - return tag_type; - - const bool is_complete = cxx_record_decl->isCompleteDefinition(); - const bool fields_loaded = - cxx_record_decl->hasLoadedFieldsFromExternalStorage(); - - // Already completed this type, nothing to be done. - if (is_complete && fields_loaded) + if (!cxx_record_decl) return tag_type; - if (!allow_completion) + clang::CXXRecordDecl *def = cxx_record_decl->getDefinition(); + if (!def) return nullptr; - // Call the field_begin() accessor to for it to use the external source - // to load the fields... - // - // TODO: if we need to complete the type but have no external source, - // shouldn't we error out instead? - clang::ExternalASTSource *external_ast_source = ast->getExternalSource(); - if (external_ast_source) { - external_ast_source->CompleteType(cxx_record_decl); - if (cxx_record_decl->isCompleteDefinition()) { - cxx_record_decl->field_begin(); - cxx_record_decl->setHasLoadedFieldsFromExternalStorage(true); - } - } - - return tag_type; + return llvm::dyn_cast_if_present(def->getTypeForDecl()); } /// Returns the clang::EnumType of the specified \ref qual_type. This @@ -2636,33 +2655,19 @@ static const clang::EnumType *GetCompleteEnumType(clang::ASTContext *ast, auto *tag_decl = enum_type->getAsTagDecl(); assert(tag_decl); - // Already completed, nothing to be done. - if (tag_decl->getDefinition()) - return enum_type; + if (clang::TagDecl *def = tag_decl->getDefinition()) + return llvm::dyn_cast_if_present(def->getTypeForDecl()); - if (!allow_completion) - return nullptr; - - // No definition but can't complete it, error out. - if (!tag_decl->hasExternalLexicalStorage()) - return nullptr; - - // We can't complete the type without an external source. - clang::ExternalASTSource *external_ast_source = ast->getExternalSource(); - if (!external_ast_source) - return nullptr; - - external_ast_source->CompleteType(tag_decl); - return enum_type; + return llvm::dyn_cast_if_present(tag_decl->getTypeForDecl()); } /// Returns the clang::ObjCObjectType of the specified \ref qual_type. This /// function will try to complete the type if necessary (and allowed /// by the specified \ref allow_completion). If we fail to return a *complete* /// type, returns nullptr. -static const clang::ObjCObjectType * -GetCompleteObjCObjectType(clang::ASTContext *ast, QualType qual_type, - bool allow_completion) { +static const clang::Type *GetCompleteObjCObjectType(clang::ASTContext *ast, + QualType qual_type, + bool allow_completion) { assert(qual_type->isObjCObjectType()); assert(ast); @@ -2677,22 +2682,9 @@ GetCompleteObjCObjectType(clang::ASTContext *ast, QualType qual_type, return objc_class_type; // Already complete, nothing to be done. - if (class_interface_decl->getDefinition()) - return objc_class_type; - - if (!allow_completion) - return nullptr; - - // No definition but can't complete it, error out. - if (!class_interface_decl->hasExternalLexicalStorage()) - return nullptr; - - // We can't complete the type without an external source. - clang::ExternalASTSource *external_ast_source = ast->getExternalSource(); - if (!external_ast_source) - return nullptr; + if (auto *def = class_interface_decl->getDefinition()) + return def->getTypeForDecl(); - external_ast_source->CompleteType(class_interface_decl); return objc_class_type; } @@ -2715,14 +2707,14 @@ static bool GetCompleteQualType(clang::ASTContext *ast, case clang::Type::Record: { if (const auto *RT = GetCompleteRecordType(ast, qual_type, allow_completion)) - return !RT->isIncompleteType(); + return true; return false; } break; case clang::Type::Enum: { if (const auto *ET = GetCompleteEnumType(ast, qual_type, allow_completion)) - return !ET->isIncompleteType(); + return true; return false; } break; @@ -2730,7 +2722,7 @@ static bool GetCompleteQualType(clang::ASTContext *ast, case clang::Type::ObjCInterface: { if (const auto *OT = GetCompleteObjCObjectType(ast, qual_type, allow_completion)) - return !OT->isIncompleteType(); + return true; return false; } break; @@ -3449,7 +3441,7 @@ bool TypeSystemClang::IsDefined(lldb::opaque_compiler_type_t type) { if (tag_type) { clang::TagDecl *tag_decl = tag_type->getDecl(); if (tag_decl) - return tag_decl->isCompleteDefinition(); + return tag_decl->getDefinition() != nullptr; return false; } else { const clang::ObjCObjectType *objc_class_type = @@ -4644,6 +4636,7 @@ CompilerType TypeSystemClang::CreateTypedef( TypeSystemClang::DeclContextGetAsDeclContext(compiler_decl_ctx); if (!decl_ctx) decl_ctx = getASTContext().getTranslationUnitDecl(); + decl_ctx = decl_ctx->getPrimaryContext(); clang::TypedefDecl *decl = clang::TypedefDecl::CreateDeserialized(clang_ast, GlobalDeclID()); @@ -5405,6 +5398,8 @@ TypeSystemClang::GetNumChildren(lldb::opaque_compiler_type_t type, objc_class_type->getInterface(); if (class_interface_decl) { + class_interface_decl = class_interface_decl->getDefinition(); + ; clang::ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass(); @@ -5578,6 +5573,9 @@ void TypeSystemClang::ForEachEnumerator( if (enum_type) { const clang::EnumDecl *enum_decl = enum_type->getDecl(); if (enum_decl) { + enum_decl = enum_decl->getDefinition(); + if (!enum_decl) + return; CompilerType integer_type = GetType(enum_decl->getIntegerType()); clang::EnumDecl::enumerator_iterator enum_pos, enum_end_pos; @@ -7721,6 +7719,8 @@ clang::CXXMethodDecl *TypeSystemClang::AddMethodToCXXRecordType( clang::CXXRecordDecl *cxx_record_decl = record_qual_type->getAsCXXRecordDecl(); + if (cxx_record_decl) + cxx_record_decl = cxx_record_decl->getDefinition(); if (cxx_record_decl == nullptr) return nullptr; @@ -8199,9 +8199,13 @@ clang::ObjCMethodDecl *TypeSystemClang::AddMethodToObjCObjectType( return nullptr; clang::ObjCInterfaceDecl *class_interface_decl = GetAsObjCInterfaceDecl(type); + if (class_interface_decl) + if (auto * def = class_interface_decl->getDefinition()) + class_interface_decl = def; if (class_interface_decl == nullptr) return nullptr; + auto ts = type.GetTypeSystem(); auto lldb_ast = ts.dyn_cast_or_null(); if (lldb_ast == nullptr) @@ -8272,8 +8276,7 @@ clang::ObjCMethodDecl *TypeSystemClang::AddMethodToObjCObjectType( clang::ObjCMethodDecl::CreateDeserialized(ast, GlobalDeclID()); objc_method_decl->setDeclName(method_selector); objc_method_decl->setReturnType(method_function_prototype->getReturnType()); - objc_method_decl->setDeclContext( - lldb_ast->GetDeclContextForType(ClangUtil::GetQualType(type))); + objc_method_decl->setDeclContext(class_interface_decl); objc_method_decl->setInstanceMethod(isInstance); objc_method_decl->setVariadic(isVariadic); objc_method_decl->setPropertyAccessor(isPropertyAccessor); @@ -8383,7 +8386,11 @@ bool TypeSystemClang::StartTagDeclarationDefinition(const CompilerType &type) { if (tag_type) { clang::TagDecl *tag_decl = tag_type->getDecl(); if (tag_decl) { - tag_decl->startDefinition(); + // There are several declarations in the redeclaration chain that could + // define this type. The most logical declaration that we could turn + // into a definition is the most recent one. + clang::TagDecl *def = tag_decl->getMostRecentDecl(); + def->startDefinition(); return true; } } @@ -8393,7 +8400,11 @@ bool TypeSystemClang::StartTagDeclarationDefinition(const CompilerType &type) { if (object_type) { clang::ObjCInterfaceDecl *interface_decl = object_type->getInterface(); if (interface_decl) { - interface_decl->startDefinition(); + // There are several declarations in the redeclaration chain that could + // define this type. The most logical declaration that we could turn + // into a definition is the most recent one. + clang::ObjCInterfaceDecl *def = interface_decl->getMostRecentDecl(); + def->startDefinition(); return true; } } @@ -8439,9 +8450,7 @@ bool TypeSystemClang::CompleteTagDeclarationDefinition( if (!cxx_record_decl->isCompleteDefinition()) cxx_record_decl->completeDefinition(); - cxx_record_decl->setHasLoadedFieldsFromExternalStorage(true); - cxx_record_decl->setHasExternalLexicalStorage(false); - cxx_record_decl->setHasExternalVisibleStorage(false); + lldb_ast->SetCXXRecordDeclAccess(cxx_record_decl, clang::AccessSpecifier::AS_none); return true; @@ -8636,7 +8645,7 @@ static bool DumpEnumValue(const clang::QualType &qual_type, Stream &s, uint32_t bitfield_bit_size) { const clang::EnumType *enutype = llvm::cast(qual_type.getTypePtr()); - const clang::EnumDecl *enum_decl = enutype->getDecl(); + const clang::EnumDecl *enum_decl = enutype->getDecl()->getDefinition(); assert(enum_decl); lldb::offset_t offset = byte_offset; const uint64_t enum_svalue = data.GetMaxS64Bitfield( @@ -8870,7 +8879,7 @@ void TypeSystemClang::DumpTypeDescription(lldb::opaque_compiler_type_t type, if (!objc_class_type) break; clang::ObjCInterfaceDecl *class_interface_decl = - objc_class_type->getInterface(); + objc_class_type->getInterface()->getDefinition(); if (!class_interface_decl) break; if (level == eDescriptionLevelVerbose) @@ -9045,6 +9054,88 @@ void TypeSystemClang::CompleteObjCInterfaceDecl( } } +/// Appends an existing declaration to the redeclaration chain. +/// \param ts The TypeSystemClang that contains the two declarations. +/// \param prev The most recent existing declaration. +/// \param redecl The new declaration which should be appended to the end of +/// redeclaration chain. +template +static void ConnectRedeclToPrev(TypeSystemClang &ts, T *prev, T *redecl) { + assert(&ts.getASTContext() == &prev->getASTContext() && "Not "); + redecl->setPreviousDecl(prev); + // Now that the redecl chain is done, create the type explicitly via + // the TypeSystemClang interface that will reuse the type of the previous + // decl. + ts.GetTypeForDecl(redecl); + // The previous decl and the redeclaration both declare the same type. + assert(prev->getTypeForDecl() == redecl->getTypeForDecl()); +} + +/// Returns the ClangModuleID for the given declaration. +static OptionalClangModuleID GetModuleForDecl(clang::Decl *d) { + if (!d->isFromASTFile() || !d->getOwningModuleID()) + return OptionalClangModuleID(); + return OptionalClangModuleID(d->getOwningModuleID()); +} + +CompilerType TypeSystemClang::CreateRedeclaration(CompilerType ct) { + // All the cases below just check for a specific declaration kind, create + // a new declaration with matching data. We don't care about metadata which + // should only be tracked in the first redeclaration and should be identical + // for all redeclarations. + + if (clang::ObjCInterfaceDecl *interface = ClangUtil::GetAsObjCDecl(ct)) { + clang::NamedDecl *res = CreateObjCDecl( + interface->getName(), interface->getDeclContext()->getRedeclContext(), + GetModuleForDecl(interface), /*isForwardDecl=*/false, + interface->isImplicit()); + clang::ObjCInterfaceDecl *redecl = llvm::cast(res); + ConnectRedeclToPrev(*this, interface, redecl); + return GetTypeForDecl(redecl); + } + + clang::TagDecl *tag_decl = ClangUtil::GetAsTagDecl(ct); + if (!tag_decl) + return {}; + + if (clang::EnumDecl *enum_decl = dyn_cast(tag_decl)) { + Declaration decl; + clang::EnumDecl *redecl = CreateEnumerationDecl( + enum_decl->getNameAsString().c_str(), + tag_decl->getDeclContext()->getRedeclContext(), + GetModuleForDecl(enum_decl), decl, GetType(enum_decl->getIntegerType()), + enum_decl->isScoped()); + ConnectRedeclToPrev(*this, enum_decl, redecl); + return GetTypeForDecl(redecl); + } + + if (auto *template_decl = + dyn_cast(tag_decl)) { + auto redecl_info = m_class_template_redecl_infos.find(template_decl); + // If we are asked to redeclare a template that we haven't declared, then + // there is nothing we can do. + assert(redecl_info != m_class_template_redecl_infos.end()); + TemplateParameterInfos template_infos = redecl_info->second.m_template_args; + auto *redecl = CreateClassTemplateSpecializationDecl( + tag_decl->getDeclContext()->getRedeclContext(), + GetModuleForDecl(template_decl), + template_decl->getSpecializedTemplate(), + llvm::to_underlying(tag_decl->getTagKind()), template_infos); + ConnectRedeclToPrev(*this, template_decl, redecl); + return GetType(clang::QualType(redecl->getTypeForDecl(), 0U)); + } + + assert(llvm::isa(tag_decl)); + clang::NamedDecl *redecl_record = CreateRecordDecl( + tag_decl->getDeclContext()->getRedeclContext(), + GetModuleForDecl(tag_decl), lldb::eAccessPublic, tag_decl->getName(), + llvm::to_underlying(tag_decl->getTagKind()), eLanguageTypeC_plus_plus, + nullptr); + clang::TagDecl *redecl = llvm::cast(redecl_record); + ConnectRedeclToPrev(*this, tag_decl, redecl); + return GetTypeForDecl(redecl); +} + DWARFASTParser *TypeSystemClang::GetDWARFParser() { if (!m_dwarf_ast_parser_up) m_dwarf_ast_parser_up = std::make_unique(*this); diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index d67b7a4c9fe72..1f02cd7b13f79 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -252,7 +252,7 @@ class TypeSystemClang : public TypeSystem { CompilerType GetTypeForDecl(clang::TagDecl *decl); - CompilerType GetTypeForDecl(clang::ObjCInterfaceDecl *objc_decl); + CompilerType GetTypeForDecl(const clang::ObjCInterfaceDecl *objc_decl); CompilerType GetTypeForDecl(clang::ValueDecl *value_decl); @@ -333,6 +333,14 @@ class TypeSystemClang : public TypeSystem { ClangASTMetadata *metadata = nullptr, bool exports_symbols = false); + clang::NamedDecl *CreateRecordDecl(clang::DeclContext *decl_ctx, + OptionalClangModuleID owning_module, + lldb::AccessType access_type, + llvm::StringRef name, int kind, + lldb::LanguageType language, + ClangASTMetadata *metadata = nullptr, + bool exports_symbols = false); + class TemplateParameterInfos { public: TemplateParameterInfos() = default; @@ -342,10 +350,27 @@ class TypeSystemClang : public TypeSystem { assert(names.size() == args_in.size()); } - TemplateParameterInfos(TemplateParameterInfos const &) = delete; TemplateParameterInfos(TemplateParameterInfos &&) = delete; - TemplateParameterInfos &operator=(TemplateParameterInfos const &) = delete; + TemplateParameterInfos(const TemplateParameterInfos &o) + : names(o.names), args(o.args), pack_name(o.pack_name) { + if (o.packed_args) + packed_args = std::make_unique(*o.packed_args); + } + + TemplateParameterInfos &operator=(const TemplateParameterInfos &o) { + auto tmp = TemplateParameterInfos(o); + swap(tmp); + return *this; + } + + void swap(TemplateParameterInfos &other) noexcept { + std::swap(names, other.names); + std::swap(args, other.args); + std::swap(pack_name, other.pack_name); + std::swap(packed_args, other.packed_args); + } + TemplateParameterInfos &operator=(TemplateParameterInfos &&) = delete; ~TemplateParameterInfos() = default; @@ -353,7 +378,7 @@ class TypeSystemClang : public TypeSystem { bool IsValid() const { // Having a pack name but no packed args doesn't make sense, so mark // these template parameters as invalid. - if (pack_name && !packed_args) + if (HasPackName() && !packed_args) return false; return args.size() == names.size() && (!packed_args || !packed_args->packed_args); @@ -394,14 +419,14 @@ class TypeSystemClang : public TypeSystem { return packed_args->GetArgs(); } - bool HasPackName() const { return pack_name && pack_name[0]; } + bool HasPackName() const { return pack_name.has_value(); } llvm::StringRef GetPackName() const { assert(HasPackName()); - return pack_name; + return pack_name.value(); } - void SetPackName(char const *name) { pack_name = name; } + void SetPackName(char const *name) { pack_name.emplace(name); } void SetParameterPack(std::unique_ptr args) { packed_args = std::move(args); @@ -413,7 +438,7 @@ class TypeSystemClang : public TypeSystem { llvm::SmallVector names; llvm::SmallVector args; - const char * pack_name = nullptr; + std::optional pack_name; std::unique_ptr packed_args; }; @@ -459,7 +484,16 @@ class TypeSystemClang : public TypeSystem { clang::DeclContext *decl_ctx, OptionalClangModuleID owning_module, bool isForwardDecl, bool isInternal, - ClangASTMetadata *metadata = nullptr); + ClangASTMetadata *metadata = nullptr) { + clang::ObjCInterfaceDecl *d = CreateObjCDecl( + name, decl_ctx, owning_module, isForwardDecl, isInternal, metadata); + return GetTypeForDecl(d); + } + + clang::ObjCInterfaceDecl * + CreateObjCDecl(llvm::StringRef name, clang::DeclContext *decl_ctx, + OptionalClangModuleID owning_module, bool isForwardDecl, + bool isInternal, ClangASTMetadata *metadata = nullptr); // Returns a mask containing bits from the TypeSystemClang::eTypeXXX // enumerations @@ -501,12 +535,23 @@ class TypeSystemClang : public TypeSystem { size_t element_count, bool is_vector); // Enumeration Types + clang::EnumDecl *CreateEnumerationDecl(llvm::StringRef name, + clang::DeclContext *decl_ctx, + OptionalClangModuleID owning_module, + const Declaration &decl, + const CompilerType &integer_qual_type, + bool is_scoped); + CompilerType CreateEnumerationType(llvm::StringRef name, clang::DeclContext *decl_ctx, OptionalClangModuleID owning_module, const Declaration &decl, const CompilerType &integer_qual_type, - bool is_scoped); + bool is_scoped) { + clang::EnumDecl *enum_decl = CreateEnumerationDecl( + name, decl_ctx, owning_module, decl, integer_qual_type, is_scoped); + return GetType(getASTContext().getTagDeclType(enum_decl)); + } // Integer type functions @@ -529,6 +574,15 @@ class TypeSystemClang : public TypeSystem { void CompleteObjCInterfaceDecl(clang::ObjCInterfaceDecl *); + /// Creates a redeclaration for the declaration specified by the given type. + /// The redeclaration will be at the end of the redeclaration chain. The + /// passed declaration has to be created via a TypeSystemClang interface. + /// + /// \param type The type which declaration should be redeclared. Has to be + /// an Objective-C interface type (or Objective-C type), RecordType or + /// EnumType. + CompilerType CreateRedeclaration(CompilerType ct); + bool LayoutRecordType( const clang::RecordDecl *record_decl, uint64_t &size, uint64_t &alignment, llvm::DenseMap &field_offsets, @@ -1212,6 +1266,18 @@ class TypeSystemClang : public TypeSystem { /// AccessSpecifier. CXXRecordDeclAccessMap m_cxx_record_decl_access; + /// The information we need to redeclare a class template but that we can't + /// gather from the forward declaration. + struct ClassTemplateRedeclInfo { + TemplateParameterInfos m_template_args; + }; + typedef llvm::DenseMap + ClassTemplateRedeclInfoMap; + // FIXME: This is in theory redundant. Instead we should change the way we + // create ClassTemplateSpecializationDecls in TypeSystemClang so that we can + // just pass the data from the forward declaration. + ClassTemplateRedeclInfoMap m_class_template_redecl_infos; + /// The sema associated that is currently used to build this ASTContext. /// May be null if we are already done parsing this ASTContext or the /// ASTContext wasn't created by parsing source code. diff --git a/lldb/test/API/lang/cpp/reference-to-outer-type/TestCppReferenceToOuterClass.py b/lldb/test/API/lang/cpp/reference-to-outer-type/TestCppReferenceToOuterClass.py index a6e419b7fcdfa..7a23559998b3e 100644 --- a/lldb/test/API/lang/cpp/reference-to-outer-type/TestCppReferenceToOuterClass.py +++ b/lldb/test/API/lang/cpp/reference-to-outer-type/TestCppReferenceToOuterClass.py @@ -6,7 +6,7 @@ class TestCase(TestBase): - @unittest.expectedFailure # The fix for this was reverted due to llvm.org/PR52257 + @expectedFailureAll(setting=('plugin.typesystem.clang.experimental-redecl-completion', 'false')) def test(self): self.build() self.dbg.CreateTarget(self.getBuildArtifact("a.out")) diff --git a/lldb/unittests/CMakeLists.txt b/lldb/unittests/CMakeLists.txt index a2585a94b6155..4d65ef563bc96 100644 --- a/lldb/unittests/CMakeLists.txt +++ b/lldb/unittests/CMakeLists.txt @@ -68,6 +68,7 @@ add_subdirectory(ScriptInterpreter) add_subdirectory(Signals) add_subdirectory(Symbol) add_subdirectory(SymbolFile) +add_subdirectory(TypeSystem) add_subdirectory(Target) add_subdirectory(tools) add_subdirectory(UnwindAssembly) diff --git a/lldb/unittests/TypeSystem/CMakeLists.txt b/lldb/unittests/TypeSystem/CMakeLists.txt new file mode 100644 index 0000000000000..17c40aee44cc2 --- /dev/null +++ b/lldb/unittests/TypeSystem/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(Clang) diff --git a/lldb/unittests/TypeSystem/Clang/CMakeLists.txt b/lldb/unittests/TypeSystem/Clang/CMakeLists.txt new file mode 100644 index 0000000000000..15e226c172d17 --- /dev/null +++ b/lldb/unittests/TypeSystem/Clang/CMakeLists.txt @@ -0,0 +1,14 @@ +add_lldb_unittest(TypeSystemClangTests + TestClangRedeclarations.cpp + + LINK_LIBS + lldbHost + lldbSymbol + lldbUtilityHelpers + lldbPluginObjectFileELF + lldbPluginObjectFileMachO + lldbPluginSymbolFileDWARF + lldbPluginSymbolFileSymtab + lldbPluginTypeSystemClang + LLVMTestingSupport + ) diff --git a/lldb/unittests/TypeSystem/Clang/TestClangRedeclarations.cpp b/lldb/unittests/TypeSystem/Clang/TestClangRedeclarations.cpp new file mode 100644 index 0000000000000..c669a22dc9f56 --- /dev/null +++ b/lldb/unittests/TypeSystem/Clang/TestClangRedeclarations.cpp @@ -0,0 +1,332 @@ +//===-- TestClangRedeclarations.cpp ---------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Plugins/ExpressionParser/Clang/ClangASTMetadata.h" +#include "Plugins/ExpressionParser/Clang/ClangUtil.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "TestingSupport/SubsystemRAII.h" +#include "TestingSupport/Symbol/ClangTestUtils.h" +#include "lldb/Core/Declaration.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/HostInfo.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Type.h" +#include "gtest/gtest.h" + +using namespace clang; +using namespace lldb; +using namespace lldb_private; + +struct TestClangRedeclarations : public testing::Test { + SubsystemRAII subsystems; + + void SetUp() override { + m_holder = + std::make_unique("test ASTContext"); + m_ast = m_holder->GetAST(); + } + + void TearDown() override { + m_ast = nullptr; + m_holder.reset(); + } + + TypeSystemClang *m_ast = nullptr; + std::unique_ptr m_holder; +}; + +TEST_F(TestClangRedeclarations, RedeclareCppClass) { + // Test redeclaring C++ classes. + + OptionalClangModuleID module_id(1); + CompilerType class_type = m_ast->CreateRecordType( + m_ast->GetTranslationUnitDecl(), module_id, lldb::eAccessNone, "A", + llvm::to_underlying(TagTypeKind::Class), lldb::eLanguageTypeC_plus_plus); + auto *record = llvm::cast(ClangUtil::GetAsTagDecl(class_type)); + + m_ast->CreateRedeclaration(class_type); + m_ast->StartTagDeclarationDefinition(class_type); + // For C++ classes, the definition is already available (but it is currently + // being defined). Make sure the definition is last in the redecl chain. + CXXRecordDecl *def = record->getDefinition(); + ASSERT_TRUE(def); + ASSERT_TRUE(def->isBeingDefined()); + ASSERT_NE(def, record); + EXPECT_EQ(def->getPreviousDecl(), record); + + // Add a method. + std::vector args; + CompilerType func_type = + m_ast->CreateFunctionType(m_ast->GetBasicType(lldb::eBasicTypeInt), + args.data(), args.size(), /*is_variadic=*/false, + /*type_quals=*/0, clang::CallingConv::CC_C); + const bool is_virtual = false; + const bool is_static = false; + const bool is_inline = false; + const bool is_explicit = false; + const bool is_attr_used = false; + const bool is_artificial = false; + clang::CXXMethodDecl *method = m_ast->AddMethodToCXXRecordType( + class_type.GetOpaqueQualType(), "A", nullptr, func_type, + lldb::eAccessPublic, is_virtual, is_static, is_inline, is_explicit, + is_attr_used, is_artificial); + // Check that the method was created and is in the definition. + ASSERT_NE(method, nullptr); + EXPECT_EQ(method->getParent(), def); + + // Add an ivar and check that it was added to the definition. + FieldDecl *member_var = m_ast->AddFieldToRecordType( + class_type, "f", m_ast->GetBasicType(lldb::eBasicTypeInt), + lldb::eAccessPublic, + /*bitfield_bit_size=*/0); + ASSERT_TRUE(member_var); + EXPECT_EQ(member_var->getParent(), def); + + // Complete the class and check that the last decl is the definition. + m_ast->CompleteTagDeclarationDefinition(class_type); + EXPECT_FALSE(record->isThisDeclarationADefinition()); + EXPECT_TRUE(def->isThisDeclarationADefinition()); + // Check that the module is identical. + EXPECT_EQ(def->getOwningModuleID(), module_id.GetValue()); + + // Make sure forward decl and definition have the same type. + EXPECT_EQ(def->getTypeForDecl(), record->getTypeForDecl()); +} + +TEST_F(TestClangRedeclarations, RedeclareCppTemplateClass) { + // Test redeclaring C++ template classes. + + OptionalClangModuleID module_id(1); + auto args = std::make_unique(); + args->InsertArg("T", TemplateArgument(m_ast->getASTContext().IntTy)); + + ClassTemplateDecl *template_decl = m_ast->CreateClassTemplateDecl( + m_ast->GetTranslationUnitDecl(), module_id, lldb::eAccessNone, "A", + llvm::to_underlying(TagTypeKind::Struct), *args); + ClassTemplateSpecializationDecl *fwd_decl = + m_ast->CreateClassTemplateSpecializationDecl( + m_ast->GetTranslationUnitDecl(), module_id, template_decl, + llvm::to_underlying(TagTypeKind::Struct), *args); + CompilerType spec_type = + m_ast->CreateClassTemplateSpecializationType(fwd_decl); + + // Delete the TemplateParameterInfos to make sure TypeSystemClang doesn't + // rely on the caller to keep them around. + args.reset(); + + m_ast->CreateRedeclaration(spec_type); + m_ast->StartTagDeclarationDefinition(spec_type); + // For C++ classes, the definition is already available (but it is currently + // being defined). Make sure the definition is last in the redecl chain. + CXXRecordDecl *def = fwd_decl->getDefinition(); + ASSERT_TRUE(def); + ASSERT_TRUE(def->isBeingDefined()); + ASSERT_NE(def, fwd_decl); + EXPECT_EQ(def->getPreviousDecl(), fwd_decl); + + // Add an ivar and check that it was added to the definition. + FieldDecl *member_var = m_ast->AddFieldToRecordType( + spec_type, "f", m_ast->GetBasicType(lldb::eBasicTypeInt), + lldb::eAccessPublic, + /*bitfield_bit_size=*/0); + ASSERT_TRUE(member_var); + EXPECT_EQ(member_var->getParent(), def); + + // Complete the class and check that the last decl is the definition. + m_ast->CompleteTagDeclarationDefinition(spec_type); + EXPECT_FALSE(fwd_decl->isThisDeclarationADefinition()); + EXPECT_TRUE(def->isThisDeclarationADefinition()); + + // Check that the module is identical. + EXPECT_EQ(def->getOwningModuleID(), module_id.GetValue()); + + // Make sure forward decl and definition have the same type. + EXPECT_EQ(def->getTypeForDecl(), fwd_decl->getTypeForDecl()); +} + +TEST_F(TestClangRedeclarations, RedeclareObjCClass) { + // Test redeclaring Objective-C interfaces. + + OptionalClangModuleID module_id(1); + CompilerType objc_class = + m_ast->CreateObjCClass("A", m_ast->GetTranslationUnitDecl(), module_id, + /*isForwardDecl=*/false, + /*isInternal=*/false); + ObjCInterfaceDecl *interface = m_ast->GetAsObjCInterfaceDecl(objc_class); + m_ast->CreateRedeclaration(objc_class); + m_ast->StartTagDeclarationDefinition(objc_class); + ObjCInterfaceDecl *def = interface->getDefinition(); + ASSERT_TRUE(def); + ASSERT_NE(def, interface); + EXPECT_EQ(def->getPreviousDecl(), interface); + + // Add a method. + std::vector args; + CompilerType func_type = + m_ast->CreateFunctionType(m_ast->GetBasicType(lldb::eBasicTypeInt), + args.data(), args.size(), /*is_variadic=*/false, + /*type_quals=*/0, clang::CallingConv::CC_C); + const bool variadic = false; + const bool artificial = false; + const bool objc_direct = false; + clang::ObjCMethodDecl *method = TypeSystemClang::AddMethodToObjCObjectType( + objc_class, "-[A foo]", func_type, artificial, variadic, objc_direct); + // Check that the method was created and is in the definition. + ASSERT_NE(method, nullptr); + EXPECT_EQ(*def->meth_begin(), method); + + // Add an ivar and check that it was added to the definition. + FieldDecl *ivar = m_ast->AddFieldToRecordType( + objc_class, "f", m_ast->GetBasicType(lldb::eBasicTypeInt), + lldb::eAccessPublic, + /*bitfield_bit_size=*/0); + ASSERT_TRUE(ivar); + EXPECT_EQ(*def->ivar_begin(), ivar); + + m_ast->CompleteTagDeclarationDefinition(objc_class); + // The forward declaration should be unchanged. + EXPECT_FALSE(interface->isThisDeclarationADefinition()); + EXPECT_TRUE(def->isThisDeclarationADefinition()); + // Check that the module is identical. + EXPECT_EQ(def->getOwningModuleID(), module_id.GetValue()); + + // Make sure forward decl and definition have the same type. + EXPECT_EQ(def->getTypeForDecl(), interface->getTypeForDecl()); +} + +TEST_F(TestClangRedeclarations, RedeclareEnum) { + // Test redeclaring enums. + + OptionalClangModuleID module_id(1); + Declaration decl; + CompilerType enum_type = m_ast->CreateEnumerationType( + "A", m_ast->GetTranslationUnitDecl(), module_id, decl, + m_ast->GetBasicType(lldb::eBasicTypeInt), /*is_scoped=*/true); + + EnumDecl *fwd_decl = m_ast->GetAsEnumDecl(enum_type); + m_ast->CreateRedeclaration(enum_type); + m_ast->StartTagDeclarationDefinition(enum_type); + m_ast->AddEnumerationValueToEnumerationType( + enum_type, decl, "case1", /*enum_value=*/1, /*enum_value_bit_size=*/32); + m_ast->CompleteTagDeclarationDefinition(enum_type); + + // There should now be a definition at the end of the redeclaration chain. + EnumDecl *def = fwd_decl->getDefinition(); + ASSERT_TRUE(def); + ASSERT_NE(def, fwd_decl); + EXPECT_EQ(def->getPreviousDecl(), fwd_decl); + // The forward declaration should be unchanged. + EXPECT_FALSE(fwd_decl->isThisDeclarationADefinition()); + EXPECT_TRUE(def->isThisDeclarationADefinition()); + // Check that the module is identical. + EXPECT_EQ(def->getOwningModuleID(), module_id.GetValue()); + + // Make sure forward decl and definition have the same type. + EXPECT_EQ(def->getTypeForDecl(), fwd_decl->getTypeForDecl()); + + // Check that ForEachEnumerator uses the definition. + bool seen_value = false; + m_ast->ForEachEnumerator(enum_type.GetOpaqueQualType(), + [&seen_value](const CompilerType &, ConstString name, + const llvm::APSInt &) { + EXPECT_EQ(name, "case1"); + seen_value = true; + return true; + }); + EXPECT_TRUE(seen_value); +} + +TEST_F(TestClangRedeclarations, NestedDecls) { + // Tests that nested declarations pick the right redeclaration as their + // DeclContext. + + // Create a class. + CompilerType context_class = m_ast->CreateRecordType( + m_ast->GetTranslationUnitDecl(), OptionalClangModuleID(), + lldb::eAccessNone, "A", llvm::to_underlying(TagTypeKind::Class), + lldb::eLanguageTypeC_plus_plus); + auto *fwd_decl = + llvm::cast(ClangUtil::GetAsTagDecl(context_class)); + + // Give it a redeclaration that defines it. + m_ast->CreateRedeclaration(context_class); + m_ast->StartTagDeclarationDefinition(context_class); + m_ast->CompleteTagDeclarationDefinition(context_class); + + // Check that there is one forward declaration and a definition now. + CXXRecordDecl *def = fwd_decl->getDefinition(); + ASSERT_TRUE(def); + EXPECT_FALSE(fwd_decl->isThisDeclarationADefinition()); + EXPECT_TRUE(def->isThisDeclarationADefinition()); + + // Create a nested class and make sure it picks the definition as its + // DeclContext. + CompilerType nested_class = m_ast->CreateRecordType( + fwd_decl, OptionalClangModuleID(), lldb::eAccessPublic, "A", + llvm::to_underlying(TagTypeKind::Class), lldb::eLanguageTypeC_plus_plus); + EXPECT_EQ(ClangUtil::GetAsTagDecl(nested_class)->getDeclContext(), def); + + CompilerType int_type = m_ast->GetBasicType(lldb::eBasicTypeInt); + + // Create a typedef and make sure it picks the definition as its DeclContext. + CompilerType nested_typedef = int_type.CreateTypedef( + "t", CompilerDeclContext(m_ast, static_cast(fwd_decl)), + /*payload=*/0); + const TypedefType *typedef_type = + ClangUtil::GetQualType(nested_typedef)->getAs(); + ASSERT_TRUE(typedef_type); + TypedefNameDecl *nested_typedef_decl = typedef_type->getDecl(); + ASSERT_TRUE(nested_typedef_decl); + EXPECT_EQ(nested_typedef_decl->getDeclContext(), def); + + TypeSystemClang::TemplateParameterInfos args; + args.InsertArg("T", TemplateArgument(m_ast->getASTContext().IntTy)); + + // Create a class template and specialization and check that their DeclContext + // is the definition. + ClassTemplateDecl *template_decl = m_ast->CreateClassTemplateDecl( + fwd_decl, OptionalClangModuleID(), lldb::eAccessPublic, "A", + llvm::to_underlying(TagTypeKind::Struct), args); + EXPECT_EQ(template_decl->getDeclContext(), def); + ClassTemplateSpecializationDecl *template_spec_decl = + m_ast->CreateClassTemplateSpecializationDecl( + fwd_decl, OptionalClangModuleID(), template_decl, + llvm::to_underlying(TagTypeKind::Struct), args); + EXPECT_EQ(template_spec_decl->getDeclContext(), def); +} + +TEST_F(TestClangRedeclarations, MetadataRedeclaration) { + // Tests that metadata is shared between redeclarations. + + // Create a class with the test metadata. + CompilerType class_with_metadata = m_ast->CreateRecordType( + m_ast->GetTranslationUnitDecl(), OptionalClangModuleID(), + lldb::eAccessPublic, "A", llvm::to_underlying(TagTypeKind::Class), + lldb::eLanguageTypeC_plus_plus); + auto *record = + llvm::cast(ClangUtil::GetAsTagDecl(class_with_metadata)); + ClangASTMetadata metadata; + metadata.SetUserID(1234); + m_ast->SetMetadata(record, metadata); + ASSERT_EQ(m_ast->GetMetadata(record)->GetUserID(), 1234U); + + // Redeclare and define the redeclaration. + m_ast->CreateRedeclaration(class_with_metadata); + m_ast->StartTagDeclarationDefinition(class_with_metadata); + m_ast->CompleteTagDeclarationDefinition(class_with_metadata); + CXXRecordDecl *def = record->getDefinition(); + ASSERT_TRUE(def); + ASSERT_NE(def, record); + EXPECT_EQ(def->getPreviousDecl(), record); + + // Check that the redeclaration has the right metadata; + ASSERT_TRUE(m_ast->GetMetadata(def)); + EXPECT_EQ(m_ast->GetMetadata(def)->GetUserID(), 1234U); +}