Index: lldb/include/lldb/Symbol/CompilerType.h =================================================================== --- lldb/include/lldb/Symbol/CompilerType.h +++ 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; Index: lldb/include/lldb/Symbol/TypeSystem.h =================================================================== --- lldb/include/lldb/Symbol/TypeSystem.h +++ 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 Index: lldb/source/API/SBType.cpp =================================================================== --- lldb/source/API/SBType.cpp +++ 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; } Index: lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h =================================================================== --- lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ 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 @@ -324,17 +324,17 @@ bool IsValid() const { // Having a pack name but no packed args doesn't make sense, so mark // these template parameters as invalid. - if (pack_name && !packed_args) + if (args.empty() && !packed_args) return false; return args.size() == names.size() && - (!packed_args || !packed_args->packed_args); + ((bool)pack_name == (bool)packed_args); } bool hasParameterPack() const { return static_cast(packed_args); } 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; Index: lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp =================================================================== --- lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ 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) { + 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,39 @@ } } +const TemplateArgument * +GetNthTemplateArgument(const clang::ClassTemplateSpecializationDecl *decl, + size_t idx, bool expand_pack) { + const auto &args = decl->getTemplateArgs(); + + assert(args.size() && "template specialization without any args"); + if (!args.size()) + return nullptr; + + if (idx < args.size() - 1) + return &args[idx]; + + if (!expand_pack || + args[args.size() - 1].getKind() != clang::TemplateArgument::Pack) + return idx >= args.size() ? nullptr : &args[idx]; + + const auto &pack = args[args.size() - 1]; + return &pack.pack_elements()[idx - (args.size() - 1)]; +} + 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 +7226,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) { Index: lldb/source/Symbol/CompilerType.cpp =================================================================== --- lldb/source/Symbol/CompilerType.cpp +++ 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; } Index: lldb/source/Symbol/TypeSystem.cpp =================================================================== --- lldb/source/Symbol/TypeSystem.cpp +++ 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; } Index: lldb/test/API/lang/cpp/class-template-parameter-pack/TestTemplatePackArgs.py =================================================================== --- /dev/null +++ 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') Index: lldb/test/API/lang/cpp/class-template-parameter-pack/main.cpp =================================================================== --- lldb/test/API/lang/cpp/class-template-parameter-pack/main.cpp +++ 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();