[Expression parser] Setting to enable use of ExternalASTMerger
This setting can be enabled like this at the target level:
(lldb) settings set target.experimental.use-modern-type-lookup true
This causes several new behaviors in the Clang expression parser:
- It completely disables use of ClangASTImporter. None are created
at all, and all users of it are now conditionalized on its
presence.
- It instead constructs a per-expression ExternalASTMerger, which
exists inside Clang and contains much of the type completion
logic that hitherto lived in ExternalASTSource,
ClangExpressionDeclMap, and ClangASTImporter.
- The expression parser uses this Merger as a backend for copying
and completing types.
- It also constructs a persistent ExternalASTMerger which is
connected to the Target's persistent AST context.
This is a major chunk of LLDB functionality moved into Clang. It
can be tested in two ways:
1. For an individual debug session, enable the setting before
running a target.
2. For the testsuite, change the option to be default-true. This
is done in Target.cpp's g_experimental_properties. The
testsuite is not yet clean with this, so I have not committed
that switch.
I have filed a Bugzilla for extending the testsuite to allow
custom settings for all tests:
https://bugs.llvm.org/show_bug.cgi?id=34771
I have also filed a Bugzilla for fixing the remaining testsuite
failures with this setting enabled:
https://bugs.llvm.org/show_bug.cgi?id=34772
llvm-svn: 314458
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
index eefe130..d102fb4 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
@@ -48,8 +48,10 @@
#include "lldb/lldb-private.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTImporter.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclarationName.h"
+#include "clang/AST/RecursiveASTVisitor.h"
#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
@@ -178,6 +180,134 @@
return ret;
}
+namespace {
+/// This class walks an AST and ensures that all DeclContexts defined inside the
+/// current source file are properly complete.
+///
+/// This is used to ensure that persistent types defined in the current source
+/// file migrate completely to the persistent AST context before they are
+/// reused. If that didn't happen, it would be impoossible to complete them
+/// because their origin would be gone.
+///
+/// The stragtegy used by this class is to check the SourceLocation (to be
+/// specific, the FileID) and see if it's the FileID for the current expression.
+/// Alternate strategies could include checking whether an ExternalASTMerger,
+/// set up to not have the current context as a source, can find an original for
+/// the type.
+class Completer : public clang::RecursiveASTVisitor<Completer> {
+private:
+ clang::ASTImporter &m_exporter; /// Used to import Decl contents
+ clang::FileID m_file; /// The file that's going away
+ llvm::DenseSet<clang::Decl *> m_completed; /// Visited Decls, to avoid cycles
+
+ bool ImportAndCheckCompletable(clang::Decl *decl) {
+ (void)m_exporter.Import(decl);
+ if (m_completed.count(decl))
+ return false;
+ if (!llvm::isa<DeclContext>(decl))
+ return false;
+ const clang::SourceLocation loc = decl->getLocation();
+ if (!loc.isValid())
+ return false;
+ const clang::FileID file =
+ m_exporter.getFromContext().getSourceManager().getFileID(loc);
+ if (file != m_file)
+ return false;
+ // We are assuming the Decl was parsed in this very expression, so it should
+ // not have external storage.
+ lldbassert(!llvm::cast<DeclContext>(decl)->hasExternalLexicalStorage());
+ return true;
+ }
+
+ void Complete(clang::Decl *decl) {
+ m_completed.insert(decl);
+ auto *decl_context = llvm::cast<DeclContext>(decl);
+ (void)m_exporter.Import(decl);
+ m_exporter.CompleteDecl(decl);
+ for (Decl *child : decl_context->decls())
+ if (ImportAndCheckCompletable(child))
+ Complete(child);
+ }
+
+ void MaybeComplete(clang::Decl *decl) {
+ if (ImportAndCheckCompletable(decl))
+ Complete(decl);
+ }
+
+public:
+ Completer(clang::ASTImporter &exporter, clang::FileID file)
+ : m_exporter(exporter), m_file(file) {}
+
+ // Implements the RecursiveASTVisitor's core API. It is called on each Decl
+ // that the RecursiveASTVisitor encounters, and returns true if the traversal
+ // should continue.
+ bool VisitDecl(clang::Decl *decl) {
+ MaybeComplete(decl);
+ return true;
+ }
+};
+}
+
+static void CompleteAllDeclContexts(clang::ASTImporter &exporter,
+ clang::FileID file,
+ clang::QualType root) {
+ clang::QualType canonical_type = root.getCanonicalType();
+ if (clang::TagDecl *tag_decl = canonical_type->getAsTagDecl()) {
+ Completer(exporter, file).TraverseDecl(tag_decl);
+ } else if (auto interface_type = llvm::dyn_cast<ObjCInterfaceType>(
+ canonical_type.getTypePtr())) {
+ Completer(exporter, file).TraverseDecl(interface_type->getDecl());
+ } else {
+ Completer(exporter, file).TraverseType(canonical_type);
+ }
+}
+
+static clang::QualType ExportAllDeclaredTypes(
+ clang::ExternalASTMerger &merger,
+ clang::ASTContext &source, clang::FileManager &source_file_manager,
+ const clang::ExternalASTMerger::OriginMap &source_origin_map,
+ clang::FileID file, clang::QualType root) {
+ clang::ExternalASTMerger::ImporterSource importer_source =
+ { source, source_file_manager, source_origin_map };
+ merger.AddSources(importer_source);
+ clang::ASTImporter &exporter = merger.ImporterForOrigin(source);
+ CompleteAllDeclContexts(exporter, file, root);
+ clang::QualType ret = exporter.Import(root);
+ merger.RemoveSources(importer_source);
+ return ret;
+}
+
+TypeFromUser ClangExpressionDeclMap::DeportType(ClangASTContext &target,
+ ClangASTContext &source,
+ TypeFromParser parser_type) {
+ assert (&target == m_target->GetScratchClangASTContext());
+ assert ((TypeSystem*)&source == parser_type.GetTypeSystem());
+ assert (source.getASTContext() == m_ast_context);
+
+ if (m_ast_importer_sp) {
+ return TypeFromUser(m_ast_importer_sp->DeportType(
+ target.getASTContext(), source.getASTContext(),
+ parser_type.GetOpaqueQualType()),
+ &target);
+ } else if (m_merger_up) {
+ clang::FileID source_file =
+ source.getASTContext()->getSourceManager().getFileID(
+ source.getASTContext()->getTranslationUnitDecl()->getLocation());
+ auto scratch_ast_context = static_cast<ClangASTContextForExpressions*>(
+ m_target->GetScratchClangASTContext());
+ clang::QualType exported_type = ExportAllDeclaredTypes(
+ scratch_ast_context->GetMergerUnchecked(),
+ *source.getASTContext(), *source.getFileManager(),
+ m_merger_up->GetOrigins(),
+ source_file,
+ clang::QualType::getFromOpaquePtr(parser_type.GetOpaqueQualType()));
+ return TypeFromUser(exported_type.getAsOpaquePtr(), &target);
+ } else {
+ lldbassert(!"No mechanism for deporting a type!");
+ return TypeFromUser();
+ }
+}
+
bool ClangExpressionDeclMap::AddPersistentVariable(const NamedDecl *decl,
const ConstString &name,
TypeFromParser parser_type,
@@ -198,12 +328,8 @@
if (target == nullptr)
return false;
- ClangASTContext *context(target->GetScratchClangASTContext());
-
- TypeFromUser user_type(m_ast_importer_sp->DeportType(
- context->getASTContext(), ast->getASTContext(),
- parser_type.GetOpaqueQualType()),
- context);
+ TypeFromUser user_type =
+ DeportType(*target->GetScratchClangASTContext(), *ast, parser_type);
uint32_t offset = m_parser_vars->m_materializer->AddResultVariable(
user_type, is_lvalue, m_keep_result_in_memory, m_result_delegate, err);
@@ -240,10 +366,7 @@
ClangASTContext *context(target->GetScratchClangASTContext());
- TypeFromUser user_type(m_ast_importer_sp->DeportType(
- context->getASTContext(), ast->getASTContext(),
- parser_type.GetOpaqueQualType()),
- context);
+ TypeFromUser user_type = DeportType(*context, *ast, parser_type);
if (!user_type.GetOpaqueQualType()) {
if (log)
@@ -688,16 +811,18 @@
}
ClangASTImporter::NamespaceMapSP namespace_map =
- m_ast_importer_sp->GetNamespaceMap(namespace_context);
+ m_ast_importer_sp
+ ? m_ast_importer_sp->GetNamespaceMap(namespace_context)
+ : ClangASTImporter::NamespaceMapSP();
+
+ if (!namespace_map)
+ return;
if (log && log->GetVerbose())
log->Printf(" CEDM::FEVD[%u] Inspecting (NamespaceMap*)%p (%d entries)",
current_id, static_cast<void *>(namespace_map.get()),
(int)namespace_map->size());
-
- if (!namespace_map)
- return;
-
+
for (ClangASTImporter::NamespaceMap::iterator i = namespace_map->begin(),
e = namespace_map->end();
i != e; ++i) {
@@ -775,8 +900,7 @@
if (!persistent_decl)
break;
- Decl *parser_persistent_decl = m_ast_importer_sp->CopyDecl(
- m_ast_context, scratch_ast_context, persistent_decl);
+ Decl *parser_persistent_decl = CopyDecl(persistent_decl);
if (!parser_persistent_decl)
break;
@@ -1320,8 +1444,7 @@
for (clang::NamedDecl *decl : decls_from_modules) {
if (llvm::isa<clang::FunctionDecl>(decl)) {
clang::NamedDecl *copied_decl =
- llvm::cast_or_null<FunctionDecl>(m_ast_importer_sp->CopyDecl(
- m_ast_context, &decl->getASTContext(), decl));
+ llvm::cast_or_null<FunctionDecl>(CopyDecl(decl));
if (copied_decl) {
context.AddNamedDecl(copied_decl);
context.m_found.function_with_type_info = true;
@@ -1363,9 +1486,7 @@
current_id, name.GetCString());
}
- clang::Decl *copied_decl = m_ast_importer_sp->CopyDecl(
- m_ast_context, &decl_from_modules->getASTContext(),
- decl_from_modules);
+ clang::Decl *copied_decl = CopyDecl(decl_from_modules);
clang::FunctionDecl *copied_function_decl =
copied_decl ? dyn_cast<clang::FunctionDecl>(copied_decl)
: nullptr;
@@ -1392,9 +1513,7 @@
current_id, name.GetCString());
}
- clang::Decl *copied_decl = m_ast_importer_sp->CopyDecl(
- m_ast_context, &decl_from_modules->getASTContext(),
- decl_from_modules);
+ clang::Decl *copied_decl = CopyDecl(decl_from_modules);
clang::VarDecl *copied_var_decl =
copied_decl ? dyn_cast_or_null<clang::VarDecl>(copied_decl)
: nullptr;
@@ -1753,7 +1872,9 @@
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr();
- ClangASTContext *scratch_ast_context = target->GetScratchClangASTContext();
+ ClangASTContextForExpressions *scratch_ast_context =
+ static_cast<ClangASTContextForExpressions*>(
+ target->GetScratchClangASTContext());
for (size_t index = 0, num_entities = m_found_entities.GetSize();
index < num_entities; ++index) {
@@ -1784,9 +1905,20 @@
var_type.getAsOpaquePtr(),
ClangASTContext::GetASTContext(&var_decl->getASTContext()));
- lldb::opaque_compiler_type_t copied_type = m_ast_importer_sp->CopyType(
- scratch_ast_context->getASTContext(), &var_decl->getASTContext(),
- var_type.getAsOpaquePtr());
+ lldb::opaque_compiler_type_t copied_type = 0;
+ if (m_ast_importer_sp) {
+ copied_type = m_ast_importer_sp->CopyType(
+ scratch_ast_context->getASTContext(), &var_decl->getASTContext(),
+ var_type.getAsOpaquePtr());
+ } else if (HasMerger()) {
+ copied_type = CopyTypeWithMerger(
+ var_decl->getASTContext(),
+ scratch_ast_context->GetMergerUnchecked(),
+ var_type).getAsOpaquePtr();
+ } else {
+ lldbassert(!"No mechanism to copy a resolved unknown type!");
+ return false;
+ }
if (!copied_type) {
if (log)
@@ -1896,9 +2028,7 @@
src_function_decl->getTemplateSpecializationInfo()->getTemplate();
clang::FunctionTemplateDecl *copied_function_template =
llvm::dyn_cast_or_null<clang::FunctionTemplateDecl>(
- m_ast_importer_sp->CopyDecl(m_ast_context,
- src_ast->getASTContext(),
- function_template));
+ CopyDecl(function_template));
if (copied_function_template) {
if (log) {
ASTDumper ast_dumper((clang::Decl *)copied_function_template);
@@ -1919,9 +2049,7 @@
} else if (src_function_decl) {
if (clang::FunctionDecl *copied_function_decl =
llvm::dyn_cast_or_null<clang::FunctionDecl>(
- m_ast_importer_sp->CopyDecl(m_ast_context,
- src_ast->getASTContext(),
- src_function_decl))) {
+ CopyDecl(src_function_decl))) {
if (log) {
ASTDumper ast_dumper((clang::Decl *)copied_function_decl);