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