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