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