31 changes: 31 additions & 0 deletions clang/unittests/AST/DeclTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -490,3 +490,34 @@ TEST(Decl, ImplicitlyDeclaredAllocationFunctionsInModules) {
ASSERT_TRUE(AlignedArrayDelete->getOwningModule());
EXPECT_TRUE(AlignedArrayDelete->getOwningModule()->isGlobalModule());
}

TEST(Decl, TemplateArgumentDefaulted) {
llvm::Annotations Code(R"cpp(
template<typename T1, typename T2>
struct Alloc {};
template <typename T1,
typename T2 = double,
int T3 = 42,
typename T4 = Alloc<T1, T2>>
struct Foo {
};
Foo<char, int, 42, Alloc<char, int>> X;
)cpp");

auto AST =
tooling::buildASTFromCodeWithArgs(Code.code(), /*Args=*/{"-std=c++20"});
ASTContext &Ctx = AST->getASTContext();

auto const *CTSD = selectFirst<ClassTemplateSpecializationDecl>(
"id",
match(classTemplateSpecializationDecl(hasName("Foo")).bind("id"), Ctx));
ASSERT_NE(CTSD, nullptr);
auto const &ArgList = CTSD->getTemplateArgs();

EXPECT_FALSE(ArgList.get(0).getIsDefaulted());
EXPECT_FALSE(ArgList.get(1).getIsDefaulted());
EXPECT_TRUE(ArgList.get(2).getIsDefaulted());
EXPECT_TRUE(ArgList.get(3).getIsDefaulted());
}
128 changes: 128 additions & 0 deletions clang/unittests/AST/TypePrinterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,131 @@ TEST(TypePrinter, TemplateIdWithNTTP) {
Policy.EntireContentsOfLargeArray = true;
}));
}

TEST(TypePrinter, TemplateArgumentsSubstitution_Expressions) {
/// Tests clang::isSubstitutedDefaultArgument on TemplateArguments
/// that are of kind TemplateArgument::Expression
constexpr char Code[] = R"cpp(
constexpr bool func() { return true; }
template <typename T1 = int,
int T2 = 42,
T1 T3 = 43,
int T4 = sizeof(T1),
bool T5 = func()
>
struct Foo {
};
Foo<int, 40 + 2> X;
)cpp";

auto AST = tooling::buildASTFromCodeWithArgs(Code, /*Args=*/{"-std=c++20"});
ASTContext &Ctx = AST->getASTContext();

auto const *CTD = selectFirst<ClassTemplateDecl>(
"id", match(classTemplateDecl(hasName("Foo")).bind("id"), Ctx));
ASSERT_NE(CTD, nullptr);
auto const *CTSD = *CTD->specializations().begin();
ASSERT_NE(CTSD, nullptr);
auto const *Params = CTD->getTemplateParameters();
ASSERT_NE(Params, nullptr);
auto const &ArgList = CTSD->getTemplateArgs();

auto createBinOpExpr = [&](uint32_t LHS, uint32_t RHS,
uint32_t Result) -> ConstantExpr * {
const int numBits = 32;
clang::APValue ResultVal{llvm::APSInt(llvm::APInt(numBits, Result))};
auto *LHSInt = IntegerLiteral::Create(Ctx, llvm::APInt(numBits, LHS),
Ctx.UnsignedIntTy, {});
auto *RHSInt = IntegerLiteral::Create(Ctx, llvm::APInt(numBits, RHS),
Ctx.UnsignedIntTy, {});
auto *BinOp = BinaryOperator::Create(
Ctx, LHSInt, RHSInt, BinaryOperatorKind::BO_Add, Ctx.UnsignedIntTy,
ExprValueKind::VK_PRValue, ExprObjectKind::OK_Ordinary, {}, {});
return ConstantExpr::Create(Ctx, dyn_cast<Expr>(BinOp), ResultVal);
};

{
// Arg is an integral '42'
auto const &Arg = ArgList.get(1);
ASSERT_EQ(Arg.getKind(), TemplateArgument::Integral);

// Param has default expr which evaluates to '42'
auto const *Param = Params->getParam(1);

EXPECT_TRUE(clang::isSubstitutedDefaultArgument(
Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));
}

{
// Arg is an integral '41'
llvm::APInt Int(32, 41);
TemplateArgument Arg(Ctx, llvm::APSInt(Int), Ctx.UnsignedIntTy);

// Param has default expr which evaluates to '42'
auto const *Param = Params->getParam(1);

EXPECT_FALSE(clang::isSubstitutedDefaultArgument(
Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));
}

{
// Arg is an integral '4'
llvm::APInt Int(32, 4);
TemplateArgument Arg(Ctx, llvm::APSInt(Int), Ctx.UnsignedIntTy);

// Param has is value-dependent expression (i.e., sizeof(T))
auto const *Param = Params->getParam(3);

EXPECT_FALSE(clang::isSubstitutedDefaultArgument(
Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));
}

{
const int LHS = 40;
const int RHS = 2;
const int Result = 42;
auto *ConstExpr = createBinOpExpr(LHS, RHS, Result);
// Arg is instantiated with '40 + 2'
TemplateArgument Arg(ConstExpr);

// Param has default expr of '42'
auto const *Param = Params->getParam(1);

EXPECT_TRUE(clang::isSubstitutedDefaultArgument(
Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));
}

{
const int LHS = 40;
const int RHS = 1;
const int Result = 41;
auto *ConstExpr = createBinOpExpr(LHS, RHS, Result);

// Arg is instantiated with '40 + 1'
TemplateArgument Arg(ConstExpr);

// Param has default expr of '42'
auto const *Param = Params->getParam(1);

EXPECT_FALSE(clang::isSubstitutedDefaultArgument(
Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));
}

