Skip to content

Commit 5211a31

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 (cherry picked from commit 47e7ecd)
1 parent b1dabed commit 5211a31

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
@@ -9561,13 +9561,42 @@ TypeSystemClang::DeclContextGetTypeSystemClang(const CompilerDeclContext &dc) {
95619561
return nullptr;
95629562
}
95639563

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

9582-
TypeSystemClang *ScratchTypeSystemClang::GetForTarget(Target &target,
9583-
bool create_on_demand) {
9611+
TypeSystemClang *
9612+
ScratchTypeSystemClang::GetForTarget(Target &target,
9613+
llvm::Optional<IsolatedASTKind> ast_kind,
9614+
bool create_on_demand) {
95849615
auto type_system_or_err = target.GetScratchTypeSystemForLanguage(
95859616
lldb::eLanguageTypeC, create_on_demand);
95869617
if (auto err = type_system_or_err.takeError()) {
95879618
LLDB_LOG_ERROR(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_TARGET),
95889619
std::move(err), "Couldn't get scratch TypeSystemClang");
95899620
return nullptr;
95909621
}
9591-
return llvm::dyn_cast<TypeSystemClang>(&type_system_or_err.get());
9622+
ScratchTypeSystemClang &scratch_ast =
9623+
llvm::cast<ScratchTypeSystemClang>(type_system_or_err.get());
9624+
// If no dedicated sub-AST was requested, just return the main AST.
9625+
if (ast_kind == DefaultAST)
9626+
return &scratch_ast;
9627+
// Search the sub-ASTs.
9628+
return &scratch_ast.GetIsolatedAST(*ast_kind);
95929629
}
95939630

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

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

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1115,22 +1115,69 @@ class TypeSystemClang : public TypeSystem {
11151115
/// The TypeSystemClang instance used for the scratch ASTContext in a
11161116
/// lldb::Target.
11171117
class ScratchTypeSystemClang : public TypeSystemClang {
1118+
/// LLVM RTTI support
1119+
static char ID;
1120+
11181121
public:
11191122
ScratchTypeSystemClang(Target &target, llvm::Triple triple);
11201123

11211124
~ScratchTypeSystemClang() override = default;
11221125

11231126
void Finalize() override;
11241127

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

11351182
UserExpression *
11361183
GetUserExpression(llvm::StringRef expr, llvm::StringRef prefix,
@@ -1148,14 +1195,41 @@ class ScratchTypeSystemClang : public TypeSystemClang {
11481195
const char *name) override;
11491196

11501197
PersistentExpressionState *GetPersistentExpressionState() override;
1198+
1199+
/// Unregisters the given ASTContext as a source from the scratch AST (and
1200+
/// all sub-ASTs).
1201+
/// \see ClangASTImporter::ForgetSource
1202+
void ForgetSource(clang::ASTContext *src_ctx, ClangASTImporter &importer);
1203+
1204+
// llvm casting support
1205+
bool isA(const void *ClassID) const override {
1206+
return ClassID == &ID || TypeSystemClang::isA(ClassID);
1207+
}
1208+
static bool classof(const TypeSystem *ts) { return ts->isA(&ID); }
1209+
11511210
private:
1211+
std::unique_ptr<ClangASTSource> CreateASTSource();
1212+
/// Returns the requested sub-AST.
1213+
/// Will lazily create the sub-AST if it hasn't been created before.
1214+
TypeSystemClang &GetIsolatedAST(IsolatedASTKind feature);
1215+
1216+
/// The target triple.
1217+
/// This was potentially adjusted and might not be identical to the triple
1218+
/// of `m_target_wp`.
1219+
llvm::Triple m_triple;
11521220
lldb::TargetWP m_target_wp;
11531221
/// The persistent variables associated with this process for the expression
11541222
/// parser.
11551223
std::unique_ptr<ClangPersistentVariables> m_persistent_variables;
11561224
/// The ExternalASTSource that performs lookups and completes minimally
11571225
/// imported types.
11581226
std::unique_ptr<ClangASTSource> m_scratch_ast_source_up;
1227+
1228+
/// Map from IsolatedASTKind to their actual TypeSystemClang instance.
1229+
/// This map is lazily filled with sub-ASTs and should be accessed via
1230+
/// `GetSubAST` (which lazily fills this map).
1231+
std::unordered_map<IsolatedASTKind, std::unique_ptr<TypeSystemClang>>
1232+
m_isolated_asts;
11591233
};
11601234

11611235
} // 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)