Index: clang/include/clang/AST/ExternalASTSource.h =================================================================== --- clang/include/clang/AST/ExternalASTSource.h +++ clang/include/clang/AST/ExternalASTSource.h @@ -39,6 +39,7 @@ class ASTConsumer; class ASTContext; +class ClassTemplateDecl; class CXXBaseSpecifier; class CXXCtorInitializer; class CXXRecordDecl; @@ -242,6 +243,11 @@ /// \c ObjCInterfaceDecl::setExternallyCompleted(). virtual void CompleteType(ObjCInterfaceDecl *Class); + /// Gives the external AST source an opportunity to insert class + /// template instantiations. + virtual bool FindClassTemplateSpecialization(ClassTemplateDecl *ClassTemplate, + ArrayRef Args); + /// Loads comment ranges. virtual void ReadComments(); Index: clang/lib/AST/ExternalASTSource.cpp =================================================================== --- clang/lib/AST/ExternalASTSource.cpp +++ clang/lib/AST/ExternalASTSource.cpp @@ -14,6 +14,7 @@ #include "clang/AST/ExternalASTSource.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclarationName.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LLVM.h" @@ -61,6 +62,11 @@ void ExternalASTSource::CompleteType(ObjCInterfaceDecl *Class) {} +bool ExternalASTSource::FindClassTemplateSpecialization( + ClassTemplateDecl *ClassTemplate, ArrayRef Args) { + return false; +} + void ExternalASTSource::ReadComments() {} void ExternalASTSource::StartedDeserializing() {} Index: clang/lib/Sema/SemaTemplate.cpp =================================================================== --- clang/lib/Sema/SemaTemplate.cpp +++ clang/lib/Sema/SemaTemplate.cpp @@ -3352,6 +3352,14 @@ void *InsertPos = nullptr; ClassTemplateSpecializationDecl *Decl = ClassTemplate->findSpecialization(Converted, InsertPos); + if (!Decl) { + // If we have external source, try to find the specialization + // in the external source. + if (auto *Source = Context.getExternalSource()) { + if (Source->FindClassTemplateSpecialization(ClassTemplate, Converted)) + Decl = ClassTemplate->findSpecialization(Converted, InsertPos); + } + } if (!Decl) { // This is the first time we have referenced this class template // specialization. Create the canonical declaration and add it to Index: lldb/packages/Python/lldbsuite/test/lang/cpp/class-template-instantiation/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/lang/cpp/class-template-instantiation/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules Index: lldb/packages/Python/lldbsuite/test/lang/cpp/class-template-instantiation/TestClassTemplateInstantiation.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/lang/cpp/class-template-instantiation/TestClassTemplateInstantiation.py @@ -0,0 +1,60 @@ +""" +Test that the expression evaluator can instantiate templates. We should be able +to instantiate at least those templates that are instantiated in the symbol +file. +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestClassTemplateInstantiation(TestBase): + mydir = TestBase.compute_mydir(__file__) + + @skipIf(debug_info=no_match(["dwarf"]),oslist=no_match(["macosx"])) + def test_instantiate_template_from_function(self): + self.main_source_file = lldb.SBFileSpec("main.cpp") + self.build() + (_, _, thread, _) = lldbutil.run_to_source_breakpoint(self, "// break main", self.main_source_file) + frame = thread.GetSelectedFrame() + + expr_result = frame.EvaluateExpression("foo::x") + self.assertTrue(expr_result.IsValid()) + self.assertEqual(expr_result.GetValue(), "43") + + expr_result = frame.EvaluateExpression("foo::x") + self.assertTrue(expr_result.IsValid()) + self.assertEqual(expr_result.GetValue(), "46") + + expr_result = frame.EvaluateExpression("sizeof(A::bar)") + self.assertTrue(expr_result.IsValid()) + self.assertEqual(expr_result.GetValue(), "2") + + expr_result = frame.EvaluateExpression("sizeof(A::bar)") + self.assertTrue(expr_result.IsValid()) + self.assertEqual(expr_result.GetValue(), "4") + + @skipIf(debug_info=no_match(["dwarf"]),oslist=no_match(["macosx"])) + def test_instantiate_template_from_method(self): + self.main_source_file = lldb.SBFileSpec("main.cpp") + self.build() + (_, _, thread, _) = lldbutil.run_to_source_breakpoint(self, "// break method", self.main_source_file) + frame = thread.GetSelectedFrame() + + expr_result = frame.EvaluateExpression("sizeof(bar)") + self.assertTrue(expr_result.IsValid()) + self.assertEqual(expr_result.GetValue(), "2") + + expr_result = frame.EvaluateExpression("sizeof(bar)") + self.assertTrue(expr_result.IsValid()) + self.assertEqual(expr_result.GetValue(), "4") + + expr_result = frame.EvaluateExpression("::foo::x") + self.assertTrue(expr_result.IsValid()) + self.assertEqual(expr_result.GetValue(), "43") + + expr_result = frame.EvaluateExpression("foo::x") + self.assertTrue(expr_result.IsValid()) + self.assertEqual(expr_result.GetValue(), "46") Index: lldb/packages/Python/lldbsuite/test/lang/cpp/class-template-instantiation/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/lang/cpp/class-template-instantiation/main.cpp @@ -0,0 +1,27 @@ +template +struct foo { + static int x; +}; + +template +int foo::x = 42 + sizeof(T); + +struct A { + template + struct bar { + T f; + }; + + bar bi; + bar bs; + + int size() { + return sizeof(bar) + sizeof(bar); // break method + } +}; + +int main() { + A a; + a.size(); + return foo::x + foo::x; // break main +} Index: lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h +++ lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h @@ -151,6 +151,18 @@ /// The Decl to be completed in place. void CompleteType(clang::ObjCInterfaceDecl *Class) override; + /// Find class template specialization for a class template. + /// + /// \param[in] ClassTemplate + /// The class template to specialize. + /// \param[in] Args + // Specialization arguments. + // \return + // True iff a specialization is found. + bool FindClassTemplateSpecialization( + clang::ClassTemplateDecl *ClassTemplate, + llvm::ArrayRef Args) override; + /// Called on entering a translation unit. Tells Clang by calling /// setHasExternalVisibleStorage() and setHasExternalLexicalStorage() that /// this object has something to say about undefined names. @@ -234,6 +246,12 @@ return m_original.CompleteType(Class); } + bool FindClassTemplateSpecialization( + clang::ClassTemplateDecl *ClassTemplate, + llvm::ArrayRef Args) override { + return m_original.FindClassTemplateSpecialization(ClassTemplate, Args); + } + bool layoutRecordType( const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment, llvm::DenseMap &FieldOffsets, Index: lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp +++ lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp @@ -514,6 +514,74 @@ return complete_iface_decl; } +namespace { + +std::string +GetNameForClassTemplateSpecialization(ClassTemplateDecl *class_template, + ArrayRef args) { + clang::LangOptions lang_options; + lang_options.CPlusPlus = true; + lang_options.Bool = true; + PrintingPolicy policy(lang_options); + + std::string name; + llvm::raw_string_ostream os(name); + class_template->printQualifiedName(os, policy); + os << "<"; + bool is_first = true; + for (auto arg : args) { + if (is_first) + is_first = false; + else + os << ", "; + arg.print(policy, os); + } + os << ">"; + return os.str(); +} + +} // namespace + +bool ClangASTSource::FindClassTemplateSpecialization( + ClassTemplateDecl *class_template, ArrayRef args) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + const ConstString name( + GetNameForClassTemplateSpecialization(class_template, args)); + + if (log) { + LLDB_LOGF( + log, + " FindClassTemplateSpecialization[%u] on (ASTContext*)%p Completing " + "(ClassTemplateDecl*)%p, searching for %s", + current_id, static_cast(m_ast_context), + static_cast(class_template), name.GetCString()); + + LLDB_LOGF(log, " FCTS[%u] Before:", current_id); + ASTDumper dumper((Decl *)class_template); + dumper.ToLog(log, " [FCTS] "); + } + + TypeList types; + llvm::DenseSet searched_symbol_files; + m_target->GetImages().FindTypes(nullptr, name, true, 1, searched_symbol_files, + types); + + size_t num_types = types.GetSize(); + for (size_t ti = 0; ti < num_types; ++ti) { + lldb::TypeSP type_sp = types.GetTypeAtIndex(ti); + CompilerType full_type = type_sp->GetFullCompilerType(); + if (GuardedCopyType(full_type)) { + LLDB_LOGF(log, " FCTS[%u] Specialization inserted.", current_id); + return true; + } + } + LLDB_LOGF(log, " FCTS[%u] Specialization not found.", current_id); + return false; +} + void ClangASTSource::FindExternalLexicalDecls( const DeclContext *decl_context, llvm::function_ref predicate, Index: lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h =================================================================== --- lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h +++ lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h @@ -41,6 +41,9 @@ DIEArray &offsets) override; void GetTypes(ConstString name, DIEArray &offsets) override; void GetTypes(const DWARFDeclContext &context, DIEArray &offsets) override; + void GetGenericTypes(ConstString name, DIEArray &offsets) override; + void GetGenericTypes(const DWARFDeclContext &context, + DIEArray &offsets) override; void GetNamespaces(ConstString name, DIEArray &offsets) override; void GetFunctions(ConstString name, SymbolFileDWARF &dwarf, const CompilerDeclContext &parent_decl_ctx, Index: lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp =================================================================== --- lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp +++ lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp @@ -148,6 +148,15 @@ m_apple_types_up->FindByName(type_name.GetStringRef(), offsets); } +void AppleDWARFIndex::GetGenericTypes(ConstString name, DIEArray &offsets) { + return; +} + +void AppleDWARFIndex::GetGenericTypes(const DWARFDeclContext &context, + DIEArray &offsets) { + return; +} + void AppleDWARFIndex::GetNamespaces(ConstString name, DIEArray &offsets) { if (m_apple_namespaces_up) m_apple_namespaces_up->FindByName(name.GetStringRef(), offsets); Index: lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h =================================================================== --- lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h +++ lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h @@ -38,6 +38,9 @@ DIEArray &offsets) = 0; virtual void GetTypes(ConstString name, DIEArray &offsets) = 0; virtual void GetTypes(const DWARFDeclContext &context, DIEArray &offsets) = 0; + virtual void GetGenericTypes(ConstString name, DIEArray &offsets) = 0; + virtual void GetGenericTypes(const DWARFDeclContext &context, + DIEArray &offsets) = 0; virtual void GetNamespaces(ConstString name, DIEArray &offsets) = 0; virtual void GetFunctions(ConstString name, SymbolFileDWARF &dwarf, const CompilerDeclContext &parent_decl_ctx, Index: lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h =================================================================== --- lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h +++ lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h @@ -33,6 +33,9 @@ DIEArray &offsets) override; void GetTypes(ConstString name, DIEArray &offsets) override; void GetTypes(const DWARFDeclContext &context, DIEArray &offsets) override; + void GetGenericTypes(ConstString name, DIEArray &offsets) override; + void GetGenericTypes(const DWARFDeclContext &context, + DIEArray &offsets) override; void GetNamespaces(ConstString name, DIEArray &offsets) override; void GetFunctions(ConstString name, SymbolFileDWARF &dwarf, const CompilerDeclContext &parent_decl_ctx, Index: lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp =================================================================== --- lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp +++ lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp @@ -204,6 +204,16 @@ } } +void DebugNamesDWARFIndex::GetGenericTypes(ConstString name, + DIEArray &offsets) { + m_fallback.GetGenericTypes(name, offsets); +} + +void DebugNamesDWARFIndex::GetGenericTypes(const DWARFDeclContext &context, + DIEArray &offsets) { + m_fallback.GetGenericTypes(context, offsets); +} + void DebugNamesDWARFIndex::GetNamespaces(ConstString name, DIEArray &offsets) { m_fallback.GetNamespaces(name, offsets); Index: lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h =================================================================== --- lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h +++ lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h @@ -34,6 +34,9 @@ DIEArray &offsets) override; void GetTypes(ConstString name, DIEArray &offsets) override; void GetTypes(const DWARFDeclContext &context, DIEArray &offsets) override; + void GetGenericTypes(ConstString name, DIEArray &offsets) override; + void GetGenericTypes(const DWARFDeclContext &context, + DIEArray &offsets) override; void GetNamespaces(ConstString name, DIEArray &offsets) override; void GetFunctions(ConstString name, SymbolFileDWARF &dwarf, const CompilerDeclContext &parent_decl_ctx, @@ -53,6 +56,7 @@ NameToDIE objc_class_selectors; NameToDIE globals; NameToDIE types; + NameToDIE generic_types; NameToDIE namespaces; }; void Index(); Index: lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp =================================================================== --- lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp +++ lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp @@ -84,6 +84,7 @@ [&]() { finalize_fn(&IndexSet::objc_class_selectors); }, [&]() { finalize_fn(&IndexSet::globals); }, [&]() { finalize_fn(&IndexSet::types); }, + [&]() { finalize_fn(&IndexSet::generic_types); }, [&]() { finalize_fn(&IndexSet::namespaces); }); } @@ -311,10 +312,20 @@ case DW_TAG_typedef: case DW_TAG_union_type: case DW_TAG_unspecified_type: - if (name && !is_declaration) - set.types.Insert(ConstString(name), ref); - if (mangled_cstr && !is_declaration) + if (name && !is_declaration) { + ConstString name_cs(name); + set.types.Insert(name_cs, ref); + if (Language::LanguageIsCPlusPlus(cu_language) && !name_cs.IsEmpty() && + name[name_cs.GetLength() - 1] == '>') { + const char *angle_bracket_pos = strchr(name, '<'); + assert(angle_bracket_pos && "missing matching angle bracket"); + size_t generic_length = angle_bracket_pos - name; + set.generic_types.Insert(ConstString(name, generic_length), ref); + } + } + if (mangled_cstr && !is_declaration) { set.types.Insert(ConstString(mangled_cstr), ref); + } break; case DW_TAG_namespace: @@ -388,6 +399,17 @@ m_set.types.Find(ConstString(context[0].name), offsets); } +void ManualDWARFIndex::GetGenericTypes(ConstString name, DIEArray &offsets) { + Index(); + m_set.generic_types.Find(name, offsets); +} + +void ManualDWARFIndex::GetGenericTypes(const DWARFDeclContext &context, + DIEArray &offsets) { + Index(); + m_set.generic_types.Find(ConstString(context[0].name), offsets); +} + void ManualDWARFIndex::GetNamespaces(ConstString name, DIEArray &offsets) { Index(); m_set.namespaces.Find(name, offsets); Index: lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp =================================================================== --- lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -2499,6 +2499,24 @@ } } + die_offsets.clear(); + m_index->GetGenericTypes(name, die_offsets); + for (size_t i = 0; i < die_offsets.size(); ++i) { + const DIERef &die_ref = die_offsets[i]; + DWARFDIE die = GetDIE(die_ref); + if (die) { + if (!DIEInDeclContext(parent_decl_ctx, die)) + continue; // The containing decl contexts don't match + if (Type *matching_type = ResolveType(die, true, true)) { + types.InsertUnique(matching_type->shared_from_this()); + if (types.GetSize() >= max_matches) + break; + } + } else { + m_index->ReportInvalidDIERef(die_ref, name.GetStringRef()); + } + } + // Next search through the reachable Clang modules. This only applies for // DWARF objects compiled with -gmodules that haven't been processed by // dsymutil. Index: lldb/source/Symbol/TypeMap.cpp =================================================================== --- lldb/source/Symbol/TypeMap.cpp +++ lldb/source/Symbol/TypeMap.cpp @@ -141,6 +141,27 @@ exact_match); } +namespace { + +bool TypeBasenamesMatch(const std::string &type_basename, + llvm::StringRef match_type_basename, + bool is_instantiation) { + if (match_type_basename == type_basename) + return true; + // If the basenames do not match, let us see if {match_type_basename} could + // be an instantiation of {type_basename}. + if (is_instantiation) + return false; + size_t basename_size = type_basename.size(); + if (match_type_basename.size() <= basename_size) + return false; + if (match_type_basename[basename_size] != '<') + return false; + return match_type_basename.take_front(basename_size) == type_basename; +} + +} // namespace + void TypeMap::RemoveMismatchedTypes(const std::string &type_scope, const std::string &type_basename, TypeClass type_class, bool exact_match) { @@ -152,6 +173,8 @@ iterator pos, end = m_types.end(); + bool is_instantiation = type_basename.find('<') != std::string::npos; + for (pos = m_types.begin(); pos != end; ++pos) { Type *the_type = pos->second.get(); bool keep_match = false; @@ -171,7 +194,8 @@ if (Type::GetTypeScopeAndBasename(match_type_name, match_type_scope, match_type_basename, match_type_class)) { - if (match_type_basename == type_basename) { + if (TypeBasenamesMatch(type_basename, match_type_basename, + is_instantiation)) { const size_t type_scope_size = type_scope.size(); const size_t match_type_scope_size = match_type_scope.size(); if (exact_match || (type_scope_size == match_type_scope_size)) { @@ -203,7 +227,9 @@ } else { // The type we are currently looking at doesn't exists in a namespace // or class, so it only matches if there is no type scope... - keep_match = type_scope.empty() && type_basename == match_type_name; + keep_match = type_scope.empty() && + TypeBasenamesMatch(type_basename, match_type_name, + is_instantiation); } }