Skip to content

Commit 158c608

Browse files
committed
[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.
1 parent 662c659 commit 158c608

File tree

12 files changed

+355
-493
lines changed

12 files changed

+355
-493
lines changed

lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp

Lines changed: 68 additions & 185 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9+
#include "Plugins/TypeSystem/Clang/ImporterBackedASTSource.h"
910
#include "lldb/Core/Module.h"
1011
#include "lldb/Utility/LLDBAssert.h"
1112
#include "lldb/Utility/LLDBLog.h"
@@ -17,6 +18,8 @@
1718
#include "clang/AST/RecordLayout.h"
1819
#include "clang/Sema/Lookup.h"
1920
#include "clang/Sema/Sema.h"
21+
#include "llvm/Support/Casting.h"
22+
#include "llvm/Support/ErrorHandling.h"
2023
#include "llvm/Support/raw_ostream.h"
2124

2225
#include "Plugins/ExpressionParser/Clang/ClangASTImporter.h"
@@ -230,35 +233,6 @@ class CompleteTagDeclsScope : public ClangASTImporter::NewDeclListener {
230233
clang::ASTContext *m_src_ctx;
231234
ClangASTImporter &importer;
232235

233-
void CompleteDecl(
234-
Decl *decl,
235-
lldb_private::ClangASTImporter::ASTContextMetadata const &to_context_md) {
236-
// The decl that should be completed has to be imported into the target
237-
// context from some other context.
238-
assert(to_context_md.hasOrigin(decl));
239-
// We should only complete decls coming from the source context.
240-
assert(to_context_md.getOrigin(decl).ctx == m_src_ctx);
241-
242-
Decl *original_decl = to_context_md.getOrigin(decl).decl;
243-
244-
// Complete the decl now.
245-
TypeSystemClang::GetCompleteDecl(m_src_ctx, original_decl);
246-
if (auto *tag_decl = dyn_cast<TagDecl>(decl)) {
247-
if (auto *original_tag_decl = dyn_cast<TagDecl>(original_decl)) {
248-
if (original_tag_decl->isCompleteDefinition()) {
249-
m_delegate->ImportDefinitionTo(tag_decl, original_tag_decl);
250-
tag_decl->setCompleteDefinition(true);
251-
}
252-
}
253-
254-
tag_decl->setHasExternalLexicalStorage(false);
255-
tag_decl->setHasExternalVisibleStorage(false);
256-
} else if (auto *container_decl = dyn_cast<ObjCContainerDecl>(decl)) {
257-
container_decl->setHasExternalLexicalStorage(false);
258-
container_decl->setHasExternalVisibleStorage(false);
259-
}
260-
}
261-
262236
public:
263237
/// Constructs a CompleteTagDeclsScope.
264238
/// \param importer The ClangASTImporter that we should observe.
@@ -280,9 +254,6 @@ class CompleteTagDeclsScope : public ClangASTImporter::NewDeclListener {
280254
while (!m_decls_to_complete.empty()) {
281255
NamedDecl *decl = m_decls_to_complete.pop_back_val();
282256
m_decls_already_completed.insert(decl);
283-
284-
CompleteDecl(decl, *to_context_md);
285-
286257
to_context_md->removeOrigin(decl);
287258
}
288259

@@ -296,6 +267,8 @@ class CompleteTagDeclsScope : public ClangASTImporter::NewDeclListener {
296267
if (!isa<TagDecl>(to) && !isa<ObjCInterfaceDecl>(to))
297268
return;
298269

270+
to = ClangUtil::GetFirstDecl(to);
271+
299272
RecordDecl *from_record_decl = dyn_cast<RecordDecl>(from);
300273
// We don't need to complete injected class name decls.
301274
if (from_record_decl && from_record_decl->isInjectedClassName())
@@ -510,14 +483,7 @@ bool ClangASTImporter::CompleteType(const CompilerType &compiler_type) {
510483
if (!CanImport(compiler_type))
511484
return false;
512485

513-
if (Import(compiler_type)) {
514-
TypeSystemClang::CompleteTagDeclarationDefinition(compiler_type);
515-
return true;
516-
}
517-
518-
TypeSystemClang::SetHasExternalStorage(compiler_type.GetOpaqueQualType(),
519-
false);
520-
return false;
486+
return Import(compiler_type);
521487
}
522488

523489
/// Copy layout information from \ref source_map to the \ref destination_map.
@@ -666,8 +632,8 @@ bool ClangASTImporter::importRecordLayoutFromOrigin(
666632

667633
int field_idx = 0, field_count = record_layout.getFieldCount();
668634

669-
for (RecordDecl::field_iterator fi = origin_record->field_begin(),
670-
fe = origin_record->field_end();
635+
for (RecordDecl::field_iterator fi = definition->field_begin(),
636+
fe = definition->field_end();
671637
fi != fe; ++fi) {
672638
if (field_idx >= field_count)
673639
return false; // Layout didn't go well. Bail out.
@@ -808,16 +774,34 @@ bool ClangASTImporter::CompleteTagDecl(clang::TagDecl *decl) {
808774
if (!decl_origin.Valid())
809775
return false;
810776

811-
if (!TypeSystemClang::GetCompleteDecl(decl_origin.ctx, decl_origin.decl))
777+
auto *origin_def = llvm::cast<TagDecl>(decl_origin.decl)->getDefinition();
778+
if (!origin_def)
812779
return false;
813780

814781
ImporterDelegateSP delegate_sp(
815782
GetDelegate(&decl->getASTContext(), decl_origin.ctx));
816783

817784
ASTImporterDelegate::CxxModuleScope std_scope(*delegate_sp,
818785
&decl->getASTContext());
819-
if (delegate_sp)
820-
delegate_sp->ImportDefinitionTo(decl, decl_origin.decl);
786+
787+
// This is expected to pull in a definition for result_decl (if in redecl
788+
// completion mode)
789+
llvm::Expected<Decl *> result = delegate_sp->Import(origin_def);
790+
if (!result) {
791+
llvm::handleAllErrors(result.takeError(),
792+
[](const clang::ASTImportError &e) {
793+
llvm::errs() << "ERR: " << e.toString() << "\n";
794+
});
795+
return false;
796+
}
797+
798+
// Create redeclaration chain with the 'to' decls.
799+
// Only need to do this if the 'result_decl' is a definition outside
800+
// of any redeclaration chain and the input 'decl' was a forward declaration
801+
TagDecl *result_decl = llvm::cast<TagDecl>(*result);
802+
if (!decl->isThisDeclarationADefinition() && result_decl != decl)
803+
if (result_decl->getPreviousDecl() == nullptr)
804+
result_decl->setPreviousDecl(decl);
821805

822806
return true;
823807
}
@@ -838,89 +822,30 @@ bool ClangASTImporter::CompleteObjCInterfaceDecl(
838822
if (!decl_origin.Valid())
839823
return false;
840824

841-
if (!TypeSystemClang::GetCompleteDecl(decl_origin.ctx, decl_origin.decl))
825+
ObjCInterfaceDecl *origin_decl =
826+
llvm::cast<ObjCInterfaceDecl>(decl_origin.decl);
827+
828+
origin_decl = origin_decl->getDefinition();
829+
if (!origin_decl)
842830
return false;
843831

844832
ImporterDelegateSP delegate_sp(
845833
GetDelegate(&interface_decl->getASTContext(), decl_origin.ctx));
846834

847-
if (delegate_sp)
848-
delegate_sp->ImportDefinitionTo(interface_decl, decl_origin.decl);
835+
llvm::Expected<Decl *> result = delegate_sp->Import(origin_decl);
836+
if (result)
837+
return true;
849838

850-
if (ObjCInterfaceDecl *super_class = interface_decl->getSuperClass())
851-
RequireCompleteType(clang::QualType(super_class->getTypeForDecl(), 0));
839+
llvm::handleAllErrors(result.takeError(),
840+
[](const clang::ASTImportError &e) {
841+
llvm::errs() << "ERR: " << e.toString() << "\n";
842+
});
852843

853-
return true;
844+
return false;
854845
}
855846

856847
bool ClangASTImporter::CompleteAndFetchChildren(clang::QualType type) {
857-
if (!RequireCompleteType(type))
858-
return false;
859-
860-
Log *log = GetLog(LLDBLog::Expressions);
861-
862-
if (const TagType *tag_type = type->getAs<TagType>()) {
863-
TagDecl *tag_decl = tag_type->getDecl();
864-
865-
DeclOrigin decl_origin = GetDeclOrigin(tag_decl);
866-
867-
if (!decl_origin.Valid())
868-
return false;
869-
870-
ImporterDelegateSP delegate_sp(
871-
GetDelegate(&tag_decl->getASTContext(), decl_origin.ctx));
872-
873-
ASTImporterDelegate::CxxModuleScope std_scope(*delegate_sp,
874-
&tag_decl->getASTContext());
875-
876-
TagDecl *origin_tag_decl = llvm::dyn_cast<TagDecl>(decl_origin.decl);
877-
878-
for (Decl *origin_child_decl : origin_tag_decl->decls()) {
879-
llvm::Expected<Decl *> imported_or_err =
880-
delegate_sp->Import(origin_child_decl);
881-
if (!imported_or_err) {
882-
LLDB_LOG_ERROR(log, imported_or_err.takeError(),
883-
"Couldn't import decl: {0}");
884-
return false;
885-
}
886-
}
887-
888-
if (RecordDecl *record_decl = dyn_cast<RecordDecl>(origin_tag_decl))
889-
record_decl->setHasLoadedFieldsFromExternalStorage(true);
890-
891-
return true;
892-
}
893-
894-
if (const ObjCObjectType *objc_object_type = type->getAs<ObjCObjectType>()) {
895-
if (ObjCInterfaceDecl *objc_interface_decl =
896-
objc_object_type->getInterface()) {
897-
DeclOrigin decl_origin = GetDeclOrigin(objc_interface_decl);
898-
899-
if (!decl_origin.Valid())
900-
return false;
901-
902-
ImporterDelegateSP delegate_sp(
903-
GetDelegate(&objc_interface_decl->getASTContext(), decl_origin.ctx));
904-
905-
ObjCInterfaceDecl *origin_interface_decl =
906-
llvm::dyn_cast<ObjCInterfaceDecl>(decl_origin.decl);
907-
908-
for (Decl *origin_child_decl : origin_interface_decl->decls()) {
909-
llvm::Expected<Decl *> imported_or_err =
910-
delegate_sp->Import(origin_child_decl);
911-
if (!imported_or_err) {
912-
LLDB_LOG_ERROR(log, imported_or_err.takeError(),
913-
"Couldn't import decl: {0}");
914-
return false;
915-
}
916-
}
917-
918-
return true;
919-
}
920-
return false;
921-
}
922-
923-
return true;
848+
return RequireCompleteType(type);
924849
}
925850

926851
bool ClangASTImporter::RequireCompleteType(clang::QualType type) {
@@ -1102,6 +1027,20 @@ ClangASTImporter::ASTImporterDelegate::ImportImpl(Decl *From) {
11021027
}
11031028
}
11041029

1030+
if (auto *source = llvm::dyn_cast<ImporterBackedASTSource>(
1031+
getToContext().getExternalSource())) {
1032+
// We added a new declaration (which is not a definition) into the
1033+
// destination AST context, so bump the declaration chain generation
1034+
// counter.
1035+
if (clang::TagDecl *td = dyn_cast<TagDecl>(From))
1036+
if (!td->isThisDeclarationADefinition())
1037+
source->MarkRedeclChainsAsOutOfDate(getToContext());
1038+
1039+
if (clang::ObjCInterfaceDecl *td = dyn_cast<ObjCInterfaceDecl>(From))
1040+
if (!td->isThisDeclarationADefinition())
1041+
source->MarkRedeclChainsAsOutOfDate(getToContext());
1042+
}
1043+
11051044
// If we have a forcefully completed type, try to find an actual definition
11061045
// for it in other modules.
11071046
const ClangASTMetadata *md = m_main.GetDeclMetadata(From);
@@ -1122,6 +1061,12 @@ ClangASTImporter::ASTImporterDelegate::ImportImpl(Decl *From) {
11221061
DeclContext::lookup_result lr = dc->lookup(*dn_or_err);
11231062
for (clang::Decl *candidate : lr) {
11241063
if (candidate->getKind() == From->getKind()) {
1064+
// If we're dealing with redecl chains, we want to find the definition,
1065+
// so skip if the decl is actually just a forwad decl.
1066+
if (auto *tag_decl = llvm::dyn_cast<TagDecl>(candidate);
1067+
!tag_decl || !tag_decl->getDefinition())
1068+
continue;
1069+
11251070
RegisterImportedDecl(From, candidate);
11261071
m_decls_to_ignore.insert(candidate);
11271072
return candidate;
@@ -1223,27 +1168,6 @@ void ClangASTImporter::ASTImporterDelegate::ImportDefinitionTo(
12231168
}
12241169
}
12251170

1226-
/// Takes a CXXMethodDecl and completes the return type if necessary. This
1227-
/// is currently only necessary for virtual functions with covariant return
1228-
/// types where Clang's CodeGen expects that the underlying records are already
1229-
/// completed.
1230-
static void MaybeCompleteReturnType(ClangASTImporter &importer,
1231-
CXXMethodDecl *to_method) {
1232-
if (!to_method->isVirtual())
1233-
return;
1234-
QualType return_type = to_method->getReturnType();
1235-
if (!return_type->isPointerType() && !return_type->isReferenceType())
1236-
return;
1237-
1238-
clang::RecordDecl *rd = return_type->getPointeeType()->getAsRecordDecl();
1239-
if (!rd)
1240-
return;
1241-
if (rd->getDefinition())
1242-
return;
1243-
1244-
importer.CompleteTagDecl(rd);
1245-
}
1246-
12471171
/// Recreate a module with its parents in \p to_source and return its id.
12481172
static OptionalClangModuleID
12491173
RemapModule(OptionalClangModuleID from_id,
@@ -1366,53 +1290,12 @@ void ClangASTImporter::ASTImporterDelegate::Imported(clang::Decl *from,
13661290
to_namespace_decl->setHasExternalVisibleStorage();
13671291
}
13681292

1369-
MarkDeclImported(from, to);
1370-
}
1371-
1372-
void ClangASTImporter::ASTImporterDelegate::MarkDeclImported(Decl *from,
1373-
Decl *to) {
1374-
Log *log = GetLog(LLDBLog::Expressions);
1375-
1376-
if (auto *to_tag_decl = dyn_cast<TagDecl>(to)) {
1377-
to_tag_decl->setHasExternalLexicalStorage();
1378-
to_tag_decl->getPrimaryContext()->setMustBuildLookupTable();
1379-
auto from_tag_decl = cast<TagDecl>(from);
1380-
1381-
LLDB_LOG(
1382-
log,
1383-
" [ClangASTImporter] To is a TagDecl - attributes {0}{1} [{2}->{3}]",
1384-
(to_tag_decl->hasExternalLexicalStorage() ? " Lexical" : ""),
1385-
(to_tag_decl->hasExternalVisibleStorage() ? " Visible" : ""),
1386-
(from_tag_decl->isCompleteDefinition() ? "complete" : "incomplete"),
1387-
(to_tag_decl->isCompleteDefinition() ? "complete" : "incomplete"));
1293+
if (clang::ObjCInterfaceDecl *td = dyn_cast<ObjCInterfaceDecl>(to)) {
1294+
if (clang::ExternalASTSource *s = getToContext().getExternalSource())
1295+
if (td->isThisDeclarationADefinition())
1296+
s->CompleteRedeclChain(td);
1297+
td->setHasExternalVisibleStorage();
13881298
}
1389-
1390-
if (auto *to_container_decl = dyn_cast<ObjCContainerDecl>(to)) {
1391-
to_container_decl->setHasExternalLexicalStorage();
1392-
to_container_decl->setHasExternalVisibleStorage();
1393-
1394-
if (log) {
1395-
if (ObjCInterfaceDecl *to_interface_decl =
1396-
llvm::dyn_cast<ObjCInterfaceDecl>(to_container_decl)) {
1397-
LLDB_LOG(
1398-
log,
1399-
" [ClangASTImporter] To is an ObjCInterfaceDecl - attributes "
1400-
"{0}{1}{2}",
1401-
(to_interface_decl->hasExternalLexicalStorage() ? " Lexical" : ""),
1402-
(to_interface_decl->hasExternalVisibleStorage() ? " Visible" : ""),
1403-
(to_interface_decl->hasDefinition() ? " HasDefinition" : ""));
1404-
} else {
1405-
LLDB_LOG(
1406-
log, " [ClangASTImporter] To is an {0}Decl - attributes {1}{2}",
1407-
((Decl *)to_container_decl)->getDeclKindName(),
1408-
(to_container_decl->hasExternalLexicalStorage() ? " Lexical" : ""),
1409-
(to_container_decl->hasExternalVisibleStorage() ? " Visible" : ""));
1410-
}
1411-
}
1412-
}
1413-
1414-
if (clang::CXXMethodDecl *to_method = dyn_cast<CXXMethodDecl>(to))
1415-
MaybeCompleteReturnType(m_main, to_method);
14161299
}
14171300

14181301
clang::Decl *

lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -327,9 +327,6 @@ void ClangASTSource::CompleteType(clang::ObjCInterfaceDecl *interface_decl) {
327327
}
328328

329329
void ClangASTSource::CompleteRedeclChain(const Decl *d) {
330-
if (!TypeSystemClang::UseRedeclCompletion())
331-
return;
332-
333330
if (const clang::TagDecl *td = llvm::dyn_cast<TagDecl>(d)) {
334331
if (td->isBeingDefined())
335332
return;
@@ -402,8 +399,7 @@ clang::ObjCInterfaceDecl *ClangASTSource::GetCompleteObjCInterface(
402399
return nullptr;
403400

404401
ObjCInterfaceDecl *complete_iface_decl(complete_interface_type->getDecl());
405-
406-
return complete_iface_decl;
402+
return complete_iface_decl->getDefinition();
407403
}
408404

409405
void ClangASTSource::FindExternalLexicalDecls(

lldb/source/Plugins/ExpressionParser/Clang/ClangExternalASTSourceCallbacks.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@ void ClangExternalASTSourceCallbacks::CompleteType(
2929

3030
void ClangExternalASTSourceCallbacks::CompleteRedeclChain(
3131
const clang::Decl *d) {
32-
if (!TypeSystemClang::UseRedeclCompletion())
33-
return;
34-
3532
if (const clang::TagDecl *td = llvm::dyn_cast<clang::TagDecl>(d)) {
3633
if (td->isBeingDefined())
3734
return;

0 commit comments

Comments
 (0)