[Lldb-commits] [lldb] a842f74 - [lldb] Support simplified template names

Arthur Eubanks via lldb-commits lldb-commits at lists.llvm.org
Fri Oct 28 16:16:19 PDT 2022


Author: Arthur Eubanks
Date: 2022-10-28T16:15:37-07:00
New Revision: a842f74056793d9ab41411aa343811368164e6a8

URL: https://github.com/llvm/llvm-project/commit/a842f74056793d9ab41411aa343811368164e6a8
DIFF: https://github.com/llvm/llvm-project/commit/a842f74056793d9ab41411aa343811368164e6a8.diff

LOG: [lldb] Support simplified template names

See https://discourse.llvm.org/t/dwarf-using-simplified-template-names/58417 for background on simplified template names.

lldb doesn't work with simplified template names because it uses DW_AT_name which doesn't contain template parameters under simplified template names.

Two major changes are required to make lldb work with simplified template names.

1) When building clang ASTs for struct-like dies, we use the name as a cache key. To distinguish between different instantiations of a template class, we need to add in the template parameters.

2) When looking up types, if the requested type name contains '<' and we didn't initially find any types from the index searching the name, strip the template parameters and search the index, then filter out results with non-matching template parameters. This takes advantage of the clang AST's ability to print full names rather than doing it by ourself.

An alternative is to fix up the names in the index to contain the fully qualified name, but that doesn't respect .debug_names.

Reviewed By: labath

Differential Revision: https://reviews.llvm.org/D134378

Added: 
    lldb/test/API/lang/cpp/unique-types2/Makefile
    lldb/test/API/lang/cpp/unique-types2/TestUniqueTypes.py
    lldb/test/API/lang/cpp/unique-types2/main.cpp

