18be30215SAlex Langford //===-- CxxModuleHandler.cpp ----------------------------------------------===//
28be30215SAlex Langford //
38be30215SAlex Langford // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
48be30215SAlex Langford // See https://llvm.org/LICENSE.txt for license information.
58be30215SAlex Langford // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
68be30215SAlex Langford //
78be30215SAlex Langford //===----------------------------------------------------------------------===//
88be30215SAlex Langford 
98be30215SAlex Langford #include "Plugins/ExpressionParser/Clang/CxxModuleHandler.h"
108be30215SAlex Langford #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
118be30215SAlex Langford 
128be30215SAlex Langford #include "lldb/Utility/Log.h"
138be30215SAlex Langford #include "clang/Sema/Lookup.h"
148be30215SAlex Langford #include "llvm/Support/Error.h"
158be30215SAlex Langford 
168be30215SAlex Langford using namespace lldb_private;
178be30215SAlex Langford using namespace clang;
188be30215SAlex Langford 
198be30215SAlex Langford CxxModuleHandler::CxxModuleHandler(ASTImporter &importer, ASTContext *target)
208be30215SAlex Langford     : m_importer(&importer),
218be30215SAlex Langford       m_sema(TypeSystemClang::GetASTContext(target)->getSema()) {
228be30215SAlex Langford 
238be30215SAlex Langford   std::initializer_list<const char *> supported_names = {
248be30215SAlex Langford       // containers
258be30215SAlex Langford       "deque",
268be30215SAlex Langford       "forward_list",
278be30215SAlex Langford       "list",
288be30215SAlex Langford       "queue",
298be30215SAlex Langford       "stack",
308be30215SAlex Langford       "vector",
318be30215SAlex Langford       // pointers
328be30215SAlex Langford       "shared_ptr",
338be30215SAlex Langford       "unique_ptr",
348be30215SAlex Langford       "weak_ptr",
358be30215SAlex Langford       // utility
368be30215SAlex Langford       "allocator",
37b8522252SRaphael Isemann       "pair",
388be30215SAlex Langford   };
398be30215SAlex Langford   m_supported_templates.insert(supported_names.begin(), supported_names.end());
408be30215SAlex Langford }
418be30215SAlex Langford 
428be30215SAlex Langford /// Builds a list of scopes that point into the given context.
438be30215SAlex Langford ///
448be30215SAlex Langford /// \param sema The sema that will be using the scopes.
458be30215SAlex Langford /// \param ctxt The context that the scope should look into.
468be30215SAlex Langford /// \param result A list of scopes. The scopes need to be freed by the caller
478be30215SAlex Langford ///               (except the TUScope which is owned by the sema).
488be30215SAlex Langford static void makeScopes(Sema &sema, DeclContext *ctxt,
498be30215SAlex Langford                        std::vector<Scope *> &result) {
508be30215SAlex Langford   // FIXME: The result should be a list of unique_ptrs, but the TUScope makes
518be30215SAlex Langford   // this currently impossible as it's owned by the Sema.
528be30215SAlex Langford 
538be30215SAlex Langford   if (auto parent = ctxt->getParent()) {
548be30215SAlex Langford     makeScopes(sema, parent, result);
558be30215SAlex Langford 
568be30215SAlex Langford     Scope *scope =
578be30215SAlex Langford         new Scope(result.back(), Scope::DeclScope, sema.getDiagnostics());
588be30215SAlex Langford     scope->setEntity(ctxt);
598be30215SAlex Langford     result.push_back(scope);
608be30215SAlex Langford   } else
618be30215SAlex Langford     result.push_back(sema.TUScope);
628be30215SAlex Langford }
638be30215SAlex Langford 
648be30215SAlex Langford /// Uses the Sema to look up the given name in the given DeclContext.
658be30215SAlex Langford static std::unique_ptr<LookupResult>
668be30215SAlex Langford emulateLookupInCtxt(Sema &sema, llvm::StringRef name, DeclContext *ctxt) {
678be30215SAlex Langford   IdentifierInfo &ident = sema.getASTContext().Idents.get(name);
688be30215SAlex Langford 
698be30215SAlex Langford   std::unique_ptr<LookupResult> lookup_result;
7006412daeSJonas Devlieghere   lookup_result = std::make_unique<LookupResult>(sema, DeclarationName(&ident),
718be30215SAlex Langford                                                  SourceLocation(),
7206412daeSJonas Devlieghere                                                  Sema::LookupOrdinaryName);
738be30215SAlex Langford 
748be30215SAlex Langford   // Usually during parsing we already encountered the scopes we would use. But
758be30215SAlex Langford   // here don't have these scopes so we have to emulate the behavior of the
768be30215SAlex Langford   // Sema during parsing.
778be30215SAlex Langford   std::vector<Scope *> scopes;
788be30215SAlex Langford   makeScopes(sema, ctxt, scopes);
798be30215SAlex Langford 
808be30215SAlex Langford   // Now actually perform the lookup with the sema.
818be30215SAlex Langford   sema.LookupName(*lookup_result, scopes.back());
828be30215SAlex Langford 
838be30215SAlex Langford   // Delete all the allocated scopes beside the translation unit scope (which
848be30215SAlex Langford   // has depth 0).
858be30215SAlex Langford   for (Scope *s : scopes)
868be30215SAlex Langford     if (s->getDepth() != 0)
878be30215SAlex Langford       delete s;
888be30215SAlex Langford 
898be30215SAlex Langford   return lookup_result;
908be30215SAlex Langford }
918be30215SAlex Langford 
928be30215SAlex Langford /// Error class for handling problems when finding a certain DeclContext.
938be30215SAlex Langford struct MissingDeclContext : public llvm::ErrorInfo<MissingDeclContext> {
948be30215SAlex Langford 
958be30215SAlex Langford   static char ID;
968be30215SAlex Langford 
978be30215SAlex Langford   MissingDeclContext(DeclContext *context, std::string error)
988be30215SAlex Langford       : m_context(context), m_error(error) {}
998be30215SAlex Langford 
1008be30215SAlex Langford   DeclContext *m_context;
1018be30215SAlex Langford   std::string m_error;
1028be30215SAlex Langford 
1038be30215SAlex Langford   void log(llvm::raw_ostream &OS) const override {
1048be30215SAlex Langford     OS << llvm::formatv("error when reconstructing context of kind {0}:{1}",
1058be30215SAlex Langford                         m_context->getDeclKindName(), m_error);
1068be30215SAlex Langford   }
1078be30215SAlex Langford 
1088be30215SAlex Langford   std::error_code convertToErrorCode() const override {
1098be30215SAlex Langford     return llvm::inconvertibleErrorCode();
1108be30215SAlex Langford   }
1118be30215SAlex Langford };
1128be30215SAlex Langford 
1138be30215SAlex Langford char MissingDeclContext::ID = 0;
1148be30215SAlex Langford 
1158be30215SAlex Langford /// Given a foreign decl context, this function finds the equivalent local
1168be30215SAlex Langford /// decl context in the ASTContext of the given Sema. Potentially deserializes
1178be30215SAlex Langford /// decls from the 'std' module if necessary.
1188be30215SAlex Langford static llvm::Expected<DeclContext *>
1198be30215SAlex Langford getEqualLocalDeclContext(Sema &sema, DeclContext *foreign_ctxt) {
1208be30215SAlex Langford 
1218be30215SAlex Langford   // Inline namespaces don't matter for lookups, so let's skip them.
1228be30215SAlex Langford   while (foreign_ctxt && foreign_ctxt->isInlineNamespace())
1238be30215SAlex Langford     foreign_ctxt = foreign_ctxt->getParent();
1248be30215SAlex Langford 
1258be30215SAlex Langford   // If the foreign context is the TU, we just return the local TU.
1268be30215SAlex Langford   if (foreign_ctxt->isTranslationUnit())
1278be30215SAlex Langford     return sema.getASTContext().getTranslationUnitDecl();
1288be30215SAlex Langford 
1298be30215SAlex Langford   // Recursively find/build the parent DeclContext.
1308be30215SAlex Langford   llvm::Expected<DeclContext *> parent =
1318be30215SAlex Langford       getEqualLocalDeclContext(sema, foreign_ctxt->getParent());
1328be30215SAlex Langford   if (!parent)
1338be30215SAlex Langford     return parent;
1348be30215SAlex Langford 
1358be30215SAlex Langford   // We currently only support building namespaces.
1368be30215SAlex Langford   if (foreign_ctxt->isNamespace()) {
1378be30215SAlex Langford     NamedDecl *ns = llvm::dyn_cast<NamedDecl>(foreign_ctxt);
1388be30215SAlex Langford     llvm::StringRef ns_name = ns->getName();
1398be30215SAlex Langford 
1408be30215SAlex Langford     auto lookup_result = emulateLookupInCtxt(sema, ns_name, *parent);
1418be30215SAlex Langford     for (NamedDecl *named_decl : *lookup_result) {
1428be30215SAlex Langford       if (DeclContext *DC = llvm::dyn_cast<DeclContext>(named_decl))
1438be30215SAlex Langford         return DC->getPrimaryContext();
1448be30215SAlex Langford     }
1458be30215SAlex Langford     return llvm::make_error<MissingDeclContext>(
1468be30215SAlex Langford         foreign_ctxt,
1478be30215SAlex Langford         "Couldn't find namespace " + ns->getQualifiedNameAsString());
1488be30215SAlex Langford   }
1498be30215SAlex Langford 
1508be30215SAlex Langford   return llvm::make_error<MissingDeclContext>(foreign_ctxt, "Unknown context ");
1518be30215SAlex Langford }
1528be30215SAlex Langford 
1538be30215SAlex Langford /// Returns true iff tryInstantiateStdTemplate supports instantiating a template
1548be30215SAlex Langford /// with the given template arguments.
1558be30215SAlex Langford static bool templateArgsAreSupported(ArrayRef<TemplateArgument> a) {
1568be30215SAlex Langford   for (const TemplateArgument &arg : a) {
1578be30215SAlex Langford     switch (arg.getKind()) {
1588be30215SAlex Langford     case TemplateArgument::Type:
1598be30215SAlex Langford     case TemplateArgument::Integral:
1608be30215SAlex Langford       break;
1618be30215SAlex Langford     default:
1628be30215SAlex Langford       // TemplateArgument kind hasn't been handled yet.
1638be30215SAlex Langford       return false;
1648be30215SAlex Langford     }
1658be30215SAlex Langford   }
1668be30215SAlex Langford   return true;
1678be30215SAlex Langford }
1688be30215SAlex Langford 
1698be30215SAlex Langford /// Constructor function for Clang declarations. Ensures that the created
1708be30215SAlex Langford /// declaration is registered with the ASTImporter.
1718be30215SAlex Langford template <typename T, typename... Args>
1728be30215SAlex Langford T *createDecl(ASTImporter &importer, Decl *from_d, Args &&... args) {
1738be30215SAlex Langford   T *to_d = T::Create(std::forward<Args>(args)...);
1748be30215SAlex Langford   importer.RegisterImportedDecl(from_d, to_d);
1758be30215SAlex Langford   return to_d;
1768be30215SAlex Langford }
1778be30215SAlex Langford 
1788be30215SAlex Langford llvm::Optional<Decl *> CxxModuleHandler::tryInstantiateStdTemplate(Decl *d) {
1798be30215SAlex Langford   Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);
1808be30215SAlex Langford 
1818be30215SAlex Langford   // If we don't have a template to instiantiate, then there is nothing to do.
1828be30215SAlex Langford   auto td = dyn_cast<ClassTemplateSpecializationDecl>(d);
1838be30215SAlex Langford   if (!td)
184*0b44bb8dSRaphael Isemann     return llvm::None;
1858be30215SAlex Langford 
1868be30215SAlex Langford   // We only care about templates in the std namespace.
1878be30215SAlex Langford   if (!td->getDeclContext()->isStdNamespace())
188*0b44bb8dSRaphael Isemann     return llvm::None;
1898be30215SAlex Langford 
190efb328f6SEric Christopher   // We have a list of supported template names.
191*0b44bb8dSRaphael Isemann   if (!m_supported_templates.contains(td->getName()))
192*0b44bb8dSRaphael Isemann     return llvm::None;
1938be30215SAlex Langford 
1948be30215SAlex Langford   // Early check if we even support instantiating this template. We do this
1958be30215SAlex Langford   // before we import anything into the target AST.
1968be30215SAlex Langford   auto &foreign_args = td->getTemplateInstantiationArgs();
1978be30215SAlex Langford   if (!templateArgsAreSupported(foreign_args.asArray()))
198*0b44bb8dSRaphael Isemann     return llvm::None;
1998be30215SAlex Langford 
2008be30215SAlex Langford   // Find the local DeclContext that corresponds to the DeclContext of our
2018be30215SAlex Langford   // decl we want to import.
2028be30215SAlex Langford   llvm::Expected<DeclContext *> to_context =
2038be30215SAlex Langford       getEqualLocalDeclContext(*m_sema, td->getDeclContext());
2048be30215SAlex Langford   if (!to_context) {
2058be30215SAlex Langford     LLDB_LOG_ERROR(log, to_context.takeError(),
2068be30215SAlex Langford                    "Got error while searching equal local DeclContext for decl "
2078be30215SAlex Langford                    "'{1}':\n{0}",
2088be30215SAlex Langford                    td->getName());
209*0b44bb8dSRaphael Isemann     return llvm::None;
2108be30215SAlex Langford   }
2118be30215SAlex Langford 
2128be30215SAlex Langford   // Look up the template in our local context.
2138be30215SAlex Langford   std::unique_ptr<LookupResult> lookup =
2148be30215SAlex Langford       emulateLookupInCtxt(*m_sema, td->getName(), *to_context);
2158be30215SAlex Langford 
2168be30215SAlex Langford   ClassTemplateDecl *new_class_template = nullptr;
2178be30215SAlex Langford   for (auto LD : *lookup) {
2188be30215SAlex Langford     if ((new_class_template = dyn_cast<ClassTemplateDecl>(LD)))
2198be30215SAlex Langford       break;
2208be30215SAlex Langford   }
2218be30215SAlex Langford   if (!new_class_template)
222*0b44bb8dSRaphael Isemann     return llvm::None;
2238be30215SAlex Langford 
2248be30215SAlex Langford   // Import the foreign template arguments.
2258be30215SAlex Langford   llvm::SmallVector<TemplateArgument, 4> imported_args;
2268be30215SAlex Langford 
2278be30215SAlex Langford   // If this logic is changed, also update templateArgsAreSupported.
2288be30215SAlex Langford   for (const TemplateArgument &arg : foreign_args.asArray()) {
2298be30215SAlex Langford     switch (arg.getKind()) {
2308be30215SAlex Langford     case TemplateArgument::Type: {
2318be30215SAlex Langford       llvm::Expected<QualType> type = m_importer->Import(arg.getAsType());
2328be30215SAlex Langford       if (!type) {
2338be30215SAlex Langford         LLDB_LOG_ERROR(log, type.takeError(), "Couldn't import type: {0}");
234*0b44bb8dSRaphael Isemann         return llvm::None;
2358be30215SAlex Langford       }
2368be30215SAlex Langford       imported_args.push_back(TemplateArgument(*type));
2378be30215SAlex Langford       break;
2388be30215SAlex Langford     }
2398be30215SAlex Langford     case TemplateArgument::Integral: {
2408be30215SAlex Langford       llvm::APSInt integral = arg.getAsIntegral();
2418be30215SAlex Langford       llvm::Expected<QualType> type =
2428be30215SAlex Langford           m_importer->Import(arg.getIntegralType());
2438be30215SAlex Langford       if (!type) {
2448be30215SAlex Langford         LLDB_LOG_ERROR(log, type.takeError(), "Couldn't import type: {0}");
245*0b44bb8dSRaphael Isemann         return llvm::None;
2468be30215SAlex Langford       }
2478be30215SAlex Langford       imported_args.push_back(
2488be30215SAlex Langford           TemplateArgument(d->getASTContext(), integral, *type));
2498be30215SAlex Langford       break;
2508be30215SAlex Langford     }
2518be30215SAlex Langford     default:
2528be30215SAlex Langford       assert(false && "templateArgsAreSupported not updated?");
2538be30215SAlex Langford     }
2548be30215SAlex Langford   }
2558be30215SAlex Langford 
2568be30215SAlex Langford   // Find the class template specialization declaration that
2578be30215SAlex Langford   // corresponds to these arguments.
2588be30215SAlex Langford   void *InsertPos = nullptr;
2598be30215SAlex Langford   ClassTemplateSpecializationDecl *result =
2608be30215SAlex Langford       new_class_template->findSpecialization(imported_args, InsertPos);
2618be30215SAlex Langford 
2628be30215SAlex Langford   if (result) {
2638be30215SAlex Langford     // We found an existing specialization in the module that fits our arguments
2648be30215SAlex Langford     // so we can treat it as the result and register it with the ASTImporter.
2658be30215SAlex Langford     m_importer->RegisterImportedDecl(d, result);
2668be30215SAlex Langford     return result;
2678be30215SAlex Langford   }
2688be30215SAlex Langford 
2698be30215SAlex Langford   // Instantiate the template.
2708be30215SAlex Langford   result = createDecl<ClassTemplateSpecializationDecl>(
2718be30215SAlex Langford       *m_importer, d, m_sema->getASTContext(),
2728be30215SAlex Langford       new_class_template->getTemplatedDecl()->getTagKind(),
2738be30215SAlex Langford       new_class_template->getDeclContext(),
2748be30215SAlex Langford       new_class_template->getTemplatedDecl()->getLocation(),
2758be30215SAlex Langford       new_class_template->getLocation(), new_class_template, imported_args,
2768be30215SAlex Langford       nullptr);
2778be30215SAlex Langford 
2788be30215SAlex Langford   new_class_template->AddSpecialization(result, InsertPos);
2798be30215SAlex Langford   if (new_class_template->isOutOfLine())
2808be30215SAlex Langford     result->setLexicalDeclContext(
2818be30215SAlex Langford         new_class_template->getLexicalDeclContext());
2828be30215SAlex Langford   return result;
2838be30215SAlex Langford }
2848be30215SAlex Langford 
2858be30215SAlex Langford llvm::Optional<Decl *> CxxModuleHandler::Import(Decl *d) {
2868be30215SAlex Langford   if (!isValid())
2878be30215SAlex Langford     return {};
2888be30215SAlex Langford 
2898be30215SAlex Langford   return tryInstantiateStdTemplate(d);
2908be30215SAlex Langford }
291