diff --git a/lldb/include/lldb/API/SBType.h b/lldb/include/lldb/API/SBType.h --- a/lldb/include/lldb/API/SBType.h +++ b/lldb/include/lldb/API/SBType.h @@ -182,6 +182,8 @@ lldb::SBType GetTemplateArgumentType(uint32_t idx); + /// Return the TemplateArgumentKind of the template argument at index idx. + /// Variadic argument packs are automatically expanded. lldb::TemplateArgumentKind GetTemplateArgumentKind(uint32_t idx); lldb::SBType GetFunctionReturnType(); diff --git a/lldb/include/lldb/Symbol/CompilerType.h b/lldb/include/lldb/Symbol/CompilerType.h --- a/lldb/include/lldb/Symbol/CompilerType.h +++ b/lldb/include/lldb/Symbol/CompilerType.h @@ -338,14 +338,28 @@ GetIndexOfChildMemberWithName(const char *name, bool omit_empty_base_classes, std::vector &child_indexes) const; - size_t GetNumTemplateArguments() const; - - lldb::TemplateArgumentKind GetTemplateArgumentKind(size_t idx) const; - CompilerType GetTypeTemplateArgument(size_t idx) const; + /// Return the number of template arguments the type has. + /// If expand_pack is true, then variadic argument packs are automatically + /// expanded to their supplied arguments. If it is false an argument pack + /// will only count as 1 argument. + size_t GetNumTemplateArguments(bool expand_pack = false) const; + + // Return the TemplateArgumentKind of the template argument at index idx. + // If expand_pack is true, then variadic argument packs are automatically + // expanded to their supplied arguments. With expand_pack set to false, an + // arguement pack will count as 1 argument and return a type of Pack. + lldb::TemplateArgumentKind + GetTemplateArgumentKind(size_t idx, bool expand_pack = false) const; + CompilerType GetTypeTemplateArgument(size_t idx, + bool expand_pack = false) const; /// Returns the value of the template argument and its type. + /// If expand_pack is true, then variadic argument packs are automatically + /// expanded to their supplied arguments. With expand_pack set to false, an + /// arguement pack will count as 1 argument and it is invalid to call this + /// method on the pack argument. llvm::Optional - GetIntegralTemplateArgument(size_t idx) const; + GetIntegralTemplateArgument(size_t idx, bool expand_pack = false) const; CompilerType GetTypeForFormatters() const; diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h --- a/lldb/include/lldb/Symbol/TypeSystem.h +++ b/lldb/include/lldb/Symbol/TypeSystem.h @@ -346,14 +346,18 @@ const char *name, bool omit_empty_base_classes, std::vector &child_indexes) = 0; - virtual size_t GetNumTemplateArguments(lldb::opaque_compiler_type_t type); + virtual size_t GetNumTemplateArguments(lldb::opaque_compiler_type_t type, + bool expand_pack); virtual lldb::TemplateArgumentKind - GetTemplateArgumentKind(lldb::opaque_compiler_type_t type, size_t idx); - virtual CompilerType GetTypeTemplateArgument(lldb::opaque_compiler_type_t type, - size_t idx); + GetTemplateArgumentKind(lldb::opaque_compiler_type_t type, size_t idx, + bool expand_pack); + virtual CompilerType + GetTypeTemplateArgument(lldb::opaque_compiler_type_t type, size_t idx, + bool expand_pack); virtual llvm::Optional - GetIntegralTemplateArgument(lldb::opaque_compiler_type_t type, size_t idx); + GetIntegralTemplateArgument(lldb::opaque_compiler_type_t type, size_t idx, + bool expand_pack); // Dumping types diff --git a/lldb/source/API/SBType.cpp b/lldb/source/API/SBType.cpp --- a/lldb/source/API/SBType.cpp +++ b/lldb/source/API/SBType.cpp @@ -542,7 +542,8 @@ LLDB_INSTRUMENT_VA(this); if (IsValid()) - return m_opaque_sp->GetCompilerType(false).GetNumTemplateArguments(); + return m_opaque_sp->GetCompilerType(false).GetNumTemplateArguments( + /*expand_pack=*/true); return 0; } @@ -553,13 +554,15 @@ return SBType(); CompilerType type; + const bool expand_pack = true; switch(GetTemplateArgumentKind(idx)) { case eTemplateArgumentKindType: - type = m_opaque_sp->GetCompilerType(false).GetTypeTemplateArgument(idx); + type = m_opaque_sp->GetCompilerType(false).GetTypeTemplateArgument( + idx, expand_pack); break; case eTemplateArgumentKindIntegral: type = m_opaque_sp->GetCompilerType(false) - .GetIntegralTemplateArgument(idx) + .GetIntegralTemplateArgument(idx, expand_pack) ->type; break; default: @@ -574,7 +577,8 @@ LLDB_INSTRUMENT_VA(this, idx); if (IsValid()) - return m_opaque_sp->GetCompilerType(false).GetTemplateArgumentKind(idx); + return m_opaque_sp->GetCompilerType(false).GetTemplateArgumentKind( + idx, /*expand_pack=*/true); return eTemplateArgumentKindNull; } diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -91,7 +91,7 @@ void SetOwningModule(OptionalClangModuleID id); /// \} }; - + /// A TypeSystem implementation based on Clang. /// /// This class uses a single clang::ASTContext as the backend for storing @@ -334,7 +334,7 @@ llvm::SmallVector names; llvm::SmallVector args; - + const char * pack_name = nullptr; std::unique_ptr packed_args; }; @@ -537,7 +537,7 @@ #ifndef NDEBUG bool Verify(lldb::opaque_compiler_type_t type) override; #endif - + bool IsArrayType(lldb::opaque_compiler_type_t type, CompilerType *element_type, uint64_t *size, bool *is_incomplete) override; @@ -810,16 +810,17 @@ const char *name, bool omit_empty_base_classes, std::vector &child_indexes) override; - size_t GetNumTemplateArguments(lldb::opaque_compiler_type_t type) override; + size_t GetNumTemplateArguments(lldb::opaque_compiler_type_t type, + bool expand_pack) override; lldb::TemplateArgumentKind - GetTemplateArgumentKind(lldb::opaque_compiler_type_t type, - size_t idx) override; + GetTemplateArgumentKind(lldb::opaque_compiler_type_t type, size_t idx, + bool expand_pack) override; CompilerType GetTypeTemplateArgument(lldb::opaque_compiler_type_t type, - size_t idx) override; + size_t idx, bool expand_pack) override; llvm::Optional - GetIntegralTemplateArgument(lldb::opaque_compiler_type_t type, - size_t idx) override; + GetIntegralTemplateArgument(lldb::opaque_compiler_type_t type, size_t idx, + bool expand_pack) override; CompilerType GetTypeForFormatters(void *type) override; diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -7098,7 +7098,8 @@ } size_t -TypeSystemClang::GetNumTemplateArguments(lldb::opaque_compiler_type_t type) { +TypeSystemClang::GetNumTemplateArguments(lldb::opaque_compiler_type_t type, + bool expand_pack) { if (!type) return 0; @@ -7113,8 +7114,17 @@ const clang::ClassTemplateSpecializationDecl *template_decl = llvm::dyn_cast( cxx_record_decl); - if (template_decl) - return template_decl->getTemplateArgs().size(); + if (template_decl) { + const auto &template_arg_list = template_decl->getTemplateArgs(); + size_t num_args = template_arg_list.size(); + assert(num_args && "template specialization without any args"); + if (expand_pack && num_args) { + const auto &pack = template_arg_list[num_args - 1]; + if (pack.getKind() == clang::TemplateArgument::Pack) + num_args += pack.pack_size() - 1; + } + return num_args; + } } } break; @@ -7151,15 +7161,51 @@ } } +const TemplateArgument * +GetNthTemplateArgument(const clang::ClassTemplateSpecializationDecl *decl, + size_t idx, bool expand_pack) { + const auto &args = decl->getTemplateArgs(); + const size_t args_size = args.size(); + + assert(args_size && "template specialization without any args"); + if (!args_size) + return nullptr; + + const size_t last_idx = args_size - 1; + + // We're asked for a template argument that can't be a parameter pack, so + // return it without worrying about 'expand_pack'. + if (idx < last_idx) + return &args[idx]; + + // We're asked for the last template argument but we don't want/need to + // expand it. + if (!expand_pack || args[last_idx].getKind() != clang::TemplateArgument::Pack) + return idx >= args.size() ? nullptr : &args[idx]; + + // Index into the expanded pack. + // Note that 'idx' counts from the beginning of all template arguments + // (including the ones preceding the parameter pack). + const auto &pack = args[last_idx]; + const size_t pack_idx = idx - last_idx; + const size_t pack_size = pack.pack_size(); + assert(pack_idx < pack_size && "parameter pack index out-of-bounds"); + return &pack.pack_elements()[pack_idx]; +} + lldb::TemplateArgumentKind TypeSystemClang::GetTemplateArgumentKind(lldb::opaque_compiler_type_t type, - size_t arg_idx) { + size_t arg_idx, bool expand_pack) { const clang::ClassTemplateSpecializationDecl *template_decl = GetAsTemplateSpecialization(type); - if (! template_decl || arg_idx >= template_decl->getTemplateArgs().size()) + if (!template_decl) + return eTemplateArgumentKindNull; + + const auto *arg = GetNthTemplateArgument(template_decl, arg_idx, expand_pack); + if (!arg) return eTemplateArgumentKindNull; - switch (template_decl->getTemplateArgs()[arg_idx].getKind()) { + switch (arg->getKind()) { case clang::TemplateArgument::Null: return eTemplateArgumentKindNull; @@ -7192,35 +7238,32 @@ CompilerType TypeSystemClang::GetTypeTemplateArgument(lldb::opaque_compiler_type_t type, - size_t idx) { + size_t idx, bool expand_pack) { const clang::ClassTemplateSpecializationDecl *template_decl = GetAsTemplateSpecialization(type); - if (!template_decl || idx >= template_decl->getTemplateArgs().size()) + if (!template_decl) return CompilerType(); - const clang::TemplateArgument &template_arg = - template_decl->getTemplateArgs()[idx]; - if (template_arg.getKind() != clang::TemplateArgument::Type) + const auto *arg = GetNthTemplateArgument(template_decl, idx, expand_pack); + if (!arg || arg->getKind() != clang::TemplateArgument::Type) return CompilerType(); - return GetType(template_arg.getAsType()); + return GetType(arg->getAsType()); } Optional TypeSystemClang::GetIntegralTemplateArgument(lldb::opaque_compiler_type_t type, - size_t idx) { + size_t idx, bool expand_pack) { const clang::ClassTemplateSpecializationDecl *template_decl = GetAsTemplateSpecialization(type); - if (! template_decl || idx >= template_decl->getTemplateArgs().size()) + if (!template_decl) return llvm::None; - const clang::TemplateArgument &template_arg = - template_decl->getTemplateArgs()[idx]; - if (template_arg.getKind() != clang::TemplateArgument::Integral) + const auto *arg = GetNthTemplateArgument(template_decl, idx, expand_pack); + if (!arg || arg->getKind() != clang::TemplateArgument::Integral) return llvm::None; - return { - {template_arg.getAsIntegral(), GetType(template_arg.getIntegralType())}}; + return {{arg->getAsIntegral(), GetType(arg->getIntegralType())}}; } CompilerType TypeSystemClang::GetTypeForFormatters(void *type) { diff --git a/lldb/source/Symbol/CompilerType.cpp b/lldb/source/Symbol/CompilerType.cpp --- a/lldb/source/Symbol/CompilerType.cpp +++ b/lldb/source/Symbol/CompilerType.cpp @@ -659,30 +659,32 @@ return 0; } -size_t CompilerType::GetNumTemplateArguments() const { +size_t CompilerType::GetNumTemplateArguments(bool expand_pack) const { if (IsValid()) { - return m_type_system->GetNumTemplateArguments(m_type); + return m_type_system->GetNumTemplateArguments(m_type, expand_pack); } return 0; } -TemplateArgumentKind CompilerType::GetTemplateArgumentKind(size_t idx) const { +TemplateArgumentKind +CompilerType::GetTemplateArgumentKind(size_t idx, bool expand_pack) const { if (IsValid()) - return m_type_system->GetTemplateArgumentKind(m_type, idx); + return m_type_system->GetTemplateArgumentKind(m_type, idx, expand_pack); return eTemplateArgumentKindNull; } -CompilerType CompilerType::GetTypeTemplateArgument(size_t idx) const { +CompilerType CompilerType::GetTypeTemplateArgument(size_t idx, + bool expand_pack) const { if (IsValid()) { - return m_type_system->GetTypeTemplateArgument(m_type, idx); + return m_type_system->GetTypeTemplateArgument(m_type, idx, expand_pack); } return CompilerType(); } llvm::Optional -CompilerType::GetIntegralTemplateArgument(size_t idx) const { +CompilerType::GetIntegralTemplateArgument(size_t idx, bool expand_pack) const { if (IsValid()) - return m_type_system->GetIntegralTemplateArgument(m_type, idx); + return m_type_system->GetIntegralTemplateArgument(m_type, idx, expand_pack); return llvm::None; } diff --git a/lldb/source/Symbol/TypeSystem.cpp b/lldb/source/Symbol/TypeSystem.cpp --- a/lldb/source/Symbol/TypeSystem.cpp +++ b/lldb/source/Symbol/TypeSystem.cpp @@ -118,23 +118,25 @@ return CompilerType(this, type); } -size_t TypeSystem::GetNumTemplateArguments(lldb::opaque_compiler_type_t type) { +size_t TypeSystem::GetNumTemplateArguments(lldb::opaque_compiler_type_t type, + bool expand_pack) { return 0; } TemplateArgumentKind -TypeSystem::GetTemplateArgumentKind(opaque_compiler_type_t type, size_t idx) { +TypeSystem::GetTemplateArgumentKind(opaque_compiler_type_t type, size_t idx, + bool expand_pack) { return eTemplateArgumentKindNull; } CompilerType TypeSystem::GetTypeTemplateArgument(opaque_compiler_type_t type, - size_t idx) { + size_t idx, bool expand_pack) { return CompilerType(); } llvm::Optional -TypeSystem::GetIntegralTemplateArgument(opaque_compiler_type_t type, - size_t idx) { +TypeSystem::GetIntegralTemplateArgument(opaque_compiler_type_t type, size_t idx, + bool expand_pack) { return llvm::None; } diff --git a/lldb/test/API/lang/cpp/class-template-parameter-pack/TestTemplatePackArgs.py b/lldb/test/API/lang/cpp/class-template-parameter-pack/TestTemplatePackArgs.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/lang/cpp/class-template-parameter-pack/TestTemplatePackArgs.py @@ -0,0 +1,38 @@ +""" +Test that the type of arguments to C++ template classes that have variadic +parameters can be enumerated. +""" +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TemplatePackArgsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test_template_argument_pack(self): + self.build() + (_, _, thread, _) = lldbutil.run_to_source_breakpoint(self, + 'breakpoint here', lldb.SBFileSpec('main.cpp'), exe_name = 'a.out') + frame = thread.GetSelectedFrame() + + empty_pack = frame.FindVariable('emptyPack') + self.assertTrue(empty_pack.IsValid(), + 'make sure we find the emptyPack variable') + + only_pack = frame.FindVariable('onlyPack') + self.assertTrue(only_pack.IsValid(), + 'make sure we find the onlyPack variable') + self.assertEqual(only_pack.GetType().GetNumberOfTemplateArguments(), 4) + self.assertEqual(only_pack.GetType().GetTemplateArgumentType(0).GetName(), 'int') + self.assertEqual(only_pack.GetType().GetTemplateArgumentType(1).GetName(), 'char') + self.assertEqual(only_pack.GetType().GetTemplateArgumentType(2).GetName(), 'double') + # Access the C template parameter. + nested_template = only_pack.GetType().GetTemplateArgumentType(3) + self.assertEqual(nested_template.GetName(), 'D') + self.assertEqual(nested_template.GetNumberOfTemplateArguments(), 3) + self.assertEqual(nested_template.GetTemplateArgumentType(0).GetName(), 'int') + self.assertEqual(nested_template.GetTemplateArgumentType(1).GetName(), 'int') + self.assertEqual(nested_template.GetTemplateArgumentType(2).GetName(), 'bool') diff --git a/lldb/test/API/lang/cpp/class-template-parameter-pack/main.cpp b/lldb/test/API/lang/cpp/class-template-parameter-pack/main.cpp --- a/lldb/test/API/lang/cpp/class-template-parameter-pack/main.cpp +++ b/lldb/test/API/lang/cpp/class-template-parameter-pack/main.cpp @@ -26,7 +26,13 @@ bool argsAre_Int_bool() { return true; } }; +template struct OnlyPack {}; +template struct EmptyPack {}; + int main(int argc, char const *argv[]) { + EmptyPack emptyPack; + OnlyPack> onlyPack; + C myC; C myLesserC; myC.member = 64; @@ -34,7 +40,7 @@ (void)C().argsAre_16_32(); (void)(myC.member != 64); D myD; - D myLesserD; + D myLesserD; // breakpoint here myD.member = 64; (void)D().argsAre_Int_bool(); (void)D().argsAre_Int_bool(); diff --git a/lldb/unittests/Symbol/TestTypeSystemClang.cpp b/lldb/unittests/Symbol/TestTypeSystemClang.cpp --- a/lldb/unittests/Symbol/TestTypeSystemClang.cpp +++ b/lldb/unittests/Symbol/TestTypeSystemClang.cpp @@ -500,18 +500,24 @@ for (CompilerType t : {type, typedef_type, auto_type}) { SCOPED_TRACE(t.GetTypeName().AsCString()); - EXPECT_EQ(m_ast->GetTemplateArgumentKind(t.GetOpaqueQualType(), 0), - eTemplateArgumentKindType); - EXPECT_EQ(m_ast->GetTypeTemplateArgument(t.GetOpaqueQualType(), 0), - int_type); - EXPECT_EQ(llvm::None, - m_ast->GetIntegralTemplateArgument(t.GetOpaqueQualType(), 0)); - - EXPECT_EQ(m_ast->GetTemplateArgumentKind(t.GetOpaqueQualType(), 1), - eTemplateArgumentKindIntegral); - EXPECT_EQ(m_ast->GetTypeTemplateArgument(t.GetOpaqueQualType(), 1), - CompilerType()); - auto result = m_ast->GetIntegralTemplateArgument(t.GetOpaqueQualType(), 1); + const bool expand_pack = false; + EXPECT_EQ( + m_ast->GetTemplateArgumentKind(t.GetOpaqueQualType(), 0, expand_pack), + eTemplateArgumentKindType); + EXPECT_EQ( + m_ast->GetTypeTemplateArgument(t.GetOpaqueQualType(), 0, expand_pack), + int_type); + EXPECT_EQ(llvm::None, m_ast->GetIntegralTemplateArgument( + t.GetOpaqueQualType(), 0, expand_pack)); + + EXPECT_EQ( + m_ast->GetTemplateArgumentKind(t.GetOpaqueQualType(), 1, expand_pack), + eTemplateArgumentKindIntegral); + EXPECT_EQ( + m_ast->GetTypeTemplateArgument(t.GetOpaqueQualType(), 1, expand_pack), + CompilerType()); + auto result = m_ast->GetIntegralTemplateArgument(t.GetOpaqueQualType(), 1, + expand_pack); ASSERT_NE(llvm::None, result); EXPECT_EQ(arg, result->value); EXPECT_EQ(int_type, result->type);