Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/CxxModuleHandler.cpp
39648 views
1
//===-- CxxModuleHandler.cpp ----------------------------------------------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include "Plugins/ExpressionParser/Clang/CxxModuleHandler.h"
10
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
11
12
#include "lldb/Utility/LLDBLog.h"
13
#include "lldb/Utility/Log.h"
14
#include "clang/Sema/Lookup.h"
15
#include "llvm/Support/Error.h"
16
#include <optional>
17
18
using namespace lldb_private;
19
using namespace clang;
20
21
CxxModuleHandler::CxxModuleHandler(ASTImporter &importer, ASTContext *target)
22
: m_importer(&importer),
23
m_sema(TypeSystemClang::GetASTContext(target)->getSema()) {
24
25
std::initializer_list<const char *> supported_names = {
26
// containers
27
"array",
28
"deque",
29
"forward_list",
30
"list",
31
"queue",
32
"stack",
33
"vector",
34
// pointers
35
"shared_ptr",
36
"unique_ptr",
37
"weak_ptr",
38
// iterator
39
"move_iterator",
40
"__wrap_iter",
41
// utility
42
"allocator",
43
"pair",
44
};
45
m_supported_templates.insert(supported_names.begin(), supported_names.end());
46
}
47
48
/// Builds a list of scopes that point into the given context.
49
///
50
/// \param sema The sema that will be using the scopes.
51
/// \param ctxt The context that the scope should look into.
52
/// \param result A list of scopes. The scopes need to be freed by the caller
53
/// (except the TUScope which is owned by the sema).
54
static void makeScopes(Sema &sema, DeclContext *ctxt,
55
std::vector<Scope *> &result) {
56
// FIXME: The result should be a list of unique_ptrs, but the TUScope makes
57
// this currently impossible as it's owned by the Sema.
58
59
if (auto parent = ctxt->getParent()) {
60
makeScopes(sema, parent, result);
61
62
Scope *scope =
63
new Scope(result.back(), Scope::DeclScope, sema.getDiagnostics());
64
scope->setEntity(ctxt);
65
result.push_back(scope);
66
} else
67
result.push_back(sema.TUScope);
68
}
69
70
/// Uses the Sema to look up the given name in the given DeclContext.
71
static std::unique_ptr<LookupResult>
72
emulateLookupInCtxt(Sema &sema, llvm::StringRef name, DeclContext *ctxt) {
73
IdentifierInfo &ident = sema.getASTContext().Idents.get(name);
74
75
std::unique_ptr<LookupResult> lookup_result;
76
lookup_result = std::make_unique<LookupResult>(sema, DeclarationName(&ident),
77
SourceLocation(),
78
Sema::LookupOrdinaryName);
79
80
// Usually during parsing we already encountered the scopes we would use. But
81
// here don't have these scopes so we have to emulate the behavior of the
82
// Sema during parsing.
83
std::vector<Scope *> scopes;
84
makeScopes(sema, ctxt, scopes);
85
86
// Now actually perform the lookup with the sema.
87
sema.LookupName(*lookup_result, scopes.back());
88
89
// Delete all the allocated scopes beside the translation unit scope (which
90
// has depth 0).
91
for (Scope *s : scopes)
92
if (s->getDepth() != 0)
93
delete s;
94
95
return lookup_result;
96
}
97
98
/// Error class for handling problems when finding a certain DeclContext.
99
struct MissingDeclContext : public llvm::ErrorInfo<MissingDeclContext> {
100
101
static char ID;
102
103
MissingDeclContext(DeclContext *context, std::string error)
104
: m_context(context), m_error(error) {}
105
106
DeclContext *m_context;
107
std::string m_error;
108
109
void log(llvm::raw_ostream &OS) const override {
110
OS << llvm::formatv("error when reconstructing context of kind {0}:{1}",
111
m_context->getDeclKindName(), m_error);
112
}
113
114
std::error_code convertToErrorCode() const override {
115
return llvm::inconvertibleErrorCode();
116
}
117
};
118
119
char MissingDeclContext::ID = 0;
120
121
/// Given a foreign decl context, this function finds the equivalent local
122
/// decl context in the ASTContext of the given Sema. Potentially deserializes
123
/// decls from the 'std' module if necessary.
124
static llvm::Expected<DeclContext *>
125
getEqualLocalDeclContext(Sema &sema, DeclContext *foreign_ctxt) {
126
127
// Inline namespaces don't matter for lookups, so let's skip them.
128
while (foreign_ctxt && foreign_ctxt->isInlineNamespace())
129
foreign_ctxt = foreign_ctxt->getParent();
130
131
// If the foreign context is the TU, we just return the local TU.
132
if (foreign_ctxt->isTranslationUnit())
133
return sema.getASTContext().getTranslationUnitDecl();
134
135
// Recursively find/build the parent DeclContext.
136
llvm::Expected<DeclContext *> parent =
137
getEqualLocalDeclContext(sema, foreign_ctxt->getParent());
138
if (!parent)
139
return parent;
140
141
// We currently only support building namespaces.
142
if (foreign_ctxt->isNamespace()) {
143
NamedDecl *ns = llvm::cast<NamedDecl>(foreign_ctxt);
144
llvm::StringRef ns_name = ns->getName();
145
146
auto lookup_result = emulateLookupInCtxt(sema, ns_name, *parent);
147
for (NamedDecl *named_decl : *lookup_result) {
148
if (DeclContext *DC = llvm::dyn_cast<DeclContext>(named_decl))
149
return DC->getPrimaryContext();
150
}
151
return llvm::make_error<MissingDeclContext>(
152
foreign_ctxt,
153
"Couldn't find namespace " + ns->getQualifiedNameAsString());
154
}
155
156
return llvm::make_error<MissingDeclContext>(foreign_ctxt, "Unknown context ");
157
}
158
159
/// Returns true iff tryInstantiateStdTemplate supports instantiating a template
160
/// with the given template arguments.
161
static bool templateArgsAreSupported(ArrayRef<TemplateArgument> a) {
162
for (const TemplateArgument &arg : a) {
163
switch (arg.getKind()) {
164
case TemplateArgument::Type:
165
case TemplateArgument::Integral:
166
break;
167
default:
168
// TemplateArgument kind hasn't been handled yet.
169
return false;
170
}
171
}
172
return true;
173
}
174
175
/// Constructor function for Clang declarations. Ensures that the created
176
/// declaration is registered with the ASTImporter.
177
template <typename T, typename... Args>
178
T *createDecl(ASTImporter &importer, Decl *from_d, Args &&... args) {
179
T *to_d = T::Create(std::forward<Args>(args)...);
180
importer.RegisterImportedDecl(from_d, to_d);
181
return to_d;
182
}
183
184
std::optional<Decl *> CxxModuleHandler::tryInstantiateStdTemplate(Decl *d) {
185
Log *log = GetLog(LLDBLog::Expressions);
186
187
// If we don't have a template to instiantiate, then there is nothing to do.
188
auto td = dyn_cast<ClassTemplateSpecializationDecl>(d);
189
if (!td)
190
return std::nullopt;
191
192
// We only care about templates in the std namespace.
193
if (!td->getDeclContext()->isStdNamespace())
194
return std::nullopt;
195
196
// We have a list of supported template names.
197
if (!m_supported_templates.contains(td->getName()))
198
return std::nullopt;
199
200
// Early check if we even support instantiating this template. We do this
201
// before we import anything into the target AST.
202
auto &foreign_args = td->getTemplateInstantiationArgs();
203
if (!templateArgsAreSupported(foreign_args.asArray()))
204
return std::nullopt;
205
206
// Find the local DeclContext that corresponds to the DeclContext of our
207
// decl we want to import.
208
llvm::Expected<DeclContext *> to_context =
209
getEqualLocalDeclContext(*m_sema, td->getDeclContext());
210
if (!to_context) {
211
LLDB_LOG_ERROR(log, to_context.takeError(),
212
"Got error while searching equal local DeclContext for decl "
213
"'{1}':\n{0}",
214
td->getName());
215
return std::nullopt;
216
}
217
218
// Look up the template in our local context.
219
std::unique_ptr<LookupResult> lookup =
220
emulateLookupInCtxt(*m_sema, td->getName(), *to_context);
221
222
ClassTemplateDecl *new_class_template = nullptr;
223
for (auto LD : *lookup) {
224
if ((new_class_template = dyn_cast<ClassTemplateDecl>(LD)))
225
break;
226
}
227
if (!new_class_template)
228
return std::nullopt;
229
230
// Import the foreign template arguments.
231
llvm::SmallVector<TemplateArgument, 4> imported_args;
232
233
// If this logic is changed, also update templateArgsAreSupported.
234
for (const TemplateArgument &arg : foreign_args.asArray()) {
235
switch (arg.getKind()) {
236
case TemplateArgument::Type: {
237
llvm::Expected<QualType> type = m_importer->Import(arg.getAsType());
238
if (!type) {
239
LLDB_LOG_ERROR(log, type.takeError(), "Couldn't import type: {0}");
240
return std::nullopt;
241
}
242
imported_args.push_back(
243
TemplateArgument(*type, /*isNullPtr*/ false, arg.getIsDefaulted()));
244
break;
245
}
246
case TemplateArgument::Integral: {
247
llvm::APSInt integral = arg.getAsIntegral();
248
llvm::Expected<QualType> type =
249
m_importer->Import(arg.getIntegralType());
250
if (!type) {
251
LLDB_LOG_ERROR(log, type.takeError(), "Couldn't import type: {0}");
252
return std::nullopt;
253
}
254
imported_args.push_back(TemplateArgument(d->getASTContext(), integral,
255
*type, arg.getIsDefaulted()));
256
break;
257
}
258
default:
259
assert(false && "templateArgsAreSupported not updated?");
260
}
261
}
262
263
// Find the class template specialization declaration that
264
// corresponds to these arguments.
265
void *InsertPos = nullptr;
266
ClassTemplateSpecializationDecl *result =
267
new_class_template->findSpecialization(imported_args, InsertPos);
268
269
if (result) {
270
// We found an existing specialization in the module that fits our arguments
271
// so we can treat it as the result and register it with the ASTImporter.
272
m_importer->RegisterImportedDecl(d, result);
273
return result;
274
}
275
276
// Instantiate the template.
277
result = createDecl<ClassTemplateSpecializationDecl>(
278
*m_importer, d, m_sema->getASTContext(),
279
new_class_template->getTemplatedDecl()->getTagKind(),
280
new_class_template->getDeclContext(),
281
new_class_template->getTemplatedDecl()->getLocation(),
282
new_class_template->getLocation(), new_class_template, imported_args,
283
nullptr);
284
285
new_class_template->AddSpecialization(result, InsertPos);
286
if (new_class_template->isOutOfLine())
287
result->setLexicalDeclContext(
288
new_class_template->getLexicalDeclContext());
289
return result;
290
}
291
292
std::optional<Decl *> CxxModuleHandler::Import(Decl *d) {
293
if (!isValid())
294
return {};
295
296
return tryInstantiateStdTemplate(d);
297
}
298
299