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