Skip to content

Commit 47e7ecd

Browse files
committed
[lldb] Introduce separate scratch ASTs for debug info types and types imported from C++ modules.
Right now we have one large AST for all types in LLDB. All ODR violations in types we reconstruct are resolved by just letting the ASTImporter handle the conflicts (either by merging types or somehow trying to introduce a duplicated declaration in the AST). This works ok for the normal types we build from debug information as most of them are just simple CXXRecordDecls or empty template declarations. However, with a loaded `std` C++ module we have alternative versions of pretty much all declarations in the `std` namespace that are much more fleshed out than the debug information declarations. They have all the information that is lost when converting to DWARF, such as default arguments, template default arguments, the actual uninstantiated template declarations and so on. When we merge these C++ module types into the big scratch AST (that might already contain debug information types) we give the ASTImporter the tricky task of somehow creating a consistent AST out of all these declarations. Usually this ends in a messy AST that contains a mostly broken mix of both module and debug info declarations. The ASTImporter in LLDB is also importing types with the MinimalImport setting, which usually means the only information we have when merging two types is often just the name of the declaration and the information that it contains some child declarations. This makes it pretty much impossible to even implement a better merging logic (as the names of C++ module declarations and debug info declarations are identical). This patch works around this whole merging problem by separating C++ module types from debug information types. This is done by splitting up the single scratch AST into two: One default AST for debug information and a dedicated AST for C++ module types. The C++ module AST is implemented as a 'specialised AST' that lives within the default ScratchTypeSystemClang. When we select the scratch AST we can explicitly request that we want such a isolated sub-AST of the scratch AST. I kept the infrastructure more general as we probably can use the same mechanism for other features that introduce conflicting types (such as programs that are compiled with a custom -wchar-size= option). There are just two places where we explicitly have request the C++ module AST: When we export persistent declarations (`$mytype`) and when we create our persistent result variable (`$0`, `$1`, ...). There are a few formatters that were previously assuming that there is only one scratch AST which I cleaned up in a preparation revision here (D92757). Reviewed By: aprantl Differential Revision: https://reviews.llvm.org/D92759
1 parent 3f70987 commit 47e7ecd

File tree

10 files changed

+301
-24
lines changed

10 files changed

