Skip to content

[lldb][TypeSystemClang] Add support for floating point template argument constants #127206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
3 changes: 2 additions & 1 deletion lldb/include/lldb/Symbol/CompilerType.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <string>
#include <vector>

#include "lldb/Utility/Scalar.h"
#include "lldb/lldb-private.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/Support/Casting.h"
Expand Down Expand Up @@ -544,7 +545,7 @@ bool operator==(const CompilerType &lhs, const CompilerType &rhs);
bool operator!=(const CompilerType &lhs, const CompilerType &rhs);

struct CompilerType::IntegralTemplateArgument {
llvm::APSInt value;
Scalar value;
CompilerType type;
};

Expand Down
4 changes: 2 additions & 2 deletions lldb/source/API/SBType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,7 @@ lldb::SBValue SBType::GetTemplateArgumentValue(lldb::SBTarget target,
std::optional<CompilerType::IntegralTemplateArgument> arg;
const bool expand_pack = true;
switch (GetTemplateArgumentKind(idx)) {
case eTemplateArgumentKindStructuralValue:
case eTemplateArgumentKindIntegral:
arg = m_opaque_sp->GetCompilerType(false).GetIntegralTemplateArgument(
idx, expand_pack);
Expand All @@ -708,9 +709,8 @@ lldb::SBValue SBType::GetTemplateArgumentValue(lldb::SBTarget target,
if (!arg)
return {};

Scalar value{arg->value};
DataExtractor data;
value.GetData(data);
arg->value.GetData(data);

ExecutionContext exe_ctx;
auto target_sp = target.GetSP();
Expand Down
2 changes: 1 addition & 1 deletion lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ lldb::ChildCacheState GenericBitsetFrontEnd::Update() {
size_t size = 0;

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

m_elements.assign(size, ValueObjectSP());
m_first =
Expand Down
2 changes: 1 addition & 1 deletion lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd::Update() {
} else if (auto arg =
m_backend.GetCompilerType().GetIntegralTemplateArgument(1)) {

m_num_elements = arg->value.getLimitedValue();
m_num_elements = arg->value.GetAPSInt().getLimitedValue();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since Scalar default initializes both float and integer members, i removed the isInt call from prior revisions here

}
}

Expand Down
57 changes: 41 additions & 16 deletions lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1973,6 +1973,33 @@ class DWARFASTParserClang::DelayedAddObjCClassProperty {
ClangASTMetadata m_metadata;
};

static std::optional<clang::APValue> MakeAPValue(const clang::ASTContext &ast,
CompilerType clang_type,
uint64_t value) {
std::optional<uint64_t> bit_width = clang_type.GetBitSize(nullptr);
if (!bit_width)
return std::nullopt;

bool is_signed = false;
const bool is_integral = clang_type.IsIntegerOrEnumerationType(is_signed);

llvm::APSInt apint(*bit_width, !is_signed);
apint = value;

if (is_integral)
return clang::APValue(apint);

uint32_t count;
bool is_complex;
// FIXME: we currently support a limited set of floating point types.
// E.g., 16-bit floats are not supported.
if (!clang_type.IsFloatingPointType(count, is_complex))
return std::nullopt;

return clang::APValue(llvm::APFloat(
ast.getFloatTypeSemantics(ClangUtil::GetQualType(clang_type)), apint));
}

bool DWARFASTParserClang::ParseTemplateDIE(
const DWARFDIE &die,
TypeSystemClang::TemplateParameterInfos &template_param_infos) {
Expand Down Expand Up @@ -2050,28 +2077,26 @@ bool DWARFASTParserClang::ParseTemplateDIE(
clang_type = m_ast.GetBasicType(eBasicTypeVoid);

if (!is_template_template_argument) {
bool is_signed = false;
// 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.InsertArg(
name, clang::TemplateArgument(ast, llvm::APSInt(apint, !is_signed),
ClangUtil::GetQualType(clang_type),
is_default_template_arg));
} else {
template_param_infos.InsertArg(
name, clang::TemplateArgument(ClangUtil::GetQualType(clang_type),
/*isNullPtr*/ false,
is_default_template_arg));
if (auto value = MakeAPValue(ast, clang_type, uval64)) {
template_param_infos.InsertArg(
name, clang::TemplateArgument(
ast, ClangUtil::GetQualType(clang_type),
std::move(*value), is_default_template_arg));
return true;
}
}

// We get here if this is a type-template parameter or we couldn't create
// a non-type template parameter.
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.InsertArg(
Expand Down
56 changes: 42 additions & 14 deletions lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1311,10 +1311,18 @@ CompilerType TypeSystemClang::CreateRecordType(
}

namespace {
/// Returns true iff the given TemplateArgument should be represented as an
/// NonTypeTemplateParmDecl in the AST.
bool IsValueParam(const clang::TemplateArgument &argument) {
return argument.getKind() == TemplateArgument::Integral;
/// Returns the type of the template argument iff the given TemplateArgument
/// should be represented as an NonTypeTemplateParmDecl in the AST. Returns
/// a null QualType otherwise.
QualType GetValueParamType(const clang::TemplateArgument &argument) {
switch (argument.getKind()) {
case TemplateArgument::Integral:
return argument.getIntegralType();
case TemplateArgument::StructuralValue:
return argument.getStructuralValueType();
default:
return {};
}
}

void AddAccessSpecifierDecl(clang::CXXRecordDecl *cxx_record_decl,
Expand Down Expand Up @@ -1361,8 +1369,8 @@ static TemplateParameterList *CreateTemplateParameterList(
if (name && name[0])
identifier_info = &ast.Idents.get(name);
TemplateArgument const &targ = args[i];
if (IsValueParam(targ)) {
QualType template_param_type = targ.getIntegralType();
QualType template_param_type = GetValueParamType(targ);
if (!template_param_type.isNull()) {
template_param_decls.push_back(NonTypeTemplateParmDecl::Create(
ast, decl_context, SourceLocation(), SourceLocation(), depth, i,
identifier_info, template_param_type, parameter_pack,
Expand All @@ -1380,10 +1388,11 @@ static TemplateParameterList *CreateTemplateParameterList(
identifier_info = &ast.Idents.get(template_param_infos.GetPackName());
const bool parameter_pack_true = true;

if (!template_param_infos.GetParameterPack().IsEmpty() &&
IsValueParam(template_param_infos.GetParameterPack().Front())) {
QualType template_param_type =
template_param_infos.GetParameterPack().Front().getIntegralType();
QualType template_param_type =
!template_param_infos.GetParameterPack().IsEmpty()
? GetValueParamType(template_param_infos.GetParameterPack().Front())
: QualType();
if (!template_param_type.isNull()) {
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 @@ -1458,10 +1467,12 @@ static bool TemplateParameterAllowsValue(NamedDecl *param,
} else if (auto *type_param =
llvm::dyn_cast<NonTypeTemplateParmDecl>(param)) {
// Compare the argument kind, i.e. ensure that <typename> != <int>.
if (!IsValueParam(value))
QualType value_param_type = GetValueParamType(value);
if (value_param_type.isNull())
return false;

// Compare the integral type, i.e. ensure that <int> != <char>.
if (type_param->getType() != value.getIntegralType())
if (type_param->getType() != value_param_type)
return false;
} else {
// There is no way to create other parameter decls at the moment, so we
Expand Down Expand Up @@ -7351,10 +7362,27 @@ TypeSystemClang::GetIntegralTemplateArgument(lldb::opaque_compiler_type_t type,
return std::nullopt;

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

return {{arg->getAsIntegral(), GetType(arg->getIntegralType())}};
switch (arg->getKind()) {
case clang::TemplateArgument::Integral:
return {{arg->getAsIntegral(), GetType(arg->getIntegralType())}};
case clang::TemplateArgument::StructuralValue: {
clang::APValue value = arg->getAsStructuralValue();
CompilerType type = GetType(arg->getStructuralValueType());

if (value.isFloat())
return {{value.getFloat(), type}};

if (value.isInt())
return {{value.getInt(), type}};

return std::nullopt;
}
default:
return std::nullopt;
}
}

CompilerType TypeSystemClang::GetTypeForFormatters(void *type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,44 @@ def test(self):
self.assertEqual(template_param_value.GetTypeName(), "char")
self.assertEqual(chr(template_param_value.GetValueAsSigned()), "v")

# FIXME: type should be Foo<float, 2.0f>
# FIXME: double/float NTTP parameter values currently not supported.
value = self.expect_expr("temp4", result_type="Foo<float, 1073741824>")
value = self.expect_expr("temp4", result_type="Foo<float, 2.000000e+00>")
template_param_value = value.GetType().GetTemplateArgumentValue(target, 1)
self.assertEqual(template_param_value.GetTypeName(), "float")
# FIXME: this should return a float
self.assertEqual(template_param_value.GetValueAsSigned(), 2)

value = self.expect_expr("temp5", result_type="Foo<double, -2.505000e+02>")
template_param_value = value.GetType().GetTemplateArgumentValue(target, 1)
self.assertEqual(template_param_value.GetTypeName(), "double")
# FIXME: this should return a float
self.assertEqual(template_param_value.GetValueAsSigned(), -250)

# FIXME: type should be Foo<int *, &temp1.member>
value = self.expect_expr("temp6", result_type="Foo<int *, int *>")
self.assertFalse(value.GetType().GetTemplateArgumentValue(target, 1))

# FIXME: support wider range of floating point types
value = self.expect_expr("temp7", result_type="Foo<__fp16, __fp16>")
self.assertFalse(value.GetType().GetTemplateArgumentValue(target, 1))

value = self.expect_expr("temp8", result_type="Foo<__fp16, __fp16>")
self.assertFalse(value.GetType().GetTemplateArgumentValue(target, 1))

value = self.expect_expr("temp9", result_type="Bar<double, 1.200000e+00>")
template_param_value = value.GetType().GetTemplateArgumentValue(target, 1)
self.assertEqual(template_param_value.GetTypeName(), "double")
# FIXME: this should return a float
self.assertEqual(template_param_value.GetValueAsSigned(), 1)

value = self.expect_expr(
"temp10", result_type="Bar<float, 1.000000e+00, 2.000000e+00>"
)
template_param_value = value.GetType().GetTemplateArgumentValue(target, 1)
self.assertEqual(template_param_value.GetTypeName(), "float")
# FIXME: this should return a float
self.assertEqual(template_param_value.GetValueAsSigned(), 1)

template_param_value = value.GetType().GetTemplateArgumentValue(target, 2)
self.assertEqual(template_param_value.GetTypeName(), "float")
# FIXME: this should return a float
self.assertEqual(template_param_value.GetValueAsSigned(), 2)
8 changes: 8 additions & 0 deletions lldb/test/API/lang/cpp/template-arguments/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,13 @@ template <typename T, T value> struct Foo {};
Foo<short, -2> temp2;
Foo<char, 'v'> temp3;
Foo<float, 2.0f> temp4;
Foo<double, -250.5> temp5;
Foo<int *, &temp1.member> temp6;
Foo<_Float16, _Float16(1.0)> temp7;
Foo<__bf16, __bf16(1.0)> temp8;

template <typename T, T... values> struct Bar {};
Bar<double, 1.2> temp9;
Bar<float, 1.0f, 2.0f> temp10;

int main() {}
42 changes: 40 additions & 2 deletions lldb/unittests/Symbol/TestTypeSystemClang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,17 @@ TEST_F(TestTypeSystemClang, TemplateArguments) {
infos.InsertArg("I", TemplateArgument(m_ast->getASTContext(), arg,
m_ast->getASTContext().IntTy));

// template<typename T, int I> struct foo;
llvm::APFloat float_arg(5.5f);
infos.InsertArg("F", TemplateArgument(m_ast->getASTContext(),
m_ast->getASTContext().FloatTy,
clang::APValue(float_arg)));

llvm::APFloat double_arg(-15.2);
infos.InsertArg("D", TemplateArgument(m_ast->getASTContext(),
m_ast->getASTContext().DoubleTy,
clang::APValue(double_arg)));

// template<typename T, int I, float F, double D> struct foo;
ClassTemplateDecl *decl = m_ast->CreateClassTemplateDecl(
m_ast->GetTranslationUnitDecl(), OptionalClangModuleID(), eAccessPublic,
"foo", llvm::to_underlying(clang::TagTypeKind::Struct), infos);
Expand Down Expand Up @@ -555,6 +565,10 @@ TEST_F(TestTypeSystemClang, TemplateArguments) {

CompilerType int_type(m_ast->weak_from_this(),
m_ast->getASTContext().IntTy.getAsOpaquePtr());
CompilerType float_type(m_ast->weak_from_this(),
m_ast->getASTContext().FloatTy.getAsOpaquePtr());
CompilerType double_type(m_ast->weak_from_this(),
m_ast->getASTContext().DoubleTy.getAsOpaquePtr());
for (CompilerType t : {type, typedef_type, auto_type}) {
SCOPED_TRACE(t.GetTypeName().AsCString());

Expand All @@ -577,8 +591,32 @@ TEST_F(TestTypeSystemClang, TemplateArguments) {
auto result = m_ast->GetIntegralTemplateArgument(t.GetOpaqueQualType(), 1,
expand_pack);
ASSERT_NE(std::nullopt, result);
EXPECT_EQ(arg, result->value);
EXPECT_EQ(arg, result->value.GetAPSInt());
EXPECT_EQ(int_type, result->type);

EXPECT_EQ(
m_ast->GetTemplateArgumentKind(t.GetOpaqueQualType(), 2, expand_pack),
eTemplateArgumentKindStructuralValue);
EXPECT_EQ(
m_ast->GetTypeTemplateArgument(t.GetOpaqueQualType(), 2, expand_pack),
CompilerType());
auto float_result = m_ast->GetIntegralTemplateArgument(
t.GetOpaqueQualType(), 2, expand_pack);
ASSERT_NE(std::nullopt, float_result);
EXPECT_EQ(float_arg, float_result->value.GetAPFloat());
EXPECT_EQ(float_type, float_result->type);

EXPECT_EQ(
m_ast->GetTemplateArgumentKind(t.GetOpaqueQualType(), 3, expand_pack),
eTemplateArgumentKindStructuralValue);
EXPECT_EQ(
m_ast->GetTypeTemplateArgument(t.GetOpaqueQualType(), 3, expand_pack),
CompilerType());
auto double_result = m_ast->GetIntegralTemplateArgument(
t.GetOpaqueQualType(), 3, expand_pack);
ASSERT_NE(std::nullopt, double_result);
EXPECT_EQ(double_arg, double_result->value.GetAPFloat());
EXPECT_EQ(double_type, double_result->type);
}
}

Expand Down