Modified: 
    lldb/include/lldb/Symbol/CompilerType.h
    lldb/include/lldb/Symbol/Type.h
    lldb/include/lldb/Symbol/TypeSystem.h
    lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
    lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
    lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
    lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
    lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
    lldb/source/Symbol/CompilerType.cpp
    lldb/source/Symbol/Type.cpp
    lldb/test/API/lang/cpp/complete-type-check/TestCppIsTypeComplete.py

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Symbol/CompilerType.h b/lldb/include/lldb/Symbol/CompilerType.h
index f0443fceb28d0..ef9f283cefec9 100644
--- a/lldb/include/lldb/Symbol/CompilerType.h
+++ b/lldb/include/lldb/Symbol/CompilerType.h
@@ -163,7 +163,7 @@ class CompilerType {
   /// \{
   TypeSystem *GetTypeSystem() const { return m_type_system; }
 
-  ConstString GetTypeName() const;
+  ConstString GetTypeName(bool BaseOnly = false) const;
 
   ConstString GetDisplayTypeName() const;
 

diff  --git a/lldb/include/lldb/Symbol/Type.h b/lldb/include/lldb/Symbol/Type.h
index 9ecd677a7106f..fd4577028faaa 100644
--- a/lldb/include/lldb/Symbol/Type.h
+++ b/lldb/include/lldb/Symbol/Type.h
@@ -135,6 +135,8 @@ class Type : public std::enable_shared_from_this<Type>, public UserID {
 
   ConstString GetName();
 
+  ConstString GetBaseName();
+
   llvm::Optional<uint64_t> GetByteSize(ExecutionContextScope *exe_scope);
 
   uint32_t GetNumChildren(bool omit_empty_base_classes);

diff  --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h
index 63f9495ad916d..93959f4596d61 100644
--- a/lldb/include/lldb/Symbol/TypeSystem.h
+++ b/lldb/include/lldb/Symbol/TypeSystem.h
@@ -208,7 +208,8 @@ class TypeSystem : public PluginInterface {
 
   // Accessors
 
-  virtual ConstString GetTypeName(lldb::opaque_compiler_type_t type) = 0;
+  virtual ConstString GetTypeName(lldb::opaque_compiler_type_t type,
+                                  bool BaseOnly) = 0;
 
   virtual ConstString GetDisplayTypeName(lldb::opaque_compiler_type_t type) = 0;
 

diff  --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
index 3f2614af64d4d..6e76203dcf94a 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -1523,6 +1523,41 @@ TypeSP DWARFASTParserClang::UpdateSymbolContextScopeForType(
   return type_sp;
 }
 
+std::string
+DWARFASTParserClang::GetTemplateParametersString(const DWARFDIE &die) {
+  if (llvm::StringRef(die.GetName()).contains("<"))
+    return std::string();
+  TypeSystemClang::TemplateParameterInfos template_param_infos;
+  if (!ParseTemplateParameterInfos(die, template_param_infos))
+    return std::string();
+  std::string all_template_names;
+  llvm::SmallVector<clang::TemplateArgument, 2> args =
+      template_param_infos.args;
+  if (template_param_infos.hasParameterPack())
+    args.append(template_param_infos.packed_args->args);
+  if (args.empty())
+    return std::string();
+  for (auto &arg : args) {
+    std::string template_name;
+    llvm::raw_string_ostream os(template_name);
+    arg.print(m_ast.getASTContext().getPrintingPolicy(), os, true);
+
+    if (!template_name.empty()) {
+      if (all_template_names.empty()) {
+        all_template_names.append("<");
+      } else {
+        all_template_names.append(", ");
+      }
+      all_template_names.append(template_name);
+    }
+  }
+  assert(!all_template_names.empty() && "no template parameters?");
+  // Spacing doesn't matter as long as we're consistent because we're only using
+  // this to deduplicate C++ symbols.
+  all_template_names.append(">");
+  return all_template_names;
+}
+
 std::string
 DWARFASTParserClang::GetCPlusPlusQualifiedName(const DWARFDIE &die) {
   if (!die.IsValid())
@@ -1534,6 +1569,9 @@ DWARFASTParserClang::GetCPlusPlusQualifiedName(const DWARFDIE &die) {
   DWARFDIE parent_decl_ctx_die = die.GetParentDeclContextDIE();
   // TODO: change this to get the correct decl context parent....
   while (parent_decl_ctx_die) {
+    // The name may not contain template parameters due to simplified template
+    // names; we must reconstruct the full name from child template parameter
+    // dies via GetTemplateParametersString().
     const dw_tag_t parent_tag = parent_decl_ctx_die.Tag();
     switch (parent_tag) {
     case DW_TAG_namespace: {
@@ -1551,6 +1589,8 @@ DWARFASTParserClang::GetCPlusPlusQualifiedName(const DWARFDIE &die) {
     case DW_TAG_structure_type:
     case DW_TAG_union_type: {
       if (const char *class_union_struct_name = parent_decl_ctx_die.GetName()) {
+        qualified_name.insert(0,
+                              GetTemplateParametersString(parent_decl_ctx_die));
         qualified_name.insert(0, "::");
         qualified_name.insert(0, class_union_struct_name);
       }
@@ -1568,6 +1608,7 @@ DWARFASTParserClang::GetCPlusPlusQualifiedName(const DWARFDIE &die) {
     qualified_name.append("::");
 
   qualified_name.append(name);
+  qualified_name.append(GetTemplateParametersString(die));
 
   return qualified_name;
 }
@@ -1772,36 +1813,36 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc,
     metadata.SetUserID(die.GetID());
     metadata.SetIsDynamicCXXType(dwarf->ClassOrStructIsVirtual(die));
 
-    if (attrs.name.GetStringRef().contains('<')) {
-      TypeSystemClang::TemplateParameterInfos template_param_infos;
-      if (ParseTemplateParameterInfos(die, template_param_infos)) {
-        clang::ClassTemplateDecl *class_template_decl =
-            m_ast.ParseClassTemplateDecl(
-                decl_ctx, GetOwningClangModule(die), attrs.accessibility,
-                attrs.name.GetCString(), tag_decl_kind, template_param_infos);
-        if (!class_template_decl) {
-          if (log) {
-            dwarf->GetObjectFile()->GetModule()->LogMessage(
-                log,
-                "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" "
-                "clang::ClassTemplateDecl failed to return a decl.",
-                static_cast<void *>(this), die.GetOffset(),
-                DW_TAG_value_to_name(tag), attrs.name.GetCString());
-          }
-          return TypeSP();
+    TypeSystemClang::TemplateParameterInfos template_param_infos;
+    if (ParseTemplateParameterInfos(die, template_param_infos) &&
+        (!template_param_infos.args.empty() ||
+         template_param_infos.packed_args)) {
+      clang::ClassTemplateDecl *class_template_decl =
+          m_ast.ParseClassTemplateDecl(
+              decl_ctx, GetOwningClangModule(die), attrs.accessibility,
+              attrs.name.GetCString(), tag_decl_kind, template_param_infos);
+      if (!class_template_decl) {
+        if (log) {
+          dwarf->GetObjectFile()->GetModule()->LogMessage(
+              log,
+              "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" "
+              "clang::ClassTemplateDecl failed to return a decl.",
+              static_cast<void *>(this), die.GetOffset(),
+              DW_TAG_value_to_name(tag), attrs.name.GetCString());
         }
+        return TypeSP();
+      }
 
-        clang::ClassTemplateSpecializationDecl *class_specialization_decl =
-            m_ast.CreateClassTemplateSpecializationDecl(
-                decl_ctx, GetOwningClangModule(die), class_template_decl,
-                tag_decl_kind, template_param_infos);
-        clang_type = m_ast.CreateClassTemplateSpecializationType(
-            class_specialization_decl);
-        clang_type_was_created = true;
+      clang::ClassTemplateSpecializationDecl *class_specialization_decl =
+          m_ast.CreateClassTemplateSpecializationDecl(
+              decl_ctx, GetOwningClangModule(die), class_template_decl,
+              tag_decl_kind, template_param_infos);
+      clang_type = m_ast.CreateClassTemplateSpecializationType(
+          class_specialization_decl);
+      clang_type_was_created = true;
 
-        m_ast.SetMetadata(class_template_decl, metadata);
-        m_ast.SetMetadata(class_specialization_decl, metadata);
-      }
+      m_ast.SetMetadata(class_template_decl, metadata);
+      m_ast.SetMetadata(class_specialization_decl, metadata);
     }
 
     if (!clang_type_was_created) {

diff  --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
index 860e5f4f7d5b9..d1ca8278f26e7 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
@@ -127,6 +127,10 @@ class DWARFASTParserClang : public DWARFASTParser {
       lldb_private::TypeSystemClang::TemplateParameterInfos
           &template_param_infos);
 
+  /// Get the template parameters of a die as a string if the die name does not
+  /// already contain them. This happens with -gsimple-template-names.
+  std::string GetTemplateParametersString(const DWARFDIE &die);
+
   std::string GetCPlusPlusQualifiedName(const DWARFDIE &die);
 
   bool ParseChildMembers(

diff  --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index 42a0302351dd6..5d5a47bc0c92c 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -2492,6 +2492,45 @@ void SymbolFileDWARF::FindTypes(
     return types.GetSize() < max_matches;
   });
 
+  // With -gsimple-template-names, a templated type's DW_AT_name will not
+  // contain the template parameters. Try again stripping '<' and anything
+  // after, filtering out entries with template parameters that don't match.
+  if (types.GetSize() < max_matches) {
+    const llvm::StringRef name_ref = name.GetStringRef();
+    auto it = name_ref.find('<');
+    if (it != llvm::StringRef::npos) {
+      const llvm::StringRef name_no_template_params = name_ref.slice(0, it);
+      const llvm::StringRef template_params = name_ref.slice(it, name_ref.size());
+      m_index->GetTypes(ConstString(name_no_template_params), [&](DWARFDIE die) {
+        if (!DIEInDeclContext(parent_decl_ctx, die))
+          return true; // The containing decl contexts don't match
+
+        const llvm::StringRef base_name = GetTypeForDIE(die)->GetBaseName().AsCString();
+        auto it = base_name.find('<');
+        // If the candidate qualified name doesn't have '<', it doesn't have
+        // template params to compare.
+        if (it == llvm::StringRef::npos)
+          return true;
+
+        // Filter out non-matching instantiations by comparing template params.
+        const llvm::StringRef base_name_template_params =
+            base_name.slice(it, base_name.size());
+
+        if (template_params != base_name_template_params)
+          return true;
+
+        Type *matching_type = ResolveType(die, true, true);
+        if (!matching_type)
+          return true;
+
+        // We found a type pointer, now find the shared pointer form our type
+        // list.
+        types.InsertUnique(matching_type->shared_from_this());
+        return types.GetSize() < max_matches;
+      });
+    }
+  }
+
   // 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/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 650f8a29d4063..4fa3c65be7396 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -3781,7 +3781,8 @@ bool TypeSystemClang::GetCompleteType(lldb::opaque_compiler_type_t type) {
                              allow_completion);
 }
 
-ConstString TypeSystemClang::GetTypeName(lldb::opaque_compiler_type_t type) {
+ConstString TypeSystemClang::GetTypeName(lldb::opaque_compiler_type_t type,
+                                         bool BaseOnly) {
   if (!type)
     return ConstString();
 
@@ -3803,7 +3804,9 @@ ConstString TypeSystemClang::GetTypeName(lldb::opaque_compiler_type_t type) {
     return ConstString(GetTypeNameForDecl(typedef_decl));
   }
 
-  return ConstString(qual_type.getAsString(GetTypePrintingPolicy()));
+  clang::PrintingPolicy printing_policy(GetTypePrintingPolicy());
+  printing_policy.SuppressScope = BaseOnly;
+  return ConstString(qual_type.getAsString(printing_policy));
 }
 
 ConstString
@@ -9353,7 +9356,9 @@ clang::ClassTemplateDecl *TypeSystemClang::ParseClassTemplateDecl(
     const TypeSystemClang::TemplateParameterInfos &template_param_infos) {
   if (template_param_infos.IsValid()) {
     std::string template_basename(parent_name);
-    template_basename.erase(template_basename.find('<'));
+    // With -gsimple-template-names we may omit template parameters in the name.
+    if (auto i = template_basename.find('<'); i != std::string::npos)
+      template_basename.erase(i);
 
     return CreateClassTemplateDecl(decl_ctx, owning_module, access_type,
                                    template_basename.c_str(), tag_decl_kind,

diff  --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
index 365fa720e0da5..291bb5d58143e 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
@@ -645,7 +645,8 @@ class TypeSystemClang : public TypeSystem {
 
   // Accessors
 
-  ConstString GetTypeName(lldb::opaque_compiler_type_t type) override;
+  ConstString GetTypeName(lldb::opaque_compiler_type_t type,
+                          bool BaseOnly) override;
 
   ConstString GetDisplayTypeName(lldb::opaque_compiler_type_t type) override;
 

diff  --git a/lldb/source/Symbol/CompilerType.cpp b/lldb/source/Symbol/CompilerType.cpp
index 3a191644cc0d0..1b02895fd390c 100644
--- a/lldb/source/Symbol/CompilerType.cpp
+++ b/lldb/source/Symbol/CompilerType.cpp
@@ -280,9 +280,9 @@ size_t CompilerType::GetPointerByteSize() const {
   return 0;
 }
 
-ConstString CompilerType::GetTypeName() const {
+ConstString CompilerType::GetTypeName(bool BaseOnly) const {
   if (IsValid()) {
-    return m_type_system->GetTypeName(m_type);
+    return m_type_system->GetTypeName(m_type, BaseOnly);
   }
   return ConstString("<invalid>");
 }

diff  --git a/lldb/source/Symbol/Type.cpp b/lldb/source/Symbol/Type.cpp
index 4aa4293aff797..3bf2c817dcff2 100644
--- a/lldb/source/Symbol/Type.cpp
+++ b/lldb/source/Symbol/Type.cpp
@@ -305,6 +305,10 @@ ConstString Type::GetName() {
   return m_name;
 }
 
+ConstString Type::GetBaseName() {
+  return GetForwardCompilerType().GetTypeName(/*BaseOnly*/ true);
+}
+
 void Type::DumpTypeName(Stream *s) { GetName().Dump(s, "<invalid-type-name>"); }
 
 void Type::DumpValue(ExecutionContext *exe_ctx, Stream *s,

diff  --git a/lldb/test/API/lang/cpp/complete-type-check/TestCppIsTypeComplete.py b/lldb/test/API/lang/cpp/complete-type-check/TestCppIsTypeComplete.py
index 81a8876743474..fe8ab3b7d6b5a 100644
--- a/lldb/test/API/lang/cpp/complete-type-check/TestCppIsTypeComplete.py
+++ b/lldb/test/API/lang/cpp/complete-type-check/TestCppIsTypeComplete.py
@@ -51,7 +51,7 @@ def test_forward_declarations(self):
         # Record types without a defining declaration are not complete.
         self.assertPointeeIncomplete("FwdClass *", "fwd_class")
         self.assertPointeeIncomplete("FwdClassTypedef *", "fwd_class_typedef")
-        self.assertPointeeIncomplete("FwdTemplateClass<> *", "fwd_template_class")
+        self.assertPointeeIncomplete("FwdTemplateClass<int> *", "fwd_template_class")
 
         # A pointer type is complete even when it points to an incomplete type.
         fwd_class_ptr = self.expect_expr("fwd_class", result_type="FwdClass *")

diff  --git a/lldb/test/API/lang/cpp/unique-types2/Makefile b/lldb/test/API/lang/cpp/unique-types2/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/lang/cpp/unique-types2/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules

diff  --git a/lldb/test/API/lang/cpp/unique-types2/TestUniqueTypes.py b/lldb/test/API/lang/cpp/unique-types2/TestUniqueTypes.py
new file mode 100644
index 0000000000000..8d04871f817e3
--- /dev/null
+++ b/lldb/test/API/lang/cpp/unique-types2/TestUniqueTypes.py
@@ -0,0 +1,61 @@
+"""
+Test that we return only the requested template instantiation.
+"""
+
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+
+class UniqueTypesTestCase(TestBase):
+    def do_test(self, debug_flags):
+        """Test that we only display the requested Foo instantiation, not all Foo instantiations."""
+        self.build(dictionary=debug_flags)
+        lldbutil.run_to_source_breakpoint(self, "// Set breakpoint here", lldb.SBFileSpec("main.cpp"))
+
+        self.expect("image lookup -A -t '::Foo<char>'", DATA_TYPES_DISPLAYED_CORRECTLY, substrs=["1 match found"])
+        self.expect("image lookup -A -t 'Foo<char>'", DATA_TYPES_DISPLAYED_CORRECTLY, substrs=["1 match found"])
+        self.expect("image lookup -A -t 'Foo<int>'", DATA_TYPES_DISPLAYED_CORRECTLY, substrs=["1 match found"])
+        self.expect("image lookup -A -t 'Foo<Foo<int> >'", DATA_TYPES_DISPLAYED_CORRECTLY, substrs=["1 match found"])
+        self.expect("image lookup -A -t 'Foo<float>'", DATA_TYPES_DISPLAYED_CORRECTLY, error=True)
+
+        self.expect("image lookup -A -t '::FooPack<char>'", DATA_TYPES_DISPLAYED_CORRECTLY, substrs=["1 match found"])
+        self.expect("image lookup -A -t 'FooPack<char>'", DATA_TYPES_DISPLAYED_CORRECTLY, substrs=["1 match found"])
+        self.expect("image lookup -A -t 'FooPack<int>'", DATA_TYPES_DISPLAYED_CORRECTLY, substrs=["1 match found"])
+        self.expect("image lookup -A -t 'FooPack<Foo<int> >'", DATA_TYPES_DISPLAYED_CORRECTLY, substrs=["1 match found"])
+        self.expect("image lookup -A -t 'FooPack<char, int>'", DATA_TYPES_DISPLAYED_CORRECTLY, substrs=["1 match found"])
+        self.expect("image lookup -A -t 'FooPack<char, float>'", DATA_TYPES_DISPLAYED_CORRECTLY, substrs=["1 match found"])
+        self.expect("image lookup -A -t 'FooPack<int, int>'", DATA_TYPES_DISPLAYED_CORRECTLY, substrs=["1 match found"])
+        self.expect("image lookup -A -t 'FooPack<int, int, int>'", DATA_TYPES_DISPLAYED_CORRECTLY, substrs=["1 match found"])
+        self.expect("image lookup -A -t 'FooPack<float>'", DATA_TYPES_DISPLAYED_CORRECTLY, error=True)
+        self.expect("image lookup -A -t 'FooPack<float, int>'", DATA_TYPES_DISPLAYED_CORRECTLY, error=True)
+
+        self.expect("image lookup -A -t '::Foo<int>::Nested<char>'", DATA_TYPES_DISPLAYED_CORRECTLY, substrs=["1 match found"])
+        self.expect("image lookup -A -t 'Foo<int>::Nested<char>'", DATA_TYPES_DISPLAYED_CORRECTLY, substrs=["1 match found"])
+        self.expect("image lookup -A -t 'Foo<char>::Nested<char>'", DATA_TYPES_DISPLAYED_CORRECTLY, error=True)
+        self.expect("image lookup -A -t 'Foo<int>::Nested<int>'", DATA_TYPES_DISPLAYED_CORRECTLY, error=True)
+        self.expect("image lookup -A -t 'Nested<char>'", DATA_TYPES_DISPLAYED_CORRECTLY, substrs=["1 match found"])
+        self.expect("image lookup -A -t '::Nested<char>'", DATA_TYPES_DISPLAYED_CORRECTLY, error=True)
+
+        self.expect_expr("t1", result_type="Foo<char>")
+        self.expect_expr("t1", result_type="Foo<char>")
+        self.expect_expr("t2", result_type="Foo<int>")
+        self.expect_expr("t3", result_type="Foo<Foo<int> >")
+        self.expect_expr("p1", result_type="FooPack<char>")
+        self.expect_expr("p2", result_type="FooPack<int>")
+        self.expect_expr("p3", result_type="FooPack<Foo<int> >")
+        self.expect_expr("p4", result_type="FooPack<char, int>")
+        self.expect_expr("p5", result_type="FooPack<char, float>")
+        self.expect_expr("p6", result_type="FooPack<int, int>")
+        self.expect_expr("p7", result_type="FooPack<int, int, int>")
+        self.expect_expr("n1", result_type="Foo<int>::Nested<char>")
+
+    @skipIf(compiler=no_match("clang"))
+    @skipIf(compiler_version=["<", "15.0"])
+    def test_simple_template_names(self):
+        self.do_test(dict(CFLAGS_EXTRAS="-gsimple-template-names"))
+
+    @skipIf(compiler=no_match("clang"))
+    @skipIf(compiler_version=["<", "15.0"])
+    def test_no_simple_template_names(self):
+        self.do_test(dict(CFLAGS_EXTRAS="-gno-simple-template-names"))

diff  --git a/lldb/test/API/lang/cpp/unique-types2/main.cpp b/lldb/test/API/lang/cpp/unique-types2/main.cpp
new file mode 100644
index 0000000000000..e980af1718015
--- /dev/null
+++ b/lldb/test/API/lang/cpp/unique-types2/main.cpp
@@ -0,0 +1,27 @@
+template <class T> struct Foo {
+  T t;
+  template <class U> class Nested {
+    U u;
+  };
+};
+
+template <class T, class... Ss> class FooPack {
+  T t;
+};
+
+int main() {
+  Foo<char> t1;
+  Foo<int> t2;
+  Foo<Foo<int>> t3;
+
+  FooPack<char> p1;
+  FooPack<int> p2;
+  FooPack<Foo<int>> p3;
+  FooPack<char, int> p4;
+  FooPack<char, float> p5;
+  FooPack<int, int> p6;
+  FooPack<int, int, int> p7;
+
+  Foo<int>::Nested<char> n1;
+  // Set breakpoint here
+}


        


More information about the lldb-commits mailing list