Skip to content

Commit 66e12b4

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. (cherry picked from commit b4cfd2d)
1 parent 4032875 commit 66e12b4

File tree

16 files changed

+385
-89
lines changed

16 files changed

+385
-89
lines changed

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

Lines changed: 115 additions & 17 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"
@@ -15,6 +16,8 @@
1516
#include "clang/AST/DeclObjC.h"
1617
#include "clang/Sema/Lookup.h"
1718
#include "clang/Sema/Sema.h"
19+
#include "llvm/Support/Casting.h"
20+
#include "llvm/Support/ErrorHandling.h"
1821
#include "llvm/Support/raw_ostream.h"
1922

2023
#include "Plugins/ExpressionParser/Clang/ClangASTImporter.h"
@@ -278,7 +281,8 @@ class CompleteTagDeclsScope : public ClangASTImporter::NewDeclListener {
278281
NamedDecl *decl = m_decls_to_complete.pop_back_val();
279282
m_decls_already_completed.insert(decl);
280283

281-
CompleteDecl(decl, *to_context_md);
284+
if (!TypeSystemClang::UseRedeclCompletion())
285+
CompleteDecl(decl, *to_context_md);
282286

283287
to_context_md->removeOrigin(decl);
284288
}
@@ -293,6 +297,9 @@ class CompleteTagDeclsScope : public ClangASTImporter::NewDeclListener {
293297
if (!isa<TagDecl>(to) && !isa<ObjCInterfaceDecl>(to))
294298
return;
295299

300+
if (TypeSystemClang::UseRedeclCompletion())
301+
to = ClangUtil::GetFirstDecl(to);
302+
296303
RecordDecl *from_record_decl = dyn_cast<RecordDecl>(from);
297304
// We don't need to complete injected class name decls.
298305
if (from_record_decl && from_record_decl->isInjectedClassName())
@@ -507,7 +514,14 @@ bool ClangASTImporter::CompleteType(const CompilerType &compiler_type) {
507514
if (!CanImport(compiler_type))
508515
return false;
509516

510-
if (Import(compiler_type)) {
517+
auto const success = Import(compiler_type);
518+
519+
// With redecl completion we don't need to manually complete
520+
// the definition.
521+
if (TypeSystemClang::UseRedeclCompletion())
522+
return success;
523+
524+
if (success) {
511525
TypeSystemClang::CompleteTagDeclarationDefinition(compiler_type);
512526
return true;
513527
}
@@ -568,16 +582,42 @@ bool ClangASTImporter::CompleteTagDecl(clang::TagDecl *decl) {
568582
if (!decl_origin.Valid())
569583
return false;
570584

571-
if (!TypeSystemClang::GetCompleteDecl(decl_origin.ctx, decl_origin.decl))
572-
return false;
573-
574585
ImporterDelegateSP delegate_sp(
575586
GetDelegate(&decl->getASTContext(), decl_origin.ctx));
576587

577588
ASTImporterDelegate::CxxModuleScope std_scope(*delegate_sp,
578589
&decl->getASTContext());
579-
if (delegate_sp)
580-
delegate_sp->ImportDefinitionTo(decl, decl_origin.decl);
590+
591+
if (!TypeSystemClang::UseRedeclCompletion()) {
592+
if (!TypeSystemClang::GetCompleteDecl(decl_origin.ctx, decl_origin.decl))
593+
return false;
594+
595+
if (delegate_sp)
596+
delegate_sp->ImportDefinitionTo(decl, decl_origin.decl);
597+
} else {
598+
auto *origin_def = llvm::cast<TagDecl>(decl_origin.decl)->getDefinition();
599+
if (!origin_def)
600+
return false;
601+
602+
// This is expected to pull in a definition for result_decl (if in redecl
603+
// completion mode)
604+
llvm::Expected<Decl *> result = delegate_sp->Import(origin_def);
605+
if (!result) {
606+
llvm::handleAllErrors(result.takeError(),
607+
[](const clang::ASTImportError &e) {
608+
llvm::errs() << "ERR: " << e.toString() << "\n";
609+
});
610+
return false;
611+
}
612+
613+
// Create redeclaration chain with the 'to' decls.
614+
// Only need to do this if the 'result_decl' is a definition outside
615+
// of any redeclaration chain and the input 'decl' was a forward declaration
616+
TagDecl *result_decl = llvm::cast<TagDecl>(*result);
617+
if (!decl->isThisDeclarationADefinition() && result_decl != decl)
618+
if (result_decl->getPreviousDecl() == nullptr)
619+
result_decl->setPreviousDecl(decl);
620+
}
581621

582622
return true;
583623
}
@@ -598,24 +638,48 @@ bool ClangASTImporter::CompleteObjCInterfaceDecl(
598638
if (!decl_origin.Valid())
599639
return false;
600640

601-
if (!TypeSystemClang::GetCompleteDecl(decl_origin.ctx, decl_origin.decl))
602-
return false;
603-
604641
ImporterDelegateSP delegate_sp(
605642
GetDelegate(&interface_decl->getASTContext(), decl_origin.ctx));
606643

607-
if (delegate_sp)
608-
delegate_sp->ImportDefinitionTo(interface_decl, decl_origin.decl);
644+
if (!TypeSystemClang::UseRedeclCompletion()) {
645+
if (!TypeSystemClang::GetCompleteDecl(decl_origin.ctx, decl_origin.decl))
646+
return false;
647+
648+
if (delegate_sp)
649+
delegate_sp->ImportDefinitionTo(interface_decl, decl_origin.decl);
609650

610-
if (ObjCInterfaceDecl *super_class = interface_decl->getSuperClass())
611-
RequireCompleteType(clang::QualType(super_class->getTypeForDecl(), 0));
651+
if (ObjCInterfaceDecl *super_class = interface_decl->getSuperClass())
652+
RequireCompleteType(clang::QualType(super_class->getTypeForDecl(), 0));
653+
} else {
654+
ObjCInterfaceDecl *origin_decl =
655+
llvm::cast<ObjCInterfaceDecl>(decl_origin.decl);
656+
657+
origin_decl = origin_decl->getDefinition();
658+
if (!origin_decl)
659+
return false;
660+
661+
auto delegate_sp(
662+
GetDelegate(&interface_decl->getASTContext(), decl_origin.ctx));
663+
664+
llvm::Expected<Decl *> result = delegate_sp->Import(origin_decl);
665+
if (result)
666+
return true;
667+
668+
llvm::handleAllErrors(result.takeError(),
669+
[](const clang::ASTImportError &e) {
670+
llvm::errs() << "ERR: " << e.toString() << "\n";
671+
});
672+
673+
return false;
674+
}
612675

613676
return true;
614677
}
615678

616679
bool ClangASTImporter::CompleteAndFetchChildren(clang::QualType type) {
617-
if (!RequireCompleteType(type))
618-
return false;
680+
const auto ret = RequireCompleteType(type);
681+
if (TypeSystemClang::UseRedeclCompletion() || !ret)
682+
return ret;
619683

620684
Log *log = GetLog(LLDBLog::Expressions);
621685

@@ -862,6 +926,22 @@ ClangASTImporter::ASTImporterDelegate::ImportImpl(Decl *From) {
862926
}
863927
}
864928

929+
if (TypeSystemClang::UseRedeclCompletion()) {
930+
if (auto *source = llvm::dyn_cast<ImporterBackedASTSource>(
931+
getToContext().getExternalSource())) {
932+
// We added a new declaration (which is not a definition) into the
933+
// destination AST context, so bump the declaration chain generation
934+
// counter.
935+
if (clang::TagDecl *td = dyn_cast<TagDecl>(From))
936+
if (!td->isThisDeclarationADefinition())
937+
source->MarkRedeclChainsAsOutOfDate(getToContext());
938+
939+
if (clang::ObjCInterfaceDecl *td = dyn_cast<ObjCInterfaceDecl>(From))
940+
if (!td->isThisDeclarationADefinition())
941+
source->MarkRedeclChainsAsOutOfDate(getToContext());
942+
}
943+
}
944+
865945
// If we have a forcefully completed type, try to find an actual definition
866946
// for it in other modules.
867947
const ClangASTMetadata *md = m_main.GetDeclMetadata(From);
@@ -883,6 +963,14 @@ ClangASTImporter::ASTImporterDelegate::ImportImpl(Decl *From) {
883963
for (clang::Decl *candidate : lr) {
884964
if (candidate->getKind() == From->getKind()) {
885965
RegisterImportedDecl(From, candidate);
966+
967+
// If we're dealing with redecl chains. We want to find the definition,
968+
// so skip if the decl is actually just a forwad decl.
969+
if (TypeSystemClang::UseRedeclCompletion())
970+
if (auto *tag_decl = llvm::dyn_cast<TagDecl>(candidate);
971+
!tag_decl || !tag_decl->getDefinition())
972+
continue;
973+
886974
m_decls_to_ignore.insert(candidate);
887975
return candidate;
888976
}
@@ -1128,7 +1216,16 @@ void ClangASTImporter::ASTImporterDelegate::Imported(clang::Decl *from,
11281216
to_namespace_decl->setHasExternalVisibleStorage();
11291217
}
11301218

1131-
MarkDeclImported(from, to);
1219+
if (TypeSystemClang::UseRedeclCompletion()) {
1220+
if (clang::ObjCInterfaceDecl *td = dyn_cast<ObjCInterfaceDecl>(to)) {
1221+
if (clang::ExternalASTSource *s = getToContext().getExternalSource())
1222+
if (td->isThisDeclarationADefinition())
1223+
s->CompleteRedeclChain(td);
1224+
td->setHasExternalVisibleStorage();
1225+
}
1226+
} else {
1227+
MarkDeclImported(from, to);
1228+
}
11321229
}
11331230

11341231
void ClangASTImporter::ASTImporterDelegate::MarkDeclImported(Decl *from,
@@ -1199,4 +1296,5 @@ ClangASTImporter::ASTImporterDelegate::ASTImporterDelegate(
11991296
// do a minimal import and the imported declarations won't be completed.
12001297
assert(target_ctx->getExternalSource() && "Missing ExternalSource");
12011298
setODRHandling(clang::ASTImporter::ODRHandlingType::Liberal);
1299+
setLLDBRedeclCompletion(TypeSystemClang::UseRedeclCompletion());
12021300
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,18 @@ clang::Decl *ClangUtil::GetFirstDecl(clang::Decl *d) {
7676
return d;
7777
}
7878

79+
clang::ObjCInterfaceDecl *ClangUtil::GetAsObjCDecl(const CompilerType &type) {
80+
clang::QualType qual_type = ClangUtil::GetCanonicalQualType(type);
81+
if (qual_type.isNull())
82+
return nullptr;
83+
84+
if (const auto *ot = qual_type->getAsObjCInterfaceType())
85+
return ot->getInterface();
86+
if (const auto *ot = qual_type->getAsObjCInterfacePointerType())
87+
return ot->getInterfaceDecl();
88+
return nullptr;
89+
}
90+
7991
std::string ClangUtil::DumpDecl(const clang::Decl *d) {
8092
if (!d)
8193
return "nullptr";

lldb/source/Plugins/ExpressionParser/Clang/ClangUtil.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ struct ClangUtil {
4545
return GetFirstDecl(const_cast<clang::Decl *>(d));
4646
}
4747

48+
static clang::ObjCInterfaceDecl *GetAsObjCDecl(const CompilerType &type);
49+
4850
/// Returns a textual representation of the given Decl's AST. Does not
4951
/// deserialize any child nodes.
5052
static std::string DumpDecl(const clang::Decl *d);

lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class lldb_private::AppleObjCExternalASTSource
3030
AppleObjCExternalASTSource(AppleObjCDeclVendor &decl_vendor)
3131
: m_decl_vendor(decl_vendor) {}
3232

33+
// FIXME: unused when 'TypeSystemClang::UseRedeclCompletion == true'
3334
bool FindExternalVisibleDeclsByName(const clang::DeclContext *decl_ctx,
3435
clang::DeclarationName name) override {
3536

@@ -179,8 +180,13 @@ AppleObjCDeclVendor::GetDeclForISA(ObjCLanguageRuntime::ObjCISA isa) {
179180
meta_data.SetISAPtr(isa);
180181
m_ast_ctx->SetMetadata(new_iface_decl, meta_data);
181182

182-
new_iface_decl->setHasExternalVisibleStorage();
183-
new_iface_decl->setHasExternalLexicalStorage();
183+
if (TypeSystemClang::UseRedeclCompletion()) {
184+
m_interface_to_isa[new_iface_decl] = isa;
185+
m_external_source->MarkRedeclChainsAsOutOfDate(m_ast_ctx->getASTContext());
186+
} else {
187+
new_iface_decl->setHasExternalVisibleStorage();
188+
new_iface_decl->setHasExternalLexicalStorage();
189+
}
184190

185191
ast_ctx.getTranslationUnitDecl()->addDecl(new_iface_decl);
186192

@@ -408,6 +414,21 @@ bool AppleObjCDeclVendor::FinishDecl(clang::ObjCInterfaceDecl *interface_decl) {
408414
Log *log(
409415
GetLog(LLDBLog::Expressions)); // FIXME - a more appropriate log channel?
410416

417+
clang::ObjCInterfaceDecl *iface_def = nullptr;
418+
if (TypeSystemClang::UseRedeclCompletion()) {
419+
// Already completed.
420+
if (interface_decl->hasDefinition())
421+
return true;
422+
423+
clang::QualType qt(interface_decl->getTypeForDecl(), 0U);
424+
CompilerType type(m_ast_ctx, qt.getAsOpaquePtr());
425+
CompilerType def_type = m_ast_ctx->CreateRedeclaration(type);
426+
iface_def = ClangUtil::GetAsObjCDecl(def_type);
427+
428+
auto isa = m_interface_to_isa[interface_decl];
429+
m_isa_to_interface[isa] = iface_def;
430+
}
431+
411432
ClangASTMetadata *metadata = m_ast_ctx->GetMetadata(interface_decl);
412433
ObjCLanguageRuntime::ObjCISA objc_isa = 0;
413434
if (metadata)
@@ -416,13 +437,20 @@ bool AppleObjCDeclVendor::FinishDecl(clang::ObjCInterfaceDecl *interface_decl) {
416437
if (!objc_isa)
417438
return false;
418439

419-
if (!interface_decl->hasExternalVisibleStorage())
440+
if (!TypeSystemClang::UseRedeclCompletion() &&
441+
!interface_decl->hasExternalVisibleStorage())
442+
return true;
443+
444+
// Could've completed during CreateRedeclaration above
445+
if (TypeSystemClang::UseRedeclCompletion() && interface_decl->hasDefinition())
420446
return true;
421447

422448
interface_decl->startDefinition();
423449

424-
interface_decl->setHasExternalVisibleStorage(false);
425-
interface_decl->setHasExternalLexicalStorage(false);
450+
if (!TypeSystemClang::UseRedeclCompletion()) {
451+
interface_decl->setHasExternalVisibleStorage(false);
452+
interface_decl->setHasExternalLexicalStorage(false);
453+
}
426454

427455
ObjCLanguageRuntime::ClassDescriptorSP descriptor =
428456
m_runtime.GetClassDescriptorFromISA(objc_isa);

lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ class AppleObjCDeclVendor : public ClangDeclVendor {
4646
ISAToInterfaceMap;
4747

4848
ISAToInterfaceMap m_isa_to_interface;
49+
50+
using InterfaceToISAMap =
51+
llvm::DenseMap<clang::ObjCInterfaceDecl *, ObjCLanguageRuntime::ObjCISA>;
52+
InterfaceToISAMap m_interface_to_isa;
4953
};
5054

5155
} // namespace lldb_private

0 commit comments

Comments
 (0)