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