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