Author: Michael Buch Date: 2025-02-17T22:03:53Z New Revision: a377cdd23db180b881f54fc7f88bf3aa85de21cc
URL: https://github.com/llvm/llvm-project/commit/a377cdd23db180b881f54fc7f88bf3aa85de21cc DIFF: https://github.com/llvm/llvm-project/commit/a377cdd23db180b881f54fc7f88bf3aa85de21cc.diff LOG: [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 https://github.com/llvm/llvm-project/pull/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`. Added: Modified: lldb/include/lldb/Symbol/CompilerType.h lldb/source/API/SBType.cpp lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp lldb/test/API/lang/cpp/template-arguments/TestCppTemplateArguments.py lldb/test/API/lang/cpp/template-arguments/main.cpp lldb/unittests/Symbol/TestTypeSystemClang.cpp Removed: ################################################################################ diff --git a/lldb/include/lldb/Symbol/CompilerType.h b/lldb/include/lldb/Symbol/CompilerType.h index 096a8f1ab68e8..fe4fcbccee370 100644 --- a/lldb/include/lldb/Symbol/CompilerType.h +++ b/lldb/include/lldb/Symbol/CompilerType.h @@ -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" @@ -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; }; diff --git a/lldb/source/API/SBType.cpp b/lldb/source/API/SBType.cpp index 6401d32c85795..9eb1f0c75ea05 100644 --- a/lldb/source/API/SBType.cpp +++ b/lldb/source/API/SBType.cpp @@ -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); @@ -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(); diff --git a/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp b/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp index f83f81fbdd1e7..934b456884ac0 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp @@ -90,7 +90,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 = diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp index ab3a5cf954ec7..21ee83041c065 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp @@ -117,7 +117,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(); } } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index ec0004c70c6da..2d4d22559963f 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -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) { @@ -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( diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index bcb63f719de10..1e0c7f0514941 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -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, @@ -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, @@ -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, @@ -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 @@ -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) { diff --git a/lldb/test/API/lang/cpp/template-arguments/TestCppTemplateArguments.py b/lldb/test/API/lang/cpp/template-arguments/TestCppTemplateArguments.py index db5388b8bcc6d..eac7b5ef1099a 100644 --- a/lldb/test/API/lang/cpp/template-arguments/TestCppTemplateArguments.py +++ b/lldb/test/API/lang/cpp/template-arguments/TestCppTemplateArguments.py @@ -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) diff --git a/lldb/test/API/lang/cpp/template-arguments/main.cpp b/lldb/test/API/lang/cpp/template-arguments/main.cpp index 0c0eb97cbc858..c08679aa0e166 100644 --- a/lldb/test/API/lang/cpp/template-arguments/main.cpp +++ b/lldb/test/API/lang/cpp/template-arguments/main.cpp @@ -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() {} diff --git a/lldb/unittests/Symbol/TestTypeSystemClang.cpp b/lldb/unittests/Symbol/TestTypeSystemClang.cpp index 23374062127e0..a9b0c87c4fbce 100644 --- a/lldb/unittests/Symbol/TestTypeSystemClang.cpp +++ b/lldb/unittests/Symbol/TestTypeSystemClang.cpp @@ -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); @@ -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()); @@ -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); } } _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits