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