diff --git a/clang/include/clang/AST/ASTImporter.h b/clang/include/clang/AST/ASTImporter.h index 4ffd913846575..9d5e848d885b7 100644 --- a/clang/include/clang/AST/ASTImporter.h +++ b/clang/include/clang/AST/ASTImporter.h @@ -211,6 +211,7 @@ class TypeSourceInfo; /// Whether to perform a minimal import. bool Minimal; + bool LLDBRedeclCompletion = false; ODRHandlingType ODRHandling; @@ -296,8 +297,10 @@ class TypeSourceInfo; /// Whether the importer will perform a minimal import, creating /// to-be-completed forward declarations when possible. bool isMinimalImport() const { return Minimal; } + bool hasLLDBRedeclCompletion() const { return LLDBRedeclCompletion; } void setODRHandling(ODRHandlingType T) { ODRHandling = T; } + void setLLDBRedeclCompletion(bool Val) { LLDBRedeclCompletion = Val; } /// \brief Import the given object, returns the result. /// diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h index 5fca81edd42ad..a95a879d9638c 100644 --- a/clang/include/clang/AST/DeclObjC.h +++ b/clang/include/clang/AST/DeclObjC.h @@ -1238,7 +1238,8 @@ class ObjCInterfaceDecl : public ObjCContainerDecl /// which will be NULL if this class has not yet been defined. /// /// The bit indicates when we don't need to check for out-of-date - /// declarations. It will be set unless modules are enabled. + /// declarations. It will be set unless there is an ExternalASTSource that + /// could provide a definition. llvm::PointerIntPair Data; ObjCInterfaceDecl(const ASTContext &C, DeclContext *DC, SourceLocation AtLoc, @@ -1527,7 +1528,7 @@ class ObjCInterfaceDecl : public ObjCContainerDecl // If the name of this class is out-of-date, bring it up-to-date, which // might bring in a definition. // Note: a null value indicates that we don't have a definition and that - // modules are enabled. + // there is a ExternalASTSource that could provide a definition. if (!Data.getOpaqueValue()) getMostRecentDecl(); @@ -2095,7 +2096,8 @@ class ObjCProtocolDecl : public ObjCContainerDecl, /// which will be NULL if this class has not yet been defined. /// /// The bit indicates when we don't need to check for out-of-date - /// declarations. It will be set unless modules are enabled. + /// declarations. It will be set unless there is an ExternalASTSource that + /// could provide a definition. llvm::PointerIntPair Data; ObjCProtocolDecl(ASTContext &C, DeclContext *DC, IdentifierInfo *Id, @@ -2232,7 +2234,7 @@ class ObjCProtocolDecl : public ObjCContainerDecl, // If the name of this protocol is out-of-date, bring it up-to-date, which // might bring in a definition. // Note: a null value indicates that we don't have a definition and that - // modules are enabled. + // there is a ExternalASTSource that could provide a definition. if (!Data.getOpaqueValue()) getMostRecentDecl(); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 5d44381f90968..29a25338a725d 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1031,7 +1031,8 @@ Expected ASTNodeImporter::import(const LambdaCapture &From) { template bool ASTNodeImporter::hasSameVisibilityContextAndLinkage(T *Found, T *From) { - if (Found->getLinkageInternal() != From->getLinkageInternal()) + if (!Importer.hasLLDBRedeclCompletion() && + Found->getLinkageInternal() != From->getLinkageInternal()) return false; if (From->hasExternalFormalLinkage()) @@ -1482,7 +1483,9 @@ ExpectedType ASTNodeImporter::VisitInjectedClassNameType( } ExpectedType ASTNodeImporter::VisitRecordType(const RecordType *T) { + // getCanonicalDecl in order to not trigger redeclaration completion Expected ToDeclOrErr = import(T->getDecl()); + if (!ToDeclOrErr) return ToDeclOrErr.takeError(); @@ -1771,8 +1774,9 @@ Error ASTNodeImporter::ImportDefinitionIfNeeded(Decl *FromD, Decl *ToD) { if (RecordDecl *FromRecord = dyn_cast(FromD)) { if (RecordDecl *ToRecord = cast(ToD)) { - if (FromRecord->getDefinition() && FromRecord->isCompleteDefinition() && - !ToRecord->getDefinition()) { + if (FromRecord->getDefinition() && !ToRecord->getDefinition() && + (Importer.hasLLDBRedeclCompletion() || + FromRecord->isCompleteDefinition())) { if (Error Err = ImportDefinition(FromRecord, ToRecord)) return Err; } @@ -1873,12 +1877,15 @@ ASTNodeImporter::ImportDeclContext(DeclContext *FromDC, bool ForceImport) { ImportedOrErr.takeError()); continue; } - FieldDecl *FieldFrom = dyn_cast_or_null(From); - Decl *ImportedDecl = *ImportedOrErr; - FieldDecl *FieldTo = dyn_cast_or_null(ImportedDecl); - if (FieldFrom && FieldTo) { - Error Err = ImportFieldDeclDefinition(FieldFrom, FieldTo); - HandleChildErrors.handleChildImportResult(ChildErrors, std::move(Err)); + + if (Importer.hasLLDBRedeclCompletion()) { + FieldDecl *FieldFrom = dyn_cast_or_null(From); + Decl *ImportedDecl = *ImportedOrErr; + FieldDecl *FieldTo = dyn_cast_or_null(ImportedDecl); + if (FieldFrom && FieldTo) { + Error Err = ImportFieldDeclDefinition(FieldFrom, FieldTo); + HandleChildErrors.handleChildImportResult(ChildErrors, std::move(Err)); + } } } @@ -2039,7 +2046,11 @@ Error ASTNodeImporter::ImportDefinition( To->completeDefinition(); }; - if (To->getDefinition() || To->isBeingDefined()) { + bool hasDef = (Importer.hasLLDBRedeclCompletion() && + To->isThisDeclarationADefinition()) || + To->getDefinition(); + + if (hasDef || To->isBeingDefined()) { if (Kind == IDK_Everything || // In case of lambdas, the class already has a definition ptr set, but // the contained decls are not imported yet. Also, isBeingDefined was @@ -2558,6 +2569,9 @@ ASTNodeImporter::VisitTypedefNameDecl(TypedefNameDecl *D, bool IsAlias) { !hasSameVisibilityContextAndLinkage(FoundR, FromR)) continue; } + + if (Importer.hasLLDBRedeclCompletion() && Importer.isMinimalImport()) + return Importer.MapImported(D, FoundTypedef); // If the "From" context has a complete underlying type but we // already have a complete underlying type then return with that. if (!FromUT->isIncompleteType() && !FoundUT->isIncompleteType()) @@ -2822,9 +2836,11 @@ ExpectedDecl ASTNodeImporter::VisitEnumDecl(EnumDecl *D) { return POIOrErr.takeError(); } + auto Kind = Importer.hasLLDBRedeclCompletion() ? IDK_Everything : IDK_Default; + // Import the definition if (D->isCompleteDefinition()) - if (Error Err = ImportDefinition(D, D2)) + if (Error Err = ImportDefinition(D, D2, Kind)) return std::move(Err); return D2; @@ -2902,7 +2918,8 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) { if (IsStructuralMatch(D, FoundRecord)) { RecordDecl *FoundDef = FoundRecord->getDefinition(); - if (D->isThisDeclarationADefinition() && FoundDef) { + if (!Importer.hasLLDBRedeclCompletion() && + D->isThisDeclarationADefinition() && FoundDef) { // FIXME: Structural equivalence check should check for same // user-defined methods. Importer.MapImported(D, FoundDef); @@ -3078,8 +3095,9 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) { if (D->isAnonymousStructOrUnion()) D2->setAnonymousStructOrUnion(true); + auto Kind = Importer.hasLLDBRedeclCompletion() ? IDK_Everything : IDK_Default; if (D->isCompleteDefinition()) - if (Error Err = ImportDefinition(D, D2, IDK_Default)) + if (Error Err = ImportDefinition(D, D2, Kind)) return std::move(Err); return D2; @@ -5203,7 +5221,8 @@ Error ASTNodeImporter::ImportDefinition( diag::note_odr_objc_missing_superclass); } - if (shouldForceImportDeclContext(Kind)) + if (Importer.hasLLDBRedeclCompletion() || + shouldForceImportDeclContext(Kind)) if (Error Err = ImportDeclContext(From)) return Err; return Error::success(); @@ -6084,8 +6103,9 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateSpecializationDecl( } } + auto Kind = Importer.hasLLDBRedeclCompletion() ? IDK_Everything : IDK_Default; if (D->isCompleteDefinition()) - if (Error Err = ImportDefinition(D, D2)) + if (Error Err = ImportDefinition(D, D2, Kind)) return std::move(Err); return D2; diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp index 2c55b13394cf2..14025bc32d6d2 100644 --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -1546,7 +1546,7 @@ ObjCInterfaceDecl *ObjCInterfaceDecl::Create(const ASTContext &C, auto *Result = new (C, DC) ObjCInterfaceDecl(C, DC, atLoc, Id, typeParamList, ClassLoc, PrevDecl, isInternal); - Result->Data.setInt(!C.getLangOpts().Modules); + Result->Data.setInt(!C.getExternalSource()); C.getObjCInterfaceType(Result, PrevDecl); return Result; } @@ -1556,7 +1556,7 @@ ObjCInterfaceDecl *ObjCInterfaceDecl::CreateDeserialized(const ASTContext &C, auto *Result = new (C, ID) ObjCInterfaceDecl(C, nullptr, SourceLocation(), nullptr, nullptr, SourceLocation(), nullptr, false); - Result->Data.setInt(!C.getLangOpts().Modules); + Result->Data.setInt(!C.getExternalSource()); return Result; } @@ -1947,7 +1947,7 @@ ObjCProtocolDecl *ObjCProtocolDecl::Create(ASTContext &C, DeclContext *DC, ObjCProtocolDecl *PrevDecl) { auto *Result = new (C, DC) ObjCProtocolDecl(C, DC, Id, nameLoc, atStartLoc, PrevDecl); - Result->Data.setInt(!C.getLangOpts().Modules); + Result->Data.setInt(!C.getExternalSource()); return Result; } @@ -1956,7 +1956,7 @@ ObjCProtocolDecl *ObjCProtocolDecl::CreateDeserialized(ASTContext &C, ObjCProtocolDecl *Result = new (C, ID) ObjCProtocolDecl(C, nullptr, nullptr, SourceLocation(), SourceLocation(), nullptr); - Result->Data.setInt(!C.getLangOpts().Modules); + Result->Data.setInt(!C.getExternalSource()); return Result; } diff --git a/lldb/include/lldb/Core/PluginManager.h b/lldb/include/lldb/Core/PluginManager.h index 318f8b63c251a..7d4a2588f345b 100644 --- a/lldb/include/lldb/Core/PluginManager.h +++ b/lldb/include/lldb/Core/PluginManager.h @@ -474,6 +474,7 @@ class PluginManager { // TypeSystem static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description, TypeSystemCreateInstance create_callback, + DebuggerInitializeCallback debugger_callback, LanguageSet supported_languages_for_types, LanguageSet supported_languages_for_expressions); @@ -528,6 +529,13 @@ class PluginManager { Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp, llvm::StringRef description, bool is_global_property); + static lldb::OptionValuePropertiesSP + GetSettingForTypeSystemPlugin(Debugger &debugger, ConstString setting_name); + + static bool CreateSettingForTypeSystemPlugin( + Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp, + ConstString description, bool is_global_property); + static bool CreateSettingForTracePlugin( Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp, llvm::StringRef description, bool is_global_property); diff --git a/lldb/source/Core/PluginManager.cpp b/lldb/source/Core/PluginManager.cpp index 23c06357e2f95..003075def8c39 100644 --- a/lldb/source/Core/PluginManager.cpp +++ b/lldb/source/Core/PluginManager.cpp @@ -1423,10 +1423,11 @@ PluginManager::GetInstrumentationRuntimeCreateCallbackAtIndex(uint32_t idx) { struct TypeSystemInstance : public PluginInstance { TypeSystemInstance(llvm::StringRef name, llvm::StringRef description, CallbackType create_callback, + DebuggerInitializeCallback debugger_init_callback, LanguageSet supported_languages_for_types, LanguageSet supported_languages_for_expressions) - : PluginInstance(name, description, - create_callback), + : PluginInstance( + name, description, create_callback, debugger_init_callback), supported_languages_for_types(supported_languages_for_types), supported_languages_for_expressions( supported_languages_for_expressions) {} @@ -1445,11 +1446,12 @@ static TypeSystemInstances &GetTypeSystemInstances() { bool PluginManager::RegisterPlugin( llvm::StringRef name, llvm::StringRef description, TypeSystemCreateInstance create_callback, + DebuggerInitializeCallback debugger_init_callback, LanguageSet supported_languages_for_types, LanguageSet supported_languages_for_expressions) { return GetTypeSystemInstances().RegisterPlugin( - name, description, create_callback, supported_languages_for_types, - supported_languages_for_expressions); + name, description, create_callback, debugger_init_callback, + supported_languages_for_types, supported_languages_for_expressions); } bool PluginManager::UnregisterPlugin(TypeSystemCreateInstance create_callback) { @@ -1536,6 +1538,7 @@ void PluginManager::DebuggerInitialize(Debugger &debugger) { GetOperatingSystemInstances().PerformDebuggerCallback(debugger); GetStructuredDataPluginInstances().PerformDebuggerCallback(debugger); GetTracePluginInstances().PerformDebuggerCallback(debugger); + GetTypeSystemInstances().PerformDebuggerCallback(debugger); } // This is the preferred new way to register plugin specific settings. e.g. @@ -1660,6 +1663,7 @@ static constexpr llvm::StringLiteral kProcessPluginName("process"); static constexpr llvm::StringLiteral kTracePluginName("trace"); static constexpr llvm::StringLiteral kObjectFilePluginName("object-file"); static constexpr llvm::StringLiteral kSymbolFilePluginName("symbol-file"); +static constexpr llvm::StringLiteral kTypeSystemPluginName("typesystem"); static constexpr llvm::StringLiteral kJITLoaderPluginName("jit-loader"); static constexpr llvm::StringLiteral kStructuredDataPluginName("structured-data"); @@ -1744,6 +1748,22 @@ bool PluginManager::CreateSettingForSymbolFilePlugin( properties_sp, description, is_global_property); } +lldb::OptionValuePropertiesSP +PluginManager::GetSettingForTypeSystemPlugin(Debugger &debugger, + ConstString setting_name) { + return GetSettingForPlugin(debugger, setting_name, + ConstString(kTypeSystemPluginName)); +} + +bool PluginManager::CreateSettingForTypeSystemPlugin( + Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp, + ConstString description, bool is_global_property) { + return CreateSettingForPlugin( + debugger, ConstString(kTypeSystemPluginName), + ConstString("Settings for type system plug-ins"), properties_sp, + description, is_global_property); +} + lldb::OptionValuePropertiesSP PluginManager::GetSettingForJITLoaderPlugin(Debugger &debugger, llvm::StringRef setting_name) { diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h b/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h index 95e8a600f8382..805daec635e2a 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h @@ -9,6 +9,7 @@ #ifndef LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CLANG_ASTUTILS_H #define LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CLANG_ASTUTILS_H +#include "Plugins/TypeSystem/Clang/ImporterBackedASTSource.h" #include "clang/Basic/Module.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/MultiplexExternalSemaSource.h" @@ -20,7 +21,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: @@ -240,7 +241,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. @@ -273,9 +274,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 72e4e9f4b88e7..9657f28377cd8 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" @@ -15,6 +16,8 @@ #include "clang/AST/DeclObjC.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" @@ -227,6 +230,35 @@ class CompleteTagDeclsScope : public ClangASTImporter::NewDeclListener { clang::ASTContext *m_src_ctx; ClangASTImporter &importer; + void CompleteDecl( + Decl *decl, + lldb_private::ClangASTImporter::ASTContextMetadata const &to_context_md) { + // 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); + } + } + public: /// Constructs a CompleteTagDeclsScope. /// \param importer The ClangASTImporter that we should observe. @@ -249,30 +281,8 @@ class CompleteTagDeclsScope : public ClangASTImporter::NewDeclListener { 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); - } + if (!TypeSystemClang::UseRedeclCompletion()) + CompleteDecl(decl, *to_context_md); to_context_md->removeOrigin(decl); } @@ -286,6 +296,10 @@ class CompleteTagDeclsScope : public ClangASTImporter::NewDeclListener { // Filter out decls that we can't complete later. if (!isa(to) && !isa(to)) return; + + if (TypeSystemClang::UseRedeclCompletion()) + 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()) @@ -356,6 +370,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; @@ -365,24 +389,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 = @@ -392,10 +402,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; @@ -507,7 +514,14 @@ bool ClangASTImporter::CompleteType(const CompilerType &compiler_type) { if (!CanImport(compiler_type)) return false; - if (Import(compiler_type)) { + auto const success = Import(compiler_type); + + // With redecl completion we don't need to manually complete + // the definition. + if (TypeSystemClang::UseRedeclCompletion()) + return success; + + if (success) { TypeSystemClang::CompleteTagDeclarationDefinition(compiler_type); return true; } @@ -525,6 +539,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); bool success = false; @@ -536,7 +553,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); success = true; } else { bit_size = 0; @@ -548,46 +564,71 @@ 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)) - return false; - ImporterDelegateSP delegate_sp( GetDelegate(&decl->getASTContext(), decl_origin.ctx)); ASTImporterDelegate::CxxModuleScope std_scope(*delegate_sp, &decl->getASTContext()); - if (delegate_sp) - delegate_sp->ImportDefinitionTo(decl, decl_origin.decl); + + if (!TypeSystemClang::UseRedeclCompletion()) { + if (!TypeSystemClang::GetCompleteDecl(decl_origin.ctx, decl_origin.decl)) + return false; + + if (delegate_sp) + delegate_sp->ImportDefinitionTo(decl, decl_origin.decl); + } else { + auto *origin_def = llvm::cast(decl_origin.decl)->getDefinition(); + if (!origin_def) + return false; + + // 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( @@ -597,24 +638,48 @@ bool ClangASTImporter::CompleteObjCInterfaceDecl( if (!decl_origin.Valid()) return false; - if (!TypeSystemClang::GetCompleteDecl(decl_origin.ctx, decl_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); + if (!TypeSystemClang::UseRedeclCompletion()) { + if (!TypeSystemClang::GetCompleteDecl(decl_origin.ctx, decl_origin.decl)) + return false; + + if (delegate_sp) + delegate_sp->ImportDefinitionTo(interface_decl, decl_origin.decl); + + if (ObjCInterfaceDecl *super_class = interface_decl->getSuperClass()) + RequireCompleteType(clang::QualType(super_class->getTypeForDecl(), 0)); + } else { + ObjCInterfaceDecl *origin_decl = + llvm::cast(decl_origin.decl); + + origin_decl = origin_decl->getDefinition(); + if (!origin_decl) + return false; - if (ObjCInterfaceDecl *super_class = interface_decl->getSuperClass()) - RequireCompleteType(clang::QualType(super_class->getTypeForDecl(), 0)); + auto delegate_sp( + GetDelegate(&interface_decl->getASTContext(), decl_origin.ctx)); + + llvm::Expected result = delegate_sp->Import(origin_decl); + if (result) + return true; + + llvm::handleAllErrors(result.takeError(), + [](const clang::ASTImportError &e) { + llvm::errs() << "ERR: " << e.toString() << "\n"; + }); + + return false; + } return true; } bool ClangASTImporter::CompleteAndFetchChildren(clang::QualType type) { - if (!RequireCompleteType(type)) - return false; + const auto ret = RequireCompleteType(type); + if (TypeSystemClang::UseRedeclCompletion() || !ret) + return ret; Log *log = GetLog(LLDBLog::Expressions); @@ -861,6 +926,22 @@ ClangASTImporter::ASTImporterDelegate::ImportImpl(Decl *From) { } } + if (TypeSystemClang::UseRedeclCompletion()) { + 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); @@ -882,6 +963,14 @@ ClangASTImporter::ASTImporterDelegate::ImportImpl(Decl *From) { for (clang::Decl *candidate : lr) { if (candidate->getKind() == From->getKind()) { RegisterImportedDecl(From, candidate); + + // 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 (TypeSystemClang::UseRedeclCompletion()) + if (auto *tag_decl = llvm::dyn_cast(candidate); + !tag_decl || !tag_decl->getDefinition()) + continue; + m_decls_to_ignore.insert(candidate); return candidate; } @@ -1122,6 +1211,27 @@ void ClangASTImporter::ASTImporterDelegate::Imported(clang::Decl *from, from, m_source_ctx, &to->getASTContext()); } + if (auto *to_namespace_decl = dyn_cast(to)) { + m_main.BuildNamespaceMap(to_namespace_decl); + to_namespace_decl->setHasExternalVisibleStorage(); + } + + if (TypeSystemClang::UseRedeclCompletion()) { + if (clang::ObjCInterfaceDecl *td = dyn_cast(to)) { + if (clang::ExternalASTSource *s = getToContext().getExternalSource()) + if (td->isThisDeclarationADefinition()) + s->CompleteRedeclChain(td); + td->setHasExternalVisibleStorage(); + } + } else { + MarkDeclImported(from, to); + } +} + +void ClangASTImporter::ASTImporterDelegate::MarkDeclImported(Decl *from, + Decl *to) { + Log *log = GetLog(LLDBLog::Expressions); + if (auto *to_tag_decl = dyn_cast(to)) { to_tag_decl->setHasExternalLexicalStorage(); to_tag_decl->getPrimaryContext()->setMustBuildLookupTable(); @@ -1136,11 +1246,6 @@ void ClangASTImporter::ASTImporterDelegate::Imported(clang::Decl *from, (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(); @@ -1173,3 +1278,23 @@ 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); + setLLDBRedeclCompletion(TypeSystemClang::UseRedeclCompletion()); +} diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h index e565a96b217ff..3478ffcc0339b 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h @@ -118,6 +118,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, @@ -135,6 +137,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). /// @@ -149,6 +153,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); @@ -260,22 +266,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 @@ -324,6 +315,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 00ab6a04bd323..35788610ee7e1 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp @@ -22,6 +22,7 @@ #include "lldb/Utility/Log.h" #include "clang/AST/ASTContext.h" #include "clang/AST/RecordLayout.h" +#include "clang/AST/Type.h" #include "clang/Basic/SourceManager.h" #include "Plugins/ExpressionParser/Clang/ClangUtil.h" @@ -282,8 +283,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)); @@ -324,6 +328,45 @@ void ClangASTSource::CompleteType(clang::ObjCInterfaceDecl *interface_decl) { LLDB_LOG(log, " [COID] {0}", ClangUtil::DumpDecl(interface_decl)); } +void ClangASTSource::CompleteRedeclChain(const Decl *d) { + if (!TypeSystemClang::UseRedeclCompletion()) + return; + + 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()); @@ -361,8 +404,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( @@ -1510,8 +1552,8 @@ bool ClangASTSource::layoutRecordType(const RecordDecl *record, uint64_t &size, 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. diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h index f3fec3f944a14..a769acdce55ab 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,8 @@ class ClangASTSource : public clang::ExternalASTSource, /// The Decl to be completed in place. void CompleteType(clang::ObjCInterfaceDecl *Class) override; + 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 +234,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 89d9ac042e57a..4e6dcbac0163c 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExternalASTSourceCallbacks.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExternalASTSourceCallbacks.cpp @@ -26,6 +26,25 @@ void ClangExternalASTSourceCallbacks::CompleteType( m_ast.CompleteObjCInterfaceDecl(objc_decl); } +void ClangExternalASTSourceCallbacks::CompleteRedeclChain( + const clang::Decl *d) { + if (!TypeSystemClang::UseRedeclCompletion()) + return; + + 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 219ed641615eb..c2ac0e2cd441d 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExternalASTSourceCallbacks.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExternalASTSourceCallbacks.h @@ -9,19 +9,22 @@ #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/Module.h" #include 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) {} @@ -38,6 +41,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 2764a2aa39fa1..c86d41c9cfe8e 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp @@ -11,6 +11,7 @@ #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 "lldb/Core/Module.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" @@ -24,11 +25,12 @@ using namespace lldb_private; class lldb_private::AppleObjCExternalASTSource - : public clang::ExternalASTSource { + : public ImporterBackedASTSource { public: AppleObjCExternalASTSource(AppleObjCDeclVendor &decl_vendor) : m_decl_vendor(decl_vendor) {} + // FIXME: unused when 'TypeSystemClang::UseRedeclCompletion == true' bool FindExternalVisibleDeclsByName(const clang::DeclContext *decl_ctx, clang::DeclarationName name) override { @@ -108,6 +110,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 +180,13 @@ 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(); + if (TypeSystemClang::UseRedeclCompletion()) { + m_interface_to_isa[new_iface_decl] = isa; + m_external_source->MarkRedeclChainsAsOutOfDate(m_ast_ctx->getASTContext()); + } else { + new_iface_decl->setHasExternalVisibleStorage(); + new_iface_decl->setHasExternalLexicalStorage(); + } ast_ctx.getTranslationUnitDecl()->addDecl(new_iface_decl); @@ -398,6 +414,21 @@ bool AppleObjCDeclVendor::FinishDecl(clang::ObjCInterfaceDecl *interface_decl) { Log *log( GetLog(LLDBLog::Expressions)); // FIXME - a more appropriate log channel? + clang::ObjCInterfaceDecl *iface_def = nullptr; + if (TypeSystemClang::UseRedeclCompletion()) { + // 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,13 +437,20 @@ bool AppleObjCDeclVendor::FinishDecl(clang::ObjCInterfaceDecl *interface_decl) { if (!objc_isa) return false; - if (!interface_decl->hasExternalVisibleStorage()) + if (!TypeSystemClang::UseRedeclCompletion() && + !interface_decl->hasExternalVisibleStorage()) + return true; + + // Could've completed during CreateRedeclaration above + if (TypeSystemClang::UseRedeclCompletion() && interface_decl->hasDefinition()) return true; interface_decl->startDefinition(); - interface_decl->setHasExternalVisibleStorage(false); - interface_decl->setHasExternalLexicalStorage(false); + if (!TypeSystemClang::UseRedeclCompletion()) { + 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 edf06eb286cf1..88d4b48d0499d 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" @@ -26,11 +28,14 @@ #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" #include "lldb/Utility/Log.h" @@ -42,6 +47,7 @@ #include "clang/AST/DeclTemplate.h" #include "clang/AST/Type.h" #include "llvm/Demangle/Demangle.h" +#include "llvm/Support/Casting.h" #include #include @@ -62,6 +68,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() {} @@ -239,7 +257,10 @@ 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()) + bool hasDef = ast.UseRedeclCompletion() + ? tag_decl_ctx->getDefinition() != nullptr + : tag_decl_ctx->isCompleteDefinition(); + if (hasDef || tag_decl_ctx->isBeingDefined()) return; // We reach this point of the tag was present in the debug info as a @@ -247,8 +268,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; @@ -260,7 +281,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) { @@ -438,7 +470,8 @@ TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, if (type_ptr) return type_ptr->shared_from_this(); // Set a bit that lets us know that we are currently parsing this - dwarf->GetDIEToType()[die.GetDIE()] = DIE_IS_BEING_PARSED; + if (!TypeSystemClang::UseRedeclCompletion()) + dwarf->GetDIEToType()[die.GetDIE()] = DIE_IS_BEING_PARSED; ParsedDWARFTypeAttributes attrs(die); @@ -514,7 +547,18 @@ TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, // TODO: We should consider making the switch above exhaustive to simplify // control flow in ParseTypeFromDWARF. Then, we could simply replace this // return statement with a call to llvm_unreachable. - return UpdateSymbolContextScopeForType(sc, die, type_sp); + lldb::TypeSP t = UpdateSymbolContextScopeForType(sc, die, type_sp); + + if (m_ast.UseRedeclCompletion()) { + 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); + } + } + + return t; } lldb::TypeSP @@ -933,6 +977,11 @@ TypeSP DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die, const clang::Decl::Kind containing_decl_kind = containing_decl_ctx->getDeclKind(); + if (TypeSystemClang::UseRedeclCompletion()) + 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 @@ -998,16 +1047,10 @@ TypeSP DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die, } if (class_opaque_type) { - // If accessibility isn't set to anything valid, assume public - // for now... - if (attrs.accessibility == eAccessNone) - attrs.accessibility = eAccessPublic; - clang::ObjCMethodDecl *objc_method_decl = m_ast.AddMethodToObjCObjectType( class_opaque_type, attrs.name.GetCString(), clang_type, - attrs.accessibility, attrs.is_artificial, is_variadic, - attrs.is_objc_direct_call); + attrs.is_artificial, is_variadic, attrs.is_objc_direct_call); type_handled = objc_method_decl != nullptr; if (type_handled) { LinkDeclContextToDIE(objc_method_decl, die); @@ -1095,7 +1138,8 @@ TypeSP DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die, CompilerType class_opaque_type = class_type->GetForwardCompilerType(); if (TypeSystemClang::IsCXXClassType(class_opaque_type)) { - if (class_opaque_type.IsBeingDefined()) { + if (TypeSystemClang::UseRedeclCompletion() || + class_opaque_type.IsBeingDefined()) { if (!is_static && !die.HasChildren()) { // We have a C++ member function with no children (this // pointer!) and clang will get mad if we try and make @@ -1582,6 +1626,30 @@ bool DWARFASTParserClang::IsSwiftInteropType(const DWARFDIE &die) { } // END SWIFT +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::RecordDecl::APK_CannotPassInRegs); + } +} + TypeSP DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, const DWARFDIE &die, @@ -1593,6 +1661,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 @@ -1763,9 +1834,6 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, CompilerType(m_ast.weak_from_this(), dwarf->GetForwardDeclDieToClangType().lookup(die.GetDIE())); if (!clang_type) { - clang::DeclContext *decl_ctx = - GetClangDeclContextContainingDIE(die, nullptr); - PrepareContextToReceiveMembers(m_ast, GetClangASTImporter(), decl_ctx, die, attrs.name.GetCString()); @@ -1812,6 +1880,10 @@ 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) { @@ -1820,13 +1892,22 @@ 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); + if (TypeSystemClang::UseRedeclCompletion()) + LinkDeclContextToDIE(GetClangDeclContextContainingDIE(die, nullptr), die); + else + LinkDeclContextToDIE(m_ast.GetDeclContextForType(clang_type), die); + type_sp = dwarf->MakeType( die.GetID(), attrs.name, attrs.byte_size, nullptr, LLDB_INVALID_UID, Type::eEncodingIsUID, &attrs.decl, clang_type, @@ -1844,10 +1925,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)) { @@ -1876,18 +1953,13 @@ 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 && + (!TypeSystemClang::UseRedeclCompletion() || + !should_directly_complete)) { if (attrs.class_language != eLanguageTypeObjC && attrs.class_language != eLanguageTypeObjC_plus_plus) - TypeSystemClang::StartTagDeclarationDefinition(clang_type); + if (!TypeSystemClang::UseRedeclCompletion()) + 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 @@ -1906,29 +1978,17 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, dwarf->GetForwardDeclClangTypeToDie().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(); + if (!TypeSystemClang::UseRedeclCompletion()) + m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), true); } } - 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::RecordDecl::APK_CannotPassInRegs); - } + if (!TypeSystemClang::UseRedeclCompletion()) + adjustArgPassing(m_ast, attrs, clang_type); + else if (should_directly_complete) + m_to_complete.push_back({clang_type, die, type_sp}); + return type_sp; } @@ -2135,16 +2195,33 @@ bool DWARFASTParserClang::ParseTemplateParameterInfos( bool DWARFASTParserClang::CompleteRecordType(const DWARFDIE &die, lldb_private::Type *type, CompilerType &clang_type) { + if (TypeSystemClang::UseRedeclCompletion()) + 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()) { + ParsedDWARFTypeAttributes attrs(die); + if (TypeSystemClang::UseRedeclCompletion() && attrs.is_forward_declaration) + return true; + + clang::DeclContext *decl_ctx = GetClangDeclContextContainingDIE(die, nullptr); + + if (TypeSystemClang::UseRedeclCompletion() && + !DirectlyCompleteType(decl_ctx, attrs)) { + clang_type = m_ast.CreateRedeclaration(clang_type); + RegisterDIE(die.GetDIE(), clang_type); + } + + if (die.HasChildren() || TypeSystemClang::UseRedeclCompletion()) { const bool type_is_objc_object_or_interface = TypeSystemClang::IsObjCObjectOrInterfaceType(clang_type); - if (type_is_objc_object_or_interface) { + if (type_is_objc_object_or_interface || + TypeSystemClang::UseRedeclCompletion()) { // For objective C we don't start the definition when the class is // created. TypeSystemClang::StartTagDeclarationDefinition(clang_type); @@ -2202,22 +2279,23 @@ bool DWARFASTParserClang::CompleteRecordType(const DWARFDIE &die, } } + if (TypeSystemClang::UseRedeclCompletion()) + 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) layout_info.bit_size = die.GetAttributeValueAsUnsigned(DW_AT_byte_size, 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 @@ -2254,7 +2332,8 @@ bool DWARFASTParserClang::CompleteTypeFromDWARF(const DWARFDIE &die, // 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 (!TypeSystemClang::UseRedeclCompletion()) + m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), false); if (!die) return false; @@ -3706,6 +3785,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 205003c5d8ce0..402caab7e25c5 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h @@ -129,12 +129,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 * @@ -227,6 +245,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..2df72f22e6a08 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 @@ -18,3 +19,15 @@ add_lldb_library(lldbPluginTypeSystemClang PLUGIN LINK_COMPONENTS Support ) + +lldb_tablegen(TypeSystemClangProperties.inc -gen-lldb-property-defs + SOURCE TypeSystemClangProperties.td + TARGET LLDBPluginTypeSystemClangPropertiesGen) + +lldb_tablegen(TypeSystemClangPropertiesEnum.inc -gen-lldb-property-enum-defs + SOURCE TypeSystemClangProperties.td + TARGET LLDBPluginTypeSystemClangPropertiesEnumGen) + +add_dependencies(lldbPluginTypeSystemClang + LLDBPluginTypeSystemClangPropertiesGen + LLDBPluginTypeSystemClangPropertiesEnumGen) 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 950d68b8c33fc..bec02459796ad 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -9,6 +9,7 @@ #include "TypeSystemClang.h" #include "clang/AST/DeclBase.h" +#include "llvm/ADT/STLForwardCompat.h" #include "llvm/Support/Casting.h" #include "llvm/Support/FormatAdapters.h" #include "llvm/Support/FormatVariadic.h" @@ -92,6 +93,42 @@ using llvm::StringSwitch; LLDB_PLUGIN_DEFINE(TypeSystemClang) +namespace { + +#define LLDB_PROPERTIES_typesystemclang +#include "TypeSystemClangProperties.inc" + +enum { +#define LLDB_PROPERTIES_typesystemclang +#include "TypeSystemClangPropertiesEnum.inc" +}; + +class PluginProperties : public Properties { +public: + static ConstString GetSettingName() { + return ConstString(TypeSystemClang::GetPluginNameStatic()); + } + + PluginProperties() { + m_collection_sp = std::make_shared(GetSettingName()); + m_collection_sp->Initialize(g_typesystemclang_properties); + } + + bool UseRedeclCompletion() const { + const auto ret = m_collection_sp->GetPropertyAtIndexAs( + ePropertyRedeclCompletion, nullptr); + + return ret && *ret; + } +}; + +static PluginProperties &GetGlobalPluginProperties() { + static PluginProperties g_settings; + return g_settings; +} + +} // namespace + namespace { static void VerifyDecl(clang::Decl *decl) { assert(decl && "VerifyDecl called with nullptr?"); @@ -648,10 +685,22 @@ LanguageSet TypeSystemClang::GetSupportedLanguagesForExpressions() { return languages; } +void TypeSystemClang::DebuggerInitialize(Debugger &debugger) { + if (PluginManager::GetSettingForTypeSystemPlugin( + debugger, PluginProperties::GetSettingName())) + return; + const bool is_global_setting = true; + PluginManager::CreateSettingForTypeSystemPlugin( + debugger, GetGlobalPluginProperties().GetValueProperties(), + ConstString("Properties for the Clang type system plug-in."), + is_global_setting); +} + void TypeSystemClang::Initialize() { PluginManager::RegisterPlugin( GetPluginNameStatic(), "clang base AST context plug-in", CreateInstance, - GetSupportedLanguagesForTypes(), GetSupportedLanguagesForExpressions()); + DebuggerInitialize, GetSupportedLanguagesForTypes(), + GetSupportedLanguagesForExpressions()); } void TypeSystemClang::Terminate() { @@ -1217,11 +1266,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()))); } #pragma mark Structure, Unions, Classes @@ -1272,6 +1328,17 @@ OptionalClangModuleID TypeSystemClang::GetOrCreateClangModule( } 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) { @@ -1279,13 +1346,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 @@ -1340,7 +1408,7 @@ CompilerType TypeSystemClang::CreateRecordType( if (decl_ctx) decl_ctx->addDecl(decl); - return GetType(ast.getTagDeclType(decl)); + return decl; } namespace { @@ -1602,6 +1670,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); @@ -1654,6 +1723,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( @@ -1694,6 +1766,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() + @@ -1713,7 +1787,6 @@ TypeSystemClang::CreateClassTemplateSpecializationDecl( class_template_specialization_decl->setInstantiationOf(class_template_decl); 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); @@ -1722,6 +1795,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; } @@ -1847,7 +1927,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) { @@ -1866,7 +1946,7 @@ CompilerType TypeSystemClang::CreateObjCClass( if (metadata) SetMetadata(decl, *metadata); - return GetType(ast.getObjCInterfaceType(decl)); + return decl; } bool TypeSystemClang::BaseSpecifierIsEmpty(const CXXBaseSpecifier *b) { @@ -2347,7 +2427,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) { @@ -2373,7 +2453,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, @@ -2475,23 +2555,29 @@ bool TypeSystemClang::GetCompleteDecl(clang::ASTContext *ast, if (tag_decl->isCompleteDefinition()) return true; - if (!tag_decl->hasExternalLexicalStorage()) + if (!UseRedeclCompletion() && !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()) + if (!UseRedeclCompletion() && + !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; } @@ -2511,9 +2597,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, @@ -2522,7 +2616,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; @@ -2615,6 +2709,120 @@ TypeSystemClang::GetDeclContextForType(clang::QualType type) { return nullptr; } +static clang::Type const *GetCompleteRecordType(clang::ASTContext *ast, + clang::QualType qual_type, + bool allow_completion = true) { + clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); + if (!cxx_record_decl) + return nullptr; + + if (TypeSystemClang::UseRedeclCompletion()) { + clang::CXXRecordDecl *def = cxx_record_decl->getDefinition(); + if (!def) + return nullptr; + return def->getTypeForDecl(); + } + + if (cxx_record_decl->hasExternalLexicalStorage()) { + const bool is_complete = cxx_record_decl->isCompleteDefinition(); + const bool fields_loaded = + cxx_record_decl->hasLoadedFieldsFromExternalStorage(); + if (is_complete && fields_loaded) + return qual_type.getTypePtr(); + + if (!allow_completion) + return nullptr; + + // Call the field_begin() accessor to for it to use the external source + // to load the fields... + 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 qual_type.getTypePtr(); + } + + return qual_type.getTypePtr(); +} + +static clang::Type const *GetCompleteEnumType(clang::ASTContext *ast, + clang::QualType qual_type, + bool allow_completion = true) { + const clang::TagType *tag_type = + llvm::dyn_cast(qual_type.getTypePtr()); + if (!tag_type) + return nullptr; + + clang::TagDecl *tag_decl = tag_type->getDecl(); + if (!tag_decl) + return nullptr; + + if (TypeSystemClang::UseRedeclCompletion()) { + if (clang::TagDecl *def = tag_decl->getDefinition()) + return def->getTypeForDecl(); + + return tag_decl->getTypeForDecl(); + } + + if (tag_decl->getDefinition()) + return tag_type; + + if (!allow_completion) + return nullptr; + + if (tag_decl->hasExternalLexicalStorage()) { + if (ast) { + clang::ExternalASTSource *external_ast_source = ast->getExternalSource(); + if (external_ast_source) { + external_ast_source->CompleteType(tag_decl); + return tag_type; + } + } + } + + return tag_type; +} + +static clang::Type const * +GetCompleteObjCInterfaceType(clang::ASTContext *ast, clang::QualType qual_type, + bool allow_completion = true) { + const clang::ObjCObjectType *objc_class_type = + llvm::dyn_cast(qual_type); + if (!objc_class_type) + return nullptr; + + clang::ObjCInterfaceDecl *class_interface_decl = + 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) + return objc_class_type; + + if (auto *def = class_interface_decl->getDefinition()) + return TypeSystemClang::UseRedeclCompletion() ? def->getTypeForDecl() + : objc_class_type; + + if (!allow_completion) + return nullptr; + + if (class_interface_decl->hasExternalLexicalStorage()) { + if (ast) { + clang::ExternalASTSource *external_ast_source = ast->getExternalSource(); + if (external_ast_source) { + external_ast_source->CompleteType(class_interface_decl); + return objc_class_type; + } + } + } + + return nullptr; +} + static bool GetCompleteQualType(clang::ASTContext *ast, clang::QualType qual_type, bool allow_completion = true) { @@ -2632,92 +2840,27 @@ static bool GetCompleteQualType(clang::ASTContext *ast, allow_completion); } break; case clang::Type::Record: { - clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); - if (cxx_record_decl) { - if (cxx_record_decl->hasExternalLexicalStorage()) { - const bool is_complete = cxx_record_decl->isCompleteDefinition(); - const bool fields_loaded = - cxx_record_decl->hasLoadedFieldsFromExternalStorage(); - if (is_complete && fields_loaded) - return true; + if (auto const *ty = llvm::dyn_cast_or_null( + GetCompleteRecordType(ast, qual_type, allow_completion))) + return TypeSystemClang::UseRedeclCompletion() || !ty->isIncompleteType(); - if (!allow_completion) - return false; - - // Call the field_begin() accessor to for it to use the external source - // to load the fields... - 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); - } - } - } - } - const clang::TagType *tag_type = - llvm::cast(qual_type.getTypePtr()); - return !tag_type->isIncompleteType(); + return false; } break; case clang::Type::Enum: { - const clang::TagType *tag_type = - llvm::dyn_cast(qual_type.getTypePtr()); - if (tag_type) { - clang::TagDecl *tag_decl = tag_type->getDecl(); - if (tag_decl) { - if (tag_decl->getDefinition()) - return true; - - if (!allow_completion) - return false; - - if (tag_decl->hasExternalLexicalStorage()) { - if (ast) { - clang::ExternalASTSource *external_ast_source = - ast->getExternalSource(); - if (external_ast_source) { - external_ast_source->CompleteType(tag_decl); - return !tag_type->isIncompleteType(); - } - } - } - return false; - } - } + if (auto const *ty = llvm::dyn_cast_or_null( + GetCompleteEnumType(ast, qual_type, allow_completion))) + return TypeSystemClang::UseRedeclCompletion() || !ty->isIncompleteType(); + return false; } break; case clang::Type::ObjCObject: case clang::Type::ObjCInterface: { - const clang::ObjCObjectType *objc_class_type = - llvm::dyn_cast(qual_type); - if (objc_class_type) { - clang::ObjCInterfaceDecl *class_interface_decl = - 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 (class_interface_decl->getDefinition()) - return true; + if (auto const *ty = llvm::dyn_cast_or_null( + GetCompleteObjCInterfaceType(ast, qual_type, allow_completion))) + return TypeSystemClang::UseRedeclCompletion() || !ty->isIncompleteType(); - if (!allow_completion) - return false; - - if (class_interface_decl->hasExternalLexicalStorage()) { - if (ast) { - clang::ExternalASTSource *external_ast_source = - ast->getExternalSource(); - if (external_ast_source) { - external_ast_source->CompleteType(class_interface_decl); - return !objc_class_type->isIncompleteType(); - } - } - } - return false; - } - } + return false; } break; case clang::Type::Attributed: @@ -3405,7 +3548,9 @@ 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 TypeSystemClang::UseRedeclCompletion() + ? tag_decl->getDefinition() != nullptr + : tag_decl->isCompleteDefinition(); return false; } else { const clang::ObjCObjectType *objc_class_type = @@ -4581,6 +4726,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, 0); @@ -5323,6 +5469,10 @@ uint32_t TypeSystemClang::GetNumChildren(lldb::opaque_compiler_type_t type, objc_class_type->getInterface(); if (class_interface_decl) { + if (TypeSystemClang::UseRedeclCompletion()) { + auto *def = class_interface_decl->getDefinition(); + class_interface_decl = def; + } clang::ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass(); @@ -5488,6 +5638,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; @@ -7561,6 +7714,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; @@ -8029,15 +8184,19 @@ clang::ObjCMethodDecl *TypeSystemClang::AddMethodToObjCObjectType( const char *name, // the full symbol name as seen in the symbol table // (lldb::opaque_compiler_type_t type, "-[NString // stringWithCString:]") - const CompilerType &method_clang_type, lldb::AccessType access, - bool is_artificial, bool is_variadic, bool is_objc_direct_call) { + const CompilerType &method_clang_type, bool is_artificial, bool is_variadic, + bool is_objc_direct_call) { if (!type || !method_clang_type.IsValid()) 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) @@ -8107,8 +8266,7 @@ clang::ObjCMethodDecl *TypeSystemClang::AddMethodToObjCObjectType( auto *objc_method_decl = clang::ObjCMethodDecl::CreateDeserialized(ast, 0); 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); @@ -8218,7 +8376,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; } } @@ -8228,7 +8390,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; } } @@ -8274,9 +8440,13 @@ 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); + + if (!TypeSystemClang::UseRedeclCompletion()) { + 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; @@ -8844,7 +9014,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( @@ -9125,7 +9295,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) @@ -9300,6 +9470,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); @@ -9318,6 +9570,10 @@ npdb::PdbAstBuilder *TypeSystemClang::GetNativePDBParser() { return m_native_pdb_ast_parser_up.get(); } +bool TypeSystemClang::UseRedeclCompletion() { + return GetGlobalPluginProperties().UseRedeclCompletion(); +} + bool TypeSystemClang::LayoutRecordType( const clang::RecordDecl *record_decl, uint64_t &bit_size, uint64_t &alignment, diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 370e088eecd8c..255b12eec23dd 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -149,6 +149,8 @@ class TypeSystemClang : public TypeSystem { static LanguageSet GetSupportedLanguagesForTypes(); static LanguageSet GetSupportedLanguagesForExpressions(); + static void DebuggerInitialize(Debugger &debugger); + static void Initialize(); static void Terminate(); @@ -247,7 +249,7 @@ class TypeSystemClang : public TypeSystem { CompilerType GetTypeForDecl(clang::TagDecl *decl); - CompilerType GetTypeForDecl(clang::ObjCInterfaceDecl *objc_decl); + CompilerType GetTypeForDecl(const clang::ObjCInterfaceDecl *objc_decl); template CompilerType @@ -327,6 +329,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; @@ -336,10 +346,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; @@ -347,7 +374,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); @@ -388,14 +415,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); @@ -407,7 +434,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; }; @@ -453,7 +480,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 @@ -495,12 +531,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 @@ -518,11 +565,28 @@ class TypeSystemClang : public TypeSystem { PDBASTParser *GetPDBParser() override; npdb::PdbAstBuilder *GetNativePDBParser() override; + /// If true, then declarations are completed by completing their redeclaration + /// chain. + /// + /// Initially declarations might just be forward declared in an AST but have a + /// defining redeclaration (that might be lazily added to the AST via the + /// ExternalASTSource). + static bool UseRedeclCompletion(); + // TypeSystemClang callbacks for external source lookups. void CompleteTagDecl(clang::TagDecl *); 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, @@ -998,8 +1062,8 @@ class TypeSystemClang : public TypeSystem { const char *name, // the full symbol name as seen in the symbol table // (lldb::opaque_compiler_type_t type, "-[NString // stringWithCString:]") - const CompilerType &method_compiler_type, lldb::AccessType access, - bool is_artificial, bool is_variadic, bool is_objc_direct_call); + const CompilerType &method_compiler_type, bool is_artificial, + bool is_variadic, bool is_objc_direct_call); static bool SetHasExternalStorage(lldb::opaque_compiler_type_t type, bool has_extern); @@ -1208,6 +1272,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/source/Plugins/TypeSystem/Clang/TypeSystemClangProperties.td b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClangProperties.td new file mode 100644 index 0000000000000..d55789e870319 --- /dev/null +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClangProperties.td @@ -0,0 +1,8 @@ +include "../../../../include/lldb/Core/PropertiesBase.td" + +let Definition = "typesystemclang" in { + def RedeclCompletion: Property<"experimental-redecl-completion", "Boolean">, + Global, + DefaultFalse, + Desc<"Use redeclarations to complete Clang types. Experimental setting.">; +} diff --git a/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwift.cpp b/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwift.cpp index 84467a147995b..e01111675ba6c 100644 --- a/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwift.cpp +++ b/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwift.cpp @@ -59,9 +59,9 @@ LanguageSet TypeSystemSwift::GetSupportedLanguagesForTypes() { void TypeSystemSwift::Initialize() { SwiftLanguageRuntime::Initialize(); LanguageSet swift = GetSupportedLanguagesForTypes(); - PluginManager::RegisterPlugin(GetPluginNameStatic(), - "Swift type system and AST context plug-in", - CreateTypeSystemInstance, swift, swift); + PluginManager::RegisterPlugin( + GetPluginNameStatic(), "Swift type system and AST context plug-in", + CreateTypeSystemInstance, nullptr, swift, swift); } void TypeSystemSwift::Terminate() { diff --git a/lldb/test/API/functionalities/limit-debug-info/TestLimitDebugInfo.py b/lldb/test/API/functionalities/limit-debug-info/TestLimitDebugInfo.py index bf45202ac316e..928b6c58d8536 100644 --- a/lldb/test/API/functionalities/limit-debug-info/TestLimitDebugInfo.py +++ b/lldb/test/API/functionalities/limit-debug-info/TestLimitDebugInfo.py @@ -183,6 +183,7 @@ def _check_incomplete_frame_variable_output(self): for command, expect_items in command_expect_pairs: self.expect(command, substrs=expect_items) + @expectedFailureAll(setting=('plugin.typesystem.clang.experimental-redecl-completion', 'true')) @skipIf(bugnumber="pr46284", debug_info="gmodules") @skipIfWindows # Clang emits type info even with -flimit-debug-info # Requires DW_CC_pass_by_* attributes from Clang 7 to correctly call @@ -223,6 +224,7 @@ def test_one_and_two_debug(self): self._check_incomplete_frame_variable_output() + @expectedFailureAll(setting=('plugin.typesystem.clang.experimental-redecl-completion', 'true')) @skipIf(bugnumber="pr46284", debug_info="gmodules") @skipIfWindows # Clang emits type info even with -flimit-debug-info # Requires DW_CC_pass_by_* attributes from Clang 7 to correctly call @@ -291,6 +293,7 @@ def test_two_debug(self): self._check_incomplete_frame_variable_output() + @expectedFailureAll(setting=('plugin.typesystem.clang.experimental-redecl-completion', 'true')) @skipIf(bugnumber="pr46284", debug_info="gmodules") @skipIfWindows # Clang emits type info even with -flimit-debug-info # Requires DW_CC_pass_by_* attributes from Clang 7 to correctly call diff --git a/lldb/test/API/functionalities/rerun_and_expr/TestRerunAndExpr.py b/lldb/test/API/functionalities/rerun_and_expr/TestRerunAndExpr.py index 1d62af4299c3b..52c3060d34e2d 100644 --- a/lldb/test/API/functionalities/rerun_and_expr/TestRerunAndExpr.py +++ b/lldb/test/API/functionalities/rerun_and_expr/TestRerunAndExpr.py @@ -16,6 +16,7 @@ class TestRerunExpr(TestBase): # the module from the ModuleList (possibly including a call to # SBDebugger::MemoryPressureDetected. @skipIfWindows + @expectedFailureAll(setting=('plugin.typesystem.clang.experimental-redecl-completion', 'true')) def test(self): """ Tests whether re-launching a process without destroying diff --git a/lldb/test/API/lang/c/modules/TestCModules.py b/lldb/test/API/lang/c/modules/TestCModules.py index ec78eb6fb27f4..a325f8e44a030 100644 --- a/lldb/test/API/lang/c/modules/TestCModules.py +++ b/lldb/test/API/lang/c/modules/TestCModules.py @@ -20,6 +20,7 @@ class CModulesTestCase(TestBase): ) @skipIf(macos_version=["<", "10.12"]) @expectedFailureNetBSD + @expectedFailureAll(setting=('plugin.typesystem.clang.experimental-redecl-completion', 'true')) def test_expr(self): self.build() exe = self.getBuildArtifact("a.out") diff --git a/lldb/test/API/lang/c/shared_lib/TestSharedLib.py b/lldb/test/API/lang/c/shared_lib/TestSharedLib.py index b375aa6a86e14..e46e812ca5568 100644 --- a/lldb/test/API/lang/c/shared_lib/TestSharedLib.py +++ b/lldb/test/API/lang/c/shared_lib/TestSharedLib.py @@ -26,6 +26,7 @@ def common_test_expr(self, preload_symbols): self.expect("expression GetMeASubFoo(my_foo_ptr)", startstr="(sub_foo *) $") + @expectedFailureAll(setting=('plugin.typesystem.clang.experimental-redecl-completion', 'true')) def test_expr(self): """Test that types work when defined in a shared library and forward-declared in the main executable""" self.common_test_expr(True) diff --git a/lldb/test/API/lang/c/shared_lib_stripped_symbols/TestSharedLibStrippedSymbols.py b/lldb/test/API/lang/c/shared_lib_stripped_symbols/TestSharedLibStrippedSymbols.py index f7035edddaf09..1684533c0d5bb 100644 --- a/lldb/test/API/lang/c/shared_lib_stripped_symbols/TestSharedLibStrippedSymbols.py +++ b/lldb/test/API/lang/c/shared_lib_stripped_symbols/TestSharedLibStrippedSymbols.py @@ -8,6 +8,7 @@ class SharedLibStrippedTestCase(TestBase): + @expectedFailureAll(setting=('plugin.typesystem.clang.experimental-redecl-completion', 'true')) @expectedFailureAll(oslist=["windows"]) def test_expr(self): """Test that types work when defined in a shared library and forwa/d-declared in the main executable""" diff --git a/lldb/test/API/lang/cpp/accelerator-table/TestCPPAccelerator.py b/lldb/test/API/lang/cpp/accelerator-table/TestCPPAccelerator.py index 0f21806da6bd3..d73bd108ae195 100644 --- a/lldb/test/API/lang/cpp/accelerator-table/TestCPPAccelerator.py +++ b/lldb/test/API/lang/cpp/accelerator-table/TestCPPAccelerator.py @@ -5,6 +5,7 @@ class CPPAcceleratorTableTestCase(TestBase): + @expectedFailureAll(setting=('plugin.typesystem.clang.experimental-redecl-completion', 'true')) @skipUnlessDarwin @skipIf(debug_info=no_match(["dwarf"])) @skipIf(dwarf_version=[">=", "5"]) 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 a7b02baeb13c5..2fad861e7591f 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 @@ -5,7 +5,7 @@ class TestCase(TestBase): - @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 d7fa3eda85d3a..d437cff47d34a 100644 --- a/lldb/unittests/CMakeLists.txt +++ b/lldb/unittests/CMakeLists.txt @@ -74,6 +74,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/Symbol/TestTypeSystemClang.cpp b/lldb/unittests/Symbol/TestTypeSystemClang.cpp index ec5cc776d960f..35a557c53ee86 100644 --- a/lldb/unittests/Symbol/TestTypeSystemClang.cpp +++ b/lldb/unittests/Symbol/TestTypeSystemClang.cpp @@ -934,8 +934,7 @@ TEST_F(TestTypeSystemClang, AddMethodToObjCObjectType) { bool artificial = false; bool objc_direct = false; clang::ObjCMethodDecl *method = TypeSystemClang::AddMethodToObjCObjectType( - c, "-[A foo]", func_type, lldb::eAccessPublic, artificial, variadic, - objc_direct); + c, "-[A foo]", func_type, artificial, variadic, objc_direct); ASSERT_NE(method, nullptr); // The interface decl should still have external lexical storage. 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..23533343928e1 --- /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::TTK_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::TTK_Struct), *args); + ClassTemplateSpecializationDecl *fwd_decl = + m_ast->CreateClassTemplateSpecializationDecl( + m_ast->GetTranslationUnitDecl(), module_id, template_decl, + llvm::to_underlying(TagTypeKind::TTK_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::TTK_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::TTK_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::TTK_Struct), args); + EXPECT_EQ(template_decl->getDeclContext(), def); + ClassTemplateSpecializationDecl *template_spec_decl = + m_ast->CreateClassTemplateSpecializationDecl( + fwd_decl, OptionalClangModuleID(), template_decl, + llvm::to_underlying(TagTypeKind::TTK_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::TTK_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); +} diff --git a/llvm/include/llvm/ADT/STLForwardCompat.h b/llvm/include/llvm/ADT/STLForwardCompat.h index 97d0bff9aaedb..6afe3610b257f 100644 --- a/llvm/include/llvm/ADT/STLForwardCompat.h +++ b/llvm/include/llvm/ADT/STLForwardCompat.h @@ -60,6 +60,13 @@ auto transformOptional(std::optional &&O, const Function &F) return std::nullopt; } +/// Returns underlying integer value of an enum. Backport of C++23 +/// std::to_underlying. +template +[[nodiscard]] constexpr std::underlying_type_t to_underlying(Enum E) { + return static_cast>(E); +} + } // namespace llvm #endif // LLVM_ADT_STLFORWARDCOMPAT_H diff --git a/llvm/unittests/ADT/STLForwardCompatTest.cpp b/llvm/unittests/ADT/STLForwardCompatTest.cpp index e9cd88cd4c27d..b0c95d09ba2c6 100644 --- a/llvm/unittests/ADT/STLForwardCompatTest.cpp +++ b/llvm/unittests/ADT/STLForwardCompatTest.cpp @@ -119,4 +119,21 @@ TEST(TransformTest, MoveTransformLlvm) { EXPECT_EQ(0u, MoveOnly::Destructions); } +TEST(TransformTest, ToUnderlying) { + enum E { A1 = 0, B1 = -1 }; + static_assert(llvm::to_underlying(A1) == 0); + static_assert(llvm::to_underlying(B1) == -1); + + enum E2 : unsigned char { A2 = 0, B2 }; + static_assert( + std::is_same_v); + static_assert(llvm::to_underlying(A2) == 0); + static_assert(llvm::to_underlying(B2) == 1); + + enum class E3 { A3 = -1, B3 }; + static_assert(std::is_same_v); + static_assert(llvm::to_underlying(E3::A3) == -1); + static_assert(llvm::to_underlying(E3::B3) == 0); +} + } // namespace