Path: blob/main/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/CxxModuleHandler.cpp
39648 views
//===-- CxxModuleHandler.cpp ----------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "Plugins/ExpressionParser/Clang/CxxModuleHandler.h"9#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"1011#include "lldb/Utility/LLDBLog.h"12#include "lldb/Utility/Log.h"13#include "clang/Sema/Lookup.h"14#include "llvm/Support/Error.h"15#include <optional>1617using namespace lldb_private;18using namespace clang;1920CxxModuleHandler::CxxModuleHandler(ASTImporter &importer, ASTContext *target)21: m_importer(&importer),22m_sema(TypeSystemClang::GetASTContext(target)->getSema()) {2324std::initializer_list<const char *> supported_names = {25// containers26"array",27"deque",28"forward_list",29"list",30"queue",31"stack",32"vector",33// pointers34"shared_ptr",35"unique_ptr",36"weak_ptr",37// iterator38"move_iterator",39"__wrap_iter",40// utility41"allocator",42"pair",43};44m_supported_templates.insert(supported_names.begin(), supported_names.end());45}4647/// Builds a list of scopes that point into the given context.48///49/// \param sema The sema that will be using the scopes.50/// \param ctxt The context that the scope should look into.51/// \param result A list of scopes. The scopes need to be freed by the caller52/// (except the TUScope which is owned by the sema).53static void makeScopes(Sema &sema, DeclContext *ctxt,54std::vector<Scope *> &result) {55// FIXME: The result should be a list of unique_ptrs, but the TUScope makes56// this currently impossible as it's owned by the Sema.5758if (auto parent = ctxt->getParent()) {59makeScopes(sema, parent, result);6061Scope *scope =62new Scope(result.back(), Scope::DeclScope, sema.getDiagnostics());63scope->setEntity(ctxt);64result.push_back(scope);65} else66result.push_back(sema.TUScope);67}6869/// Uses the Sema to look up the given name in the given DeclContext.70static std::unique_ptr<LookupResult>71emulateLookupInCtxt(Sema &sema, llvm::StringRef name, DeclContext *ctxt) {72IdentifierInfo &ident = sema.getASTContext().Idents.get(name);7374std::unique_ptr<LookupResult> lookup_result;75lookup_result = std::make_unique<LookupResult>(sema, DeclarationName(&ident),76SourceLocation(),77Sema::LookupOrdinaryName);7879// Usually during parsing we already encountered the scopes we would use. But80// here don't have these scopes so we have to emulate the behavior of the81// Sema during parsing.82std::vector<Scope *> scopes;83makeScopes(sema, ctxt, scopes);8485// Now actually perform the lookup with the sema.86sema.LookupName(*lookup_result, scopes.back());8788// Delete all the allocated scopes beside the translation unit scope (which89// has depth 0).90for (Scope *s : scopes)91if (s->getDepth() != 0)92delete s;9394return lookup_result;95}9697/// Error class for handling problems when finding a certain DeclContext.98struct MissingDeclContext : public llvm::ErrorInfo<MissingDeclContext> {99100static char ID;101102MissingDeclContext(DeclContext *context, std::string error)103: m_context(context), m_error(error) {}104105DeclContext *m_context;106std::string m_error;107108void log(llvm::raw_ostream &OS) const override {109OS << llvm::formatv("error when reconstructing context of kind {0}:{1}",110m_context->getDeclKindName(), m_error);111}112113std::error_code convertToErrorCode() const override {114return llvm::inconvertibleErrorCode();115}116};117118char MissingDeclContext::ID = 0;119120/// Given a foreign decl context, this function finds the equivalent local121/// decl context in the ASTContext of the given Sema. Potentially deserializes122/// decls from the 'std' module if necessary.123static llvm::Expected<DeclContext *>124getEqualLocalDeclContext(Sema &sema, DeclContext *foreign_ctxt) {125126// Inline namespaces don't matter for lookups, so let's skip them.127while (foreign_ctxt && foreign_ctxt->isInlineNamespace())128foreign_ctxt = foreign_ctxt->getParent();129130// If the foreign context is the TU, we just return the local TU.131if (foreign_ctxt->isTranslationUnit())132return sema.getASTContext().getTranslationUnitDecl();133134// Recursively find/build the parent DeclContext.135llvm::Expected<DeclContext *> parent =136getEqualLocalDeclContext(sema, foreign_ctxt->getParent());137if (!parent)138return parent;139140// We currently only support building namespaces.141if (foreign_ctxt->isNamespace()) {142NamedDecl *ns = llvm::cast<NamedDecl>(foreign_ctxt);143llvm::StringRef ns_name = ns->getName();144145auto lookup_result = emulateLookupInCtxt(sema, ns_name, *parent);146for (NamedDecl *named_decl : *lookup_result) {147if (DeclContext *DC = llvm::dyn_cast<DeclContext>(named_decl))148return DC->getPrimaryContext();149}150return llvm::make_error<MissingDeclContext>(151foreign_ctxt,152"Couldn't find namespace " + ns->getQualifiedNameAsString());153}154155return llvm::make_error<MissingDeclContext>(foreign_ctxt, "Unknown context ");156}157158/// Returns true iff tryInstantiateStdTemplate supports instantiating a template159/// with the given template arguments.160static bool templateArgsAreSupported(ArrayRef<TemplateArgument> a) {161for (const TemplateArgument &arg : a) {162switch (arg.getKind()) {163case TemplateArgument::Type:164case TemplateArgument::Integral:165break;166default:167// TemplateArgument kind hasn't been handled yet.168return false;169}170}171return true;172}173174/// Constructor function for Clang declarations. Ensures that the created175/// declaration is registered with the ASTImporter.176template <typename T, typename... Args>177T *createDecl(ASTImporter &importer, Decl *from_d, Args &&... args) {178T *to_d = T::Create(std::forward<Args>(args)...);179importer.RegisterImportedDecl(from_d, to_d);180return to_d;181}182183std::optional<Decl *> CxxModuleHandler::tryInstantiateStdTemplate(Decl *d) {184Log *log = GetLog(LLDBLog::Expressions);185186// If we don't have a template to instiantiate, then there is nothing to do.187auto td = dyn_cast<ClassTemplateSpecializationDecl>(d);188if (!td)189return std::nullopt;190191// We only care about templates in the std namespace.192if (!td->getDeclContext()->isStdNamespace())193return std::nullopt;194195// We have a list of supported template names.196if (!m_supported_templates.contains(td->getName()))197return std::nullopt;198199// Early check if we even support instantiating this template. We do this200// before we import anything into the target AST.201auto &foreign_args = td->getTemplateInstantiationArgs();202if (!templateArgsAreSupported(foreign_args.asArray()))203return std::nullopt;204205// Find the local DeclContext that corresponds to the DeclContext of our206// decl we want to import.207llvm::Expected<DeclContext *> to_context =208getEqualLocalDeclContext(*m_sema, td->getDeclContext());209if (!to_context) {210LLDB_LOG_ERROR(log, to_context.takeError(),211"Got error while searching equal local DeclContext for decl "212"'{1}':\n{0}",213td->getName());214return std::nullopt;215}216217// Look up the template in our local context.218std::unique_ptr<LookupResult> lookup =219emulateLookupInCtxt(*m_sema, td->getName(), *to_context);220221ClassTemplateDecl *new_class_template = nullptr;222for (auto LD : *lookup) {223if ((new_class_template = dyn_cast<ClassTemplateDecl>(LD)))224break;225}226if (!new_class_template)227return std::nullopt;228229// Import the foreign template arguments.230llvm::SmallVector<TemplateArgument, 4> imported_args;231232// If this logic is changed, also update templateArgsAreSupported.233for (const TemplateArgument &arg : foreign_args.asArray()) {234switch (arg.getKind()) {235case TemplateArgument::Type: {236llvm::Expected<QualType> type = m_importer->Import(arg.getAsType());237if (!type) {238LLDB_LOG_ERROR(log, type.takeError(), "Couldn't import type: {0}");239return std::nullopt;240}241imported_args.push_back(242TemplateArgument(*type, /*isNullPtr*/ false, arg.getIsDefaulted()));243break;244}245case TemplateArgument::Integral: {246llvm::APSInt integral = arg.getAsIntegral();247llvm::Expected<QualType> type =248m_importer->Import(arg.getIntegralType());249if (!type) {250LLDB_LOG_ERROR(log, type.takeError(), "Couldn't import type: {0}");251return std::nullopt;252}253imported_args.push_back(TemplateArgument(d->getASTContext(), integral,254*type, arg.getIsDefaulted()));255break;256}257default:258assert(false && "templateArgsAreSupported not updated?");259}260}261262// Find the class template specialization declaration that263// corresponds to these arguments.264void *InsertPos = nullptr;265ClassTemplateSpecializationDecl *result =266new_class_template->findSpecialization(imported_args, InsertPos);267268if (result) {269// We found an existing specialization in the module that fits our arguments270// so we can treat it as the result and register it with the ASTImporter.271m_importer->RegisterImportedDecl(d, result);272return result;273}274275// Instantiate the template.276result = createDecl<ClassTemplateSpecializationDecl>(277*m_importer, d, m_sema->getASTContext(),278new_class_template->getTemplatedDecl()->getTagKind(),279new_class_template->getDeclContext(),280new_class_template->getTemplatedDecl()->getLocation(),281new_class_template->getLocation(), new_class_template, imported_args,282nullptr);283284new_class_template->AddSpecialization(result, InsertPos);285if (new_class_template->isOutOfLine())286result->setLexicalDeclContext(287new_class_template->getLexicalDeclContext());288return result;289}290291std::optional<Decl *> CxxModuleHandler::Import(Decl *d) {292if (!isValid())293return {};294295return tryInstantiateStdTemplate(d);296}297298299