diff options
Diffstat (limited to 'lldb/source/Symbol/CxxModuleHandler.cpp')
| -rw-r--r-- | lldb/source/Symbol/CxxModuleHandler.cpp | 289 | 
1 files changed, 289 insertions, 0 deletions
diff --git a/lldb/source/Symbol/CxxModuleHandler.cpp b/lldb/source/Symbol/CxxModuleHandler.cpp new file mode 100644 index 000000000000..19e80e5036bc --- /dev/null +++ b/lldb/source/Symbol/CxxModuleHandler.cpp @@ -0,0 +1,289 @@ +//===-- CxxModuleHandler.cpp ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Symbol/CxxModuleHandler.h" + +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Utility/Log.h" +#include "clang/Sema/Lookup.h" +#include "llvm/Support/Error.h" + +using namespace lldb_private; +using namespace clang; + +CxxModuleHandler::CxxModuleHandler(ASTImporter &importer, ASTContext *target) +    : m_importer(&importer), +      m_sema(ClangASTContext::GetASTContext(target)->getSema()) { + +  std::initializer_list<const char *> supported_names = { +      // containers +      "deque", +      "forward_list", +      "list", +      "queue", +      "stack", +      "vector", +      // pointers +      "shared_ptr", +      "unique_ptr", +      "weak_ptr", +      // utility +      "allocator", +  }; +  m_supported_templates.insert(supported_names.begin(), supported_names.end()); +} + +/// Builds a list of scopes that point into the given context. +/// +/// \param sema The sema that will be using the scopes. +/// \param ctxt The context that the scope should look into. +/// \param result A list of scopes. The scopes need to be freed by the caller +///               (except the TUScope which is owned by the sema). +static void makeScopes(Sema &sema, DeclContext *ctxt, +                       std::vector<Scope *> &result) { +  // FIXME: The result should be a list of unique_ptrs, but the TUScope makes +  // this currently impossible as it's owned by the Sema. + +  if (auto parent = ctxt->getParent()) { +    makeScopes(sema, parent, result); + +    Scope *scope = +        new Scope(result.back(), Scope::DeclScope, sema.getDiagnostics()); +    scope->setEntity(ctxt); +    result.push_back(scope); +  } else +    result.push_back(sema.TUScope); +} + +/// Uses the Sema to look up the given name in the given DeclContext. +static std::unique_ptr<LookupResult> +emulateLookupInCtxt(Sema &sema, llvm::StringRef name, DeclContext *ctxt) { +  IdentifierInfo &ident = sema.getASTContext().Idents.get(name); + +  std::unique_ptr<LookupResult> lookup_result; +  lookup_result.reset(new LookupResult(sema, DeclarationName(&ident), +                                       SourceLocation(), +                                       Sema::LookupOrdinaryName)); + +  // Usually during parsing we already encountered the scopes we would use. But +  // here don't have these scopes so we have to emulate the behavior of the +  // Sema during parsing. +  std::vector<Scope *> scopes; +  makeScopes(sema, ctxt, scopes); + +  // Now actually perform the lookup with the sema. +  sema.LookupName(*lookup_result, scopes.back()); + +  // Delete all the allocated scopes beside the translation unit scope (which +  // has depth 0). +  for (Scope *s : scopes) +    if (s->getDepth() != 0) +      delete s; + +  return lookup_result; +} + +/// Error class for handling problems when finding a certain DeclContext. +struct MissingDeclContext : public llvm::ErrorInfo<MissingDeclContext> { + +  static char ID; + +  MissingDeclContext(DeclContext *context, std::string error) +      : m_context(context), m_error(error) {} + +  DeclContext *m_context; +  std::string m_error; + +  void log(llvm::raw_ostream &OS) const override { +    OS << llvm::formatv("error when reconstructing context of kind {0}:{1}", +                        m_context->getDeclKindName(), m_error); +  } + +  std::error_code convertToErrorCode() const override { +    return llvm::inconvertibleErrorCode(); +  } +}; + +char MissingDeclContext::ID = 0; + +/// Given a foreign decl context, this function finds the equivalent local +/// decl context in the ASTContext of the given Sema. Potentially deserializes +/// decls from the 'std' module if necessary. +static llvm::Expected<DeclContext *> +getEqualLocalDeclContext(Sema &sema, DeclContext *foreign_ctxt) { + +  // Inline namespaces don't matter for lookups, so let's skip them. +  while (foreign_ctxt && foreign_ctxt->isInlineNamespace()) +    foreign_ctxt = foreign_ctxt->getParent(); + +  // If the foreign context is the TU, we just return the local TU. +  if (foreign_ctxt->isTranslationUnit()) +    return sema.getASTContext().getTranslationUnitDecl(); + +  // Recursively find/build the parent DeclContext. +  llvm::Expected<DeclContext *> parent = +      getEqualLocalDeclContext(sema, foreign_ctxt->getParent()); +  if (!parent) +    return parent; + +  // We currently only support building namespaces. +  if (foreign_ctxt->isNamespace()) { +    NamedDecl *ns = llvm::dyn_cast<NamedDecl>(foreign_ctxt); +    llvm::StringRef ns_name = ns->getName(); + +    auto lookup_result = emulateLookupInCtxt(sema, ns_name, *parent); +    for (NamedDecl *named_decl : *lookup_result) { +      if (DeclContext *DC = llvm::dyn_cast<DeclContext>(named_decl)) +        return DC->getPrimaryContext(); +    } +    return llvm::make_error<MissingDeclContext>( +        foreign_ctxt, +        "Couldn't find namespace " + ns->getQualifiedNameAsString()); +  } + +  return llvm::make_error<MissingDeclContext>(foreign_ctxt, "Unknown context "); +} + +/// Returns true iff tryInstantiateStdTemplate supports instantiating a template +/// with the given template arguments. +static bool templateArgsAreSupported(ArrayRef<TemplateArgument> a) { +  for (const TemplateArgument &arg : a) { +    switch (arg.getKind()) { +    case TemplateArgument::Type: +    case TemplateArgument::Integral: +      break; +    default: +      // TemplateArgument kind hasn't been handled yet. +      return false; +    } +  } +  return true; +} + +/// Constructor function for Clang declarations. Ensures that the created +/// declaration is registered with the ASTImporter. +template <typename T, typename... Args> +T *createDecl(ASTImporter &importer, Decl *from_d, Args &&... args) { +  T *to_d = T::Create(std::forward<Args>(args)...); +  importer.RegisterImportedDecl(from_d, to_d); +  return to_d; +} + +llvm::Optional<Decl *> CxxModuleHandler::tryInstantiateStdTemplate(Decl *d) { +  Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS); + +  // If we don't have a template to instiantiate, then there is nothing to do. +  auto td = dyn_cast<ClassTemplateSpecializationDecl>(d); +  if (!td) +    return {}; + +  // We only care about templates in the std namespace. +  if (!td->getDeclContext()->isStdNamespace()) +    return {}; + +  // We have a whitelist of supported template names. +  if (m_supported_templates.find(td->getName()) == m_supported_templates.end()) +    return {}; + +  // Early check if we even support instantiating this template. We do this +  // before we import anything into the target AST. +  auto &foreign_args = td->getTemplateInstantiationArgs(); +  if (!templateArgsAreSupported(foreign_args.asArray())) +    return {}; + +  // Find the local DeclContext that corresponds to the DeclContext of our +  // decl we want to import. +  llvm::Expected<DeclContext *> to_context = +      getEqualLocalDeclContext(*m_sema, td->getDeclContext()); +  if (!to_context) { +    LLDB_LOG_ERROR(log, to_context.takeError(), +                   "Got error while searching equal local DeclContext for decl " +                   "'{1}':\n{0}", +                   td->getName()); +    return {}; +  } + +  // Look up the template in our local context. +  std::unique_ptr<LookupResult> lookup = +      emulateLookupInCtxt(*m_sema, td->getName(), *to_context); + +  ClassTemplateDecl *new_class_template = nullptr; +  for (auto LD : *lookup) { +    if ((new_class_template = dyn_cast<ClassTemplateDecl>(LD))) +      break; +  } +  if (!new_class_template) +    return {}; + +  // Import the foreign template arguments. +  llvm::SmallVector<TemplateArgument, 4> imported_args; + +  // If this logic is changed, also update templateArgsAreSupported. +  for (const TemplateArgument &arg : foreign_args.asArray()) { +    switch (arg.getKind()) { +    case TemplateArgument::Type: { +      llvm::Expected<QualType> type = m_importer->Import(arg.getAsType()); +      if (!type) { +        LLDB_LOG_ERROR(log, type.takeError(), "Couldn't import type: {0}"); +        return {}; +      } +      imported_args.push_back(TemplateArgument(*type)); +      break; +    } +    case TemplateArgument::Integral: { +      llvm::APSInt integral = arg.getAsIntegral(); +      llvm::Expected<QualType> type = +          m_importer->Import(arg.getIntegralType()); +      if (!type) { +        LLDB_LOG_ERROR(log, type.takeError(), "Couldn't import type: {0}"); +        return {}; +      } +      imported_args.push_back( +          TemplateArgument(d->getASTContext(), integral, *type)); +      break; +    } +    default: +      assert(false && "templateArgsAreSupported not updated?"); +    } +  } + +  // Find the class template specialization declaration that +  // corresponds to these arguments. +  void *InsertPos = nullptr; +  ClassTemplateSpecializationDecl *result = +      new_class_template->findSpecialization(imported_args, InsertPos); + +  if (result) { +    // We found an existing specialization in the module that fits our arguments +    // so we can treat it as the result and register it with the ASTImporter. +    m_importer->RegisterImportedDecl(d, result); +    return result; +  } + +  // Instantiate the template. +  result = createDecl<ClassTemplateSpecializationDecl>( +      *m_importer, d, m_sema->getASTContext(), +      new_class_template->getTemplatedDecl()->getTagKind(), +      new_class_template->getDeclContext(), +      new_class_template->getTemplatedDecl()->getLocation(), +      new_class_template->getLocation(), new_class_template, imported_args, +      nullptr); + +  new_class_template->AddSpecialization(result, InsertPos); +  if (new_class_template->isOutOfLine()) +    result->setLexicalDeclContext( +        new_class_template->getLexicalDeclContext()); +  return result; +} + +llvm::Optional<Decl *> CxxModuleHandler::Import(Decl *d) { +  if (!isValid()) +    return {}; + +  return tryInstantiateStdTemplate(d); +}  | 
