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