This patch works around an assertion that we hit in the `LambdaExpr`
constructor when we call it from `ASTNodeImporter::VisitLambdaExpr` (see
https://github.com/llvm/llvm-project/issues/149477). The lambda that we
imported doesn't have the `NumCaptures` field accurately set to the one
on the source decl. This is because in `MinimalImport` mode, we skip
importing of lambda definitions:
e21b0dd819/clang/lib/AST/ASTImporter.cpp (L2499)
In practice we have seen this assertion occur in our `import-std-module`
test-suite when libc++ headers decide to use lambdas inside inline
function bodies (the latest failure being caused by
https://github.com/llvm/llvm-project/pull/144602).
To avoid running into this whenever libc++ decides to use lambdas in
headers, this patch skips `ASTImport` of lambdas alltogether. Ideally
this would bubble up to the user or log as an error, but we swallow the
`ASTImportError`s currently. The only way this codepath is hit is when
lambdas are used inside functions in defined in the expression
evaluator, or when importing AST nodes from Clang modules. Both of these
are very niche use-cases (for now), so a workaround seemed appropriate.
1441 lines
51 KiB
C++
1441 lines
51 KiB
C++
//===-- ClangASTImporter.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 "lldb/Core/Module.h"
|
|
#include "lldb/Utility/LLDBAssert.h"
|
|
#include "lldb/Utility/LLDBLog.h"
|
|
#include "lldb/Utility/Log.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/AST/DeclCXX.h"
|
|
#include "clang/AST/DeclObjC.h"
|
|
#include "clang/AST/RecordLayout.h"
|
|
#include "clang/Sema/Lookup.h"
|
|
#include "clang/Sema/Sema.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
#include "Plugins/ExpressionParser/Clang/ClangASTImporter.h"
|
|
#include "Plugins/ExpressionParser/Clang/ClangASTMetadata.h"
|
|
#include "Plugins/ExpressionParser/Clang/ClangASTSource.h"
|
|
#include "Plugins/ExpressionParser/Clang/ClangExternalASTSourceCallbacks.h"
|
|
#include "Plugins/ExpressionParser/Clang/ClangUtil.h"
|
|
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
|
|
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <type_traits>
|
|
|
|
using namespace lldb_private;
|
|
using namespace clang;
|
|
|
|
CompilerType ClangASTImporter::CopyType(TypeSystemClang &dst_ast,
|
|
const CompilerType &src_type) {
|
|
clang::ASTContext &dst_clang_ast = dst_ast.getASTContext();
|
|
|
|
auto src_ast = src_type.GetTypeSystem<TypeSystemClang>();
|
|
if (!src_ast)
|
|
return CompilerType();
|
|
|
|
clang::ASTContext &src_clang_ast = src_ast->getASTContext();
|
|
|
|
clang::QualType src_qual_type = ClangUtil::GetQualType(src_type);
|
|
|
|
ImporterDelegateSP delegate_sp(GetDelegate(&dst_clang_ast, &src_clang_ast));
|
|
if (!delegate_sp)
|
|
return CompilerType();
|
|
|
|
ASTImporterDelegate::CxxModuleScope std_scope(*delegate_sp, &dst_clang_ast);
|
|
|
|
llvm::Expected<QualType> ret_or_error = delegate_sp->Import(src_qual_type);
|
|
if (!ret_or_error) {
|
|
Log *log = GetLog(LLDBLog::Expressions);
|
|
LLDB_LOG_ERROR(log, ret_or_error.takeError(),
|
|
"Couldn't import type: {0}");
|
|
return CompilerType();
|
|
}
|
|
|
|
lldb::opaque_compiler_type_t dst_clang_type = ret_or_error->getAsOpaquePtr();
|
|
|
|
if (dst_clang_type)
|
|
return CompilerType(dst_ast.weak_from_this(), dst_clang_type);
|
|
return CompilerType();
|
|
}
|
|
|
|
clang::Decl *ClangASTImporter::CopyDecl(clang::ASTContext *dst_ast,
|
|
clang::Decl *decl) {
|
|
ImporterDelegateSP delegate_sp;
|
|
|
|
clang::ASTContext *src_ast = &decl->getASTContext();
|
|
delegate_sp = GetDelegate(dst_ast, src_ast);
|
|
|
|
ASTImporterDelegate::CxxModuleScope std_scope(*delegate_sp, dst_ast);
|
|
|
|
if (!delegate_sp)
|
|
return nullptr;
|
|
|
|
llvm::Expected<clang::Decl *> result = delegate_sp->Import(decl);
|
|
if (!result) {
|
|
Log *log = GetLog(LLDBLog::Expressions);
|
|
LLDB_LOG_ERROR(log, result.takeError(), "Couldn't import decl: {0}");
|
|
if (log) {
|
|
lldb::user_id_t user_id = LLDB_INVALID_UID;
|
|
if (std::optional<ClangASTMetadata> metadata = GetDeclMetadata(decl))
|
|
user_id = metadata->GetUserID();
|
|
|
|
if (NamedDecl *named_decl = dyn_cast<NamedDecl>(decl))
|
|
LLDB_LOG(log,
|
|
" [ClangASTImporter] WARNING: Failed to import a {0} "
|
|
"'{1}', metadata {2}",
|
|
decl->getDeclKindName(), named_decl->getNameAsString(),
|
|
user_id);
|
|
else
|
|
LLDB_LOG(log,
|
|
" [ClangASTImporter] WARNING: Failed to import a {0}, "
|
|
"metadata {1}",
|
|
decl->getDeclKindName(), user_id);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
return *result;
|
|
}
|
|
|
|
class DeclContextOverride {
|
|
private:
|
|
struct Backup {
|
|
clang::DeclContext *decl_context;
|
|
clang::DeclContext *lexical_decl_context;
|
|
};
|
|
|
|
llvm::DenseMap<clang::Decl *, Backup> m_backups;
|
|
|
|
void OverrideOne(clang::Decl *decl) {
|
|
if (m_backups.contains(decl)) {
|
|
return;
|
|
}
|
|
|
|
m_backups[decl] = {decl->getDeclContext(), decl->getLexicalDeclContext()};
|
|
|
|
decl->setDeclContext(decl->getASTContext().getTranslationUnitDecl());
|
|
decl->setLexicalDeclContext(decl->getASTContext().getTranslationUnitDecl());
|
|
// Changing the DeclContext might change the linkage. For example, if the
|
|
// entity was previously declared inside a function, it will not be
|
|
// external, but changing the declaration context to the TU will make it
|
|
// external. Make sure this will recompute the linkage if it was computed
|
|
// before.
|
|
decl->invalidateCachedLinkage();
|
|
}
|
|
|
|
bool ChainPassesThrough(
|
|
clang::Decl *decl, clang::DeclContext *base,
|
|
clang::DeclContext *(clang::Decl::*contextFromDecl)(),
|
|
clang::DeclContext *(clang::DeclContext::*contextFromContext)()) {
|
|
for (DeclContext *decl_ctx = (decl->*contextFromDecl)(); decl_ctx;
|
|
decl_ctx = (decl_ctx->*contextFromContext)()) {
|
|
if (decl_ctx == base) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
clang::Decl *GetEscapedChild(clang::Decl *decl,
|
|
clang::DeclContext *base = nullptr) {
|
|
if (base) {
|
|
// decl's DeclContext chains must pass through base.
|
|
|
|
if (!ChainPassesThrough(decl, base, &clang::Decl::getDeclContext,
|
|
&clang::DeclContext::getParent) ||
|
|
!ChainPassesThrough(decl, base, &clang::Decl::getLexicalDeclContext,
|
|
&clang::DeclContext::getLexicalParent)) {
|
|
return decl;
|
|
}
|
|
} else {
|
|
base = clang::dyn_cast<clang::DeclContext>(decl);
|
|
|
|
if (!base) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
if (clang::DeclContext *context =
|
|
clang::dyn_cast<clang::DeclContext>(decl)) {
|
|
for (clang::Decl *decl : context->decls()) {
|
|
if (clang::Decl *escaped_child = GetEscapedChild(decl)) {
|
|
return escaped_child;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void Override(clang::Decl *decl) {
|
|
if (clang::Decl *escaped_child = GetEscapedChild(decl)) {
|
|
Log *log = GetLog(LLDBLog::Expressions);
|
|
|
|
LLDB_LOG(log,
|
|
" [ClangASTImporter] DeclContextOverride couldn't "
|
|
"override ({0}Decl*){1} - its child ({2}Decl*){3} escapes",
|
|
decl->getDeclKindName(), decl, escaped_child->getDeclKindName(),
|
|
escaped_child);
|
|
lldbassert(0 && "Couldn't override!");
|
|
}
|
|
|
|
OverrideOne(decl);
|
|
}
|
|
|
|
public:
|
|
DeclContextOverride() = default;
|
|
|
|
void OverrideAllDeclsFromContainingFunction(clang::Decl *decl) {
|
|
for (DeclContext *decl_context = decl->getLexicalDeclContext();
|
|
decl_context; decl_context = decl_context->getLexicalParent()) {
|
|
DeclContext *redecl_context = decl_context->getRedeclContext();
|
|
|
|
if (llvm::isa<FunctionDecl>(redecl_context) &&
|
|
llvm::isa<TranslationUnitDecl>(redecl_context->getLexicalParent())) {
|
|
for (clang::Decl *child_decl : decl_context->decls()) {
|
|
Override(child_decl);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
~DeclContextOverride() {
|
|
for (const std::pair<clang::Decl *, Backup> &backup : m_backups) {
|
|
backup.first->setDeclContext(backup.second.decl_context);
|
|
backup.first->setLexicalDeclContext(backup.second.lexical_decl_context);
|
|
}
|
|
}
|
|
};
|
|
|
|
namespace {
|
|
/// Completes all imported TagDecls at the end of the scope.
|
|
///
|
|
/// While in a CompleteTagDeclsScope, every decl that could be completed will
|
|
/// be completed at the end of the scope (including all Decls that are
|
|
/// imported while completing the original Decls).
|
|
class CompleteTagDeclsScope : public ClangASTImporter::NewDeclListener {
|
|
ClangASTImporter::ImporterDelegateSP m_delegate;
|
|
/// List of declarations in the target context that need to be completed.
|
|
/// Every declaration should only be completed once and therefore should only
|
|
/// be once in this list.
|
|
llvm::SetVector<NamedDecl *> m_decls_to_complete;
|
|
/// Set of declarations that already were successfully completed (not just
|
|
/// added to m_decls_to_complete).
|
|
llvm::SmallPtrSet<NamedDecl *, 32> m_decls_already_completed;
|
|
clang::ASTContext *m_dst_ctx;
|
|
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<TagDecl>(decl)) {
|
|
if (auto *original_tag_decl = dyn_cast<TagDecl>(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<ObjCContainerDecl>(decl)) {
|
|
container_decl->setHasExternalLexicalStorage(false);
|
|
container_decl->setHasExternalVisibleStorage(false);
|
|
}
|
|
}
|
|
|
|
public:
|
|
/// Constructs a CompleteTagDeclsScope.
|
|
/// \param importer The ClangASTImporter that we should observe.
|
|
/// \param dst_ctx The ASTContext to which Decls are imported.
|
|
/// \param src_ctx The ASTContext from which Decls are imported.
|
|
explicit CompleteTagDeclsScope(ClangASTImporter &importer,
|
|
clang::ASTContext *dst_ctx,
|
|
clang::ASTContext *src_ctx)
|
|
: m_delegate(importer.GetDelegate(dst_ctx, src_ctx)), m_dst_ctx(dst_ctx),
|
|
m_src_ctx(src_ctx), importer(importer) {
|
|
m_delegate->SetImportListener(this);
|
|
}
|
|
|
|
~CompleteTagDeclsScope() override {
|
|
ClangASTImporter::ASTContextMetadataSP to_context_md =
|
|
importer.GetContextMetadata(m_dst_ctx);
|
|
|
|
// Complete all decls we collected until now.
|
|
while (!m_decls_to_complete.empty()) {
|
|
NamedDecl *decl = m_decls_to_complete.pop_back_val();
|
|
m_decls_already_completed.insert(decl);
|
|
|
|
CompleteDecl(decl, *to_context_md);
|
|
|
|
to_context_md->removeOrigin(decl);
|
|
}
|
|
|
|
// Stop listening to imported decls. We do this after clearing the
|
|
// Decls we needed to import to catch all Decls they might have pulled in.
|
|
m_delegate->RemoveImportListener();
|
|
}
|
|
|
|
void NewDeclImported(clang::Decl *from, clang::Decl *to) override {
|
|
// Filter out decls that we can't complete later.
|
|
if (!isa<TagDecl>(to) && !isa<ObjCInterfaceDecl>(to))
|
|
return;
|
|
auto *from_record_decl = dyn_cast<CXXRecordDecl>(from);
|
|
// We don't need to complete injected class name decls.
|
|
if (from_record_decl && from_record_decl->isInjectedClassName())
|
|
return;
|
|
|
|
NamedDecl *to_named_decl = dyn_cast<NamedDecl>(to);
|
|
// Check if we already completed this type.
|
|
if (m_decls_already_completed.contains(to_named_decl))
|
|
return;
|
|
// Queue this type to be completed.
|
|
m_decls_to_complete.insert(to_named_decl);
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
CompilerType ClangASTImporter::DeportType(TypeSystemClang &dst,
|
|
const CompilerType &src_type) {
|
|
Log *log = GetLog(LLDBLog::Expressions);
|
|
|
|
auto src_ctxt = src_type.GetTypeSystem<TypeSystemClang>();
|
|
if (!src_ctxt)
|
|
return {};
|
|
|
|
LLDB_LOG(log,
|
|
" [ClangASTImporter] DeportType called on ({0}Type*){1:x} "
|
|
"from (ASTContext*){2:x} to (ASTContext*){3:x}",
|
|
src_type.GetTypeName(), src_type.GetOpaqueQualType(),
|
|
&src_ctxt->getASTContext(), &dst.getASTContext());
|
|
|
|
DeclContextOverride decl_context_override;
|
|
|
|
if (auto *t = ClangUtil::GetQualType(src_type)->getAs<TagType>())
|
|
decl_context_override.OverrideAllDeclsFromContainingFunction(
|
|
t->getOriginalDecl());
|
|
|
|
CompleteTagDeclsScope complete_scope(*this, &dst.getASTContext(),
|
|
&src_ctxt->getASTContext());
|
|
return CopyType(dst, src_type);
|
|
}
|
|
|
|
clang::Decl *ClangASTImporter::DeportDecl(clang::ASTContext *dst_ctx,
|
|
clang::Decl *decl) {
|
|
Log *log = GetLog(LLDBLog::Expressions);
|
|
|
|
clang::ASTContext *src_ctx = &decl->getASTContext();
|
|
LLDB_LOG(log,
|
|
" [ClangASTImporter] DeportDecl called on ({0}Decl*){1:x} from "
|
|
"(ASTContext*){2:x} to (ASTContext*){3:x}",
|
|
decl->getDeclKindName(), decl, src_ctx, dst_ctx);
|
|
|
|
DeclContextOverride decl_context_override;
|
|
|
|
decl_context_override.OverrideAllDeclsFromContainingFunction(decl);
|
|
|
|
clang::Decl *result;
|
|
{
|
|
CompleteTagDeclsScope complete_scope(*this, dst_ctx, src_ctx);
|
|
result = CopyDecl(dst_ctx, decl);
|
|
}
|
|
|
|
if (!result)
|
|
return nullptr;
|
|
|
|
LLDB_LOG(log,
|
|
" [ClangASTImporter] DeportDecl deported ({0}Decl*){1:x} to "
|
|
"({2}Decl*){3:x}",
|
|
decl->getDeclKindName(), decl, result->getDeclKindName(), result);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool ClangASTImporter::CanImport(const Decl *d) {
|
|
if (!d)
|
|
return false;
|
|
if (isa<TagDecl>(d))
|
|
return GetDeclOrigin(d).Valid();
|
|
if (isa<ObjCInterfaceDecl>(d))
|
|
return GetDeclOrigin(d).Valid();
|
|
return false;
|
|
}
|
|
|
|
bool ClangASTImporter::CanImport(const CompilerType &type) {
|
|
if (!ClangUtil::IsClangType(type))
|
|
return false;
|
|
|
|
clang::QualType qual_type(
|
|
ClangUtil::GetCanonicalQualType(ClangUtil::RemoveFastQualifiers(type)));
|
|
|
|
const clang::Type::TypeClass type_class = qual_type->getTypeClass();
|
|
switch (type_class) {
|
|
case clang::Type::Record:
|
|
return CanImport(qual_type->getAsCXXRecordDecl());
|
|
case clang::Type::Enum:
|
|
return CanImport(llvm::cast<clang::EnumType>(qual_type)->getOriginalDecl());
|
|
case clang::Type::ObjCObject:
|
|
case clang::Type::ObjCInterface: {
|
|
const clang::ObjCObjectType *objc_class_type =
|
|
llvm::dyn_cast<clang::ObjCObjectType>(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...
|
|
return CanImport(class_interface_decl);
|
|
}
|
|
} break;
|
|
|
|
case clang::Type::Typedef:
|
|
return CanImport(CompilerType(type.GetTypeSystem(),
|
|
llvm::cast<clang::TypedefType>(qual_type)
|
|
->getDecl()
|
|
->getUnderlyingType()
|
|
.getAsOpaquePtr()));
|
|
|
|
case clang::Type::Auto:
|
|
return CanImport(CompilerType(type.GetTypeSystem(),
|
|
llvm::cast<clang::AutoType>(qual_type)
|
|
->getDeducedType()
|
|
.getAsOpaquePtr()));
|
|
|
|
case clang::Type::Paren:
|
|
return CanImport(CompilerType(
|
|
type.GetTypeSystem(),
|
|
llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr()));
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ClangASTImporter::Import(const CompilerType &type) {
|
|
if (!ClangUtil::IsClangType(type))
|
|
return false;
|
|
|
|
clang::QualType qual_type(
|
|
ClangUtil::GetCanonicalQualType(ClangUtil::RemoveFastQualifiers(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 CompleteAndFetchChildren(qual_type);
|
|
}
|
|
} break;
|
|
|
|
case clang::Type::Enum: {
|
|
clang::EnumDecl *enum_decl =
|
|
llvm::cast<clang::EnumType>(qual_type)->getOriginalDecl();
|
|
if (enum_decl) {
|
|
if (GetDeclOrigin(enum_decl).Valid())
|
|
return CompleteAndFetchChildren(qual_type);
|
|
}
|
|
} break;
|
|
|
|
case clang::Type::ObjCObject:
|
|
case clang::Type::ObjCInterface: {
|
|
const clang::ObjCObjectType *objc_class_type =
|
|
llvm::dyn_cast<clang::ObjCObjectType>(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 (GetDeclOrigin(class_interface_decl).Valid())
|
|
return CompleteAndFetchChildren(qual_type);
|
|
}
|
|
}
|
|
} break;
|
|
|
|
case clang::Type::Typedef:
|
|
return Import(CompilerType(type.GetTypeSystem(),
|
|
llvm::cast<clang::TypedefType>(qual_type)
|
|
->getDecl()
|
|
->getUnderlyingType()
|
|
.getAsOpaquePtr()));
|
|
|
|
case clang::Type::Auto:
|
|
return Import(CompilerType(type.GetTypeSystem(),
|
|
llvm::cast<clang::AutoType>(qual_type)
|
|
->getDeducedType()
|
|
.getAsOpaquePtr()));
|
|
|
|
case clang::Type::Paren:
|
|
return Import(CompilerType(
|
|
type.GetTypeSystem(),
|
|
llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr()));
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ClangASTImporter::CompleteType(const CompilerType &compiler_type) {
|
|
if (!CanImport(compiler_type))
|
|
return false;
|
|
|
|
if (Import(compiler_type)) {
|
|
TypeSystemClang::CompleteTagDeclarationDefinition(compiler_type);
|
|
return true;
|
|
}
|
|
|
|
TypeSystemClang::SetHasExternalStorage(compiler_type.GetOpaqueQualType(),
|
|
false);
|
|
return false;
|
|
}
|
|
|
|
/// Copy layout information from \ref source_map to the \ref destination_map.
|
|
///
|
|
/// In the process of copying over layout info, we may need to import
|
|
/// decls from the \ref source_map. This function will use the supplied
|
|
/// \ref importer to import the necessary decls into \ref dest_ctx.
|
|
///
|
|
/// \param[in,out] dest_ctx Destination ASTContext into which we import
|
|
/// decls from the \ref source_map.
|
|
/// \param[out] destination_map A map from decls in \ref dest_ctx to an
|
|
/// integral offest, which will be copies
|
|
/// of the decl/offest pairs in \ref source_map
|
|
/// if successful.
|
|
/// \param[in] source_map A map from decls to integral offests. These will
|
|
/// be copied into \ref destination_map.
|
|
/// \param[in,out] importer Used to import decls into \ref dest_ctx.
|
|
///
|
|
/// \returns On success, will return 'true' and the offsets in \ref
|
|
/// destination_map
|
|
/// are usable copies of \ref source_map.
|
|
template <class D, class O>
|
|
static bool ImportOffsetMap(clang::ASTContext *dest_ctx,
|
|
llvm::DenseMap<const D *, O> &destination_map,
|
|
llvm::DenseMap<const D *, O> &source_map,
|
|
ClangASTImporter &importer) {
|
|
// When importing fields into a new record, clang has a hard requirement that
|
|
// fields be imported in field offset order. Since they are stored in a
|
|
// DenseMap with a pointer as the key type, this means we cannot simply
|
|
// iterate over the map, as the order will be non-deterministic. Instead we
|
|
// have to sort by the offset and then insert in sorted order.
|
|
typedef llvm::DenseMap<const D *, O> MapType;
|
|
typedef typename MapType::value_type PairType;
|
|
std::vector<PairType> sorted_items;
|
|
sorted_items.reserve(source_map.size());
|
|
sorted_items.assign(source_map.begin(), source_map.end());
|
|
llvm::sort(sorted_items, llvm::less_second());
|
|
|
|
for (const auto &item : sorted_items) {
|
|
DeclFromUser<D> user_decl(const_cast<D *>(item.first));
|
|
DeclFromParser<D> parser_decl(user_decl.Import(dest_ctx, importer));
|
|
if (parser_decl.IsInvalid())
|
|
return false;
|
|
destination_map.insert(
|
|
std::pair<const D *, O>(parser_decl.decl, item.second));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// Given a CXXRecordDecl, will calculate and populate \ref base_offsets
|
|
/// with the integral offsets of any of its (possibly virtual) base classes.
|
|
///
|
|
/// \param[in] record_layout ASTRecordLayout of \ref record.
|
|
/// \param[in] record The record that we're calculating the base layouts of.
|
|
/// \param[out] base_offsets Map of base-class decl to integral offset which
|
|
/// this function will fill in.
|
|
///
|
|
/// \returns On success, will return 'true' and the offsets in \ref base_offsets
|
|
/// are usable.
|
|
template <bool IsVirtual>
|
|
bool ExtractBaseOffsets(const ASTRecordLayout &record_layout,
|
|
DeclFromUser<const CXXRecordDecl> &record,
|
|
llvm::DenseMap<const clang::CXXRecordDecl *,
|
|
clang::CharUnits> &base_offsets) {
|
|
for (CXXRecordDecl::base_class_const_iterator
|
|
bi = (IsVirtual ? record->vbases_begin() : record->bases_begin()),
|
|
be = (IsVirtual ? record->vbases_end() : record->bases_end());
|
|
bi != be; ++bi) {
|
|
if (!IsVirtual && bi->isVirtual())
|
|
continue;
|
|
|
|
const clang::Type *origin_base_type = bi->getType().getTypePtr();
|
|
const clang::RecordType *origin_base_record_type =
|
|
origin_base_type->getAs<RecordType>();
|
|
|
|
if (!origin_base_record_type)
|
|
return false;
|
|
|
|
DeclFromUser<RecordDecl> origin_base_record(
|
|
origin_base_record_type->getOriginalDecl());
|
|
|
|
if (origin_base_record.IsInvalid())
|
|
return false;
|
|
|
|
DeclFromUser<CXXRecordDecl> origin_base_cxx_record(
|
|
DynCast<CXXRecordDecl>(origin_base_record));
|
|
|
|
if (origin_base_cxx_record.IsInvalid())
|
|
return false;
|
|
|
|
CharUnits base_offset;
|
|
|
|
if (IsVirtual)
|
|
base_offset =
|
|
record_layout.getVBaseClassOffset(origin_base_cxx_record.decl);
|
|
else
|
|
base_offset =
|
|
record_layout.getBaseClassOffset(origin_base_cxx_record.decl);
|
|
|
|
base_offsets.insert(std::pair<const CXXRecordDecl *, CharUnits>(
|
|
origin_base_cxx_record.decl, base_offset));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ClangASTImporter::importRecordLayoutFromOrigin(
|
|
const RecordDecl *record, uint64_t &size, uint64_t &alignment,
|
|
llvm::DenseMap<const clang::FieldDecl *, uint64_t> &field_offsets,
|
|
llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
|
|
&base_offsets,
|
|
llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
|
|
&vbase_offsets) {
|
|
|
|
Log *log = GetLog(LLDBLog::Expressions);
|
|
|
|
clang::ASTContext &dest_ctx = record->getASTContext();
|
|
LLDB_LOG(log,
|
|
"LayoutRecordType on (ASTContext*){0:x} '{1}' for (RecordDecl*)"
|
|
"{2:x} [name = '{3}']",
|
|
&dest_ctx,
|
|
TypeSystemClang::GetASTContext(&dest_ctx)->getDisplayName(), record,
|
|
record->getName());
|
|
|
|
DeclFromParser<const RecordDecl> parser_record(record);
|
|
DeclFromUser<const RecordDecl> origin_record(parser_record.GetOrigin(*this));
|
|
|
|
if (origin_record.IsInvalid())
|
|
return false;
|
|
|
|
std::remove_reference_t<decltype(field_offsets)> origin_field_offsets;
|
|
std::remove_reference_t<decltype(base_offsets)> origin_base_offsets;
|
|
std::remove_reference_t<decltype(vbase_offsets)> origin_virtual_base_offsets;
|
|
|
|
TypeSystemClang::GetCompleteDecl(
|
|
&origin_record->getASTContext(),
|
|
const_cast<RecordDecl *>(origin_record.decl));
|
|
|
|
clang::RecordDecl *definition = origin_record.decl->getDefinition();
|
|
if (!definition || !definition->isCompleteDefinition())
|
|
return false;
|
|
|
|
const ASTRecordLayout &record_layout(
|
|
origin_record->getASTContext().getASTRecordLayout(origin_record.decl));
|
|
|
|
int field_idx = 0, field_count = record_layout.getFieldCount();
|
|
|
|
for (RecordDecl::field_iterator fi = origin_record->field_begin(),
|
|
fe = origin_record->field_end();
|
|
fi != fe; ++fi) {
|
|
if (field_idx >= field_count)
|
|
return false; // Layout didn't go well. Bail out.
|
|
|
|
uint64_t field_offset = record_layout.getFieldOffset(field_idx);
|
|
|
|
origin_field_offsets.insert(
|
|
std::pair<const FieldDecl *, uint64_t>(*fi, field_offset));
|
|
|
|
field_idx++;
|
|
}
|
|
|
|
DeclFromUser<const CXXRecordDecl> origin_cxx_record(
|
|
DynCast<const CXXRecordDecl>(origin_record));
|
|
|
|
if (origin_cxx_record.IsValid()) {
|
|
if (!ExtractBaseOffsets<false>(record_layout, origin_cxx_record,
|
|
origin_base_offsets) ||
|
|
!ExtractBaseOffsets<true>(record_layout, origin_cxx_record,
|
|
origin_virtual_base_offsets))
|
|
return false;
|
|
}
|
|
|
|
if (!ImportOffsetMap(&dest_ctx, field_offsets, origin_field_offsets, *this) ||
|
|
!ImportOffsetMap(&dest_ctx, base_offsets, origin_base_offsets, *this) ||
|
|
!ImportOffsetMap(&dest_ctx, vbase_offsets, origin_virtual_base_offsets,
|
|
*this))
|
|
return false;
|
|
|
|
size = record_layout.getSize().getQuantity() * dest_ctx.getCharWidth();
|
|
alignment =
|
|
record_layout.getAlignment().getQuantity() * dest_ctx.getCharWidth();
|
|
|
|
if (log) {
|
|
LLDB_LOG(log, "LRT returned:");
|
|
LLDB_LOG(log, "LRT Original = (RecordDecl*){0:x}",
|
|
static_cast<const void *>(origin_record.decl));
|
|
LLDB_LOG(log, "LRT Size = {0}", size);
|
|
LLDB_LOG(log, "LRT Alignment = {0}", alignment);
|
|
LLDB_LOG(log, "LRT Fields:");
|
|
for (RecordDecl::field_iterator fi = record->field_begin(),
|
|
fe = record->field_end();
|
|
fi != fe; ++fi) {
|
|
LLDB_LOG(
|
|
log,
|
|
"LRT (FieldDecl*){0:x}, Name = '{1}', Type = '{2}', Offset = "
|
|
"{3} bits",
|
|
*fi, fi->getName(), fi->getType().getAsString(), field_offsets[*fi]);
|
|
}
|
|
DeclFromParser<const CXXRecordDecl> parser_cxx_record =
|
|
DynCast<const CXXRecordDecl>(parser_record);
|
|
if (parser_cxx_record.IsValid()) {
|
|
LLDB_LOG(log, "LRT Bases:");
|
|
for (CXXRecordDecl::base_class_const_iterator
|
|
bi = parser_cxx_record->bases_begin(),
|
|
be = parser_cxx_record->bases_end();
|
|
bi != be; ++bi) {
|
|
bool is_virtual = bi->isVirtual();
|
|
|
|
QualType base_type = bi->getType();
|
|
const RecordType *base_record_type = base_type->getAs<RecordType>();
|
|
DeclFromParser<RecordDecl> base_record(
|
|
base_record_type->getOriginalDecl());
|
|
DeclFromParser<CXXRecordDecl> base_cxx_record =
|
|
DynCast<CXXRecordDecl>(base_record);
|
|
|
|
LLDB_LOG(log,
|
|
"LRT {0}(CXXRecordDecl*){1:x}, Name = '{2}', Offset = "
|
|
"{3} chars",
|
|
(is_virtual ? "Virtual " : ""), base_cxx_record.decl,
|
|
base_cxx_record.decl->getName(),
|
|
(is_virtual
|
|
? vbase_offsets[base_cxx_record.decl].getQuantity()
|
|
: base_offsets[base_cxx_record.decl].getQuantity()));
|
|
}
|
|
} else {
|
|
LLDB_LOG(log, "LRD Not a CXXRecord, so no bases");
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ClangASTImporter::LayoutRecordType(
|
|
const clang::RecordDecl *record_decl, uint64_t &bit_size,
|
|
uint64_t &alignment,
|
|
llvm::DenseMap<const clang::FieldDecl *, uint64_t> &field_offsets,
|
|
llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
|
|
&base_offsets,
|
|
llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
|
|
&vbase_offsets) {
|
|
RecordDeclToLayoutMap::iterator pos =
|
|
m_record_decl_to_layout_map.find(record_decl);
|
|
base_offsets.clear();
|
|
vbase_offsets.clear();
|
|
if (pos != m_record_decl_to_layout_map.end()) {
|
|
bit_size = pos->second.bit_size;
|
|
alignment = pos->second.alignment;
|
|
field_offsets.swap(pos->second.field_offsets);
|
|
base_offsets.swap(pos->second.base_offsets);
|
|
vbase_offsets.swap(pos->second.vbase_offsets);
|
|
m_record_decl_to_layout_map.erase(pos);
|
|
return true;
|
|
}
|
|
|
|
// It's possible that we calculated the layout in a different
|
|
// ClangASTImporter instance. Try to import such layout if
|
|
// our decl has an origin.
|
|
if (auto origin = GetDeclOrigin(record_decl); origin.Valid())
|
|
if (importRecordLayoutFromOrigin(record_decl, bit_size, alignment,
|
|
field_offsets, base_offsets,
|
|
vbase_offsets))
|
|
return true;
|
|
|
|
bit_size = 0;
|
|
alignment = 0;
|
|
field_offsets.clear();
|
|
|
|
return false;
|
|
}
|
|
|
|
void ClangASTImporter::SetRecordLayout(clang::RecordDecl *decl,
|
|
const LayoutInfo &layout) {
|
|
m_record_decl_to_layout_map.insert(std::make_pair(decl, layout));
|
|
}
|
|
|
|
bool ClangASTImporter::CompleteTagDecl(clang::TagDecl *decl) {
|
|
DeclOrigin decl_origin = GetDeclOrigin(decl);
|
|
|
|
if (!decl_origin.Valid())
|
|
return false;
|
|
|
|
if (!TypeSystemClang::GetCompleteDecl(decl_origin.ctx, decl_origin.decl))
|
|
return false;
|
|
|
|
ImporterDelegateSP delegate_sp(
|
|
GetDelegate(&decl->getASTContext(), decl_origin.ctx));
|
|
|
|
ASTImporterDelegate::CxxModuleScope std_scope(*delegate_sp,
|
|
&decl->getASTContext());
|
|
if (delegate_sp)
|
|
delegate_sp->ImportDefinitionTo(decl, decl_origin.decl);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ClangASTImporter::CompleteTagDeclWithOrigin(clang::TagDecl *decl,
|
|
clang::TagDecl *origin_decl) {
|
|
clang::ASTContext *origin_ast_ctx = &origin_decl->getASTContext();
|
|
|
|
if (!TypeSystemClang::GetCompleteDecl(origin_ast_ctx, origin_decl))
|
|
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;
|
|
}
|
|
|
|
bool ClangASTImporter::CompleteObjCInterfaceDecl(
|
|
clang::ObjCInterfaceDecl *interface_decl) {
|
|
DeclOrigin decl_origin = GetDeclOrigin(interface_decl);
|
|
|
|
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 (ObjCInterfaceDecl *super_class = interface_decl->getSuperClass())
|
|
RequireCompleteType(clang::QualType(super_class->getTypeForDecl(), 0));
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ClangASTImporter::CompleteAndFetchChildren(clang::QualType type) {
|
|
if (!RequireCompleteType(type))
|
|
return false;
|
|
|
|
Log *log = GetLog(LLDBLog::Expressions);
|
|
|
|
if (const TagType *tag_type = type->getAs<TagType>()) {
|
|
TagDecl *tag_decl = tag_type->getOriginalDecl();
|
|
|
|
DeclOrigin decl_origin = GetDeclOrigin(tag_decl);
|
|
|
|
if (!decl_origin.Valid())
|
|
return false;
|
|
|
|
ImporterDelegateSP delegate_sp(
|
|
GetDelegate(&tag_decl->getASTContext(), decl_origin.ctx));
|
|
|
|
ASTImporterDelegate::CxxModuleScope std_scope(*delegate_sp,
|
|
&tag_decl->getASTContext());
|
|
|
|
TagDecl *origin_tag_decl = llvm::dyn_cast<TagDecl>(decl_origin.decl);
|
|
|
|
for (Decl *origin_child_decl : origin_tag_decl->decls()) {
|
|
llvm::Expected<Decl *> imported_or_err =
|
|
delegate_sp->Import(origin_child_decl);
|
|
if (!imported_or_err) {
|
|
LLDB_LOG_ERROR(log, imported_or_err.takeError(),
|
|
"Couldn't import decl: {0}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (RecordDecl *record_decl = dyn_cast<RecordDecl>(origin_tag_decl))
|
|
record_decl->setHasLoadedFieldsFromExternalStorage(true);
|
|
|
|
return true;
|
|
}
|
|
|
|
if (const ObjCObjectType *objc_object_type = type->getAs<ObjCObjectType>()) {
|
|
if (ObjCInterfaceDecl *objc_interface_decl =
|
|
objc_object_type->getInterface()) {
|
|
DeclOrigin decl_origin = GetDeclOrigin(objc_interface_decl);
|
|
|
|
if (!decl_origin.Valid())
|
|
return false;
|
|
|
|
ImporterDelegateSP delegate_sp(
|
|
GetDelegate(&objc_interface_decl->getASTContext(), decl_origin.ctx));
|
|
|
|
ObjCInterfaceDecl *origin_interface_decl =
|
|
llvm::dyn_cast<ObjCInterfaceDecl>(decl_origin.decl);
|
|
|
|
for (Decl *origin_child_decl : origin_interface_decl->decls()) {
|
|
llvm::Expected<Decl *> imported_or_err =
|
|
delegate_sp->Import(origin_child_decl);
|
|
if (!imported_or_err) {
|
|
LLDB_LOG_ERROR(log, imported_or_err.takeError(),
|
|
"Couldn't import decl: {0}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ClangASTImporter::RequireCompleteType(clang::QualType type) {
|
|
if (type.isNull())
|
|
return false;
|
|
|
|
if (const TagType *tag_type = type->getAs<TagType>()) {
|
|
TagDecl *tag_decl = tag_type->getOriginalDecl();
|
|
|
|
if (tag_decl->getDefinition())
|
|
return true;
|
|
|
|
return CompleteTagDecl(tag_decl);
|
|
}
|
|
if (const ObjCObjectType *objc_object_type = type->getAs<ObjCObjectType>()) {
|
|
if (ObjCInterfaceDecl *objc_interface_decl =
|
|
objc_object_type->getInterface())
|
|
return CompleteObjCInterfaceDecl(objc_interface_decl);
|
|
return false;
|
|
}
|
|
if (const ArrayType *array_type = type->getAsArrayTypeUnsafe())
|
|
return RequireCompleteType(array_type->getElementType());
|
|
if (const AtomicType *atomic_type = type->getAs<AtomicType>())
|
|
return RequireCompleteType(atomic_type->getPointeeType());
|
|
|
|
return true;
|
|
}
|
|
|
|
std::optional<ClangASTMetadata>
|
|
ClangASTImporter::GetDeclMetadata(const clang::Decl *decl) {
|
|
DeclOrigin decl_origin = GetDeclOrigin(decl);
|
|
|
|
if (decl_origin.Valid()) {
|
|
TypeSystemClang *ast = TypeSystemClang::GetASTContext(decl_origin.ctx);
|
|
return ast->GetMetadata(decl_origin.decl);
|
|
}
|
|
TypeSystemClang *ast = TypeSystemClang::GetASTContext(&decl->getASTContext());
|
|
return ast->GetMetadata(decl);
|
|
}
|
|
|
|
ClangASTImporter::DeclOrigin
|
|
ClangASTImporter::GetDeclOrigin(const clang::Decl *decl) {
|
|
ASTContextMetadataSP context_md = GetContextMetadata(&decl->getASTContext());
|
|
|
|
return context_md->getOrigin(decl);
|
|
}
|
|
|
|
void ClangASTImporter::SetDeclOrigin(const clang::Decl *decl,
|
|
clang::Decl *original_decl) {
|
|
ASTContextMetadataSP context_md = GetContextMetadata(&decl->getASTContext());
|
|
context_md->setOrigin(
|
|
decl, DeclOrigin(&original_decl->getASTContext(), original_decl));
|
|
}
|
|
|
|
void ClangASTImporter::RegisterNamespaceMap(const clang::NamespaceDecl *decl,
|
|
NamespaceMapSP &namespace_map) {
|
|
ASTContextMetadataSP context_md = GetContextMetadata(&decl->getASTContext());
|
|
|
|
context_md->m_namespace_maps[decl] = namespace_map;
|
|
}
|
|
|
|
ClangASTImporter::NamespaceMapSP
|
|
ClangASTImporter::GetNamespaceMap(const clang::NamespaceDecl *decl) {
|
|
ASTContextMetadataSP context_md = GetContextMetadata(&decl->getASTContext());
|
|
|
|
NamespaceMetaMap &namespace_maps = context_md->m_namespace_maps;
|
|
|
|
NamespaceMetaMap::iterator iter = namespace_maps.find(decl);
|
|
|
|
if (iter != namespace_maps.end())
|
|
return iter->second;
|
|
return NamespaceMapSP();
|
|
}
|
|
|
|
void ClangASTImporter::BuildNamespaceMap(const clang::NamespaceDecl *decl) {
|
|
assert(decl);
|
|
ASTContextMetadataSP context_md = GetContextMetadata(&decl->getASTContext());
|
|
|
|
const DeclContext *parent_context = decl->getDeclContext();
|
|
const NamespaceDecl *parent_namespace =
|
|
dyn_cast<NamespaceDecl>(parent_context);
|
|
NamespaceMapSP parent_map;
|
|
|
|
if (parent_namespace)
|
|
parent_map = GetNamespaceMap(parent_namespace);
|
|
|
|
NamespaceMapSP new_map;
|
|
|
|
new_map = std::make_shared<NamespaceMap>();
|
|
|
|
if (context_md->m_map_completer) {
|
|
std::string namespace_string = decl->getDeclName().getAsString();
|
|
|
|
context_md->m_map_completer->CompleteNamespaceMap(
|
|
new_map, ConstString(namespace_string.c_str()), parent_map);
|
|
}
|
|
|
|
context_md->m_namespace_maps[decl] = new_map;
|
|
}
|
|
|
|
void ClangASTImporter::ForgetDestination(clang::ASTContext *dst_ast) {
|
|
Log *log = GetLog(LLDBLog::Expressions);
|
|
|
|
LLDB_LOG(log,
|
|
" [ClangASTImporter] Forgetting destination (ASTContext*){0:x}",
|
|
dst_ast);
|
|
|
|
m_metadata_map.erase(dst_ast);
|
|
}
|
|
|
|
void ClangASTImporter::ForgetSource(clang::ASTContext *dst_ast,
|
|
clang::ASTContext *src_ast) {
|
|
ASTContextMetadataSP md = MaybeGetContextMetadata(dst_ast);
|
|
|
|
Log *log = GetLog(LLDBLog::Expressions);
|
|
|
|
LLDB_LOG(log,
|
|
" [ClangASTImporter] Forgetting source->dest "
|
|
"(ASTContext*){0:x}->(ASTContext*){1:x}",
|
|
src_ast, dst_ast);
|
|
|
|
if (!md)
|
|
return;
|
|
|
|
md->m_delegates.erase(src_ast);
|
|
md->removeOriginsWithContext(src_ast);
|
|
}
|
|
|
|
ClangASTImporter::MapCompleter::~MapCompleter() = default;
|
|
|
|
llvm::Expected<Decl *>
|
|
ClangASTImporter::ASTImporterDelegate::ImportImpl(Decl *From) {
|
|
// FIXME: The Minimal import mode of clang::ASTImporter does not correctly
|
|
// import Lambda definitions. Work around this for now by not importing
|
|
// lambdas at all. This is most likely encountered when importing decls from
|
|
// the `std` module (not from debug-info), where lambdas can be defined in
|
|
// inline function bodies. Those will be imported by LLDB.
|
|
if (const auto *CXX = llvm::dyn_cast<clang::CXXRecordDecl>(From))
|
|
if (CXX->isLambda())
|
|
return llvm::make_error<ASTImportError>(
|
|
ASTImportError::UnsupportedConstruct);
|
|
|
|
if (m_std_handler) {
|
|
std::optional<Decl *> D = m_std_handler->Import(From);
|
|
if (D) {
|
|
// Make sure we don't use this decl later to map it back to it's original
|
|
// decl. The decl the CxxModuleHandler created has nothing to do with
|
|
// the one from debug info, and linking those two would just cause the
|
|
// ASTImporter to try 'updating' the module decl with the minimal one from
|
|
// the debug info.
|
|
m_decls_to_ignore.insert(*D);
|
|
return *D;
|
|
}
|
|
}
|
|
|
|
// Check which ASTContext this declaration originally came from.
|
|
DeclOrigin origin = m_main.GetDeclOrigin(From);
|
|
|
|
// Prevent infinite recursion when the origin tracking contains a cycle.
|
|
assert(origin.decl != From && "Origin points to itself?");
|
|
|
|
// If it originally came from the target ASTContext then we can just
|
|
// pretend that the original is the one we imported. This can happen for
|
|
// example when inspecting a persistent declaration from the scratch
|
|
// ASTContext (which will provide the declaration when parsing the
|
|
// expression and then we later try to copy the declaration back to the
|
|
// scratch ASTContext to store the result).
|
|
// Without this check we would ask the ASTImporter to import a declaration
|
|
// into the same ASTContext where it came from (which doesn't make a lot of
|
|
// sense).
|
|
if (origin.Valid() && origin.ctx == &getToContext()) {
|
|
RegisterImportedDecl(From, origin.decl);
|
|
return origin.decl;
|
|
}
|
|
|
|
// This declaration came originally from another ASTContext. Instead of
|
|
// copying our potentially incomplete 'From' Decl we instead go to the
|
|
// original ASTContext and copy the original to the target. This is not
|
|
// only faster than first completing our current decl and then copying it
|
|
// to the target, but it also prevents that indirectly copying the same
|
|
// declaration to the same target requires the ASTImporter to merge all
|
|
// the different decls that appear to come from different ASTContexts (even
|
|
// though all these different source ASTContexts just got a copy from
|
|
// one source AST).
|
|
if (origin.Valid()) {
|
|
auto R = m_main.CopyDecl(&getToContext(), origin.decl);
|
|
if (R) {
|
|
RegisterImportedDecl(From, R);
|
|
return R;
|
|
}
|
|
}
|
|
|
|
// If we have a forcefully completed type, try to find an actual definition
|
|
// for it in other modules.
|
|
std::optional<ClangASTMetadata> md = m_main.GetDeclMetadata(From);
|
|
auto *td = dyn_cast<TagDecl>(From);
|
|
if (td && md && md->IsForcefullyCompleted()) {
|
|
Log *log = GetLog(LLDBLog::Expressions);
|
|
LLDB_LOG(log,
|
|
"[ClangASTImporter] Searching for a complete definition of {0} in "
|
|
"other modules",
|
|
td->getName());
|
|
Expected<DeclContext *> dc_or_err = ImportContext(td->getDeclContext());
|
|
if (!dc_or_err)
|
|
return dc_or_err.takeError();
|
|
Expected<DeclarationName> dn_or_err = Import(td->getDeclName());
|
|
if (!dn_or_err)
|
|
return dn_or_err.takeError();
|
|
DeclContext *dc = *dc_or_err;
|
|
DeclContext::lookup_result lr = dc->lookup(*dn_or_err);
|
|
for (clang::Decl *candidate : lr) {
|
|
if (candidate->getKind() == From->getKind()) {
|
|
RegisterImportedDecl(From, candidate);
|
|
m_decls_to_ignore.insert(candidate);
|
|
return candidate;
|
|
}
|
|
}
|
|
LLDB_LOG(log, "[ClangASTImporter] Complete definition not found");
|
|
}
|
|
|
|
return ASTImporter::ImportImpl(From);
|
|
}
|
|
|
|
void ClangASTImporter::ASTImporterDelegate::ImportDefinitionTo(
|
|
clang::Decl *to, clang::Decl *from) {
|
|
Log *log = GetLog(LLDBLog::Expressions);
|
|
|
|
auto getDeclName = [](Decl const *decl) {
|
|
std::string name_string;
|
|
if (auto const *from_named_decl = dyn_cast<clang::NamedDecl>(decl)) {
|
|
llvm::raw_string_ostream name_stream(name_string);
|
|
from_named_decl->printName(name_stream);
|
|
}
|
|
|
|
return name_string;
|
|
};
|
|
|
|
if (log) {
|
|
if (auto *D = GetAlreadyImportedOrNull(from); D && D != to) {
|
|
LLDB_LOG(
|
|
log,
|
|
"[ClangASTImporter] ERROR: overwriting an already imported decl "
|
|
"'{0:x}' ('{1}') from '{2:x}' with '{3:x}'. Likely due to a name "
|
|
"conflict when importing '{1}'.",
|
|
D, getDeclName(from), from, to);
|
|
}
|
|
}
|
|
|
|
// We might have a forward declaration from a shared library that we
|
|
// gave external lexical storage so that Clang asks us about the full
|
|
// definition when it needs it. In this case the ASTImporter isn't aware
|
|
// that the forward decl from the shared library is the actual import
|
|
// target but would create a second declaration that would then be defined.
|
|
// We want that 'to' is actually complete after this function so let's
|
|
// tell the ASTImporter that 'to' was imported from 'from'.
|
|
MapImported(from, to);
|
|
|
|
if (llvm::Error err = ImportDefinition(from)) {
|
|
LLDB_LOG_ERROR(log, std::move(err),
|
|
"[ClangASTImporter] Error during importing definition: {0}");
|
|
return;
|
|
}
|
|
|
|
if (clang::TagDecl *to_tag = dyn_cast<clang::TagDecl>(to)) {
|
|
if (clang::TagDecl *from_tag = dyn_cast<clang::TagDecl>(from)) {
|
|
to_tag->setCompleteDefinition(from_tag->isCompleteDefinition());
|
|
|
|
if (Log *log_ast = GetLog(LLDBLog::AST)) {
|
|
LLDB_LOG(log_ast,
|
|
"==== [ClangASTImporter][TUDecl: {0:x}] Imported "
|
|
"({1}Decl*){2:x}, named {3} (from "
|
|
"(Decl*){4:x})",
|
|
static_cast<void *>(to->getTranslationUnitDecl()),
|
|
from->getDeclKindName(), static_cast<void *>(to),
|
|
getDeclName(from), static_cast<void *>(from));
|
|
|
|
// Log the AST of the TU.
|
|
std::string ast_string;
|
|
llvm::raw_string_ostream ast_stream(ast_string);
|
|
to->getTranslationUnitDecl()->dump(ast_stream);
|
|
LLDB_LOG(log_ast, "{0}", ast_string);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we're dealing with an Objective-C class, ensure that the inheritance
|
|
// has been set up correctly. The ASTImporter may not do this correctly if
|
|
// the class was originally sourced from symbols.
|
|
|
|
if (ObjCInterfaceDecl *to_objc_interface = dyn_cast<ObjCInterfaceDecl>(to)) {
|
|
ObjCInterfaceDecl *to_superclass = to_objc_interface->getSuperClass();
|
|
|
|
if (to_superclass)
|
|
return; // we're not going to override it if it's set
|
|
|
|
ObjCInterfaceDecl *from_objc_interface = dyn_cast<ObjCInterfaceDecl>(from);
|
|
|
|
if (!from_objc_interface)
|
|
return;
|
|
|
|
ObjCInterfaceDecl *from_superclass = from_objc_interface->getSuperClass();
|
|
|
|
if (!from_superclass)
|
|
return;
|
|
|
|
llvm::Expected<Decl *> imported_from_superclass_decl =
|
|
Import(from_superclass);
|
|
|
|
if (!imported_from_superclass_decl) {
|
|
LLDB_LOG_ERROR(log, imported_from_superclass_decl.takeError(),
|
|
"Couldn't import decl: {0}");
|
|
return;
|
|
}
|
|
|
|
ObjCInterfaceDecl *imported_from_superclass =
|
|
dyn_cast<ObjCInterfaceDecl>(*imported_from_superclass_decl);
|
|
|
|
if (!imported_from_superclass)
|
|
return;
|
|
|
|
if (!to_objc_interface->hasDefinition())
|
|
to_objc_interface->startDefinition();
|
|
|
|
to_objc_interface->setSuperClass(m_source_ctx->getTrivialTypeSourceInfo(
|
|
m_source_ctx->getObjCInterfaceType(imported_from_superclass)));
|
|
}
|
|
}
|
|
|
|
/// Takes a CXXMethodDecl and completes the return type if necessary. This
|
|
/// is currently only necessary for virtual functions with covariant return
|
|
/// types where Clang's CodeGen expects that the underlying records are already
|
|
/// completed.
|
|
static void MaybeCompleteReturnType(ClangASTImporter &importer,
|
|
CXXMethodDecl *to_method) {
|
|
if (!to_method->isVirtual())
|
|
return;
|
|
QualType return_type = to_method->getReturnType();
|
|
if (!return_type->isPointerType() && !return_type->isReferenceType())
|
|
return;
|
|
|
|
clang::RecordDecl *rd = return_type->getPointeeType()->getAsRecordDecl();
|
|
if (!rd)
|
|
return;
|
|
if (rd->getDefinition())
|
|
return;
|
|
|
|
importer.CompleteTagDecl(rd);
|
|
}
|
|
|
|
/// Recreate a module with its parents in \p to_source and return its id.
|
|
static OptionalClangModuleID
|
|
RemapModule(OptionalClangModuleID from_id,
|
|
ClangExternalASTSourceCallbacks &from_source,
|
|
ClangExternalASTSourceCallbacks &to_source) {
|
|
if (!from_id.HasValue())
|
|
return {};
|
|
clang::Module *module = from_source.getModule(from_id.GetValue());
|
|
OptionalClangModuleID parent = RemapModule(
|
|
from_source.GetIDForModule(module->Parent), from_source, to_source);
|
|
TypeSystemClang &to_ts = to_source.GetTypeSystem();
|
|
return to_ts.GetOrCreateClangModule(module->Name, parent, module->IsFramework,
|
|
module->IsExplicit);
|
|
}
|
|
|
|
void ClangASTImporter::ASTImporterDelegate::Imported(clang::Decl *from,
|
|
clang::Decl *to) {
|
|
Log *log = GetLog(LLDBLog::Expressions);
|
|
|
|
// Some decls shouldn't be tracked here because they were not created by
|
|
// copying 'from' to 'to'. Just exit early for those.
|
|
if (m_decls_to_ignore.count(to))
|
|
return;
|
|
|
|
// Transfer module ownership information.
|
|
auto *from_source = llvm::dyn_cast_or_null<ClangExternalASTSourceCallbacks>(
|
|
getFromContext().getExternalSource());
|
|
// Can also be a ClangASTSourceProxy.
|
|
auto *to_source = llvm::dyn_cast_or_null<ClangExternalASTSourceCallbacks>(
|
|
getToContext().getExternalSource());
|
|
if (from_source && to_source) {
|
|
OptionalClangModuleID from_id(from->getOwningModuleID());
|
|
OptionalClangModuleID to_id =
|
|
RemapModule(from_id, *from_source, *to_source);
|
|
TypeSystemClang &to_ts = to_source->GetTypeSystem();
|
|
to_ts.SetOwningModule(to, to_id);
|
|
}
|
|
|
|
lldb::user_id_t user_id = LLDB_INVALID_UID;
|
|
if (std::optional<ClangASTMetadata> metadata = m_main.GetDeclMetadata(from))
|
|
user_id = metadata->GetUserID();
|
|
|
|
if (log) {
|
|
if (NamedDecl *from_named_decl = dyn_cast<clang::NamedDecl>(from)) {
|
|
std::string name_string;
|
|
llvm::raw_string_ostream name_stream(name_string);
|
|
from_named_decl->printName(name_stream);
|
|
|
|
LLDB_LOG(
|
|
log,
|
|
" [ClangASTImporter] Imported ({0}Decl*){1:x}, named {2} (from "
|
|
"(Decl*){3:x}), metadata {4}",
|
|
from->getDeclKindName(), to, name_string, from, user_id);
|
|
} else {
|
|
LLDB_LOG(log,
|
|
" [ClangASTImporter] Imported ({0}Decl*){1:x} (from "
|
|
"(Decl*){2:x}), metadata {3}",
|
|
from->getDeclKindName(), to, from, user_id);
|
|
}
|
|
}
|
|
|
|
ASTContextMetadataSP to_context_md =
|
|
m_main.GetContextMetadata(&to->getASTContext());
|
|
ASTContextMetadataSP from_context_md =
|
|
m_main.MaybeGetContextMetadata(m_source_ctx);
|
|
|
|
if (from_context_md) {
|
|
DeclOrigin origin = from_context_md->getOrigin(from);
|
|
|
|
if (origin.Valid()) {
|
|
if (origin.ctx != &to->getASTContext()) {
|
|
if (!to_context_md->hasOrigin(to) || user_id != LLDB_INVALID_UID)
|
|
to_context_md->setOrigin(to, origin);
|
|
|
|
LLDB_LOG(log,
|
|
" [ClangASTImporter] Propagated origin "
|
|
"(Decl*){0:x}/(ASTContext*){1:x} from (ASTContext*){2:x} to "
|
|
"(ASTContext*){3:x}",
|
|
origin.decl, origin.ctx, &from->getASTContext(),
|
|
&to->getASTContext());
|
|
}
|
|
} else {
|
|
if (m_new_decl_listener)
|
|
m_new_decl_listener->NewDeclImported(from, to);
|
|
|
|
if (!to_context_md->hasOrigin(to) || user_id != LLDB_INVALID_UID)
|
|
to_context_md->setOrigin(to, DeclOrigin(m_source_ctx, from));
|
|
|
|
LLDB_LOG(log,
|
|
" [ClangASTImporter] Decl has no origin information in "
|
|
"(ASTContext*){0:x}",
|
|
&from->getASTContext());
|
|
}
|
|
|
|
if (auto *to_namespace = dyn_cast<clang::NamespaceDecl>(to)) {
|
|
auto *from_namespace = cast<clang::NamespaceDecl>(from);
|
|
|
|
NamespaceMetaMap &namespace_maps = from_context_md->m_namespace_maps;
|
|
|
|
NamespaceMetaMap::iterator namespace_map_iter =
|
|
namespace_maps.find(from_namespace);
|
|
|
|
if (namespace_map_iter != namespace_maps.end())
|
|
to_context_md->m_namespace_maps[to_namespace] =
|
|
namespace_map_iter->second;
|
|
}
|
|
} else {
|
|
to_context_md->setOrigin(to, DeclOrigin(m_source_ctx, from));
|
|
|
|
LLDB_LOG(log,
|
|
" [ClangASTImporter] Sourced origin "
|
|
"(Decl*){0:x}/(ASTContext*){1:x} into (ASTContext*){2:x}",
|
|
from, m_source_ctx, &to->getASTContext());
|
|
}
|
|
|
|
if (auto *to_namespace_decl = dyn_cast<NamespaceDecl>(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<TagDecl>(to)) {
|
|
to_tag_decl->setHasExternalLexicalStorage();
|
|
to_tag_decl->getPrimaryContext()->setMustBuildLookupTable();
|
|
auto from_tag_decl = cast<TagDecl>(from);
|
|
|
|
LLDB_LOG(
|
|
log,
|
|
" [ClangASTImporter] To is a TagDecl - attributes {0}{1} [{2}->{3}]",
|
|
(to_tag_decl->hasExternalLexicalStorage() ? " Lexical" : ""),
|
|
(to_tag_decl->hasExternalVisibleStorage() ? " Visible" : ""),
|
|
(from_tag_decl->isCompleteDefinition() ? "complete" : "incomplete"),
|
|
(to_tag_decl->isCompleteDefinition() ? "complete" : "incomplete"));
|
|
}
|
|
|
|
if (auto *to_container_decl = dyn_cast<ObjCContainerDecl>(to)) {
|
|
to_container_decl->setHasExternalLexicalStorage();
|
|
to_container_decl->setHasExternalVisibleStorage();
|
|
|
|
if (log) {
|
|
if (ObjCInterfaceDecl *to_interface_decl =
|
|
llvm::dyn_cast<ObjCInterfaceDecl>(to_container_decl)) {
|
|
LLDB_LOG(
|
|
log,
|
|
" [ClangASTImporter] To is an ObjCInterfaceDecl - attributes "
|
|
"{0}{1}{2}",
|
|
(to_interface_decl->hasExternalLexicalStorage() ? " Lexical" : ""),
|
|
(to_interface_decl->hasExternalVisibleStorage() ? " Visible" : ""),
|
|
(to_interface_decl->hasDefinition() ? " HasDefinition" : ""));
|
|
} else {
|
|
LLDB_LOG(
|
|
log, " [ClangASTImporter] To is an {0}Decl - attributes {1}{2}",
|
|
((Decl *)to_container_decl)->getDeclKindName(),
|
|
(to_container_decl->hasExternalLexicalStorage() ? " Lexical" : ""),
|
|
(to_container_decl->hasExternalVisibleStorage() ? " Visible" : ""));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (clang::CXXMethodDecl *to_method = dyn_cast<CXXMethodDecl>(to))
|
|
MaybeCompleteReturnType(m_main, to_method);
|
|
}
|
|
|
|
clang::Decl *
|
|
ClangASTImporter::ASTImporterDelegate::GetOriginalDecl(clang::Decl *To) {
|
|
return m_main.GetDeclOrigin(To).decl;
|
|
}
|