1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
|
//===-- CxxModuleHandler.cpp ----------------------------------------------===//
//
// 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 "Plugins/ExpressionParser/Clang/CxxModuleHandler.h"
#include "Plugins/TypeSystem/Clang/TypeSystemClang.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(TypeSystemClang::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 = std::make_unique<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 list 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);
}
|