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