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