{
const int LHS = 4;
const int RHS = 0;
const int Result = 4;
auto *ConstExpr = createBinOpExpr(LHS, RHS, Result);

// Arg is instantiated with '4 + 0'
TemplateArgument Arg(ConstExpr);

// Param has is value-dependent expression (i.e., sizeof(T))
auto const *Param = Params->getParam(3);

EXPECT_FALSE(clang::isSubstitutedDefaultArgument(
Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));
}
}
46 changes: 26 additions & 20 deletions lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1980,14 +1980,14 @@ bool DWARFASTParserClang::ParseTemplateDIE(

switch (tag) {
case DW_TAG_GNU_template_parameter_pack: {
template_param_infos.packed_args =
std::make_unique<TypeSystemClang::TemplateParameterInfos>();
template_param_infos.SetParameterPack(
std::make_unique<TypeSystemClang::TemplateParameterInfos>());
for (DWARFDIE child_die : die.children()) {
if (!ParseTemplateDIE(child_die, *template_param_infos.packed_args))
if (!ParseTemplateDIE(child_die, template_param_infos.GetParameterPack()))
return false;
}
if (const char *name = die.GetName()) {
template_param_infos.pack_name = name;
template_param_infos.SetPackName(name);
}
return true;
}
Expand All @@ -2003,6 +2003,7 @@ bool DWARFASTParserClang::ParseTemplateDIE(
CompilerType clang_type;
uint64_t uval64 = 0;
bool uval64_valid = false;
bool is_default_template_arg = false;
if (num_attributes > 0) {
DWARFFormValue form_value;
for (size_t i = 0; i < num_attributes; ++i) {
Expand Down Expand Up @@ -2033,6 +2034,10 @@ bool DWARFASTParserClang::ParseTemplateDIE(
uval64 = form_value.Unsigned();
}
break;
case DW_AT_default_value:
if (attributes.ExtractFormValueAtIndex(i, form_value))
is_default_template_arg = form_value.Boolean();
break;
default:
break;
}
Expand All @@ -2044,31 +2049,33 @@ bool DWARFASTParserClang::ParseTemplateDIE(

if (!is_template_template_argument) {
bool is_signed = false;
if (name && name[0])
template_param_infos.names.push_back(name);
else
template_param_infos.names.push_back(nullptr);

// Get the signed value for any integer or enumeration if available
clang_type.IsIntegerOrEnumerationType(is_signed);

if (name && !name[0])
name = nullptr;

if (tag == DW_TAG_template_value_parameter && uval64_valid) {
std::optional<uint64_t> size = clang_type.GetBitSize(nullptr);
if (!size)
return false;
llvm::APInt apint(*size, uval64, is_signed);
template_param_infos.args.push_back(
template_param_infos.InsertArg(
name,
clang::TemplateArgument(ast, llvm::APSInt(apint, !is_signed),
ClangUtil::GetQualType(clang_type)));
ClangUtil::GetQualType(clang_type),
is_default_template_arg));
} else {
template_param_infos.args.push_back(
clang::TemplateArgument(ClangUtil::GetQualType(clang_type)));
template_param_infos.InsertArg(
name, clang::TemplateArgument(ClangUtil::GetQualType(clang_type),
/*isNullPtr*/ false,
is_default_template_arg));
}
} else {
auto *tplt_type = m_ast.CreateTemplateTemplateParmDecl(template_name);
template_param_infos.names.push_back(name);
template_param_infos.args.push_back(
clang::TemplateArgument(clang::TemplateName(tplt_type)));
template_param_infos.InsertArg(
name, clang::TemplateArgument(clang::TemplateName(tplt_type),
is_default_template_arg));
}
}
}
Expand Down Expand Up @@ -2102,10 +2109,9 @@ bool DWARFASTParserClang::ParseTemplateParameterInfos(
break;
}
}
return template_param_infos.args.size() ==
template_param_infos.names.size() &&
(!template_param_infos.args.empty() ||
template_param_infos.packed_args);

return !template_param_infos.IsEmpty() ||
template_param_infos.hasParameterPack();
}

