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