[lldb] Automatically unwrap parameter packs in template argument accessors

When looking at template arguments in LLDB, we usually care about what
the user passed in his code, not whether some of those arguments where
passed as a variadic parameter pack.

This patch extends all the C++ APIs to look at template parameters to
take an additional 'expand_pack' boolean that automatically unwraps the
potential argument packs. The equivalent SBAPI calls have been changed
to pass true for this parameter.

A byproduct of the patch is to also fix the support for template type
that have only a parameter pack as argument (like the OnlyPack type in
the test). Those were not recognized as template instanciations before.

The added test verifies that the SBAPI is able to iterate over the
arguments of a variadic template.

The original patch was written by Fred Riss almost 4 years ago.

Differential revision: https://reviews.llvm.org/D51387

(cherry picked from commit b706f56133a77f9d7c55270ac24ff59e6fce3fa4)
This commit is contained in:
Jonas Devlieghere 2022-08-16 17:53:34 -07:00 committed by Tobias Hieta
parent 1e1c5204c2
commit 2d5c43ad48
11 changed files with 188 additions and 66 deletions

View file

@ -182,6 +182,8 @@ public:
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();

View file

@ -338,14 +338,28 @@ public:
GetIndexOfChildMemberWithName(const char *name, bool omit_empty_base_classes,
std::vector<uint32_t> &child_indexes) const;
size_t GetNumTemplateArguments() 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;
lldb::TemplateArgumentKind GetTemplateArgumentKind(size_t idx) const;
CompilerType GetTypeTemplateArgument(size_t idx) 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<IntegralTemplateArgument>
GetIntegralTemplateArgument(size_t idx) const;
GetIntegralTemplateArgument(size_t idx, bool expand_pack = false) const;
CompilerType GetTypeForFormatters() const;

View file

@ -346,14 +346,18 @@ public:
const char *name, bool omit_empty_base_classes,
std::vector<uint32_t> &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<CompilerType::IntegralTemplateArgument>
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

View file

@ -542,7 +542,8 @@ uint32_t SBType::GetNumberOfTemplateArguments() {
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 @@ lldb::SBType SBType::GetTemplateArgumentType(uint32_t idx) {
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::TemplateArgumentKind SBType::GetTemplateArgumentKind(uint32_t idx) {
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;
}

View file

@ -7096,7 +7096,8 @@ TypeSystemClang::GetIndexOfChildWithName(lldb::opaque_compiler_type_t type,
}
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;
@ -7111,8 +7112,17 @@ TypeSystemClang::GetNumTemplateArguments(lldb::opaque_compiler_type_t type) {
const clang::ClassTemplateSpecializationDecl *template_decl =
llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(
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;
@ -7149,15 +7159,51 @@ TypeSystemClang::GetAsTemplateSpecialization(
}
}
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;
switch (template_decl->getTemplateArgs()[arg_idx].getKind()) {
const auto *arg = GetNthTemplateArgument(template_decl, arg_idx, expand_pack);
if (!arg)
return eTemplateArgumentKindNull;
switch (arg->getKind()) {
case clang::TemplateArgument::Null:
return eTemplateArgumentKindNull;
@ -7190,35 +7236,32 @@ TypeSystemClang::GetTemplateArgumentKind(lldb::opaque_compiler_type_t type,
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<CompilerType::IntegralTemplateArgument>
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) {

View file

@ -91,7 +91,7 @@ public:
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 @@ public:
llvm::SmallVector<const char *, 2> names;
llvm::SmallVector<clang::TemplateArgument, 2> args;
const char * pack_name = nullptr;
std::unique_ptr<TemplateParameterInfos> packed_args;
};
@ -537,7 +537,7 @@ public:
#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 @@ public:
const char *name, bool omit_empty_base_classes,
std::vector<uint32_t> &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<CompilerType::IntegralTemplateArgument>
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;

View file

@ -659,30 +659,32 @@ size_t CompilerType::GetIndexOfChildMemberWithName(
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::IntegralTemplateArgument>
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;
}

View file

@ -118,23 +118,25 @@ CompilerType TypeSystem::GetTypeForFormatters(void *type) {
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<CompilerType::IntegralTemplateArgument>
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;
}

View file

@ -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<double, 42> template parameter.
nested_template = only_pack.GetType().GetTemplateArgumentType(3)
self.assertEqual(nested_template.GetName(), 'D<int, int, bool>')
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')

View file

@ -26,7 +26,13 @@ template <> struct D<int, int, bool> : D<int, int> {
bool argsAre_Int_bool() { return true; }
};
template <typename... Args> struct OnlyPack {};
template <typename T, typename... Args> struct EmptyPack {};
int main(int argc, char const *argv[]) {
EmptyPack<int> emptyPack;
OnlyPack<int, char, double, D<int, int, bool>> onlyPack;
C<int, 16, 32> myC;
C<int, 16> myLesserC;
myC.member = 64;
@ -34,7 +40,7 @@ int main(int argc, char const *argv[]) {
(void)C<int, 16>().argsAre_16_32();
(void)(myC.member != 64);
D<int, int, bool> myD;
D<int, int> myLesserD;
D<int, int> myLesserD; // breakpoint here
myD.member = 64;
(void)D<int, int, bool>().argsAre_Int_bool();
(void)D<int, int>().argsAre_Int_bool();

View file

@ -500,18 +500,24 @@ TEST_F(TestTypeSystemClang, TemplateArguments) {
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));
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),
eTemplateArgumentKindIntegral);
EXPECT_EQ(m_ast->GetTypeTemplateArgument(t.GetOpaqueQualType(), 1),
CompilerType());
auto result = m_ast->GetIntegralTemplateArgument(t.GetOpaqueQualType(), 1);
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);