Skip to content

Commit a377cdd

Browse files
authored
[lldb][TypeSystemClang] Add support for floating point template argument constants (#127206)
This patch adds support for template arguments of `clang::TemplateArgument::ArgKind::StructuralValue` kind (added in #78041). These are used for non-type template parameters such as floating point constants. When LLDB created `clang::NonTypeTemplateParmDecl`s, it previously assumed integral values, this patch accounts for structural values too. Anywhere LLDB assumed a `DW_TAG_template_value_parameter` was `Integral`, it will now also check for `StructuralValue`, and will unpack the `TemplateArgument` value and type accordingly. We can rely on the fact that any `TemplateArgument` of `StructuralValue` kind that the `DWARFASTParserClang` creates will have a valid value, because it gets those from `DW_AT_const_value`.
1 parent 93a1184 commit a377cdd

File tree

9 files changed

+174
-40
lines changed

9 files changed

+174
-40
lines changed

lldb/include/lldb/Symbol/CompilerType.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <string>
1515
#include <vector>
1616

17+
#include "lldb/Utility/Scalar.h"
1718
#include "lldb/lldb-private.h"
1819
#include "llvm/ADT/APSInt.h"
1920
#include "llvm/Support/Casting.h"
@@ -544,7 +545,7 @@ bool operator==(const CompilerType &lhs, const CompilerType &rhs);
544545
bool operator!=(const CompilerType &lhs, const CompilerType &rhs);
545546

546547
struct CompilerType::IntegralTemplateArgument {
547-
llvm::APSInt value;
548+
Scalar value;
548549
CompilerType type;
549550
};
550551

lldb/source/API/SBType.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,7 @@ lldb::SBValue SBType::GetTemplateArgumentValue(lldb::SBTarget target,
697697
std::optional<CompilerType::IntegralTemplateArgument> arg;
698698
const bool expand_pack = true;
699699
switch (GetTemplateArgumentKind(idx)) {
700+
case eTemplateArgumentKindStructuralValue:
700701
case eTemplateArgumentKindIntegral:
701702
arg = m_opaque_sp->GetCompilerType(false).GetIntegralTemplateArgument(
702703
idx, expand_pack);
@@ -708,9 +709,8 @@ lldb::SBValue SBType::GetTemplateArgumentValue(lldb::SBTarget target,
708709
if (!arg)
709710
return {};
710711

711-
Scalar value{arg->value};
712712
DataExtractor data;
713-
value.GetData(data);
713+
arg->value.GetData(data);
714714

715715
ExecutionContext exe_ctx;
716716
auto target_sp = target.GetSP();

lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ lldb::ChildCacheState GenericBitsetFrontEnd::Update() {
9090
size_t size = 0;
9191

9292
if (auto arg = m_backend.GetCompilerType().GetIntegralTemplateArgument(0))
93-
size = arg->value.getLimitedValue();
93+
size = arg->value.GetAPSInt().getLimitedValue();
9494

9595
m_elements.assign(size, ValueObjectSP());
9696
m_first =

lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd::Update() {
117117
} else if (auto arg =
118118
m_backend.GetCompilerType().GetIntegralTemplateArgument(1)) {
119119

120-
m_num_elements = arg->value.getLimitedValue();
120+
m_num_elements = arg->value.GetAPSInt().getLimitedValue();
121121
}
122122
}
123123

lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1973,6 +1973,33 @@ class DWARFASTParserClang::DelayedAddObjCClassProperty {
19731973
ClangASTMetadata m_metadata;
19741974
};
19751975

1976+
static std::optional<clang::APValue> MakeAPValue(const clang::ASTContext &ast,
1977+
CompilerType clang_type,
1978+
uint64_t value) {
1979+
std::optional<uint64_t> bit_width = clang_type.GetBitSize(nullptr);
1980+
if (!bit_width)
1981+
return std::nullopt;
1982+
1983+
bool is_signed = false;
1984+
const bool is_integral = clang_type.IsIntegerOrEnumerationType(is_signed);
1985+
1986+
llvm::APSInt apint(*bit_width, !is_signed);
1987+
apint = value;
1988+
1989+
if (is_integral)
1990+
return clang::APValue(apint);
1991+
1992+
uint32_t count;
1993+
bool is_complex;
1994+
// FIXME: we currently support a limited set of floating point types.
1995+
// E.g., 16-bit floats are not supported.
1996+
if (!clang_type.IsFloatingPointType(count, is_complex))
1997+
return std::nullopt;
1998+
1999+
return clang::APValue(llvm::APFloat(
2000+
ast.getFloatTypeSemantics(ClangUtil::GetQualType(clang_type)), apint));
2001+
}
2002+
19762003
bool DWARFASTParserClang::ParseTemplateDIE(
19772004
const DWARFDIE &die,
19782005
TypeSystemClang::TemplateParameterInfos &template_param_infos) {
@@ -2050,28 +2077,26 @@ bool DWARFASTParserClang::ParseTemplateDIE(
20502077
clang_type = m_ast.GetBasicType(eBasicTypeVoid);
20512078

20522079
if (!is_template_template_argument) {
2053-
bool is_signed = false;
2054-
// Get the signed value for any integer or enumeration if available
2055-
clang_type.IsIntegerOrEnumerationType(is_signed);
20562080

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

20602084
if (tag == DW_TAG_template_value_parameter && uval64_valid) {
2061-
std::optional<uint64_t> size = clang_type.GetBitSize(nullptr);
2062-
if (!size)
2063-
return false;
2064-
llvm::APInt apint(*size, uval64, is_signed);
2065-
template_param_infos.InsertArg(
2066-
name, clang::TemplateArgument(ast, llvm::APSInt(apint, !is_signed),
2067-
ClangUtil::GetQualType(clang_type),
2068-
is_default_template_arg));
2069-
} else {
2070-
template_param_infos.InsertArg(
2071-
name, clang::TemplateArgument(ClangUtil::GetQualType(clang_type),
2072-
/*isNullPtr*/ false,
2073-
is_default_template_arg));
2085+
if (auto value = MakeAPValue(ast, clang_type, uval64)) {
2086+
template_param_infos.InsertArg(
2087+
name, clang::TemplateArgument(
2088+
ast, ClangUtil::GetQualType(clang_type),
2089+
std::move(*value), is_default_template_arg));
2090+
return true;
2091+
}
20742092
}
2093+
2094+
// We get here if this is a type-template parameter or we couldn't create
2095+
// a non-type template parameter.
2096+
template_param_infos.InsertArg(
2097+
name, clang::TemplateArgument(ClangUtil::GetQualType(clang_type),
2098+
/*isNullPtr*/ false,
2099+
is_default_template_arg));
20752100
} else {
20762101
auto *tplt_type = m_ast.CreateTemplateTemplateParmDecl(template_name);
20772102
template_param_infos.InsertArg(

lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,10 +1311,18 @@ CompilerType TypeSystemClang::CreateRecordType(
13111311
}
13121312

13131313
namespace {
1314-
/// Returns true iff the given TemplateArgument should be represented as an
1315-
/// NonTypeTemplateParmDecl in the AST.
1316-
bool IsValueParam(const clang::TemplateArgument &argument) {
1317-
return argument.getKind() == TemplateArgument::Integral;
1314+
/// Returns the type of the template argument iff the given TemplateArgument
1315+
/// should be represented as an NonTypeTemplateParmDecl in the AST. Returns
1316+
/// a null QualType otherwise.
1317+
QualType GetValueParamType(const clang::TemplateArgument &argument) {
1318+
switch (argument.getKind()) {
1319+
case TemplateArgument::Integral:
1320+
return argument.getIntegralType();
1321+
case TemplateArgument::StructuralValue:
1322+
return argument.getStructuralValueType();
1323+
default:
1324+
return {};
1325+
}
13181326
}
13191327

13201328
void AddAccessSpecifierDecl(clang::CXXRecordDecl *cxx_record_decl,
@@ -1361,8 +1369,8 @@ static TemplateParameterList *CreateTemplateParameterList(
13611369
if (name && name[0])
13621370
identifier_info = &ast.Idents.get(name);
13631371
TemplateArgument const &targ = args[i];
1364-
if (IsValueParam(targ)) {
1365-
QualType template_param_type = targ.getIntegralType();
1372+
QualType template_param_type = GetValueParamType(targ);
1373+
if (!template_param_type.isNull()) {
13661374
template_param_decls.push_back(NonTypeTemplateParmDecl::Create(
13671375
ast, decl_context, SourceLocation(), SourceLocation(), depth, i,
13681376
identifier_info, template_param_type, parameter_pack,
@@ -1380,10 +1388,11 @@ static TemplateParameterList *CreateTemplateParameterList(
13801388
identifier_info = &ast.Idents.get(template_param_infos.GetPackName());
13811389
const bool parameter_pack_true = true;
13821390

1383-
if (!template_param_infos.GetParameterPack().IsEmpty() &&
1384-
IsValueParam(template_param_infos.GetParameterPack().Front())) {
1385-
QualType template_param_type =
1386-
template_param_infos.GetParameterPack().Front().getIntegralType();
1391+
QualType template_param_type =
1392+
!template_param_infos.GetParameterPack().IsEmpty()
1393+
? GetValueParamType(template_param_infos.GetParameterPack().Front())
1394+
: QualType();
1395+
if (!template_param_type.isNull()) {
13871396
template_param_decls.push_back(NonTypeTemplateParmDecl::Create(
13881397
ast, decl_context, SourceLocation(), SourceLocation(), depth,
13891398
num_template_params, identifier_info, template_param_type,
@@ -1458,10 +1467,12 @@ static bool TemplateParameterAllowsValue(NamedDecl *param,
14581467
} else if (auto *type_param =
14591468
llvm::dyn_cast<NonTypeTemplateParmDecl>(param)) {
14601469
// Compare the argument kind, i.e. ensure that <typename> != <int>.
1461-
if (!IsValueParam(value))
1470+
QualType value_param_type = GetValueParamType(value);
1471+
if (value_param_type.isNull())
14621472
return false;
1473+
14631474
// Compare the integral type, i.e. ensure that <int> != <char>.
1464-
if (type_param->getType() != value.getIntegralType())
1475+
if (type_param->getType() != value_param_type)
14651476
return false;
14661477
} else {
14671478
// There is no way to create other parameter decls at the moment, so we
@@ -7351,10 +7362,27 @@ TypeSystemClang::GetIntegralTemplateArgument(lldb::opaque_compiler_type_t type,
73517362
return std::nullopt;
73527363

73537364
const auto *arg = GetNthTemplateArgument(template_decl, idx, expand_pack);
7354-
if (!arg || arg->getKind() != clang::TemplateArgument::Integral)
7365+
if (!arg)
73557366
return std::nullopt;
73567367

7357-
return {{arg->getAsIntegral(), GetType(arg->getIntegralType())}};
7368+
switch (arg->getKind()) {
7369+
case clang::TemplateArgument::Integral:
7370+
return {{arg->getAsIntegral(), GetType(arg->getIntegralType())}};
7371+
case clang::TemplateArgument::StructuralValue: {
7372+
clang::APValue value = arg->getAsStructuralValue();
7373+
CompilerType type = GetType(arg->getStructuralValueType());
7374+
7375+
if (value.isFloat())
7376+
return {{value.getFloat(), type}};
7377+
7378+
if (value.isInt())
7379+
return {{value.getInt(), type}};
7380+
7381+
return std::nullopt;
7382+
}
7383+
default:
7384+
return std::nullopt;
7385+
}
73587386
}
73597387

73607388
CompilerType TypeSystemClang::GetTypeForFormatters(void *type) {

lldb/test/API/lang/cpp/template-arguments/TestCppTemplateArguments.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,44 @@ def test(self):
6262
self.assertEqual(template_param_value.GetTypeName(), "char")
6363
self.assertEqual(chr(template_param_value.GetValueAsSigned()), "v")
6464

65-
# FIXME: type should be Foo<float, 2.0f>
66-
# FIXME: double/float NTTP parameter values currently not supported.
67-
value = self.expect_expr("temp4", result_type="Foo<float, 1073741824>")
65+
value = self.expect_expr("temp4", result_type="Foo<float, 2.000000e+00>")
6866
template_param_value = value.GetType().GetTemplateArgumentValue(target, 1)
6967
self.assertEqual(template_param_value.GetTypeName(), "float")
7068
# FIXME: this should return a float
7169
self.assertEqual(template_param_value.GetValueAsSigned(), 2)
70+
71+
value = self.expect_expr("temp5", result_type="Foo<double, -2.505000e+02>")
72+
template_param_value = value.GetType().GetTemplateArgumentValue(target, 1)
73+
self.assertEqual(template_param_value.GetTypeName(), "double")
74+
# FIXME: this should return a float
75+
self.assertEqual(template_param_value.GetValueAsSigned(), -250)
76+
77+
# FIXME: type should be Foo<int *, &temp1.member>
78+
value = self.expect_expr("temp6", result_type="Foo<int *, int *>")
79+
self.assertFalse(value.GetType().GetTemplateArgumentValue(target, 1))
80+
81+
# FIXME: support wider range of floating point types
82+
value = self.expect_expr("temp7", result_type="Foo<__fp16, __fp16>")
83+
self.assertFalse(value.GetType().GetTemplateArgumentValue(target, 1))
84+
85+
value = self.expect_expr("temp8", result_type="Foo<__fp16, __fp16>")
86+
self.assertFalse(value.GetType().GetTemplateArgumentValue(target, 1))
87+
88+
value = self.expect_expr("temp9", result_type="Bar<double, 1.200000e+00>")
89+
template_param_value = value.GetType().GetTemplateArgumentValue(target, 1)
90+
self.assertEqual(template_param_value.GetTypeName(), "double")
91+
# FIXME: this should return a float
92+
self.assertEqual(template_param_value.GetValueAsSigned(), 1)
93+
94+
value = self.expect_expr(
95+
"temp10", result_type="Bar<float, 1.000000e+00, 2.000000e+00>"
96+
)
97+
template_param_value = value.GetType().GetTemplateArgumentValue(target, 1)
98+
self.assertEqual(template_param_value.GetTypeName(), "float")
99+
# FIXME: this should return a float
100+
self.assertEqual(template_param_value.GetValueAsSigned(), 1)
101+
102+
template_param_value = value.GetType().GetTemplateArgumentValue(target, 2)
103+
self.assertEqual(template_param_value.GetTypeName(), "float")
104+
# FIXME: this should return a float
105+
self.assertEqual(template_param_value.GetValueAsSigned(), 2)

lldb/test/API/lang/cpp/template-arguments/main.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,13 @@ template <typename T, T value> struct Foo {};
99
Foo<short, -2> temp2;
1010
Foo<char, 'v'> temp3;
1111
Foo<float, 2.0f> temp4;
12+
Foo<double, -250.5> temp5;
13+
Foo<int *, &temp1.member> temp6;
14+
Foo<_Float16, _Float16(1.0)> temp7;
15+
Foo<__bf16, __bf16(1.0)> temp8;
16+
17+
template <typename T, T... values> struct Bar {};
18+
Bar<double, 1.2> temp9;
19+
Bar<float, 1.0f, 2.0f> temp10;
1220

1321
int main() {}

lldb/unittests/Symbol/TestTypeSystemClang.cpp

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,17 @@ TEST_F(TestTypeSystemClang, TemplateArguments) {
525525
infos.InsertArg("I", TemplateArgument(m_ast->getASTContext(), arg,
526526
m_ast->getASTContext().IntTy));
527527

528-
// template<typename T, int I> struct foo;
528+
llvm::APFloat float_arg(5.5f);
529+
infos.InsertArg("F", TemplateArgument(m_ast->getASTContext(),
530+
m_ast->getASTContext().FloatTy,
531+
clang::APValue(float_arg)));
532+
533+
llvm::APFloat double_arg(-15.2);
534+
infos.InsertArg("D", TemplateArgument(m_ast->getASTContext(),
535+
m_ast->getASTContext().DoubleTy,
536+
clang::APValue(double_arg)));
537+
538+
// template<typename T, int I, float F, double D> struct foo;
529539
ClassTemplateDecl *decl = m_ast->CreateClassTemplateDecl(
530540
m_ast->GetTranslationUnitDecl(), OptionalClangModuleID(), eAccessPublic,
531541
"foo", llvm::to_underlying(clang::TagTypeKind::Struct), infos);
@@ -555,6 +565,10 @@ TEST_F(TestTypeSystemClang, TemplateArguments) {
555565

556566
CompilerType int_type(m_ast->weak_from_this(),
557567
m_ast->getASTContext().IntTy.getAsOpaquePtr());
568+
CompilerType float_type(m_ast->weak_from_this(),
569+
m_ast->getASTContext().FloatTy.getAsOpaquePtr());
570+
CompilerType double_type(m_ast->weak_from_this(),
571+
m_ast->getASTContext().DoubleTy.getAsOpaquePtr());
558572
for (CompilerType t : {type, typedef_type, auto_type}) {
559573
SCOPED_TRACE(t.GetTypeName().AsCString());
560574

@@ -577,8 +591,32 @@ TEST_F(TestTypeSystemClang, TemplateArguments) {
577591
auto result = m_ast->GetIntegralTemplateArgument(t.GetOpaqueQualType(), 1,
578592
expand_pack);
579593
ASSERT_NE(std::nullopt, result);
580-
EXPECT_EQ(arg, result->value);
594+
EXPECT_EQ(arg, result->value.GetAPSInt());
581595
EXPECT_EQ(int_type, result->type);
596+
597+
EXPECT_EQ(
598+
m_ast->GetTemplateArgumentKind(t.GetOpaqueQualType(), 2, expand_pack),
599+
eTemplateArgumentKindStructuralValue);
600+
EXPECT_EQ(
601+
m_ast->GetTypeTemplateArgument(t.GetOpaqueQualType(), 2, expand_pack),
602+
CompilerType());
603+
auto float_result = m_ast->GetIntegralTemplateArgument(
604+
t.GetOpaqueQualType(), 2, expand_pack);
605+
ASSERT_NE(std::nullopt, float_result);
606+
EXPECT_EQ(float_arg, float_result->value.GetAPFloat());
607+
EXPECT_EQ(float_type, float_result->type);
608+
609+
EXPECT_EQ(
610+
m_ast->GetTemplateArgumentKind(t.GetOpaqueQualType(), 3, expand_pack),
611+
eTemplateArgumentKindStructuralValue);
612+
EXPECT_EQ(
613+
m_ast->GetTypeTemplateArgument(t.GetOpaqueQualType(), 3, expand_pack),
614+
CompilerType());
615+
auto double_result = m_ast->GetIntegralTemplateArgument(
616+
t.GetOpaqueQualType(), 3, expand_pack);
617+
ASSERT_NE(std::nullopt, double_result);
618+
EXPECT_EQ(double_arg, double_result->value.GetAPFloat());
619+
EXPECT_EQ(double_type, double_result->type);
582620
}
583621
}
584622

0 commit comments

Comments
 (0)