]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/lldb/source/Symbol/CxxModuleHandler.cpp
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / lldb / source / Symbol / CxxModuleHandler.cpp
1 //===-- 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 "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
19 CxxModuleHandler::CxxModuleHandler(ASTImporter &importer, ASTContext *target)
20     : m_importer(&importer),
21       m_sema(ClangASTContext::GetASTContext(target)->getSema()) {
22
23   std::initializer_list<const char *> supported_names = {
24       // containers
25       "deque",
26       "forward_list",
27       "list",
28       "queue",
29       "stack",
30       "vector",
31       // 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).
47 static 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.
64 static std::unique_ptr<LookupResult>
65 emulateLookupInCtxt(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.
92 struct 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
112 char 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.
117 static llvm::Expected<DeclContext *>
118 getEqualLocalDeclContext(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.
154 static 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.
170 template <typename T, typename... Args>
171 T *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
177 llvm::Optional<Decl *> CxxModuleHandler::tryInstantiateStdTemplate(Decl *d) {
178   // If we don't have a template to instiantiate, then there is nothing to do.
179   auto td = dyn_cast<ClassTemplateSpecializationDecl>(d);
180   if (!td)
181     return {};
182
183   // We only care about templates in the std namespace.
184   if (!td->getDeclContext()->isStdNamespace())
185     return {};
186
187   // We have a whitelist of supported template names.
188   if (m_supported_templates.find(td->getName()) == m_supported_templates.end())
189     return {};
190
191   // Early check if we even support instantiating this template. We do this
192   // before we import anything into the target AST.
193   auto &foreign_args = td->getTemplateInstantiationArgs();
194   if (!templateArgsAreSupported(foreign_args.asArray()))
195     return {};
196
197   // Find the local DeclContext that corresponds to the DeclContext of our
198   // decl we want to import.
199   auto to_context = getEqualLocalDeclContext(*m_sema, td->getDeclContext());
200   if (!to_context)
201     return {};
202
203   // Look up the template in our local context.
204   std::unique_ptr<LookupResult> lookup =
205       emulateLookupInCtxt(*m_sema, td->getName(), *to_context);
206
207   ClassTemplateDecl *new_class_template = nullptr;
208   for (auto LD : *lookup) {
209     if ((new_class_template = dyn_cast<ClassTemplateDecl>(LD)))
210       break;
211   }
212   if (!new_class_template)
213     return {};
214
215   // Import the foreign template arguments.
216   llvm::SmallVector<TemplateArgument, 4> imported_args;
217
218   Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);
219
220   // If this logic is changed, also update templateArgsAreSupported.
221   for (const TemplateArgument &arg : foreign_args.asArray()) {
222     switch (arg.getKind()) {
223     case TemplateArgument::Type: {
224       llvm::Expected<QualType> type = m_importer->Import(arg.getAsType());
225       if (!type) {
226         LLDB_LOG_ERROR(log, type.takeError(), "Couldn't import type: {0}");
227         return {};
228       }
229       imported_args.push_back(TemplateArgument(*type));
230       break;
231     }
232     case TemplateArgument::Integral: {
233       llvm::APSInt integral = arg.getAsIntegral();
234       llvm::Expected<QualType> type =
235           m_importer->Import(arg.getIntegralType());
236       if (!type) {
237         LLDB_LOG_ERROR(log, type.takeError(), "Couldn't import type: {0}");
238         return {};
239       }
240       imported_args.push_back(
241           TemplateArgument(d->getASTContext(), integral, *type));
242       break;
243     }
244     default:
245       assert(false && "templateArgsAreSupported not updated?");
246     }
247   }
248
249   // Find the class template specialization declaration that
250   // corresponds to these arguments.
251   void *InsertPos = nullptr;
252   ClassTemplateSpecializationDecl *result =
253       new_class_template->findSpecialization(imported_args, InsertPos);
254
255   if (result) {
256     // We found an existing specialization in the module that fits our arguments
257     // so we can treat it as the result and register it with the ASTImporter.
258     m_importer->RegisterImportedDecl(d, result);
259     return result;
260   }
261
262   // Instantiate the template.
263   result = createDecl<ClassTemplateSpecializationDecl>(
264       *m_importer, d, m_sema->getASTContext(),
265       new_class_template->getTemplatedDecl()->getTagKind(),
266       new_class_template->getDeclContext(),
267       new_class_template->getTemplatedDecl()->getLocation(),
268       new_class_template->getLocation(), new_class_template, imported_args,
269       nullptr);
270
271   new_class_template->AddSpecialization(result, InsertPos);
272   if (new_class_template->isOutOfLine())
273     result->setLexicalDeclContext(
274         new_class_template->getLexicalDeclContext());
275   return result;
276 }
277
278 llvm::Optional<Decl *> CxxModuleHandler::Import(Decl *d) {
279   if (!isValid())
280     return {};
281
282   return tryInstantiateStdTemplate(d);
283 }