From 4cefb2974e4373c677ffcec84b1e5173a24849f7 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Thu, 30 Nov 2023 05:15:57 +0000 Subject: [PATCH 01/20] [lldb][DWARFASTParserClang][NFC] Remove redundant parameter to AddMethodToObjCObjectType (#73832) (cherry picked from commit bcb621f0a1ececfe42995c736ec4e5b9530e7c71) --- .../Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp | 8 +------- lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp | 4 ++-- lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h | 4 ++-- lldb/unittests/Symbol/TestTypeSystemClang.cpp | 3 +-- 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index edf06eb286cf1..7f75f179e211d 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -998,16 +998,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); diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 950d68b8c33fc..b4d76c669a50b 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -8029,8 +8029,8 @@ 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; diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 370e088eecd8c..5c691806767c9 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -998,8 +998,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); 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. From 405141d43f7ef76337f66b5ea28b976be00ef8d4 Mon Sep 17 00:00:00 2001 From: Vlad Serebrennikov Date: Mon, 30 Oct 2023 23:06:28 +0400 Subject: [PATCH 02/20] [ADT] Backport std::to_underlying from C++23 (#70681) This patch backports a one-liner `std::to_underlying` that came with C++23. This is useful for refactoring unscoped enums into scoped enums, because the latter are not implicitly convertible to integer types. I followed libc++ implementation, but I consider their testing too heavy for us, so I wrote a simpler set of tests. (cherry picked from commit d0caa4eef702f6eda1ce5ab3d72faabb55b15ca9) --- llvm/include/llvm/ADT/STLForwardCompat.h | 7 +++++++ llvm/unittests/ADT/STLForwardCompatTest.cpp | 17 +++++++++++++++++ 2 files changed, 24 insertions(+) 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 From 8904ba7286ae9c29e637b5ecf3bd3b264cb1f6d2 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Tue, 13 Feb 2024 14:26:11 +0000 Subject: [PATCH 03/20] [lldb][NFC] Add an ImporterBackedASTSource that serves as base class for LLDB ExternalASTSources that use the ASTImporter This gives all ExternalASTSource classes a common base class that allows other classes (in LLDB's case ClangASTImporter) to invalidate the redeclaration chains in an ASTContext. The implementation just calls the (private) function to bump the generation counter which is how we track whether a redeclaration chain is out of date. This is used in D101950 to let the ClangASTImporter mark redeclaration chains as out-of-date so that they update themselves via the ExternalASTSource. Patch itself is NFC as it just changes the inheritance of a bunch of classes and changes the LLVM RTTI to make sure we can reliably cast to it. Will be merged with its dependent patch that lets the ClangASTImporter handle redeclaration chains. (cherry picked from commit f400b2fb1486e28d22c248a33d30bd251082d4e4) --- .../Plugins/ExpressionParser/Clang/ASTUtils.h | 5 +- .../ExpressionParser/Clang/ClangASTSource.h | 3 +- .../Clang/ClangExternalASTSourceCallbacks.h | 7 ++- .../AppleObjCRuntime/AppleObjCDeclVendor.cpp | 3 +- .../Plugins/TypeSystem/Clang/CMakeLists.txt | 1 + .../Clang/ImporterBackedASTSource.cpp | 13 ++++++ .../Clang/ImporterBackedASTSource.h | 46 +++++++++++++++++++ 7 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 lldb/source/Plugins/TypeSystem/Clang/ImporterBackedASTSource.cpp create mode 100644 lldb/source/Plugins/TypeSystem/Clang/ImporterBackedASTSource.h diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h b/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h index 95e8a600f8382..507547558c9eb 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. diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h index f3fec3f944a14..a6a1d5929fcd4 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 diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExternalASTSourceCallbacks.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangExternalASTSourceCallbacks.h index 219ed641615eb..76259cb22e28c 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) {} diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp index 2764a2aa39fa1..575740d9af066 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,7 +25,7 @@ using namespace lldb_private; class lldb_private::AppleObjCExternalASTSource - : public clang::ExternalASTSource { + : public ImporterBackedASTSource { public: AppleObjCExternalASTSource(AppleObjCDeclVendor &decl_vendor) : m_decl_vendor(decl_vendor) {} diff --git a/lldb/source/Plugins/TypeSystem/Clang/CMakeLists.txt b/lldb/source/Plugins/TypeSystem/Clang/CMakeLists.txt index 37a3142da7274..4b33789479478 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/CMakeLists.txt +++ b/lldb/source/Plugins/TypeSystem/Clang/CMakeLists.txt @@ -1,4 +1,5 @@ add_lldb_library(lldbPluginTypeSystemClang PLUGIN + ImporterBackedASTSource.cpp TypeSystemClang.cpp LINK_LIBS diff --git a/lldb/source/Plugins/TypeSystem/Clang/ImporterBackedASTSource.cpp b/lldb/source/Plugins/TypeSystem/Clang/ImporterBackedASTSource.cpp new file mode 100644 index 0000000000000..f9445b4548cc7 --- /dev/null +++ b/lldb/source/Plugins/TypeSystem/Clang/ImporterBackedASTSource.cpp @@ -0,0 +1,13 @@ +//===-- ImporterBackedASTSource.cpp ---------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Plugins/TypeSystem/Clang/ImporterBackedASTSource.h" + +using namespace lldb_private; + +char ImporterBackedASTSource::ID; diff --git a/lldb/source/Plugins/TypeSystem/Clang/ImporterBackedASTSource.h b/lldb/source/Plugins/TypeSystem/Clang/ImporterBackedASTSource.h new file mode 100644 index 0000000000000..00d1a578a0596 --- /dev/null +++ b/lldb/source/Plugins/TypeSystem/Clang/ImporterBackedASTSource.h @@ -0,0 +1,46 @@ +//===-- ImporterBackedASTSource.h -------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_TYPESYSTEM_CLANG_IMPORTERBACKEDASTSOURCE +#define LLDB_SOURCE_PLUGINS_TYPESYSTEM_CLANG_IMPORTERBACKEDASTSOURCE + +#include "clang/AST/ASTContext.h" +#include "clang/Sema/ExternalSemaSource.h" + +namespace lldb_private { + +/// The base class of all ExternalASTSources in LLDB that use the +/// ClangASTImporter to move declarations from other ASTs to the ASTContext they +/// are attached to. +class ImporterBackedASTSource : public clang::ExternalSemaSource { + /// LLVM RTTI support. + static char ID; + +public: + /// LLVM RTTI support. + bool isA(const void *ClassID) const override { + return ClassID == &ID || ExternalSemaSource::isA(ClassID); + } + static bool classof(const clang::ExternalASTSource *s) { return s->isA(&ID); } + + /// This marks all redeclaration chains in the ASTContext as out-of-date and + /// that this ExternalASTSource should be consulted to get the complete + /// redeclaration chain. + /// + /// \see ExternalASTSource::CompleteRedeclChain + void MarkRedeclChainsAsOutOfDate(clang::ASTContext &c) { + // This invalidates redeclaration chains but also other things such as + // identifiers. There isn't a more precise way at the moment that only + // affects redecl chains. + incrementGeneration(c); + } +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_TYPESYSTEM_CLANG_IMPORTERBACKEDASTSOURCE From 6b186f9c549490bfcf3f229f2c6f3e428140cbbb Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Tue, 13 Feb 2024 14:43:44 +0000 Subject: [PATCH 04/20] [lldb][TypeSystemClang][NFC] Make TemplateParamterInfos::pack_name a std::optional (cherry picked from commit 36b6f1320f2b974238dba20d62901fab48459ceb) --- lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 5c691806767c9..a69e9bc288829 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -347,7 +347,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 +388,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 +407,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; }; From fa12f6441dd1748ec2ece444614b58e630c04975 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Tue, 13 Feb 2024 16:18:32 +0000 Subject: [PATCH 05/20] [lldb][TypeSystemClang][NFC] Make TemplateParameterInfos copyable (cherry picked from commit 0e4d68a4752ebe30c4a1a531cecf8ed1c518903e) --- .../TypeSystem/Clang/TypeSystemClang.h | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index a69e9bc288829..af73d1c784090 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -336,10 +336,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; From c79c9cc975dc96cb0a81dea312237d73d643b720 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Wed, 14 Feb 2024 18:47:28 +0000 Subject: [PATCH 06/20] [lldb][ClangASTImporter][NFCI] Implement CompleteTagDeclWithOrigin in terms of CompleteTagDecl (cherry picked from commit ac8b1f4abbcfd812a078f4d9481360161a80e136) --- .../ExpressionParser/Clang/ClangASTImporter.cpp | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp index 72e4e9f4b88e7..5c2908525b744 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp @@ -573,21 +573,8 @@ bool ClangASTImporter::CompleteTagDecl(clang::TagDecl *decl) { 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)) - 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); + return CompleteTagDecl(decl); } bool ClangASTImporter::CompleteObjCInterfaceDecl( From f6adaee39224bae8d3237a0924ade6ad42fed68c Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Tue, 13 Feb 2024 16:38:38 +0000 Subject: [PATCH 07/20] [lldb] Make TypeSystemClang work with redeclarations This patch is a part of D101950 which makes LLDB use redeclarations in its internal clang ASTs. This patch does the following: Add an API that allows redeclaring a declaration inside a TypeSystemClang. Make the TypeSystemClang APIs work with redeclarations. Mainly making sure we look at redeclarations of DeclContext (see the added calls to getPrimaryContext which for example finds the definition of a class. See also the test for nested types) Removes code that assumes a 1-1 mapping between Type and Decl (which is no longer true for redeclarations). The biggest offender here are the APIs for creating classes/templates/etc. as they return all CompilerType (which doesn't identify a unique Decl, but only a redeclaration chain). Removed code that tries to eagerly create types for declarations. Types should be created via GetTypeForDecl so that existing types from forward declarations are reused. Otherwise we end up with The patch also makes that we store the TemplateParameterInfos for template specializations in the TypeSystemClang. The only reason for this is that Clang is using a different data structure for storing this info internally so we can't just reuse the information from the forward declaration when creating the redeclaration (see CreateRedeclaration). This can be removed once we updated the DWARF parser and the TypeSystemClang APIs to accept the information as we can find it in the Clang AST. This shouldn't affect LLDB's functionality on its own as the DWARF parser currently doesn't create redeclarations. That's why all the tests are unit tests. The added logic for looking for the right redeclaration should work as before in ASTs that only have single-decl redeclaration chains (which is what we currently do). (cherry picked from commit cac0d81ae617a2a73b14bfe3509e94d619246500) --- .../Clang/ClangASTImporter.cpp | 10 +- .../ExpressionParser/Clang/ClangASTImporter.h | 2 + .../ExpressionParser/Clang/ClangASTSource.cpp | 5 +- .../ExpressionParser/Clang/ClangUtil.cpp | 11 + .../ExpressionParser/Clang/ClangUtil.h | 9 + .../TypeSystem/Clang/TypeSystemClang.cpp | 183 ++++++++-- .../TypeSystem/Clang/TypeSystemClang.h | 55 ++- lldb/unittests/CMakeLists.txt | 1 + lldb/unittests/TypeSystem/CMakeLists.txt | 1 + .../unittests/TypeSystem/Clang/CMakeLists.txt | 14 + .../Clang/TestClangRedeclarations.cpp | 332 ++++++++++++++++++ 11 files changed, 597 insertions(+), 26 deletions(-) create mode 100644 lldb/unittests/TypeSystem/CMakeLists.txt create mode 100644 lldb/unittests/TypeSystem/Clang/CMakeLists.txt create mode 100644 lldb/unittests/TypeSystem/Clang/TestClangRedeclarations.cpp diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp index 5c2908525b744..f54ef02526353 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp @@ -286,6 +286,7 @@ class CompleteTagDeclsScope : public ClangASTImporter::NewDeclListener { // Filter out decls that we can't complete later. if (!isa(to) && !isa(to)) return; + 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()) @@ -525,6 +526,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; @@ -548,6 +552,7 @@ bool ClangASTImporter::LayoutRecordType( void ClangASTImporter::SetRecordLayout(clang::RecordDecl *decl, const LayoutInfo &layout) { + decl = llvm::cast(decl->getFirstDecl()); m_record_decl_to_layout_map.insert(std::make_pair(decl, layout)); } @@ -573,7 +578,10 @@ bool ClangASTImporter::CompleteTagDecl(clang::TagDecl *decl) { bool ClangASTImporter::CompleteTagDeclWithOrigin(clang::TagDecl *decl, clang::TagDecl *origin_decl) { - SetDeclOrigin(decl, origin_decl); + if (!origin_decl->getDefinition()) + return false; + + SetDeclOrigin(decl, origin_decl->getFirstDecl()); return CompleteTagDecl(decl); } diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h index e565a96b217ff..92e12478c8b10 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h @@ -149,6 +149,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); diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp index 00ab6a04bd323..4e17200541c1c 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp @@ -282,8 +282,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)); diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.cpp index 2e0bb318cb507..025b19be1eaf0 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,16 @@ 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; +} + 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..a8db09277e434 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.h @@ -36,6 +36,15 @@ 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)); + } + /// 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/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index b4d76c669a50b..a0fb29a17cd0c 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" @@ -1217,11 +1218,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 +1280,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 +1298,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 +1360,7 @@ CompilerType TypeSystemClang::CreateRecordType( if (decl_ctx) decl_ctx->addDecl(decl); - return GetType(ast.getTagDeclType(decl)); + return decl; } namespace { @@ -1602,6 +1622,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); @@ -1694,6 +1715,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 +1736,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 +1744,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 +1876,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 +1895,7 @@ CompilerType TypeSystemClang::CreateObjCClass( if (metadata) SetMetadata(decl, *metadata); - return GetType(ast.getObjCInterfaceType(decl)); + return decl; } bool TypeSystemClang::BaseSpecifierIsEmpty(const CXXBaseSpecifier *b) { @@ -2347,7 +2376,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 +2402,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, @@ -2480,7 +2509,9 @@ bool TypeSystemClang::GetCompleteDecl(clang::ASTContext *ast, 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()) @@ -2491,7 +2522,10 @@ bool TypeSystemClang::GetCompleteDecl(clang::ASTContext *ast, 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 +2545,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 +2564,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; @@ -4581,6 +4623,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); @@ -5488,6 +5531,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 +7607,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; @@ -8035,9 +8083,13 @@ clang::ObjCMethodDecl *TypeSystemClang::AddMethodToObjCObjectType( return nullptr; clang::ObjCInterfaceDecl *class_interface_decl = GetAsObjCInterfaceDecl(type); + if (class_interface_decl) + if (auto * def = class_interface_decl->getDefinition()) + class_interface_decl = def; if (class_interface_decl == nullptr) return nullptr; + auto ts = type.GetTypeSystem(); auto lldb_ast = ts.dyn_cast_or_null(); if (lldb_ast == nullptr) @@ -8107,8 +8159,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 +8269,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 +8283,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; } } @@ -8844,7 +8903,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 +9184,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 +9359,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()); +} + +void 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 = GetAsObjCInterfaceDecl(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; + } + + 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; + } + + 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; + } + + 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; +} + DWARFASTParser *TypeSystemClang::GetDWARFParser() { if (!m_dwarf_ast_parser_up) m_dwarf_ast_parser_up = std::make_unique(*this); diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index af73d1c784090..22c5d1baeae83 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -247,7 +247,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 +327,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; @@ -470,7 +478,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 @@ -512,12 +529,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 @@ -540,6 +568,15 @@ class TypeSystemClang : public TypeSystem { void CompleteObjCInterfaceDecl(clang::ObjCInterfaceDecl *); + /// Creates a redeclaration for the declaration specified by the given type. + /// The redeclaration will be at the end of the redeclaration chain. The + /// passed declaration has to be created via a TypeSystemClang interface. + /// + /// \param type The type which declaration should be redeclared. Has to be + /// an Objective-C interface type (or Objective-C type), RecordType or + /// EnumType. + void CreateRedeclaration(CompilerType ct); + bool LayoutRecordType( const clang::RecordDecl *record_decl, uint64_t &size, uint64_t &alignment, llvm::DenseMap &field_offsets, @@ -1225,6 +1262,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/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/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); +} From 6b4c274fb23b414e72b4f1b5fec0a5e446cc32ae Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Wed, 31 Jan 2024 11:28:35 +0000 Subject: [PATCH 08/20] [lldb] Add typesystem.experimental.redecl-completion setting (cherry picked from commit 980b4a2c2498fdde9e6751c5106b50085174a59a) --- lldb/include/lldb/Core/PluginManager.h | 8 +++ lldb/source/Core/PluginManager.cpp | 28 ++++++++-- .../Plugins/TypeSystem/Clang/CMakeLists.txt | 12 +++++ .../TypeSystem/Clang/TypeSystemClang.cpp | 54 ++++++++++++++++++- .../TypeSystem/Clang/TypeSystemClang.h | 10 ++++ .../Clang/TypeSystemClangProperties.td | 8 +++ .../TypeSystem/Swift/TypeSystemSwift.cpp | 6 +-- 7 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 lldb/source/Plugins/TypeSystem/Clang/TypeSystemClangProperties.td 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/TypeSystem/Clang/CMakeLists.txt b/lldb/source/Plugins/TypeSystem/Clang/CMakeLists.txt index 4b33789479478..2df72f22e6a08 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/CMakeLists.txt +++ b/lldb/source/Plugins/TypeSystem/Clang/CMakeLists.txt @@ -19,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/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index a0fb29a17cd0c..07c9faed78bba 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -93,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?"); @@ -649,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() { @@ -9459,6 +9507,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 22c5d1baeae83..019a71f7d04f3 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(); @@ -563,6 +565,14 @@ 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 *); 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() { From 3a8d247d1e324b8e65a2b70267a140ff324c3132 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Wed, 14 Feb 2024 13:55:53 +0000 Subject: [PATCH 09/20] [clang][modules] Always let Obj-C decls query the ExternalASTSource for complete redecl chains ObjCInterfaceDecls and ObjCProtocolDecls currently have a flag that indicates whether they need to query the ExternalASTSource to complete their redeclaration chain. This flag is currently only set when LangOpts.Modules == true. The idea behind that seems to be that only with LangOpts.Modules == true we have a chance to find a definition for an existing declaration via the ExternalASTSource, so we optimize the non-modules case by avoiding the work of looking for an external definition. The idea that LangOpts.Modules implies that the ExternalASTSource can provide a definition doesn't work within LLDB where we are not always setting the Modules flag but we always have an ExternalASTSource that can complete the redeclaration chain (and provide a definition). This patch sets the flag whenever we have any valid ExternalASTSource so that the ExternalASTSource in LLDB is also queried for definitions. In Clang this patch maintains the current behaviour for parsing with enabled and disabled modules. The only behaviour change in Clang is that we now also query the ExternalASTSource for Obj-C interface/protocol definitions when using (chained) PCHs. (cherry picked from commit 4a0bd20d84d2ae4b4feb6cddfa740e7e8ba674fe) --- clang/include/clang/AST/DeclObjC.h | 10 ++++++---- clang/lib/AST/DeclObjC.cpp | 8 ++++---- 2 files changed, 10 insertions(+), 8 deletions(-) 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/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; } From 3ddb1d64fadea431e6f43d16734b6577d020df23 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Wed, 14 Feb 2024 16:26:34 +0000 Subject: [PATCH 10/20] [lldb][ASTUtils] Short-circuit multiplexed CompleteRedeclChain when we find definition (cherry picked from commit 084af9ab5baa64549054e93cc23cfa4925df6da1) --- lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h b/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h index 507547558c9eb..805daec635e2a 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h @@ -274,9 +274,16 @@ class SemaSourceWithPriorities : public ImporterBackedASTSource { 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 { From a4ee6a939c25e5db6037609096b35a8267322e62 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Wed, 14 Feb 2024 16:01:57 +0000 Subject: [PATCH 11/20] [lldb][ClangASTImporter] Assert that we never overwrite a record layout (cherry picked from commit 5111f5ead68f6751b7008c9f0bdee12b88082bc8) --- .../ExpressionParser/Clang/ClangASTImporter.cpp | 9 ++++++++- .../Plugins/ExpressionParser/Clang/ClangASTImporter.h | 2 ++ .../Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp | 10 ++++------ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp index f54ef02526353..29ae30e4a17fd 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp @@ -540,7 +540,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; @@ -553,9 +552,17 @@ 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); diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h index 92e12478c8b10..49f9235721b56 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, diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 7f75f179e211d..77d0b50856d77 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -2200,18 +2200,16 @@ bool DWARFASTParserClang::CompleteRecordType(const DWARFDIE &die, 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 From 1400e44c35870b1c8d6a78be37a69c7052fbd0b7 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Wed, 14 Feb 2024 16:37:55 +0000 Subject: [PATCH 12/20] [lldb][ClangASTImporter][NFC] Factor completion logic out of ~CompleteTagDeclsScope (cherry picked from commit 694a5be3cc63f7055682c604a077806bb1f09690) --- .../Clang/ClangASTImporter.cpp | 54 ++++++++++--------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp index 29ae30e4a17fd..30736ee9c855f 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp @@ -227,6 +227,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 +278,7 @@ 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); - } + CompleteDecl(decl, *to_context_md); to_context_md->removeOrigin(decl); } From 4e1b12896d1909ac626d37c8b59390f9b50de93e Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Wed, 14 Feb 2024 17:03:42 +0000 Subject: [PATCH 13/20] [lldb][ClangASTImporter][NFCI] Factor setting external storage out of ASTImporterDelegate::Imported (cherry picked from commit 8836040da9d6f03de7de5a7890d80ab6265cd2ac) --- .../ExpressionParser/Clang/ClangASTImporter.cpp | 17 ++++++++++++----- .../ExpressionParser/Clang/ClangASTImporter.h | 2 ++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp index 30736ee9c855f..382ca029380ca 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp @@ -1130,6 +1130,18 @@ 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(); + } + + 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(); @@ -1144,11 +1156,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(); diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h index 49f9235721b56..5f54971107f3e 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h @@ -328,6 +328,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 From bc1a466a82516e84a98c52813de2afb13d3248dc Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Thu, 15 Feb 2024 13:47:19 +0000 Subject: [PATCH 14/20] [lldb][ClangASTSource] Use getDefinition() where appropriate These code-paths want to use the definition rather than a possible non-defining redeclaration. It just happened to work because the declarations were also the definitions. This will be necessary once we start making use of the `CompleteRedeclChain` APIs in upcoming patches. (cherry picked from commit 6b79056144f9a30f17ccca60d4450874ab9852cc) --- .../Plugins/ExpressionParser/Clang/ClangASTSource.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp index 4e17200541c1c..79588b9bf0819 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp @@ -364,8 +364,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( @@ -1513,8 +1512,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. From 27246c758d94d7ea1e2bc22f16215c2e9d3989ff Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Thu, 15 Feb 2024 14:51:27 +0000 Subject: [PATCH 15/20] [lldb][ExpressionParser][NFC] Implement CompleteRedeclChain APIs (cherry picked from commit 5a2f2f397796affd598011772ec1880ff8841093) --- .../ExpressionParser/Clang/ClangASTSource.cpp | 40 +++++++++++++++++++ .../ExpressionParser/Clang/ClangASTSource.h | 6 +++ .../Clang/ClangExternalASTSourceCallbacks.cpp | 19 +++++++++ .../Clang/ClangExternalASTSourceCallbacks.h | 2 + .../AppleObjCRuntime/AppleObjCDeclVendor.cpp | 9 +++++ 5 files changed, 76 insertions(+) diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp index 79588b9bf0819..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" @@ -327,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()); diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h index a6a1d5929fcd4..a769acdce55ab 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h @@ -154,6 +154,8 @@ class ClangASTSource : public ImporterBackedASTSource, /// 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. @@ -232,6 +234,10 @@ class ClangASTSource : public ImporterBackedASTSource, 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 76259cb22e28c..c2ac0e2cd441d 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExternalASTSourceCallbacks.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExternalASTSourceCallbacks.h @@ -41,6 +41,8 @@ class ClangExternalASTSourceCallbacks : public ImporterBackedASTSource { 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/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp index 575740d9af066..2e53f2f22c1f3 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp @@ -109,6 +109,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, From 0c17fe081fff66a9870cd9d222fc34d9ee1a95db Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Fri, 16 Feb 2024 12:20:23 +0000 Subject: [PATCH 16/20] [lldb][ClangASTImporter][NFC] Factor out CanImport logic (cherry picked from commit cb255e8501b45ad6ddd6c72c09228aa3e4d8aea6) --- .../Clang/ClangASTImporter.cpp | 37 ++++++++----------- .../ExpressionParser/Clang/ClangASTImporter.h | 2 + 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp index 382ca029380ca..baa81d19d4d2d 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp @@ -363,6 +363,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; @@ -372,24 +382,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 = @@ -399,10 +395,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; diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h index 5f54971107f3e..a01965df8582e 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h @@ -137,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). /// From 0335368daadae5f99a3dd58c72e628a0e0ac8943 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Fri, 16 Feb 2024 14:35:34 +0000 Subject: [PATCH 17/20] [lldb][TypeSystemClang][NFCI] Factor completion logic out of GetCompleteQualType (cherry picked from commit a1202f476ab7327e522d06d55737eb5f6a690811) --- .../TypeSystem/Clang/TypeSystemClang.cpp | 188 +++++++++++------- 1 file changed, 111 insertions(+), 77 deletions(-) diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 07c9faed78bba..5795976fa03ca 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -2705,6 +2705,105 @@ 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 (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 (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 (class_interface_decl->getDefinition()) + return 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) { @@ -2722,92 +2821,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 !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 !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 (!allow_completion) - return false; + if (auto const *ty = llvm::dyn_cast_or_null( + GetCompleteObjCInterfaceType(ast, qual_type, allow_completion))) + return !ty->isIncompleteType(); - 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: From 313a1af4c224632c1ef207ea4519a44f2706f0ed Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Mon, 19 Feb 2024 12:21:04 +0000 Subject: [PATCH 18/20] [lldb][ClangASTImporter][NFC] Move ASTImproterDelegate constructor definition into source file (cherry picked from commit a83739912e5490e48745d0505e29556649eb6b9f) --- .../Clang/ClangASTImporter.cpp | 19 +++++++++++++++++++ .../ExpressionParser/Clang/ClangASTImporter.h | 17 +---------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp index baa81d19d4d2d..74b328c84ebdf 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp @@ -1181,3 +1181,22 @@ clang::Decl * ClangASTImporter::ASTImporterDelegate::GetOriginalDecl(clang::Decl *To) { return m_main.GetDeclOrigin(To).decl; } + +ClangASTImporter::ASTImporterDelegate::ASTImporterDelegate( + ClangASTImporter &main, clang::ASTContext *target_ctx, + clang::ASTContext *source_ctx) + : clang::ASTImporter(*target_ctx, main.m_file_manager, *source_ctx, + main.m_file_manager, true /*minimal*/), + m_main(main), m_source_ctx(source_ctx) { + // Target and source ASTContext shouldn't be identical. Importing AST + // nodes within the same AST doesn't make any sense as the whole idea + // is to import them to a different AST. + lldbassert(target_ctx != source_ctx && "Can't import into itself"); + // This is always doing a minimal import of any declarations. This means + // that there has to be an ExternalASTSource in the target ASTContext + // (that should implement the callbacks that complete any declarations + // on demand). Without an ExternalASTSource, this ASTImporter will just + // do a minimal import and the imported declarations won't be completed. + assert(target_ctx->getExternalSource() && "Missing ExternalSource"); + setODRHandling(clang::ASTImporter::ODRHandlingType::Liberal); +} diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h index a01965df8582e..3478ffcc0339b 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h @@ -266,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 From 403287590c753582ccef4e154deee8c1ad7d773d Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Mon, 19 Feb 2024 12:16:20 +0000 Subject: [PATCH 19/20] [clang][ASTImporter] Add support for LLDB's new type lookup mechanism --- clang/include/clang/AST/ASTImporter.h | 3 ++ clang/lib/AST/ASTImporter.cpp | 50 +++++++++++++++++++-------- 2 files changed, 38 insertions(+), 15 deletions(-) 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/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; From 66e12b4b319a51386040be29ed49e344f35dd17e Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Thu, 15 Feb 2024 17:20:36 +0000 Subject: [PATCH 20/20] [lldb][WIP] Use forward decls with redeclared definitions instead of minimal import for records This patch is rewriting the way we move Clang types between different ASTS in Clang. The motivation is that the current approach for 'completing types' in LLDB just doesn't work. We have all kinds of situations where we either have Clang crash due to for example having a Record without DefinitionData but with FieldDecls in it. The reason for that is that at the moment we create record types ([CXX]RecordDecls and ObjCInterfaceDecls) and we always pretend that a type potentially has a definition that somehow could be lazily pulled in. However, Clang doesn't have any interface (at least none that is consistently called) that turns a RecordDecl without DefinitionData into a RecordDecl with DefinitionData. The only relevant API in the ExternalASTSource is CompleteType is suffering from the fact that it's essentially not used by generic Clang code. The part of the ExternalASTSource API that is consistently called to pull in a potential definition is CompleteRedeclChain (which is for example automatically called when calling getDefinition on a RecordDecl). The problem with CompleteRedeclChain however is that it's not supposed to add definition data to the Decl its called on, but instead it should provide a redeclaration that is defining the record. That's a very different system than what we currently have and we can't just swap it out under the hood. To make it short: We probably need to rewrite that part of LLDB. So the solution here is essentially rewriting all our completion code to do this: * Instead of creating these weirdly defined records that have fields but maybe not definition and so on, we only create forward decls at the start. * We then bump the GenerationCounter of the ExternalASTSource of the current AST to force rebuilding of the redeclaration chain. * When Clang asks for the definition of the record, it will ask the ExternalASTSource to complete the redeclaration chain. At this point we build the definition and add it as a second decl. The ASTImporter can now also stop using the minimal mode for records as there is no reason anymore. It just imports first the forward declaration and then the definition when needed. (cherry picked from commit b4cfd2d) --- .../Clang/ClangASTImporter.cpp | 132 ++++++++++-- .../ExpressionParser/Clang/ClangUtil.cpp | 12 ++ .../ExpressionParser/Clang/ClangUtil.h | 2 + .../AppleObjCRuntime/AppleObjCDeclVendor.cpp | 38 +++- .../AppleObjCRuntime/AppleObjCDeclVendor.h | 4 + .../SymbolFile/DWARF/DWARFASTParserClang.cpp | 188 +++++++++++++----- .../SymbolFile/DWARF/DWARFASTParserClang.h | 21 ++ .../TypeSystem/Clang/TypeSystemClang.cpp | 65 ++++-- .../TypeSystem/Clang/TypeSystemClang.h | 2 +- .../limit-debug-info/TestLimitDebugInfo.py | 3 + .../rerun_and_expr/TestRerunAndExpr.py | 1 + lldb/test/API/lang/c/modules/TestCModules.py | 1 + .../API/lang/c/shared_lib/TestSharedLib.py | 1 + .../TestSharedLibStrippedSymbols.py | 1 + .../accelerator-table/TestCPPAccelerator.py | 1 + .../TestCppReferenceToOuterClass.py | 2 +- 16 files changed, 385 insertions(+), 89 deletions(-) diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp index 74b328c84ebdf..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" @@ -278,7 +281,8 @@ class CompleteTagDeclsScope : public ClangASTImporter::NewDeclListener { NamedDecl *decl = m_decls_to_complete.pop_back_val(); m_decls_already_completed.insert(decl); - CompleteDecl(decl, *to_context_md); + if (!TypeSystemClang::UseRedeclCompletion()) + CompleteDecl(decl, *to_context_md); to_context_md->removeOrigin(decl); } @@ -293,6 +297,9 @@ class CompleteTagDeclsScope : public ClangASTImporter::NewDeclListener { 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()) @@ -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; } @@ -568,16 +582,42 @@ bool ClangASTImporter::CompleteTagDecl(clang::TagDecl *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; } @@ -598,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)); + 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; + + 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); @@ -862,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); @@ -883,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; } @@ -1128,7 +1216,16 @@ void ClangASTImporter::ASTImporterDelegate::Imported(clang::Decl *from, to_namespace_decl->setHasExternalVisibleStorage(); } - MarkDeclImported(from, to); + 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, @@ -1199,4 +1296,5 @@ ClangASTImporter::ASTImporterDelegate::ASTImporterDelegate( // 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/ClangUtil.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.cpp index 025b19be1eaf0..a604779fa8e17 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.cpp @@ -76,6 +76,18 @@ clang::Decl *ClangUtil::GetFirstDecl(clang::Decl *d) { 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 a8db09277e434..199c6639b68fb 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.h @@ -45,6 +45,8 @@ struct ClangUtil { 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 2e53f2f22c1f3..c86d41c9cfe8e 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp @@ -30,6 +30,7 @@ class lldb_private::AppleObjCExternalASTSource 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 { @@ -179,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); @@ -408,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) @@ -416,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 77d0b50856d77..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 @@ -1089,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 @@ -1576,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, @@ -1587,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 @@ -1757,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()); @@ -1806,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) { @@ -1814,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, @@ -1838,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)) { @@ -1870,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 @@ -1900,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; } @@ -2129,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); @@ -2196,6 +2279,9 @@ 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); @@ -2246,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; @@ -3698,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/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 5795976fa03ca..bec02459796ad 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -1723,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( @@ -2552,7 +2555,7 @@ 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); @@ -2565,7 +2568,8 @@ bool TypeSystemClang::GetCompleteDecl(clang::ASTContext *ast, 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); @@ -2712,6 +2716,13 @@ static clang::Type const *GetCompleteRecordType(clang::ASTContext *ast, 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 = @@ -2751,6 +2762,13 @@ static clang::Type const *GetCompleteEnumType(clang::ASTContext *ast, 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; @@ -2785,8 +2803,9 @@ GetCompleteObjCInterfaceType(clang::ASTContext *ast, clang::QualType qual_type, if (!class_interface_decl) return objc_class_type; - if (class_interface_decl->getDefinition()) - return objc_class_type; + if (auto *def = class_interface_decl->getDefinition()) + return TypeSystemClang::UseRedeclCompletion() ? def->getTypeForDecl() + : objc_class_type; if (!allow_completion) return nullptr; @@ -2823,7 +2842,7 @@ static bool GetCompleteQualType(clang::ASTContext *ast, case clang::Type::Record: { if (auto const *ty = llvm::dyn_cast_or_null( GetCompleteRecordType(ast, qual_type, allow_completion))) - return !ty->isIncompleteType(); + return TypeSystemClang::UseRedeclCompletion() || !ty->isIncompleteType(); return false; } break; @@ -2831,7 +2850,7 @@ static bool GetCompleteQualType(clang::ASTContext *ast, case clang::Type::Enum: { if (auto const *ty = llvm::dyn_cast_or_null( GetCompleteEnumType(ast, qual_type, allow_completion))) - return !ty->isIncompleteType(); + return TypeSystemClang::UseRedeclCompletion() || !ty->isIncompleteType(); return false; } break; @@ -2839,7 +2858,7 @@ static bool GetCompleteQualType(clang::ASTContext *ast, case clang::Type::ObjCInterface: { if (auto const *ty = llvm::dyn_cast_or_null( GetCompleteObjCInterfaceType(ast, qual_type, allow_completion))) - return !ty->isIncompleteType(); + return TypeSystemClang::UseRedeclCompletion() || !ty->isIncompleteType(); return false; } break; @@ -3529,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 = @@ -5448,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(); @@ -8415,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; @@ -9465,25 +9494,25 @@ static OptionalClangModuleID GetModuleForDecl(clang::Decl *d) { return OptionalClangModuleID(d->getOwningModuleID()); } -void TypeSystemClang::CreateRedeclaration(CompilerType ct) { +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 = GetAsObjCInterfaceDecl(ct)) { + 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; + return GetTypeForDecl(redecl); } clang::TagDecl *tag_decl = ClangUtil::GetAsTagDecl(ct); if (!tag_decl) - return; + return {}; if (clang::EnumDecl *enum_decl = dyn_cast(tag_decl)) { Declaration decl; @@ -9493,7 +9522,7 @@ void TypeSystemClang::CreateRedeclaration(CompilerType ct) { GetModuleForDecl(enum_decl), decl, GetType(enum_decl->getIntegerType()), enum_decl->isScoped()); ConnectRedeclToPrev(*this, enum_decl, redecl); - return; + return GetTypeForDecl(redecl); } if (auto *template_decl = @@ -9509,7 +9538,7 @@ void TypeSystemClang::CreateRedeclaration(CompilerType ct) { template_decl->getSpecializedTemplate(), llvm::to_underlying(tag_decl->getTagKind()), template_infos); ConnectRedeclToPrev(*this, template_decl, redecl); - return; + return GetType(clang::QualType(redecl->getTypeForDecl(), 0U)); } assert(llvm::isa(tag_decl)); @@ -9520,7 +9549,7 @@ void TypeSystemClang::CreateRedeclaration(CompilerType ct) { nullptr); clang::TagDecl *redecl = llvm::cast(redecl_record); ConnectRedeclToPrev(*this, tag_decl, redecl); - return; + return GetTypeForDecl(redecl); } DWARFASTParser *TypeSystemClang::GetDWARFParser() { diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 019a71f7d04f3..255b12eec23dd 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -585,7 +585,7 @@ class TypeSystemClang : public TypeSystem { /// \param type The type which declaration should be redeclared. Has to be /// an Objective-C interface type (or Objective-C type), RecordType or /// EnumType. - void CreateRedeclaration(CompilerType ct); + CompilerType CreateRedeclaration(CompilerType ct); bool LayoutRecordType( const clang::RecordDecl *record_decl, uint64_t &size, uint64_t &alignment, 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"))