[Lldb-commits] [lldb] [lldb][TypeSystemClang] Add support for floating point template argument constants (PR #127206)

Michael Buch via lldb-commits lldb-commits at lists.llvm.org
Fri Feb 14 04:14:58 PST 2025


https://github.com/Michael137 created https://github.com/llvm/llvm-project/pull/127206

This patch adds support for the recently added `clang::TemplateArgument::ArgKind::StructuralValue` (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`.

>From 759f0569807d00a059a78aeb3bd1eddeffcbdf36 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Fri, 14 Feb 2025 11:43:20 +0000
Subject: [PATCH] Init

---
 lldb/include/lldb/Symbol/CompilerType.h       |  3 +-
 lldb/source/API/SBType.cpp                    |  8 +++-
 .../Language/CPlusPlus/GenericBitset.cpp      |  2 +-
 .../Plugins/Language/CPlusPlus/LibCxxSpan.cpp |  2 +-
 .../SymbolFile/DWARF/DWARFASTParserClang.cpp  | 33 ++++++++++---
 .../TypeSystem/Clang/TypeSystemClang.cpp      | 47 +++++++++++++------
 .../TestCppTemplateArguments.py               | 31 ++++++++++--
 .../API/lang/cpp/template-arguments/main.cpp  |  6 +++
 lldb/unittests/Symbol/TestTypeSystemClang.cpp | 42 ++++++++++++++++-
 9 files changed, 143 insertions(+), 31 deletions(-)

diff --git a/lldb/include/lldb/Symbol/CompilerType.h b/lldb/include/lldb/Symbol/CompilerType.h
index 096a8f1ab68e8..f7e3e552f3e45 100644
--- a/lldb/include/lldb/Symbol/CompilerType.h
+++ b/lldb/include/lldb/Symbol/CompilerType.h
@@ -15,6 +15,7 @@
 #include <vector>
 
 #include "lldb/lldb-private.h"
+#include "clang/AST/APValue.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;
+  clang::APValue value;
   CompilerType type;
 };
 
diff --git a/lldb/source/API/SBType.cpp b/lldb/source/API/SBType.cpp
index 6401d32c85795..72f590947dff6 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,7 +709,12 @@ lldb::SBValue SBType::GetTemplateArgumentValue(lldb::SBTarget target,
   if (!arg)
     return {};
 
-  Scalar value{arg->value};
+  Scalar value;
+  if (arg->value.isFloat())
+    value = arg->value.getFloat();
+  else
+    value = arg->value.getInt();
+
   DataExtractor data;
   value.GetData(data);
 
diff --git a/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp b/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp
index 33955dccb6ccc..99ff975825c71 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp
@@ -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.getInt().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 15040295efe6d..687ef1739ad11 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp
@@ -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.getInt().getLimitedValue();
     }
   }
 
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
index ec0004c70c6da..70af283ab7443 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -1973,6 +1973,27 @@ class DWARFASTParserClang::DelayedAddObjCClassProperty {
   ClangASTMetadata m_metadata;
 };
 
+static clang::APValue MakeAPValue(CompilerType clang_type, uint64_t bit_width,
+                                  uint64_t value) {
+  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;
+  assert(clang_type.IsFloatingPointType(count, is_complex));
+
+  if (bit_width == 32)
+    return clang::APValue(llvm::APFloat(apint.bitsToFloat()));
+
+  return clang::APValue(llvm::APFloat(apint.bitsToDouble()));
+}
+
 bool DWARFASTParserClang::ParseTemplateDIE(
     const DWARFDIE &die,
     TypeSystemClang::TemplateParameterInfos &template_param_infos) {
@@ -2050,9 +2071,6 @@ 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;
@@ -2061,11 +2079,12 @@ bool DWARFASTParserClang::ParseTemplateDIE(
         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));
+            name,
+            clang::TemplateArgument(ast, ClangUtil::GetQualType(clang_type),
+                                    MakeAPValue(clang_type, *size, uval64),
+                                    is_default_template_arg));
       } else {
         template_param_infos.InsertArg(
             name, clang::TemplateArgument(ClangUtil::GetQualType(clang_type),
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index bcb63f719de10..567819fc2572c 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,9 @@ 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))
-      return false;
+    QualType value_param_type = GetValueParamType(value);
     // 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 +7359,19 @@ 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 {{clang::APValue(arg->getAsIntegral()),
+             GetType(arg->getIntegralType())}};
+  case clang::TemplateArgument::StructuralValue:
+    return {
+        {arg->getAsStructuralValue(), GetType(arg->getStructuralValueType())}};
+  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..fbfc7d1ca9868 100644
--- a/lldb/test/API/lang/cpp/template-arguments/TestCppTemplateArguments.py
+++ b/lldb/test/API/lang/cpp/template-arguments/TestCppTemplateArguments.py
@@ -62,10 +62,35 @@ 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))
+
+        value = self.expect_expr("temp7", 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("temp8", 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..e1add12170b54 100644
--- a/lldb/test/API/lang/cpp/template-arguments/main.cpp
+++ b/lldb/test/API/lang/cpp/template-arguments/main.cpp
@@ -9,5 +9,11 @@ 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;
+
+template <typename T, T... values> struct Bar {};
+Bar<double, 1.2> temp7;
+Bar<float, 1.0f, 2.0f> temp8;
 
 int main() {}
diff --git a/lldb/unittests/Symbol/TestTypeSystemClang.cpp b/lldb/unittests/Symbol/TestTypeSystemClang.cpp
index 23374062127e0..446d1976481db 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.getInt());
     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.getFloat());
+    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.getFloat());
+    EXPECT_EQ(double_type, double_result->type);
   }
 }
 



More information about the lldb-commits mailing list