diff --git a/lldb/packages/Python/lldbsuite/test/lang/cpp/class-template-instantiation/Makefile b/lldb/packages/Python/lldbsuite/test/lang/cpp/class-template-instantiation/Makefile new file mode 100644 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/lang/cpp/class-template-instantiation/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/packages/Python/lldbsuite/test/lang/cpp/class-template-instantiation/TestClassTemplateInstantiation.py b/lldb/packages/Python/lldbsuite/test/lang/cpp/class-template-instantiation/TestClassTemplateInstantiation.py new file mode 100644 --- /dev/null +++ b/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") diff --git a/lldb/packages/Python/lldbsuite/test/lang/cpp/class-template-instantiation/main.cpp b/lldb/packages/Python/lldbsuite/test/lang/cpp/class-template-instantiation/main.cpp new file mode 100644 --- /dev/null +++ b/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 +} diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp @@ -808,6 +808,27 @@ name_string_ref.startswith("_$"); } +namespace { + +ClassTemplateDecl * +TryGetSpecializedTemplateFromInstantiation(const CompilerType compiler_type) { + clang::QualType qual_type = ClangUtil::GetQualType(compiler_type); + if (qual_type->getTypeClass() == clang::Type::Record) { + const clang::CXXRecordDecl *cxx_record_decl = + qual_type->getAsCXXRecordDecl(); + if (cxx_record_decl) { + const clang::ClassTemplateSpecializationDecl *d = + llvm::dyn_cast( + cxx_record_decl); + if (d) + return d->getSpecializedTemplate(); + } + } + return nullptr; +} + +} // namespace + void ClangASTSource::FindExternalVisibleDecls( NameSearchContext &context, lldb::ModuleSP module_sp, CompilerDeclContext &namespace_decl, unsigned int current_id) { @@ -905,6 +926,24 @@ continue; } + bool prefer_generic = + name.IsEmpty() || name.GetStringRef()[name.GetLength() - 1] != '>'; + if (prefer_generic) { + if (ClassTemplateDecl *t = TryGetSpecializedTemplateFromInstantiation( + copied_clang_type)) { + // Try to import all instances of the template class and add the + // relevant class template to the context. + for (size_t gti = ti + 1; gti < num_types; ++gti) { + lldb::TypeSP inst_type_sp = types.GetTypeAtIndex(gti); + + GuardedCopyType(inst_type_sp->GetForwardCompilerType()); + } + context.AddNamedDecl(t); + context.m_found.type = true; + break; + } + } + context.AddTypeDecl(copied_clang_type); context.m_found.type = true; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h b/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h --- a/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h +++ b/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, diff --git a/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp +++ b/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); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h +++ b/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, diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h b/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h --- a/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h +++ b/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, diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp +++ b/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); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h --- a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h +++ b/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(); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp +++ b/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); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -2419,6 +2419,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. diff --git a/lldb/source/Symbol/TypeMap.cpp b/lldb/source/Symbol/TypeMap.cpp --- a/lldb/source/Symbol/TypeMap.cpp +++ b/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); } }