1 //===-- CxxModuleHandler.cpp ----------------------------------------------===//
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-exception
7 //===----------------------------------------------------------------------===//
9 #include "Plugins/ExpressionParser/Clang/CxxModuleHandler.h"
10 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
12 #include "lldb/Utility/LLDBLog.h"
13 #include "lldb/Utility/Log.h"
14 #include "clang/Sema/Lookup.h"
15 #include "llvm/Support/Error.h"
18 using namespace lldb_private
;
19 using namespace clang
;
21 CxxModuleHandler::CxxModuleHandler(ASTImporter
&importer
, ASTContext
*target
)
22 : m_importer(&importer
),
23 m_sema(TypeSystemClang::GetASTContext(target
)->getSema()) {
25 std::initializer_list
<const char *> supported_names
= {
45 m_supported_templates
.insert(supported_names
.begin(), supported_names
.end());
48 /// Builds a list of scopes that point into the given context.
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 caller
53 /// (except the TUScope which is owned by the sema).
54 static 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 makes
57 // this currently impossible as it's owned by the Sema.
59 if (auto parent
= ctxt
->getParent()) {
60 makeScopes(sema
, parent
, result
);
63 new Scope(result
.back(), Scope::DeclScope
, sema
.getDiagnostics());
64 scope
->setEntity(ctxt
);
65 result
.push_back(scope
);
67 result
.push_back(sema
.TUScope
);
70 /// Uses the Sema to look up the given name in the given DeclContext.
71 static std::unique_ptr
<LookupResult
>
72 emulateLookupInCtxt(Sema
&sema
, llvm::StringRef name
, DeclContext
*ctxt
) {
73 IdentifierInfo
&ident
= sema
.getASTContext().Idents
.get(name
);
75 std::unique_ptr
<LookupResult
> lookup_result
;
76 lookup_result
= std::make_unique
<LookupResult
>(sema
, DeclarationName(&ident
),
78 Sema::LookupOrdinaryName
);
80 // Usually during parsing we already encountered the scopes we would use. But
81 // here don't have these scopes so we have to emulate the behavior of the
82 // Sema during parsing.
83 std::vector
<Scope
*> scopes
;
84 makeScopes(sema
, ctxt
, scopes
);
86 // Now actually perform the lookup with the sema.
87 sema
.LookupName(*lookup_result
, scopes
.back());
89 // Delete all the allocated scopes beside the translation unit scope (which
91 for (Scope
*s
: scopes
)
92 if (s
->getDepth() != 0)
98 /// Error class for handling problems when finding a certain DeclContext.
99 struct MissingDeclContext
: public llvm::ErrorInfo
<MissingDeclContext
> {
103 MissingDeclContext(DeclContext
*context
, std::string error
)
104 : m_context(context
), m_error(error
) {}
106 DeclContext
*m_context
;
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
);
114 std::error_code
convertToErrorCode() const override
{
115 return llvm::inconvertibleErrorCode();
119 char MissingDeclContext::ID
= 0;
121 /// Given a foreign decl context, this function finds the equivalent local
122 /// decl context in the ASTContext of the given Sema. Potentially deserializes
123 /// decls from the 'std' module if necessary.
124 static llvm::Expected
<DeclContext
*>
125 getEqualLocalDeclContext(Sema
&sema
, DeclContext
*foreign_ctxt
) {
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();
131 // If the foreign context is the TU, we just return the local TU.
132 if (foreign_ctxt
->isTranslationUnit())
133 return sema
.getASTContext().getTranslationUnitDecl();
135 // Recursively find/build the parent DeclContext.
136 llvm::Expected
<DeclContext
*> parent
=
137 getEqualLocalDeclContext(sema
, foreign_ctxt
->getParent());
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();
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();
151 return llvm::make_error
<MissingDeclContext
>(
153 "Couldn't find namespace " + ns
->getQualifiedNameAsString());
156 return llvm::make_error
<MissingDeclContext
>(foreign_ctxt
, "Unknown context ");
159 /// Returns true iff tryInstantiateStdTemplate supports instantiating a template
160 /// with the given template arguments.
161 static bool templateArgsAreSupported(ArrayRef
<TemplateArgument
> a
) {
162 for (const TemplateArgument
&arg
: a
) {
163 switch (arg
.getKind()) {
164 case TemplateArgument::Type
:
165 case TemplateArgument::Integral
:
168 // TemplateArgument kind hasn't been handled yet.
175 /// Constructor function for Clang declarations. Ensures that the created
176 /// declaration is registered with the ASTImporter.
177 template <typename T
, typename
... Args
>
178 T
*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
);
184 std::optional
<Decl
*> CxxModuleHandler::tryInstantiateStdTemplate(Decl
*d
) {
185 Log
*log
= GetLog(LLDBLog::Expressions
);
187 // If we don't have a template to instiantiate, then there is nothing to do.
188 auto td
= dyn_cast
<ClassTemplateSpecializationDecl
>(d
);
192 // We only care about templates in the std namespace.
193 if (!td
->getDeclContext()->isStdNamespace())
196 // We have a list of supported template names.
197 if (!m_supported_templates
.contains(td
->getName()))
200 // Early check if we even support instantiating this template. We do this
201 // before we import anything into the target AST.
202 auto &foreign_args
= td
->getTemplateInstantiationArgs();
203 if (!templateArgsAreSupported(foreign_args
.asArray()))
206 // Find the local DeclContext that corresponds to the DeclContext of our
207 // decl we want to import.
208 llvm::Expected
<DeclContext
*> to_context
=
209 getEqualLocalDeclContext(*m_sema
, td
->getDeclContext());
211 LLDB_LOG_ERROR(log
, to_context
.takeError(),
212 "Got error while searching equal local DeclContext for decl "
218 // Look up the template in our local context.
219 std::unique_ptr
<LookupResult
> lookup
=
220 emulateLookupInCtxt(*m_sema
, td
->getName(), *to_context
);
222 ClassTemplateDecl
*new_class_template
= nullptr;
223 for (auto LD
: *lookup
) {
224 if ((new_class_template
= dyn_cast
<ClassTemplateDecl
>(LD
)))
227 if (!new_class_template
)
230 // Import the foreign template arguments.
231 llvm::SmallVector
<TemplateArgument
, 4> imported_args
;
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());
239 LLDB_LOG_ERROR(log
, type
.takeError(), "Couldn't import type: {0}");
242 imported_args
.push_back(
243 TemplateArgument(*type
, /*isNullPtr*/ false, arg
.getIsDefaulted()));
246 case TemplateArgument::Integral
: {
247 llvm::APSInt integral
= arg
.getAsIntegral();
248 llvm::Expected
<QualType
> type
=
249 m_importer
->Import(arg
.getIntegralType());
251 LLDB_LOG_ERROR(log
, type
.takeError(), "Couldn't import type: {0}");
254 imported_args
.push_back(TemplateArgument(d
->getASTContext(), integral
,
255 *type
, arg
.getIsDefaulted()));
259 assert(false && "templateArgsAreSupported not updated?");
263 // Find the class template specialization declaration that
264 // corresponds to these arguments.
265 void *InsertPos
= nullptr;
266 ClassTemplateSpecializationDecl
*result
=
267 new_class_template
->findSpecialization(imported_args
, InsertPos
);
270 // We found an existing specialization in the module that fits our arguments
271 // so we can treat it as the result and register it with the ASTImporter.
272 m_importer
->RegisterImportedDecl(d
, result
);
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
,
285 new_class_template
->AddSpecialization(result
, InsertPos
);
286 if (new_class_template
->isOutOfLine())
287 result
->setLexicalDeclContext(
288 new_class_template
->getLexicalDeclContext());
292 std::optional
<Decl
*> CxxModuleHandler::Import(Decl
*d
) {
296 return tryInstantiateStdTemplate(d
);