+301
-24
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,9 @@ void ASTResultSynthesizer::CommitPersistentDecls() {
443443
return;
444444

445445
auto *persistent_vars = llvm::cast<ClangPersistentVariables>(state);
446-
TypeSystemClang *scratch_ctx = ScratchTypeSystemClang::GetForTarget(m_target);
446+
447+
TypeSystemClang *scratch_ctx = ScratchTypeSystemClang::GetForTarget(
448+
m_target, m_ast_context->getLangOpts());
447449

448450
for (clang::NamedDecl *decl : m_decls) {
449451
StringRef name = decl->getName();

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

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -71,20 +71,22 @@ ClangASTSource::~ClangASTSource() {
7171

7272
if (!m_target)
7373
return;
74-
// We are in the process of destruction, don't create clang ast context on
75-
// demand by passing false to
76-
// Target::GetScratchTypeSystemClang(create_on_demand).
77-
TypeSystemClang *scratch_clang_ast_context =
78-
ScratchTypeSystemClang::GetForTarget(*m_target, false);
7974

80-
if (!scratch_clang_ast_context)
81-
return;
75+
// Unregister the current ASTContext as a source for all scratch
76+
// ASTContexts in the ClangASTImporter. Without this the scratch AST might
77+
// query the deleted ASTContext for additional type information.
78+
// We unregister from *all* scratch ASTContexts in case a type got exported
79+
// to a scratch AST that isn't the best fitting scratch ASTContext.
80+
TypeSystemClang *scratch_ast = ScratchTypeSystemClang::GetForTarget(
81+
*m_target, ScratchTypeSystemClang::DefaultAST, false);
8282

83-
clang::ASTContext &scratch_ast_context =
84-
scratch_clang_ast_context->getASTContext();
83+
if (!scratch_ast)
84+
return;
8585

86-
if (m_ast_context != &scratch_ast_context && m_ast_importer_sp)
87-
m_ast_importer_sp->ForgetSource(&scratch_ast_context, m_ast_context);
86+
ScratchTypeSystemClang *default_scratch_ast =
87+
llvm::cast<ScratchTypeSystemClang>(scratch_ast);
88+
// Unregister from the default scratch AST (and all sub-ASTs).
89+
default_scratch_ast->ForgetSource(m_ast_context, *m_ast_importer_sp);
8890
}
8991

9092
void ClangASTSource::StartTranslationUnit(ASTConsumer *Consumer) {

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ ClangExpressionDeclMap::TargetInfo ClangExpressionDeclMap::GetTargetInfo() {
184184
TypeFromUser ClangExpressionDeclMap::DeportType(TypeSystemClang &target,
185185
TypeSystemClang &source,
186186
TypeFromParser parser_type) {
187-
assert(&target == ScratchTypeSystemClang::GetForTarget(*m_target));
187+
assert(&target == GetScratchContext(*m_target));
188188
assert((TypeSystem *)&source == parser_type.GetTypeSystem());
189189
assert(&source.getASTContext() == m_ast_context);
190190

@@ -222,7 +222,7 @@ bool ClangExpressionDeclMap::AddPersistentVariable(const NamedDecl *decl,
222222
if (target == nullptr)
223223
return false;
224224

225-
auto *clang_ast_context = ScratchTypeSystemClang::GetForTarget(*target);
225+
auto *clang_ast_context = GetScratchContext(*target);
226226
if (!clang_ast_context)
227227
return false;
228228

@@ -260,7 +260,7 @@ bool ClangExpressionDeclMap::AddPersistentVariable(const NamedDecl *decl,
260260
if (target == nullptr)
261261
return false;
262262

263-
TypeSystemClang *context = ScratchTypeSystemClang::GetForTarget(*target);
263+
TypeSystemClang *context = GetScratchContext(*target);
264264
if (!context)
265265
return false;
266266

@@ -1638,8 +1638,7 @@ void ClangExpressionDeclMap::AddOneGenericVariable(NameSearchContext &context,
16381638
if (target == nullptr)
16391639
return;
16401640

1641-
TypeSystemClang *scratch_ast_context =
1642-
ScratchTypeSystemClang::GetForTarget(*target);
1641+
TypeSystemClang *scratch_ast_context = GetScratchContext(*target);
16431642
if (!scratch_ast_context)
16441643
return;
16451644

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,11 @@ class ClangExpressionDeclMap : public ClangASTSource {
380380
/// Deallocate struct variables
381381
void DisableStructVars() { m_struct_vars.reset(); }
382382

383+
TypeSystemClang *GetScratchContext(Target &target) {
384+
return ScratchTypeSystemClang::GetForTarget(target,
385+
m_ast_context->getLangOpts());
386+
}
387+
383388
/// Get this parser's ID for use in extracting parser- and JIT-specific data
384389
/// from persistent variables.
385390
uint64_t GetParserID() { return (uint64_t) this; }

lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9558,13 +9558,42 @@ TypeSystemClang::DeclContextGetTypeSystemClang(const CompilerDeclContext &dc) {
95589558
return nullptr;
95599559
}
95609560

9561+
namespace {
9562+
/// A specialized scratch AST used within ScratchTypeSystemClang.
9563+
/// These are the ASTs backing the different IsolatedASTKinds. They behave
9564+
/// like a normal ScratchTypeSystemClang but they don't own their own
9565+
/// persistent storage or target reference.
9566+
class SpecializedScratchAST : public TypeSystemClang {
9567+
public:
9568+
/// \param name The display name of the TypeSystemClang instance.
9569+
/// \param triple The triple used for the TypeSystemClang instance.
9570+
/// \param ast_source The ClangASTSource that should be used to complete
9571+
/// type information.
9572+
SpecializedScratchAST(llvm::StringRef name, llvm::Triple triple,
9573+
std::unique_ptr<ClangASTSource> ast_source)
9574+
: TypeSystemClang(name, triple),
9575+
m_scratch_ast_source_up(std::move(ast_source)) {
9576+
// Setup the ClangASTSource to complete this AST.
9577+
m_scratch_ast_source_up->InstallASTContext(*this);
9578+
llvm::IntrusiveRefCntPtr<clang::ExternalASTSource> proxy_ast_source(
9579+
m_scratch_ast_source_up->CreateProxy());
9580+
SetExternalSource(proxy_ast_source);
9581+
}
9582+
9583+
/// The ExternalASTSource that performs lookups and completes types.
9584+
std::unique_ptr<ClangASTSource> m_scratch_ast_source_up;
9585+
};
9586+
} // namespace
9587+
9588+
char ScratchTypeSystemClang::ID;
9589+
const llvm::NoneType ScratchTypeSystemClang::DefaultAST = llvm::None;
9590+
95619591
ScratchTypeSystemClang::ScratchTypeSystemClang(Target &target,
95629592
llvm::Triple triple)
9563-
: TypeSystemClang("scratch ASTContext", triple),
9593+
: TypeSystemClang("scratch ASTContext", triple), m_triple(triple),
95649594
m_target_wp(target.shared_from_this()),
95659595
m_persistent_variables(new ClangPersistentVariables) {
9566-
m_scratch_ast_source_up = std::make_unique<ClangASTSource>(
9567-
target.shared_from_this(), m_persistent_variables->GetClangASTImporter());
9596+
m_scratch_ast_source_up = CreateASTSource();
95689597
m_scratch_ast_source_up->InstallASTContext(*this);
95699598
llvm::IntrusiveRefCntPtr<clang::ExternalASTSource> proxy_ast_source(
95709599
m_scratch_ast_source_up->CreateProxy());
@@ -9576,16 +9605,24 @@ void ScratchTypeSystemClang::Finalize() {
95769605
m_scratch_ast_source_up.reset();
95779606
}
95789607

9579-
TypeSystemClang *ScratchTypeSystemClang::GetForTarget(Target &target,
9580-
bool create_on_demand) {
9608+
TypeSystemClang *
9609+
ScratchTypeSystemClang::GetForTarget(Target &target,
9610+
llvm::Optional<IsolatedASTKind> ast_kind,
9611+
bool create_on_demand) {
95819612
auto type_system_or_err = target.GetScratchTypeSystemForLanguage(
95829613
lldb::eLanguageTypeC, create_on_demand);
95839614
if (auto err = type_system_or_err.takeError()) {
95849615
LLDB_LOG_ERROR(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_TARGET),
95859616
std::move(err), "Couldn't get scratch TypeSystemClang");
95869617
return nullptr;
95879618
}
9588-
return llvm::dyn_cast<TypeSystemClang>(&type_system_or_err.get());
9619+
ScratchTypeSystemClang &scratch_ast =
9620+
llvm::cast<ScratchTypeSystemClang>(type_system_or_err.get());
9621+
// If no dedicated sub-AST was requested, just return the main AST.
9622+
if (ast_kind == DefaultAST)
9623+
return &scratch_ast;
9624+
// Search the sub-ASTs.
9625+
return &scratch_ast.GetIsolatedAST(*ast_kind);
95899626
}
95909627

95919628
UserExpression *ScratchTypeSystemClang::GetUserExpression(
@@ -9630,3 +9667,41 @@ PersistentExpressionState *
96309667
ScratchTypeSystemClang::GetPersistentExpressionState() {
96319668
return m_persistent_variables.get();
96329669
}
9670+
9671+
void ScratchTypeSystemClang::ForgetSource(ASTContext *src_ctx,
9672+
ClangASTImporter &importer) {
9673+
// Remove it as a source from the main AST.
9674+
importer.ForgetSource(&getASTContext(), src_ctx);
9675+
// Remove it as a source from all created sub-ASTs.
9676+
for (const auto &a : m_isolated_asts)
9677+
importer.ForgetSource(&a.second->getASTContext(), src_ctx);
9678+
}
9679+
9680+
std::unique_ptr<ClangASTSource> ScratchTypeSystemClang::CreateASTSource() {
9681+
return std::make_unique<ClangASTSource>(
9682+
m_target_wp.lock()->shared_from_this(),
9683+
m_persistent_variables->GetClangASTImporter());
9684+
}
9685+
9686+
static llvm::StringRef
9687+
GetSpecializedASTName(ScratchTypeSystemClang::IsolatedASTKind feature) {
9688+
switch (feature) {
9689+
case ScratchTypeSystemClang::IsolatedASTKind::CppModules:
9690+
return "scratch ASTContext for C++ module types";
9691+
}
9692+
llvm_unreachable("Unimplemented ASTFeature kind?");
9693+
}
9694+
9695+
TypeSystemClang &ScratchTypeSystemClang::GetIsolatedAST(
9696+
ScratchTypeSystemClang::IsolatedASTKind feature) {
9697+
auto found_ast = m_isolated_asts.find(feature);
9698+
if (found_ast != m_isolated_asts.end())
9699+
return *found_ast->second;
9700+
9701+
// Couldn't find the requested sub-AST, so create it now.
9702+
std::unique_ptr<TypeSystemClang> new_ast;
9703+
new_ast.reset(new SpecializedScratchAST(GetSpecializedASTName(feature),
9704+
m_triple, CreateASTSource()));
9705+
m_isolated_asts[feature] = std::move(new_ast);
9706+
return *m_isolated_asts[feature];
9707+
}

lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1110,22 +1110,69 @@ class TypeSystemClang : public TypeSystem {
11101110
/// The TypeSystemClang instance used for the scratch ASTContext in a
11111111
/// lldb::Target.
11121112
class ScratchTypeSystemClang : public TypeSystemClang {
1113+
/// LLVM RTTI support
1114+
static char ID;
1115+
11131116
public:
11141117
ScratchTypeSystemClang(Target &target, llvm::Triple triple);
11151118

11161119
~ScratchTypeSystemClang() override = default;
11171120

11181121
void Finalize() override;
11191122

1123+
/// The different kinds of isolated ASTs within the scratch TypeSystem.
1124+
///
1125+
/// These ASTs are isolated from the main scratch AST and are each
1126+
/// dedicated to a special language option/feature that makes the contained
1127+
/// AST nodes incompatible with other AST nodes.
1128+
enum class IsolatedASTKind {
1129+
/// The isolated AST for declarations/types from expressions that imported
1130+
/// type information from a C++ module. The templates from a C++ module
1131+
/// often conflict with the templates we generate from debug information,
1132+
/// so we put these types in their own AST.
1133+
CppModules
1134+
};
1135+
1136+
/// Alias for requesting the default scratch TypeSystemClang in GetForTarget.
1137+
// This isn't constexpr as gtest/llvm::Optional comparison logic is trying
1138+
// to get the address of this for pretty-printing.
1139+
static const llvm::NoneType DefaultAST;
1140+
1141+
/// Infers the appropriate sub-AST from Clang's LangOptions.
1142+
static llvm::Optional<IsolatedASTKind>
1143+
InferIsolatedASTKindFromLangOpts(const clang::LangOptions &l) {
1144+
// If modules are activated we want the dedicated C++ module AST.
1145+
// See IsolatedASTKind::CppModules for more info.
1146+
if (l.Modules)
1147+
return IsolatedASTKind::CppModules;
1148+
return DefaultAST;
1149+
}
1150+
11201151
/// Returns the scratch TypeSystemClang for the given target.
11211152
/// \param target The Target which scratch TypeSystemClang should be returned.
1153+
/// \param ast_kind Allows requesting a specific sub-AST instead of the
1154+
/// default scratch AST. See also `IsolatedASTKind`.
11221155
/// \param create_on_demand If the scratch TypeSystemClang instance can be
11231156
/// created by this call if it doesn't exist yet. If it doesn't exist yet and
11241157
/// this parameter is false, this function returns a nullptr.
11251158
/// \return The scratch type system of the target or a nullptr in case an
11261159
/// error occurred.
1160+
static TypeSystemClang *
1161+
GetForTarget(Target &target,
1162+
llvm::Optional<IsolatedASTKind> ast_kind = DefaultAST,
1163+
bool create_on_demand = true);
1164+
1165+
/// Returns the scratch TypeSystemClang for the given target. The returned
1166+
/// TypeSystemClang will be the scratch AST or a sub-AST, depending on which
1167+
/// fits best to the passed LangOptions.
1168+
/// \param target The Target which scratch TypeSystemClang should be returned.
1169+
/// \param lang_opts The LangOptions of a clang ASTContext that the caller
1170+
/// wants to export type information from. This is used to
1171+
/// find the best matching sub-AST that will be returned.
11271172
static TypeSystemClang *GetForTarget(Target &target,
1128-
bool create_on_demand = true);
1173+
const clang::LangOptions &lang_opts) {
1174+
return GetForTarget(target, InferIsolatedASTKindFromLangOpts(lang_opts));
1175+
}
11291176

11301177
UserExpression *
11311178
GetUserExpression(llvm::StringRef expr, llvm::StringRef prefix,
@@ -1143,14 +1190,41 @@ class ScratchTypeSystemClang : public TypeSystemClang {
11431190
CreateUtilityFunction(std::string text, std::string name) override;
11441191

11451192
PersistentExpressionState *GetPersistentExpressionState() override;
1193+
1194+
/// Unregisters the given ASTContext as a source from the scratch AST (and
1195+
/// all sub-ASTs).
1196+
/// \see ClangASTImporter::ForgetSource
1197+
void ForgetSource(clang::ASTContext *src_ctx, ClangASTImporter &importer);
1198+
1199+
// llvm casting support
1200+
bool isA(const void *ClassID) const override {
1201+
return ClassID == &ID || TypeSystemClang::isA(ClassID);
1202+
}
1203+
static bool classof(const TypeSystem *ts) { return ts->isA(&ID); }
1204+
11461205
private:
1206+
std::unique_ptr<ClangASTSource> CreateASTSource();
1207+
/// Returns the requested sub-AST.
1208+
/// Will lazily create the sub-AST if it hasn't been created before.
1209+
TypeSystemClang &GetIsolatedAST(IsolatedASTKind feature);
1210+
1211+
/// The target triple.
1212+
/// This was potentially adjusted and might not be identical to the triple
1213+
/// of `m_target_wp`.
1214+
llvm::Triple m_triple;
11471215
lldb::TargetWP m_target_wp;
11481216
/// The persistent variables associated with this process for the expression
11491217
/// parser.
11501218
std::unique_ptr<ClangPersistentVariables> m_persistent_variables;
11511219
/// The ExternalASTSource that performs lookups and completes minimally
11521220
/// imported types.
11531221
std::unique_ptr<ClangASTSource> m_scratch_ast_source_up;
1222+
1223+
/// Map from IsolatedASTKind to their actual TypeSystemClang instance.
1224+
/// This map is lazily filled with sub-ASTs and should be accessed via
1225+
/// `GetSubAST` (which lazily fills this map).
1226+
std::unordered_map<IsolatedASTKind, std::unique_ptr<TypeSystemClang>>
1227+
m_isolated_asts;
11541228
};
11551229

11561230
} // namespace lldb_private
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
USE_LIBCPP := 1
2+
CXX_SOURCES := main.cpp
3+
include Makefile.rules

0 commit comments

Comments
 (0)