bool DWARFASTParserClang::CompleteRecordType(const DWARFDIE &die,
Expand Down
51 changes: 28 additions & 23 deletions lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1379,18 +1379,21 @@ static TemplateParameterList *CreateTemplateParameterList(
const bool parameter_pack = false;
const bool is_typename = false;
const unsigned depth = 0;
const size_t num_template_params = template_param_infos.args.size();
const size_t num_template_params = template_param_infos.Size();
DeclContext *const decl_context =
ast.getTranslationUnitDecl(); // Is this the right decl context?,

auto const &args = template_param_infos.GetArgs();
auto const &names = template_param_infos.GetNames();
for (size_t i = 0; i < num_template_params; ++i) {
const char *name = template_param_infos.names[i];
const char *name = names[i];

IdentifierInfo *identifier_info = nullptr;
if (name && name[0])
identifier_info = &ast.Idents.get(name);
if (IsValueParam(template_param_infos.args[i])) {
QualType template_param_type =
template_param_infos.args[i].getIntegralType();
TemplateArgument const &targ = args[i];
if (IsValueParam(targ)) {
QualType template_param_type = targ.getIntegralType();
template_param_decls.push_back(NonTypeTemplateParmDecl::Create(
ast, decl_context, SourceLocation(), SourceLocation(), depth, i,
identifier_info, template_param_type, parameter_pack,
Expand All @@ -1402,16 +1405,16 @@ static TemplateParameterList *CreateTemplateParameterList(
}
}

if (template_param_infos.packed_args) {
if (template_param_infos.hasParameterPack()) {
IdentifierInfo *identifier_info = nullptr;
if (template_param_infos.pack_name && template_param_infos.pack_name[0])
identifier_info = &ast.Idents.get(template_param_infos.pack_name);
if (template_param_infos.HasPackName())
identifier_info = &ast.Idents.get(template_param_infos.GetPackName());
const bool parameter_pack_true = true;

if (!template_param_infos.packed_args->args.empty() &&
IsValueParam(template_param_infos.packed_args->args[0])) {
if (!template_param_infos.GetParameterPack().IsEmpty() &&
IsValueParam(template_param_infos.GetParameterPack().Front())) {
QualType template_param_type =
template_param_infos.packed_args->args[0].getIntegralType();
template_param_infos.GetParameterPack().Front().getIntegralType();
template_param_decls.push_back(NonTypeTemplateParmDecl::Create(
ast, decl_context, SourceLocation(), SourceLocation(), depth,
num_template_params, identifier_info, template_param_type,
Expand Down Expand Up @@ -1485,8 +1488,8 @@ clang::FunctionTemplateDecl *TypeSystemClang::CreateFunctionTemplateDecl(
void TypeSystemClang::CreateFunctionTemplateSpecializationInfo(
FunctionDecl *func_decl, clang::FunctionTemplateDecl *func_tmpl_decl,
const TemplateParameterInfos &infos) {
TemplateArgumentList *template_args_ptr =
TemplateArgumentList::CreateCopy(func_decl->getASTContext(), infos.args);
TemplateArgumentList *template_args_ptr = TemplateArgumentList::CreateCopy(
func_decl->getASTContext(), infos.GetArgs());

func_decl->setFunctionTemplateSpecialization(func_tmpl_decl,
template_args_ptr, nullptr);
Expand Down Expand Up @@ -1557,7 +1560,7 @@ static bool ClassTemplateAllowsToInstantiationArgs(
// The found template needs to have compatible non-pack template arguments.
// E.g., ensure that <typename, typename> != <typename>.
// The pack parameters are compared later.
if (non_pack_params != instantiation_values.args.size())
if (non_pack_params != instantiation_values.Size())
return false;

// Ensure that <typename...> != <typename>.
Expand All @@ -1568,14 +1571,15 @@ static bool ClassTemplateAllowsToInstantiationArgs(
// parameter value. The special case of having an empty parameter pack value
// always fits to a pack parameter.
// E.g., ensure that <int...> != <typename...>.
if (pack_parameter && !instantiation_values.packed_args->args.empty() &&
if (pack_parameter && !instantiation_values.GetParameterPack().IsEmpty() &&
!TemplateParameterAllowsValue(
*pack_parameter, instantiation_values.packed_args->args.front()))
*pack_parameter, instantiation_values.GetParameterPack().Front()))
return false;

// Compare all the non-pack parameters now.
// E.g., ensure that <int> != <long>.
for (const auto pair : llvm::zip_first(instantiation_values.args, params)) {
for (const auto pair :
llvm::zip_first(instantiation_values.GetArgs(), params)) {
const TemplateArgument &passed_arg = std::get<0>(pair);
NamedDecl *found_param = std::get<1>(pair);
if (!TemplateParameterAllowsValue(found_param, passed_arg))
Expand Down Expand Up @@ -1688,13 +1692,14 @@ TypeSystemClang::CreateClassTemplateSpecializationDecl(
const TemplateParameterInfos &template_param_infos) {
ASTContext &ast = getASTContext();
llvm::SmallVector<clang::TemplateArgument, 2> args(
template_param_infos.args.size() +
(template_param_infos.packed_args ? 1 : 0));
std::copy(template_param_infos.args.begin(), template_param_infos.args.end(),
args.begin());
if (template_param_infos.packed_args) {
template_param_infos.Size() +
(template_param_infos.hasParameterPack() ? 1 : 0));

auto const &orig_args = template_param_infos.GetArgs();
std::copy(orig_args.begin(), orig_args.end(), args.begin());
if (template_param_infos.hasParameterPack()) {
args[args.size() - 1] = TemplateArgument::CreatePackCopy(
ast, template_param_infos.packed_args->args);
ast, template_param_infos.GetParameterPackArgs());
}
ClassTemplateSpecializationDecl *class_template_specialization_decl =
ClassTemplateSpecializationDecl::CreateDeserialized(ast, 0);
Expand Down
66 changes: 65 additions & 1 deletion lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
Original file line number Diff line number Diff line change
Expand Up @@ -331,17 +331,81 @@ class TypeSystemClang : public TypeSystem {

class TemplateParameterInfos {
public:
TemplateParameterInfos() = default;
TemplateParameterInfos(llvm::ArrayRef<const char *> names_in,
llvm::ArrayRef<clang::TemplateArgument> args_in)
: names(names_in), args(args_in) {
assert(names.size() == args_in.size());
}

TemplateParameterInfos(TemplateParameterInfos const &) = delete;
TemplateParameterInfos(TemplateParameterInfos &&) = delete;

TemplateParameterInfos &operator=(TemplateParameterInfos const &) = delete;
TemplateParameterInfos &operator=(TemplateParameterInfos &&) = delete;

~TemplateParameterInfos() = default;

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)
return false;
return args.size() == names.size() &&
(!packed_args || !packed_args->packed_args);
(!packed_args || !packed_args->packed_args);
}

bool IsEmpty() const { return args.empty(); }
size_t Size() const { return args.size(); }

llvm::ArrayRef<clang::TemplateArgument> GetArgs() const { return args; }
llvm::ArrayRef<const char *> GetNames() const { return names; }

clang::TemplateArgument const &Front() const {
assert(!args.empty());
return args.front();
}

void InsertArg(char const *name, clang::TemplateArgument arg) {
args.emplace_back(std::move(arg));
names.push_back(name);
}

// Parameter pack related

bool hasParameterPack() const { return static_cast<bool>(packed_args); }

TemplateParameterInfos const &GetParameterPack() const {
assert(packed_args != nullptr);
return *packed_args;
}

TemplateParameterInfos &GetParameterPack() {
assert(packed_args != nullptr);
return *packed_args;
}

llvm::ArrayRef<clang::TemplateArgument> GetParameterPackArgs() const {
assert(packed_args != nullptr);
return packed_args->GetArgs();
}

bool HasPackName() const { return pack_name && pack_name[0]; }

llvm::StringRef GetPackName() const {
assert(HasPackName());
return pack_name;
}

void SetPackName(char const *name) { pack_name = name; }

void SetParameterPack(std::unique_ptr<TemplateParameterInfos> args) {
packed_args = std::move(args);
}

private:
/// Element 'names[i]' holds the template argument name
/// of 'args[i]'
llvm::SmallVector<const char *, 2> names;
llvm::SmallVector<clang::TemplateArgument, 2> args;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def test_shared_ptr_variables(self):

valobj = self.expect_var_path(
"sp_str",
type="std::shared_ptr<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >",
type="std::shared_ptr<std::basic_string<char> >",
children=[ValueCheck(name="__ptr_", summary='"hello"')],
)
self.assertRegex(valobj.summary, r'^"hello"( strong=1)? weak=1$')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ def cleanup():
'(%s::u32string) u32_string = U"🍄🍅🍆🍌"'%ns,
# FIXME: This should have a 'U' prefix.
'(%s::u32string) u32_empty = ""'%ns,
'(%s::basic_string<unsigned char, %s::char_traits<unsigned char>, '
'%s::allocator<unsigned char> >) uchar = "aaaaa"'%(ns,ns,ns),
'(%s::basic_string<unsigned char>) uchar = "aaaaa"'%(ns),
'(%s::string *) null_str = nullptr'%ns,
])

Expand Down Expand Up @@ -108,8 +107,7 @@ def cleanup():
'(%s::u16string) u16_string = u"ß水氶"'%ns,
'(%s::u32string) u32_string = U"🍄🍅🍆🍌"'%ns,
'(%s::u32string) u32_empty = ""'%ns,
'(%s::basic_string<unsigned char, %s::char_traits<unsigned char>, '
'%s::allocator<unsigned char> >) uchar = "aaaaa"'%(ns,ns,ns),
'(%s::basic_string<unsigned char>) uchar = "aaaaa"'%(ns),
'(%s::string *) null_str = nullptr'%ns,
])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ def cleanup():
type='std::u32string_view',
summary='""')
self.expect_var_path('uchar_source',
type='std::basic_string<unsigned char, std::char_traits<unsigned char>, std::allocator<unsigned char> >',
type='std::basic_string<unsigned char>',
summary='"aaaaaaaaaa"')
self.expect_var_path('uchar',
type='std::basic_string_view<unsigned char, std::char_traits<unsigned char> >',
type='std::basic_string_view<unsigned char>',
summary='"aaaaa"')
self.expect_var_path('oops',
type='std::string_view',
Expand Down Expand Up @@ -172,10 +172,10 @@ def cleanup():
type='std::u32string_view',
summary='""')
self.expect_var_path('uchar_source',
type='std::basic_string<unsigned char, std::char_traits<unsigned char>, std::allocator<unsigned char> >',
type='std::basic_string<unsigned char>',
summary='"aaaaaaaaaa"')
self.expect_var_path('uchar',
type='std::basic_string_view<unsigned char, std::char_traits<unsigned char> >',
type='std::basic_string_view<unsigned char>',
summary='"aaaaa"')

self.runCmd('cont')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def test_unique_ptr_variables(self):

valobj = self.expect_var_path(
"up_empty",
type="std::unique_ptr<int, std::default_delete<int> >",
type="std::unique_ptr<int>",
summary="nullptr",
children=[ValueCheck(name="__value_")],
)
Expand All @@ -36,37 +36,37 @@ def test_unique_ptr_variables(self):

valobj = self.expect_var_path(
"up_int",
type="std::unique_ptr<int, std::default_delete<int> >",
type="std::unique_ptr<int>",
summary="10",
children=[ValueCheck(name="__value_")],
)
self.assertNotEqual(valobj.child[0].unsigned, 0)

valobj = self.expect_var_path(
"up_int_ref",
type="std::unique_ptr<int, std::default_delete<int> > &",
type="std::unique_ptr<int> &",
summary="10",
children=[ValueCheck(name="__value_")],
)
self.assertNotEqual(valobj.child[0].unsigned, 0)

valobj = self.expect_var_path(
"up_int_ref_ref",
type="std::unique_ptr<int, std::default_delete<int> > &&",
type="std::unique_ptr<int> &&",
summary="10",
children=[ValueCheck(name="__value_")],
)
self.assertNotEqual(valobj.child[0].unsigned, 0)

valobj = self.expect_var_path(
"up_str",
type="std::unique_ptr<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::default_delete<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >",
type="std::unique_ptr<std::basic_string<char> >",
summary='"hello"',
children=[ValueCheck(name="__value_", summary='"hello"')],
)

valobj = self.expect_var_path(
"up_user", type="std::unique_ptr<User, std::default_delete<User> >"
"up_user", type="std::unique_ptr<User>"
)
self.assertRegex(valobj.summary, "^User @ 0x0*[1-9a-f][0-9a-f]+$")
self.assertNotEqual(valobj.child[0].unsigned, 0)
Expand Down
140 changes: 78 additions & 62 deletions lldb/unittests/Symbol/TestTypeSystemClang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -470,11 +470,10 @@ TEST_F(TestTypeSystemClang, TestRecordHasFields) {

TEST_F(TestTypeSystemClang, TemplateArguments) {
TypeSystemClang::TemplateParameterInfos infos;
infos.names.push_back("T");
infos.args.push_back(TemplateArgument(m_ast->getASTContext().IntTy));
infos.names.push_back("I");
infos.InsertArg("T", TemplateArgument(m_ast->getASTContext().IntTy));

llvm::APSInt arg(llvm::APInt(8, 47));
infos.args.push_back(TemplateArgument(m_ast->getASTContext(), arg,
infos.InsertArg("I", TemplateArgument(m_ast->getASTContext(), arg,
m_ast->getASTContext().IntTy));

// template<typename T, int I> struct foo;
Expand Down Expand Up @@ -598,126 +597,143 @@ TEST_F(TestCreateClassTemplateDecl, FindExistingTemplates) {
// The behaviour should follow the C++ rules for redeclaring templates
// (e.g., parameter names can be changed/omitted.)

// This describes a class template *instantiation* from which we will infer
// the structure of the class template.
TypeSystemClang::TemplateParameterInfos infos;

// Test an empty template parameter list: <>
ExpectNewTemplate("<>", infos);
ExpectNewTemplate("<>", {{}, {}});

clang::TemplateArgument intArg1(m_ast->getASTContext().IntTy);
clang::TemplateArgument intArg2(m_ast->getASTContext(),
llvm::APSInt(llvm::APInt(32, 47)),
m_ast->getASTContext().IntTy);
clang::TemplateArgument floatArg(m_ast->getASTContext().FloatTy);
clang::TemplateArgument charArg1(m_ast->getASTContext(),
llvm::APSInt(llvm::APInt(8, 47)),
m_ast->getASTContext().SignedCharTy);

clang::TemplateArgument charArg2(m_ast->getASTContext(),
llvm::APSInt(llvm::APInt(8, 123)),
m_ast->getASTContext().SignedCharTy);

// Test that <typename T> with T = int creates a new template.
infos.names = {"T"};
infos.args = {TemplateArgument(m_ast->getASTContext().IntTy)};
ClassTemplateDecl *single_type_arg = ExpectNewTemplate("<typename T>", infos);
ClassTemplateDecl *single_type_arg =
ExpectNewTemplate("<typename T>", {{"T"}, {intArg1}});

// Test that changing the parameter name doesn't create a new class template.
infos.names = {"A"};
ExpectReusedTemplate("<typename A> (A = int)", infos, single_type_arg);
ExpectReusedTemplate("<typename A> (A = int)", {{"A"}, {intArg1}},
single_type_arg);

// Test that changing the used type doesn't create a new class template.
infos.args = {TemplateArgument(m_ast->getASTContext().FloatTy)};
ExpectReusedTemplate("<typename A> (A = float)", infos, single_type_arg);
ExpectReusedTemplate("<typename A> (A = float)", {{"A"}, {floatArg}},
single_type_arg);

// Test that <typename A, signed char I> creates a new template with A = int
// and I = 47;
infos.names.push_back("I");
infos.args.push_back(TemplateArgument(m_ast->getASTContext(),
llvm::APSInt(llvm::APInt(8, 47)),
m_ast->getASTContext().SignedCharTy));
ClassTemplateDecl *type_and_char_value =
ExpectNewTemplate("<typename A, signed char I> (I = 47)", infos);
ExpectNewTemplate("<typename A, signed char I> (I = 47)",
{{"A", "I"}, {floatArg, charArg1}});

// Change the value of the I parameter to 123. The previously created
// class template should still be reused.
infos.args.pop_back();
infos.args.push_back(TemplateArgument(m_ast->getASTContext(),
llvm::APSInt(llvm::APInt(8, 123)),
m_ast->getASTContext().SignedCharTy));
ExpectReusedTemplate("<typename A, signed char I> (I = 123)", infos,
type_and_char_value);
ExpectReusedTemplate("<typename A, signed char I> (I = 123)",
{{"A", "I"}, {floatArg, charArg2}}, type_and_char_value);

// Change the type of the I parameter to int so we have <typename A, int I>.
// The class template from above can't be reused.
infos.args.pop_back();
infos.args.push_back(TemplateArgument(m_ast->getASTContext(),
llvm::APSInt(llvm::APInt(32, 47)),
m_ast->getASTContext().IntTy));
ExpectNewTemplate("<typename A, int I> (I = 123)", infos);
ExpectNewTemplate("<typename A, int I> (I = 123)",
{{"A", "I"}, {floatArg, intArg2}});

// Test a second type parameter will also cause a new template to be created.
// We now have <typename A, int I, typename B>.
infos.names.push_back("B");
infos.args.push_back(TemplateArgument(m_ast->getASTContext().IntTy));
ClassTemplateDecl *type_and_char_value_and_type =
ExpectNewTemplate("<typename A, int I, typename B>", infos);
ExpectNewTemplate("<typename A, int I, typename B>",
{{"A", "I", "B"}, {floatArg, intArg2, intArg1}});

// Remove all the names from the parameters which shouldn't influence the
// way the templates get merged.
infos.names = {"", "", ""};
ExpectReusedTemplate("<typename, int, typename>", infos,
ExpectReusedTemplate("<typename, int, typename>",
{{"", "", ""}, {floatArg, intArg2, intArg1}},
type_and_char_value_and_type);
}

TEST_F(TestCreateClassTemplateDecl, FindExistingTemplatesWithParameterPack) {
// The same as FindExistingTemplates but for templates with parameter packs.

TypeSystemClang::TemplateParameterInfos infos;
infos.packed_args =
std::make_unique<TypeSystemClang::TemplateParameterInfos>();
infos.packed_args->names = {"", ""};
infos.packed_args->args = {TemplateArgument(m_ast->getASTContext().IntTy),
TemplateArgument(m_ast->getASTContext().IntTy)};
clang::TemplateArgument intArg1(m_ast->getASTContext().IntTy);
clang::TemplateArgument intArg2(m_ast->getASTContext(),
llvm::APSInt(llvm::APInt(32, 1)),
m_ast->getASTContext().IntTy);
clang::TemplateArgument intArg3(m_ast->getASTContext(),
llvm::APSInt(llvm::APInt(32, 123)),
m_ast->getASTContext().IntTy);
clang::TemplateArgument longArg1(m_ast->getASTContext().LongTy);
clang::TemplateArgument longArg2(m_ast->getASTContext(),
llvm::APSInt(llvm::APInt(64, 1)),
m_ast->getASTContext().LongTy);

infos.SetParameterPack(
std::make_unique<TypeSystemClang::TemplateParameterInfos>(
llvm::SmallVector<const char *>{"", ""},
llvm::SmallVector<TemplateArgument>{intArg1, intArg1}));

ClassTemplateDecl *type_pack =
ExpectNewTemplate("<typename ...> (int, int)", infos);

// Special case: An instantiation for a parameter pack with no values fits
// to whatever class template we find. There isn't enough information to
// do an actual comparison here.
infos.packed_args =
std::make_unique<TypeSystemClang::TemplateParameterInfos>();
infos.SetParameterPack(
std::make_unique<TypeSystemClang::TemplateParameterInfos>());
ExpectReusedTemplate("<...> (no values in pack)", infos, type_pack);

// Change the type content of pack type values.
infos.packed_args->names = {"", ""};
infos.packed_args->args = {TemplateArgument(m_ast->getASTContext().IntTy),
TemplateArgument(m_ast->getASTContext().LongTy)};
infos.SetParameterPack(
std::make_unique<TypeSystemClang::TemplateParameterInfos>(
llvm::SmallVector<const char *>{"", ""},
llvm::SmallVector<TemplateArgument>{intArg1, longArg1}));
ExpectReusedTemplate("<typename ...> (int, long)", infos, type_pack);

// Change the number of pack values.
infos.packed_args->args = {TemplateArgument(m_ast->getASTContext().IntTy)};
infos.SetParameterPack(
std::make_unique<TypeSystemClang::TemplateParameterInfos>(
llvm::SmallVector<const char *>{""},
llvm::SmallVector<TemplateArgument>{intArg1}));
ExpectReusedTemplate("<typename ...> (int)", infos, type_pack);

// The names of the pack values shouldn't matter.
infos.packed_args->names = {"A", "B"};
infos.SetParameterPack(
std::make_unique<TypeSystemClang::TemplateParameterInfos>(
llvm::SmallVector<const char *>{"A"},
llvm::SmallVector<TemplateArgument>{intArg1}));
ExpectReusedTemplate("<typename ...> (int)", infos, type_pack);

// Changing the kind of template argument will create a new template.
infos.packed_args->args = {TemplateArgument(m_ast->getASTContext(),
llvm::APSInt(llvm::APInt(32, 1)),
m_ast->getASTContext().IntTy)};
infos.SetParameterPack(
std::make_unique<TypeSystemClang::TemplateParameterInfos>(
llvm::SmallVector<const char *>{"A"},
llvm::SmallVector<TemplateArgument>{intArg2}));
ClassTemplateDecl *int_pack = ExpectNewTemplate("<int ...> (int = 1)", infos);

// Changing the value of integral parameters will not create a new template.
infos.packed_args->args = {TemplateArgument(
m_ast->getASTContext(), llvm::APSInt(llvm::APInt(32, 123)),
m_ast->getASTContext().IntTy)};
infos.SetParameterPack(
std::make_unique<TypeSystemClang::TemplateParameterInfos>(
llvm::SmallVector<const char *>{"A"},
llvm::SmallVector<TemplateArgument>{intArg3}));
ExpectReusedTemplate("<int ...> (int = 123)", infos, int_pack);

// Changing the integral type will create a new template.
infos.packed_args->args = {TemplateArgument(m_ast->getASTContext(),
llvm::APSInt(llvm::APInt(64, 1)),
m_ast->getASTContext().LongTy)};
infos.SetParameterPack(
std::make_unique<TypeSystemClang::TemplateParameterInfos>(
llvm::SmallVector<const char *>{"A"},
llvm::SmallVector<TemplateArgument>{longArg2}));
ExpectNewTemplate("<long ...> (long = 1)", infos);

// Prependinding a non-pack parameter will create a new template.
infos.names = {"T"};
infos.args = {TemplateArgument(m_ast->getASTContext().IntTy)};
infos.InsertArg("T", intArg1);
ExpectNewTemplate("<typename T, long...> (T = int, long = 1)", infos);
}

TEST_F(TestTypeSystemClang, OnlyPackName) {
TypeSystemClang::TemplateParameterInfos infos;
infos.pack_name = "A";
infos.SetPackName("A");
EXPECT_FALSE(infos.IsValid());
}

Expand Down
3 changes: 2 additions & 1 deletion lldb/unittests/SymbolFile/DWARF/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ add_lldb_unittest(SymbolFileDWARFTests
)

set(test_inputs
test-dwarf.exe)
test-dwarf.exe
DW_AT_default_value-test.yaml)

add_unittest_inputs(SymbolFileDWARFTests "${test_inputs}")
62 changes: 61 additions & 1 deletion lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "Plugins/SymbolFile/DWARF/DWARFDIE.h"
#include "TestingSupport/Symbol/ClangTestUtils.h"
#include "TestingSupport/Symbol/YAMLModuleTester.h"
#include "lldb/Core/Debugger.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"

Expand All @@ -19,7 +20,16 @@ using namespace lldb_private;
using namespace lldb_private::dwarf;

namespace {
class DWARFASTParserClangTests : public testing::Test {};
static std::once_flag debugger_initialize_flag;

class DWARFASTParserClangTests : public testing::Test {
void SetUp() override {
HostInfo::Initialize();
std::call_once(debugger_initialize_flag,
[]() { Debugger::Initialize(nullptr); });
}
void TearDown() override { HostInfo::Terminate(); }
};

class DWARFASTParserClangStub : public DWARFASTParserClang {
public:
Expand Down Expand Up @@ -404,3 +414,53 @@ TEST_F(ExtractIntFromFormValueTest, TestUnsignedInt) {
EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, uint_max + 2),
llvm::Failed());
}

TEST_F(DWARFASTParserClangTests, TestDefaultTemplateParamParsing) {
// Tests parsing DW_AT_default_value for template parameters.
auto BufferOrError = llvm::MemoryBuffer::getFile(
GetInputFilePath("DW_AT_default_value-test.yaml"), /*IsText=*/true);
ASSERT_TRUE(BufferOrError);
YAMLModuleTester t(BufferOrError.get()->getBuffer());

DWARFUnit *unit = t.GetDwarfUnit();
ASSERT_NE(unit, nullptr);
const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE();
ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit);
DWARFDIE cu_die(unit, cu_entry);

auto holder = std::make_unique<clang_utils::TypeSystemClangHolder>("ast");
auto &ast_ctx = *holder->GetAST();
DWARFASTParserClangStub ast_parser(ast_ctx);

llvm::SmallVector<lldb::TypeSP, 2> types;
for (DWARFDIE die : cu_die.children()) {
if (die.Tag() == DW_TAG_class_type) {
SymbolContext sc;
bool new_type = false;
types.push_back(ast_parser.ParseTypeFromDWARF(sc, die, &new_type));
}
}

ASSERT_EQ(types.size(), 3U);

auto check_decl = [](auto const *decl) {
clang::ClassTemplateSpecializationDecl const *ctsd =
llvm::dyn_cast_or_null<clang::ClassTemplateSpecializationDecl>(decl);
ASSERT_NE(ctsd, nullptr);

auto const &args = ctsd->getTemplateArgs();
ASSERT_GT(args.size(), 0U);

for (auto const &arg : args.asArray()) {
EXPECT_TRUE(arg.getIsDefaulted());
}
};

for (auto const &type_sp : types) {
ASSERT_NE(type_sp, nullptr);
auto const *decl = ClangUtil::GetAsTagDecl(type_sp->GetFullCompilerType());
if (decl->getName() == "bar" || decl->getName() == "baz") {
check_decl(decl);
}
}
}
314 changes: 314 additions & 0 deletions lldb/unittests/SymbolFile/DWARF/Inputs/DW_AT_default_value-test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,314 @@
# template <typename T>
# class foo {};
#
# template <template <typename T> class CT = foo>
# class baz {};
#
# template <typename T = char, int i = 3, bool b = true,
# typename c = foo<T>>
# class bar {};
#
# int main() {
# bar<> br;
# baz<> bz;
# return 0;
# }
#
# YAML generated on Linux using obj2yaml on the above program
# compiled with Clang.
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_REL
Machine: EM_AARCH64
SectionHeaderStringTable: .strtab
Sections:
- Name: .text
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
AddressAlign: 0x4
Content: FF4300D1E0031F2AFF0F00B9FF430091C0035FD6
- Name: .linker-options
Type: SHT_LLVM_LINKER_OPTIONS
Flags: [ SHF_EXCLUDE ]
AddressAlign: 0x1
Content: ''
- Name: .debug_abbrev
Type: SHT_PROGBITS
AddressAlign: 0x1
Content: 011101252513050325721710171B25111B120673170000022E01111B1206401803253A0B3B0B49133F190000033400021803253A0B3B0B4913000004240003253E0B0B0B0000050201360B03250B0B3A0B3B0B0000062F00491303251E190000073000491303251E191C0D0000083000491303251E191C0F000009020003253C1900000A8682010003251E19904225000000
- Name: .debug_info
Type: SHT_PROGBITS
AddressAlign: 0x1
Content: 7F00000005000108000000000100210001000000000000000002001400000000000000020014000000016F03000B490000000302910B05000C4D0000000302910A0E000D78000000000404050405050D010009066E000000070749000000080308720000000A0106760000000C000406080104090201090B0505110100050A0F100000
- Name: .debug_str_offsets
Type: SHT_PROGBITS
AddressAlign: 0x1
Content: 4C00000005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
- Name: .comment
Type: SHT_PROGBITS
Flags: [ SHF_MERGE, SHF_STRINGS ]
AddressAlign: 0x1
EntSize: 0x1
Content: 00636C616E672076657273696F6E2031362E302E30202868747470733A2F2F6769746875622E636F6D2F6C6C766D2F6C6C766D2D70726F6A65637420343764323862376138323638653337616130646537366238353966343530386533646261633663652900
- Name: .note.GNU-stack
Type: SHT_PROGBITS
AddressAlign: 0x1
- Name: .eh_frame
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
AddressAlign: 0x8
Content: 1000000000000000017A5200017C1E011B0C1F001800000018000000000000001400000000440E104C0E000000000000
- Name: .debug_line
Type: SHT_PROGBITS
AddressAlign: 0x1
Content: 580000000500080037000000010101FB0E0D00010101010000000100000101011F010000000003011F020F051E01000000000019537E33C1D1006B79E3D1C33D6EE6A304000009020000000000000000030A0105050ABD0208000101
- Name: .debug_line_str
Type: SHT_PROGBITS
Flags: [ SHF_MERGE, SHF_STRINGS ]
AddressAlign: 0x1
EntSize: 0x1
Content: 2F686F6D652F6761726465690064656661756C74732E63707000
- Name: .rela.debug_info
Type: SHT_RELA
Flags: [ SHF_INFO_LINK ]
Link: .symtab
AddressAlign: 0x8
Info: .debug_info
Relocations:
- Offset: 0x8
Symbol: .debug_abbrev
Type: R_AARCH64_ABS32
- Offset: 0x11
Symbol: .debug_str_offsets
Type: R_AARCH64_ABS32
Addend: 8
- Offset: 0x15
Symbol: .debug_line
Type: R_AARCH64_ABS32
- Offset: 0x1F
Symbol: .debug_addr
Type: R_AARCH64_ABS32
Addend: 8
- Name: .rela.debug_str_offsets
Type: SHT_RELA
Flags: [ SHF_INFO_LINK ]
Link: .symtab
AddressAlign: 0x8
Info: .debug_str_offsets
Relocations:
- Offset: 0x8
Symbol: .debug_str
Type: R_AARCH64_ABS32
- Offset: 0xC
Symbol: .debug_str
Type: R_AARCH64_ABS32
Addend: 101
- Offset: 0x10
Symbol: .debug_str
Type: R_AARCH64_ABS32
Addend: 114
- Offset: 0x14
Symbol: .debug_str
Type: R_AARCH64_ABS32
Addend: 127
- Offset: 0x18
Symbol: .debug_str
Type: R_AARCH64_ABS32
Addend: 132
- Offset: 0x1C
Symbol: .debug_str
Type: R_AARCH64_ABS32
Addend: 136
- Offset: 0x20
Symbol: .debug_str
Type: R_AARCH64_ABS32
Addend: 139
- Offset: 0x24
Symbol: .debug_str
Type: R_AARCH64_ABS32
Addend: 144
- Offset: 0x28
Symbol: .debug_str
Type: R_AARCH64_ABS32
Addend: 146
- Offset: 0x2C
Symbol: .debug_str
Type: R_AARCH64_ABS32
Addend: 148
- Offset: 0x30
Symbol: .debug_str
Type: R_AARCH64_ABS32
Addend: 153
- Offset: 0x34
Symbol: .debug_str
Type: R_AARCH64_ABS32
Addend: 155
- Offset: 0x38
Symbol: .debug_str
Type: R_AARCH64_ABS32
Addend: 165
- Offset: 0x3C
Symbol: .debug_str
Type: R_AARCH64_ABS32
Addend: 167
- Offset: 0x40
Symbol: .debug_str
Type: R_AARCH64_ABS32
Addend: 198
- Offset: 0x44
Symbol: .debug_str
Type: R_AARCH64_ABS32
Addend: 201
- Offset: 0x48
Symbol: .debug_str
Type: R_AARCH64_ABS32
Addend: 204
- Offset: 0x4C
Symbol: .debug_str
Type: R_AARCH64_ABS32
Addend: 208
- Name: .rela.debug_addr
Type: SHT_RELA
Flags: [ SHF_INFO_LINK ]
Link: .symtab
AddressAlign: 0x8
Info: .debug_addr
Relocations:
- Offset: 0x8
Symbol: .text
Type: R_AARCH64_ABS64
- Name: .rela.eh_frame
Type: SHT_RELA
Flags: [ SHF_INFO_LINK ]
Link: .symtab
AddressAlign: 0x8
Info: .eh_frame
Relocations:
- Offset: 0x1C
Symbol: .text
Type: R_AARCH64_PREL32
- Name: .rela.debug_line
Type: SHT_RELA
Flags: [ SHF_INFO_LINK ]
Link: .symtab
AddressAlign: 0x8
Info: .debug_line
Relocations:
- Offset: 0x22
Symbol: .debug_line_str
Type: R_AARCH64_ABS32
- Offset: 0x2E
Symbol: .debug_line_str
Type: R_AARCH64_ABS32
Addend: 13
- Offset: 0x48
Symbol: .text
Type: R_AARCH64_ABS64
- Name: .llvm_addrsig
Type: SHT_LLVM_ADDRSIG
Flags: [ SHF_EXCLUDE ]
Link: .symtab
AddressAlign: 0x1
Offset: 0x818
Symbols: [ ]
- Type: SectionHeaderTable
Sections:
- Name: .strtab
- Name: .text
- Name: .linker-options
- Name: .debug_abbrev
- Name: .debug_info
- Name: .rela.debug_info
- Name: .debug_str_offsets
- Name: .rela.debug_str_offsets
- Name: .debug_str
- Name: .debug_addr
- Name: .rela.debug_addr
- Name: .comment
- Name: .note.GNU-stack
- Name: .eh_frame
- Name: .rela.eh_frame
- Name: .debug_line
- Name: .rela.debug_line
- Name: .debug_line_str
- Name: .llvm_addrsig
- Name: .symtab
Symbols:
- Name: defaults.cpp
Type: STT_FILE
Index: SHN_ABS
- Name: .text
Type: STT_SECTION
Section: .text
- Name: '$x.0'
Section: .text
- Name: .debug_abbrev
Type: STT_SECTION
Section: .debug_abbrev
- Name: '$d.1'
Section: .debug_abbrev
- Name: '$d.2'
Section: .debug_info
- Name: .debug_str_offsets
Type: STT_SECTION
Section: .debug_str_offsets
- Name: '$d.3'
Section: .debug_str_offsets
- Name: .debug_str
Type: STT_SECTION
Section: .debug_str
- Name: '$d.4'
Section: .debug_str
- Name: .debug_addr
Type: STT_SECTION
Section: .debug_addr
- Name: '$d.5'
Section: .debug_addr
- Name: '$d.6'
Section: .comment
- Name: '$d.7'
Section: .eh_frame
- Name: .debug_line
Type: STT_SECTION
Section: .debug_line
- Name: '$d.8'
Section: .debug_line
- Name: .debug_line_str
Type: STT_SECTION
Section: .debug_line_str
- Name: '$d.9'
Section: .debug_line_str
- Name: main
Type: STT_FUNC
Section: .text
Binding: STB_GLOBAL
Size: 0x14
DWARF:
debug_str:
- 'clang version 16.0.0 (https://github.com/llvm/llvm-project 47d28b7a8268e37aa0de76b859f4508e3dbac6ce)'
- defaults.cpp
- '/home/gardei'
- main
- int
- br
- char
- T
- i
- bool
- b
- 'foo<char>'
- c
- 'bar<char, 3, true, foo<char> >'
- bz
- CT
- foo
- 'baz<foo>'
debug_addr:
- Length: 0xC
Version: 0x5
AddressSize: 0x8
Entries:
- {}
...
11 changes: 11 additions & 0 deletions llvm/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,23 @@ Changes to the Metadata Info
Changes to the Debug Info
---------------------------------

* The DWARFv5 feature of attaching `DW_AT_default_value` to defaulted template
parameters will now be available in any non-strict DWARF mode and in a wider
range of cases than previously. (`D139953 <https://reviews.llvm.org/D139953>`_, `D139988 <https://reviews.llvm.org/D139988>`_)

* The `DW_AT_name` on `DW_AT_typedef`s for alias templates will now omit defaulted
template parameters. (`D142268 <https://reviews.llvm.org/D142268>`_)

Changes to the LLVM tools
---------------------------------

Changes to LLDB
---------------------------------

* In the results of commands such as `expr` and `frame var`, type summaries will now
omit defaulted template parameters. The full template parameter list can still be
viewed with `expr --raw-output`/`frame var --raw-output`. (`D141828 <https://reviews.llvm.org/D141828>`_)

Changes to Sanitizers
---------------------

Expand Down