brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.1 KiB · 9381a29 Raw
299 lines · cpp
1//===-- CxxModuleHandler.cpp ----------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "Plugins/ExpressionParser/Clang/CxxModuleHandler.h"10#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"11 12#include "lldb/Utility/LLDBLog.h"13#include "lldb/Utility/Log.h"14#include "clang/Sema/Lookup.h"15#include "llvm/Support/Error.h"16#include <optional>17 18using namespace lldb_private;19using namespace clang;20 21CxxModuleHandler::CxxModuleHandler(ASTImporter &importer, ASTContext *target)22    : m_importer(&importer),23      m_sema(TypeSystemClang::GetASTContext(target)->getSema()) {24 25  std::initializer_list<const char *> supported_names = {26      // containers27      "array",28      "deque",29      "forward_list",30      "list",31      "queue",32      "stack",33      "vector",34      // pointers35      "shared_ptr",36      "unique_ptr",37      "weak_ptr",38      // iterator39      "move_iterator",40      "__wrap_iter",41      // utility42      "allocator",43      "pair",44  };45  m_supported_templates.insert_range(supported_names);46}47 48/// Builds a list of scopes that point into the given context.49///50/// \param sema The sema that will be using the scopes.51/// \param ctxt The context that the scope should look into.52/// \param result A list of scopes. The scopes need to be freed by the caller53///               (except the TUScope which is owned by the sema).54static void makeScopes(Sema &sema, DeclContext *ctxt,55                       std::vector<Scope *> &result) {56  // FIXME: The result should be a list of unique_ptrs, but the TUScope makes57  // this currently impossible as it's owned by the Sema.58 59  if (auto parent = ctxt->getParent()) {60    makeScopes(sema, parent, result);61 62    Scope *scope =63        new Scope(result.back(), Scope::DeclScope, sema.getDiagnostics());64    scope->setEntity(ctxt);65    result.push_back(scope);66  } else67    result.push_back(sema.TUScope);68}69 70/// Uses the Sema to look up the given name in the given DeclContext.71static std::unique_ptr<LookupResult>72emulateLookupInCtxt(Sema &sema, llvm::StringRef name, DeclContext *ctxt) {73  IdentifierInfo &ident = sema.getASTContext().Idents.get(name);74 75  std::unique_ptr<LookupResult> lookup_result;76  lookup_result = std::make_unique<LookupResult>(sema, DeclarationName(&ident),77                                                 SourceLocation(),78                                                 Sema::LookupOrdinaryName);79 80  // Usually during parsing we already encountered the scopes we would use. But81  // here don't have these scopes so we have to emulate the behavior of the82  // Sema during parsing.83  std::vector<Scope *> scopes;84  makeScopes(sema, ctxt, scopes);85 86  // Now actually perform the lookup with the sema.87  sema.LookupName(*lookup_result, scopes.back());88 89  // Delete all the allocated scopes beside the translation unit scope (which90  // has depth 0).91  for (Scope *s : scopes)92    if (s->getDepth() != 0)93      delete s;94 95  return lookup_result;96}97 98/// Error class for handling problems when finding a certain DeclContext.99struct MissingDeclContext : public llvm::ErrorInfo<MissingDeclContext> {100 101  static char ID;102 103  MissingDeclContext(DeclContext *context, std::string error)104      : m_context(context), m_error(error) {}105 106  DeclContext *m_context;107  std::string m_error;108 109  void log(llvm::raw_ostream &OS) const override {110    OS << llvm::formatv("error when reconstructing context of kind {0}:{1}",111                        m_context->getDeclKindName(), m_error);112  }113 114  std::error_code convertToErrorCode() const override {115    return llvm::inconvertibleErrorCode();116  }117};118 119char MissingDeclContext::ID = 0;120 121/// Given a foreign decl context, this function finds the equivalent local122/// decl context in the ASTContext of the given Sema. Potentially deserializes123/// decls from the 'std' module if necessary.124static llvm::Expected<DeclContext *>125getEqualLocalDeclContext(Sema &sema, DeclContext *foreign_ctxt) {126 127  // Inline namespaces don't matter for lookups, so let's skip them.128  while (foreign_ctxt && foreign_ctxt->isInlineNamespace())129    foreign_ctxt = foreign_ctxt->getParent();130 131  // If the foreign context is the TU, we just return the local TU.132  if (foreign_ctxt->isTranslationUnit())133    return sema.getASTContext().getTranslationUnitDecl();134 135  // Recursively find/build the parent DeclContext.136  llvm::Expected<DeclContext *> parent =137      getEqualLocalDeclContext(sema, foreign_ctxt->getParent());138  if (!parent)139    return parent;140 141  // We currently only support building namespaces.142  if (foreign_ctxt->isNamespace()) {143    NamedDecl *ns = llvm::cast<NamedDecl>(foreign_ctxt);144    llvm::StringRef ns_name = ns->getName();145 146    auto lookup_result = emulateLookupInCtxt(sema, ns_name, *parent);147    for (NamedDecl *named_decl : *lookup_result) {148      if (DeclContext *DC = llvm::dyn_cast<DeclContext>(named_decl))149        return DC->getPrimaryContext();150    }151    return llvm::make_error<MissingDeclContext>(152        foreign_ctxt,153        "Couldn't find namespace " + ns->getQualifiedNameAsString());154  }155 156  return llvm::make_error<MissingDeclContext>(foreign_ctxt, "Unknown context ");157}158 159/// Returns true iff tryInstantiateStdTemplate supports instantiating a template160/// with the given template arguments.161static bool templateArgsAreSupported(ArrayRef<TemplateArgument> a) {162  for (const TemplateArgument &arg : a) {163    switch (arg.getKind()) {164    case TemplateArgument::Type:165    case TemplateArgument::Integral:166      break;167    default:168      // TemplateArgument kind hasn't been handled yet.169      return false;170    }171  }172  return true;173}174 175/// Constructor function for Clang declarations. Ensures that the created176/// declaration is registered with the ASTImporter.177template <typename T, typename... Args>178T *createDecl(ASTImporter &importer, Decl *from_d, Args &&... args) {179  T *to_d = T::Create(std::forward<Args>(args)...);180  importer.RegisterImportedDecl(from_d, to_d);181  return to_d;182}183 184std::optional<Decl *> CxxModuleHandler::tryInstantiateStdTemplate(Decl *d) {185  Log *log = GetLog(LLDBLog::Expressions);186 187  // If we don't have a template to instiantiate, then there is nothing to do.188  auto td = dyn_cast<ClassTemplateSpecializationDecl>(d);189  if (!td)190    return std::nullopt;191 192  // We only care about templates in the std namespace.193  if (!td->getDeclContext()->isStdNamespace())194    return std::nullopt;195 196  // We have a list of supported template names.197  if (!m_supported_templates.contains(td->getName()))198    return std::nullopt;199 200  // Early check if we even support instantiating this template. We do this201  // before we import anything into the target AST.202  auto &foreign_args = td->getTemplateInstantiationArgs();203  if (!templateArgsAreSupported(foreign_args.asArray()))204    return std::nullopt;205 206  // Find the local DeclContext that corresponds to the DeclContext of our207  // decl we want to import.208  llvm::Expected<DeclContext *> to_context =209      getEqualLocalDeclContext(*m_sema, td->getDeclContext());210  if (!to_context) {211    LLDB_LOG_ERROR(log, to_context.takeError(),212                   "Got error while searching equal local DeclContext for decl "213                   "'{1}':\n{0}",214                   td->getName());215    return std::nullopt;216  }217 218  // Look up the template in our local context.219  std::unique_ptr<LookupResult> lookup =220      emulateLookupInCtxt(*m_sema, td->getName(), *to_context);221 222  ClassTemplateDecl *new_class_template = nullptr;223  for (auto LD : *lookup) {224    if ((new_class_template = dyn_cast<ClassTemplateDecl>(LD)))225      break;226  }227  if (!new_class_template)228    return std::nullopt;229 230  // Import the foreign template arguments.231  llvm::SmallVector<TemplateArgument, 4> imported_args;232 233  // If this logic is changed, also update templateArgsAreSupported.234  for (const TemplateArgument &arg : foreign_args.asArray()) {235    switch (arg.getKind()) {236    case TemplateArgument::Type: {237      llvm::Expected<QualType> type = m_importer->Import(arg.getAsType());238      if (!type) {239        LLDB_LOG_ERROR(log, type.takeError(), "Couldn't import type: {0}");240        return std::nullopt;241      }242      imported_args.push_back(243          TemplateArgument(*type, /*isNullPtr*/ false, arg.getIsDefaulted()));244      break;245    }246    case TemplateArgument::Integral: {247      llvm::APSInt integral = arg.getAsIntegral();248      llvm::Expected<QualType> type =249          m_importer->Import(arg.getIntegralType());250      if (!type) {251        LLDB_LOG_ERROR(log, type.takeError(), "Couldn't import type: {0}");252        return std::nullopt;253      }254      imported_args.push_back(TemplateArgument(d->getASTContext(), integral,255                                               *type, arg.getIsDefaulted()));256      break;257    }258    default:259      assert(false && "templateArgsAreSupported not updated?");260    }261  }262 263  // Find the class template specialization declaration that264  // corresponds to these arguments.265  void *InsertPos = nullptr;266  ClassTemplateSpecializationDecl *result =267      new_class_template->findSpecialization(imported_args, InsertPos);268 269  if (result) {270    // We found an existing specialization in the module that fits our arguments271    // so we can treat it as the result and register it with the ASTImporter.272    m_importer->RegisterImportedDecl(d, result);273    return result;274  }275 276  // Instantiate the template.277  result = createDecl<ClassTemplateSpecializationDecl>(278      *m_importer, d, m_sema->getASTContext(),279      new_class_template->getTemplatedDecl()->getTagKind(),280      new_class_template->getDeclContext(),281      new_class_template->getTemplatedDecl()->getLocation(),282      new_class_template->getLocation(), new_class_template, imported_args,283      td->hasStrictPackMatch(),284      /*PrevDecl=*/nullptr);285 286  new_class_template->AddSpecialization(result, InsertPos);287  if (new_class_template->isOutOfLine())288    result->setLexicalDeclContext(289        new_class_template->getLexicalDeclContext());290  return result;291}292 293std::optional<Decl *> CxxModuleHandler::Import(Decl *d) {294  if (!isValid())295    return {};296 297  return tryInstantiateStdTemplate(d);298}299