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