[Lldb-commits] [lldb] 7d297de - Reland "[lldb] Add support for using integral const static data members in the expression evaluator"

Andy Yankovsky via lldb-commits lldb-commits at lists.llvm.org
Fri Jul 15 03:52:48 PDT 2022


Author: Andy Yankovsky
Date: 2022-07-15T10:52:35Z
New Revision: 7d297de95117e783bbfaecbba1f72fc55de05a30

URL: https://github.com/llvm/llvm-project/commit/7d297de95117e783bbfaecbba1f72fc55de05a30
DIFF: https://github.com/llvm/llvm-project/commit/7d297de95117e783bbfaecbba1f72fc55de05a30.diff

LOG: Reland "[lldb] Add support for using integral const static data members in the expression evaluator"

Reland 486787210d which broke tests on Arm and Windows.

* Windows -- on Windows const static data members with no out-of-class
  definition do have valid addresses, in constract to other platforms
  (Linux, macos) where they don't. Adjusted the test to expect success
  on Windows and failure on other platforms.

* Arm -- `int128` is not available on 32-bit ARM, so disable the test
  for this architecture.

Added: 
    lldb/test/API/lang/cpp/const_static_integral_member/Makefile
    lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py
    lldb/test/API/lang/cpp/const_static_integral_member/main.cpp
    lldb/test/API/lang/cpp/const_static_integral_member_int128/Makefile
    lldb/test/API/lang/cpp/const_static_integral_member_int128/TestConstStaticIntegralMemberInt128.py
    lldb/test/API/lang/cpp/const_static_integral_member_int128/main.cpp

Modified: 
    lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp
    lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
    lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
    lldb/unittests/SymbolFile/DWARF/CMakeLists.txt
    lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp
index 7a1ac78705474..799ae29e2841c 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp
@@ -197,6 +197,27 @@ bool ASTResultSynthesizer::SynthesizeObjCMethodResult(
   return ret;
 }
 
+/// Returns true if LLDB can take the address of the given lvalue for the sake
+/// of capturing the expression result. Returns false if LLDB should instead
+/// store the expression result in a result variable.
+static bool CanTakeAddressOfLValue(const Expr *lvalue_expr) {
+  assert(lvalue_expr->getValueKind() == VK_LValue &&
+         "lvalue_expr not a lvalue");
+
+  QualType qt = lvalue_expr->getType();
+  // If the lvalue has const-qualified non-volatile integral or enum type, then
+  // the underlying value might come from a const static data member as
+  // described in C++11 [class.static.data]p3. If that's the case, then the
+  // value might not have an address if the user didn't also define the member
+  // in a namespace scope. Taking the address would cause that LLDB later fails
+  // to link the expression, so those lvalues should be stored in a result
+  // variable.
+  if (qt->isIntegralOrEnumerationType() && qt.isConstQualified() &&
+      !qt.isVolatileQualified())
+    return false;
+  return true;
+}
+
 bool ASTResultSynthesizer::SynthesizeBodyResult(CompoundStmt *Body,
                                                 DeclContext *DC) {
   Log *log = GetLog(LLDBLog::Expressions);
@@ -265,6 +286,10 @@ bool ASTResultSynthesizer::SynthesizeBodyResult(CompoundStmt *Body,
   //   - During dematerialization, $0 is marked up as a load address with value
   //     equal to the contents of the structure entry.
   //
+  //   - Note: if we cannot take an address of the resulting Lvalue (e.g. it's
+  //     a static const member without an out-of-class definition), then we
+  //     follow the Rvalue route.
+  //
   // For Rvalues
   //
   //   - In AST result synthesis the expression E is transformed into an
@@ -304,7 +329,7 @@ bool ASTResultSynthesizer::SynthesizeBodyResult(CompoundStmt *Body,
 
   clang::VarDecl *result_decl = nullptr;
 
-  if (is_lvalue) {
+  if (is_lvalue && CanTakeAddressOfLValue(last_expr)) {
     IdentifierInfo *result_ptr_id;
 
     if (expr_type->isFunctionType())

diff  --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
index 4b9354371bda3..10dc8d1fb7c39 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -2390,6 +2390,7 @@ struct MemberAttributes {
   uint64_t data_bit_offset = UINT64_MAX;
   AccessType accessibility = eAccessNone;
   llvm::Optional<uint64_t> byte_size;
+  llvm::Optional<DWARFFormValue> const_value_form;
   DWARFFormValue encoding_form;
   /// Indicates the byte offset of the word from the base address of the
   /// structure.
@@ -2436,6 +2437,9 @@ MemberAttributes::MemberAttributes(const DWARFDIE &die,
       case DW_AT_byte_size:
         byte_size = form_value.Unsigned();
         break;
+      case DW_AT_const_value:
+        const_value_form = form_value;
+        break;
       case DW_AT_data_bit_offset:
         data_bit_offset = form_value.Unsigned();
         break;
@@ -2587,12 +2591,65 @@ void DWARFASTParserClang::ParseObjCProperty(
       propAttrs.prop_getter_name, propAttrs.prop_attributes, &metadata));
 }
 
+llvm::Expected<llvm::APInt> DWARFASTParserClang::ExtractIntFromFormValue(
+    const CompilerType &int_type, const DWARFFormValue &form_value) const {
+  clang::QualType qt = ClangUtil::GetQualType(int_type);
+  assert(qt->isIntegralOrEnumerationType());
+  TypeSystemClang &ts = *llvm::cast<TypeSystemClang>(int_type.GetTypeSystem());
+  clang::ASTContext &ast = ts.getASTContext();
+
+  const unsigned type_bits = ast.getIntWidth(qt);
+  const bool is_unsigned = qt->isUnsignedIntegerType();
+
+  // The maximum int size supported at the moment by this function. Limited
+  // by the uint64_t return type of DWARFFormValue::Signed/Unsigned.
+  constexpr std::size_t max_bit_size = 64;
+
+  // For values bigger than 64 bit (e.g. __int128_t values),
+  // DWARFFormValue's Signed/Unsigned functions will return wrong results so
+  // emit an error for now.
+  if (type_bits > max_bit_size) {
+    auto msg = llvm::formatv("Can only parse integers with up to {0} bits, but "
+                             "given integer has {1} bits.",
+                             max_bit_size, type_bits);
+    return llvm::createStringError(llvm::inconvertibleErrorCode(), msg.str());
+  }
+
+  // Construct an APInt with the maximum bit size and the given integer.
+  llvm::APInt result(max_bit_size, form_value.Unsigned(), !is_unsigned);
+
+  // Calculate how many bits are required to represent the input value.
+  // For unsigned types, take the number of active bits in the APInt.
+  // For signed types, ask APInt how many bits are required to represent the
+  // signed integer.
+  const unsigned required_bits =
+      is_unsigned ? result.getActiveBits() : result.getMinSignedBits();
+
+  // If the input value doesn't fit into the integer type, return an error.
+  if (required_bits > type_bits) {
+    std::string value_as_str = is_unsigned
+                                   ? std::to_string(form_value.Unsigned())
+                                   : std::to_string(form_value.Signed());
+    auto msg = llvm::formatv("Can't store {0} value {1} in integer with {2} "
+                             "bits.",
+                             (is_unsigned ? "unsigned" : "signed"),
+                             value_as_str, type_bits);
+    return llvm::createStringError(llvm::inconvertibleErrorCode(), msg.str());
+  }
+
+  // Trim the result to the bit width our the int type.
+  if (result.getBitWidth() > type_bits)
+    result = result.trunc(type_bits);
+  return result;
+}
+
 void DWARFASTParserClang::ParseSingleMember(
     const DWARFDIE &die, const DWARFDIE &parent_die,
     const lldb_private::CompilerType &class_clang_type,
     lldb::AccessType default_accessibility,
     lldb_private::ClangASTImporter::LayoutInfo &layout_info,
     FieldInfo &last_field_info) {
+  Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups);
   // This function can only parse DW_TAG_member.
   assert(die.Tag() == DW_TAG_member);
 
@@ -2623,9 +2680,27 @@ void DWARFASTParserClang::ParseSingleMember(
     if (var_type) {
       if (attrs.accessibility == eAccessNone)
         attrs.accessibility = eAccessPublic;
-      TypeSystemClang::AddVariableToRecordType(
-          class_clang_type, attrs.name, var_type->GetForwardCompilerType(),
-          attrs.accessibility);
+      CompilerType ct = var_type->GetForwardCompilerType();
+      clang::VarDecl *v = TypeSystemClang::AddVariableToRecordType(
+          class_clang_type, attrs.name, ct, attrs.accessibility);
+      if (!v) {
+        LLDB_LOG(log, "Failed to add variable to the record type");
+        return;
+      }
+
+      bool unused;
+      // TODO: Support float/double static members as well.
+      if (!attrs.const_value_form || !ct.IsIntegerOrEnumerationType(unused))
+        return;
+      llvm::Expected<llvm::APInt> const_value_or_err =
+          ExtractIntFromFormValue(ct, *attrs.const_value_form);
+      if (!const_value_or_err) {
+        LLDB_LOG_ERROR(log, const_value_or_err.takeError(),
+                       "Failed to add const value to variable {1}: {0}",
+                       v->getQualifiedNameAsString());
+        return;
+      }
+      TypeSystemClang::SetIntegerInitializerForVariable(v, *const_value_or_err);
     }
     return;
   }

diff  --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
index f97c0c470ab07..733ffa230f1e8 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
@@ -68,6 +68,22 @@ class DWARFASTParserClang : public DWARFASTParser {
 
   lldb_private::ClangASTImporter &GetClangASTImporter();
 
+  /// Extracts an value for a given Clang integer type from a DWARFFormValue.
+  ///
+  /// \param int_type The Clang type that defines the bit size and signedness
+  ///                 of the integer that should be extracted. Has to be either
+  ///                 an integer type or an enum type. For enum types the
+  ///                 underlying integer type will be considered as the
+  ///                 expected integer type that should be extracted.
+  /// \param form_value The DWARFFormValue that contains the integer value.
+  /// \return An APInt containing the same integer value as the given
+  ///         DWARFFormValue with the bit width of the given integer type.
+  ///         Returns an error if the value in the DWARFFormValue does not fit
+  ///         into the given integer type or the integer type isn't supported.
+  llvm::Expected<llvm::APInt>
+  ExtractIntFromFormValue(const lldb_private::CompilerType &int_type,
+                          const DWARFFormValue &form_value) const;
+
 protected:
   /// Protected typedefs and members.
   /// @{

diff  --git a/lldb/test/API/lang/cpp/const_static_integral_member/Makefile b/lldb/test/API/lang/cpp/const_static_integral_member/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/lang/cpp/const_static_integral_member/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules

diff  --git a/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py b/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py
new file mode 100644
index 0000000000000..c2bf1fe2a7175
--- /dev/null
+++ b/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py
@@ -0,0 +1,97 @@
+"""
+Tests const static data members as specified by C++11 [class.static.data]p3.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+class TestCase(TestBase):
+
+    def test(self):
+        self.build()
+        lldbutil.run_to_source_breakpoint(self, "// break here",
+                                          lldb.SBFileSpec("main.cpp"))
+
+        # Test using a simple const static integer member.
+        self.expect_expr("A::int_val", result_value="1")
+
+        # Try accessing the int member via some expressions that still produce
+        # an lvalue.
+        self.expect_expr("a.int_val", result_value="1")
+        self.expect_expr("(A::int_val)", result_value="1")
+        self.expect_expr("+A::int_val", result_value="1")
+        self.expect_expr("1,A::int_val", result_value="1")
+        self.expect_expr("true ? A::int_val : A::int_val", result_value="1")
+
+        # Test a simple integer member that was also defined in a namespace
+        # scope and has an address.
+        self.expect_expr("A::int_val_with_address", result_value="2")
+
+        # Test a bool member.
+        self.expect_expr("A::bool_val", result_value="true")
+
+        # Test that minimum and maximum values for each data type are right.
+        self.expect_expr("A::char_max == char_max", result_value="true")
+        self.expect_expr("A::uchar_max == uchar_max", result_value="true")
+        self.expect_expr("A::int_max == int_max", result_value="true")
+        self.expect_expr("A::uint_max == uint_max", result_value="true")
+        self.expect_expr("A::long_max == long_max", result_value="true")
+        self.expect_expr("A::ulong_max == ulong_max", result_value="true")
+        self.expect_expr("A::longlong_max == longlong_max", result_value="true")
+        self.expect_expr("A::ulonglong_max == ulonglong_max", result_value="true")
+
+        self.expect_expr("A::char_min == char_min", result_value="true")
+        self.expect_expr("A::uchar_min == uchar_min", result_value="true")
+        self.expect_expr("A::int_min == int_min", result_value="true")
+        self.expect_expr("A::uint_min == uint_min", result_value="true")
+        self.expect_expr("A::long_min == long_min", result_value="true")
+        self.expect_expr("A::ulong_min == ulong_min", result_value="true")
+        self.expect_expr("A::longlong_min == longlong_min", result_value="true")
+        self.expect_expr("A::ulonglong_min == ulonglong_min", result_value="true")
+
+        # Test an unscoped enum.
+        self.expect_expr("A::enum_val", result_value="enum_case2")
+        # Test an unscoped enum with an invalid enum case.
+        self.expect_expr("A::invalid_enum_val", result_value="enum_case1 | enum_case2 | 0x4")
+
+        # Test a scoped enum.
+        self.expect_expr("A::scoped_enum_val", result_value="scoped_enum_case2")
+        # Test an scoped enum with an invalid enum case.
+        self.expect_expr("A::invalid_scoped_enum_val", result_value="scoped_enum_case1 | 0x4")
+
+        # Test an enum with fixed underlying type.
+        self.expect_expr("A::scoped_char_enum_val", result_value="case2")
+        self.expect_expr("A::scoped_ll_enum_val_neg", result_value="case0")
+        self.expect_expr("A::scoped_ll_enum_val", result_value="case2")
+
+        # Test taking address.
+        if lldbplatformutil.getPlatform() == "windows":
+            # On Windows data members without the out-of-class definitions still have
+            # valid adresses and the following expression works fine.
+            self.expect_expr("const int *i = &A::int_val; *i", result_value="1")
+        else:
+            # On other platforms (Linux, macos) data members without the out-of-class
+            # definitions don't have valid addresses and the following code produces
+            # a linker error.
+            self.expect("expr const int *i = &A::int_val; *i", error=True,
+                        substrs=["Couldn't lookup symbols:"])
+
+        # This should work on all platforms.
+        self.expect_expr("const int *i = &A::int_val_with_address; *i",
+                         result_value="2")
+
+    # dsymutil strips the debug info for classes that only have const static
+    # data members without a definition namespace scope.
+    @expectedFailureAll(debug_info=["dsym"])
+    def test_class_with_only_const_static(self):
+        self.build()
+        lldbutil.run_to_source_breakpoint(self, "// break here", lldb.SBFileSpec("main.cpp"))
+
+        self.expect_expr("ClassWithOnlyConstStatic::member", result_value="3")
+
+        # Test `constexpr static`.
+        self.expect_expr("ClassWithConstexprs::member", result_value="2")
+        self.expect_expr("ClassWithConstexprs::enum_val", result_value="enum_case2")
+        self.expect_expr("ClassWithConstexprs::scoped_enum_val", result_value="scoped_enum_case2")

diff  --git a/lldb/test/API/lang/cpp/const_static_integral_member/main.cpp b/lldb/test/API/lang/cpp/const_static_integral_member/main.cpp
new file mode 100644
index 0000000000000..9762424f2345c
--- /dev/null
+++ b/lldb/test/API/lang/cpp/const_static_integral_member/main.cpp
@@ -0,0 +1,102 @@
+#include <limits>
+
+enum Enum {
+  enum_case1 = 1,
+  enum_case2 = 2,
+};
+
+enum class ScopedEnum {
+  scoped_enum_case1 = 1,
+  scoped_enum_case2 = 2,
+};
+
+enum class ScopedCharEnum : char {
+  case1 = 1,
+  case2 = 2,
+};
+
+enum class ScopedLongLongEnum : long long {
+  case0 = std::numeric_limits<long long>::min(),
+  case1 = 1,
+  case2 = std::numeric_limits<long long>::max(),
+};
+
+struct A {
+  const static int int_val = 1;
+  const static int int_val_with_address = 2;
+  const static bool bool_val = true;
+
+  const static auto char_max = std::numeric_limits<signed char>::max();
+  const static auto uchar_max = std::numeric_limits<unsigned char>::max();
+  const static auto int_max = std::numeric_limits<int>::max();
+  const static auto uint_max = std::numeric_limits<unsigned>::max();
+  const static auto long_max = std::numeric_limits<long>::max();
+  const static auto ulong_max = std::numeric_limits<unsigned long>::max();
+  const static auto longlong_max = std::numeric_limits<long long>::max();
+  const static auto ulonglong_max =
+      std::numeric_limits<unsigned long long>::max();
+
+  const static auto char_min = std::numeric_limits<char>::min();
+  const static auto uchar_min = std::numeric_limits<unsigned char>::min();
+  const static auto int_min = std::numeric_limits<int>::min();
+  const static auto uint_min = std::numeric_limits<unsigned>::min();
+  const static auto long_min = std::numeric_limits<long>::min();
+  const static auto ulong_min = std::numeric_limits<unsigned long>::min();
+  const static auto longlong_min = std::numeric_limits<long long>::min();
+  const static auto ulonglong_min =
+      std::numeric_limits<unsigned long long>::min();
+
+  const static Enum enum_val = enum_case2;
+  const static Enum invalid_enum_val = static_cast<Enum>(enum_case2 + 5);
+  const static ScopedEnum scoped_enum_val = ScopedEnum::scoped_enum_case2;
+  const static ScopedEnum invalid_scoped_enum_val = static_cast<ScopedEnum>(5);
+  const static ScopedCharEnum scoped_char_enum_val = ScopedCharEnum::case2;
+  const static ScopedLongLongEnum scoped_ll_enum_val_neg =
+      ScopedLongLongEnum::case0;
+  const static ScopedLongLongEnum scoped_ll_enum_val =
+      ScopedLongLongEnum::case2;
+};
+
+const int A::int_val_with_address;
+
+struct ClassWithOnlyConstStatic {
+  const static int member = 3;
+};
+
+struct ClassWithConstexprs {
+  constexpr static int member = 2;
+  constexpr static Enum enum_val = enum_case2;
+  constexpr static ScopedEnum scoped_enum_val = ScopedEnum::scoped_enum_case2;
+} cwc;
+
+int main() {
+  A a;
+
+  auto char_max = A::char_max;
+  auto uchar_max = A::uchar_max;
+  auto int_max = A::int_max;
+  auto uint_max = A::uint_max;
+  auto long_max = A::long_max;
+  auto ulong_max = A::ulong_max;
+  auto longlong_max = A::longlong_max;
+  auto ulonglong_max = A::ulonglong_max;
+
+  auto char_min = A::char_min;
+  auto uchar_min = A::uchar_min;
+  auto int_min = A::int_min;
+  auto uint_min = A::uint_min;
+  auto long_min = A::long_min;
+  auto ulong_min = A::ulong_min;
+  auto longlong_min = A::longlong_min;
+  auto ulonglong_min = A::ulonglong_min;
+
+  int member_copy = ClassWithOnlyConstStatic::member;
+
+  Enum e = A::enum_val;
+  e = A::invalid_enum_val;
+  ScopedEnum se = A::scoped_enum_val;
+  se = A::invalid_scoped_enum_val;
+  ScopedCharEnum sce = A::scoped_char_enum_val;
+  ScopedLongLongEnum sle = A::scoped_ll_enum_val;
+  return 0; // break here
+}

diff  --git a/lldb/test/API/lang/cpp/const_static_integral_member_int128/Makefile b/lldb/test/API/lang/cpp/const_static_integral_member_int128/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/lang/cpp/const_static_integral_member_int128/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules

diff  --git a/lldb/test/API/lang/cpp/const_static_integral_member_int128/TestConstStaticIntegralMemberInt128.py b/lldb/test/API/lang/cpp/const_static_integral_member_int128/TestConstStaticIntegralMemberInt128.py
new file mode 100644
index 0000000000000..1d6907463893b
--- /dev/null
+++ b/lldb/test/API/lang/cpp/const_static_integral_member_int128/TestConstStaticIntegralMemberInt128.py
@@ -0,0 +1,31 @@
+"""
+Tests const static data members as specified by C++11 [class.static.data]p3
+with (u)int128_t types.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+class TestCase(TestBase):
+
+    # int128 is not available on 32-bit ARM.
+    @skipIf(archs=["arm"])
+    def test_int128(self):
+        self.build()
+        lldbutil.run_to_source_breakpoint(self, "// break here",
+                                          lldb.SBFileSpec("main.cpp"))
+
+        # Try to use the (u)int128_t data members which are not supported at
+        # the moment. Just verify that LLDB doesn't report an incorrect value
+        # for them and just treats them as normal variables (which will lead
+        # to linker errors as they are not defined anywhere).
+        self.expect("expr A::int128_max", error=True,
+                    substrs=["Couldn't lookup symbols:"])
+        self.expect("expr A::uint128_max", error=True,
+                    substrs=["Couldn't lookup symbols:"])
+        self.expect("expr A::int128_min", error=True,
+                    substrs=["Couldn't lookup symbols:"])
+        self.expect("expr A::uint128_min", error=True,
+                    substrs=["Couldn't lookup symbols:"])

diff  --git a/lldb/test/API/lang/cpp/const_static_integral_member_int128/main.cpp b/lldb/test/API/lang/cpp/const_static_integral_member_int128/main.cpp
new file mode 100644
index 0000000000000..bac996d5f66d5
--- /dev/null
+++ b/lldb/test/API/lang/cpp/const_static_integral_member_int128/main.cpp
@@ -0,0 +1,18 @@
+#include <limits>
+
+struct A {
+  const static auto uint128_max = std::numeric_limits<__uint128_t>::max();
+  const static auto uint128_min = std::numeric_limits<__uint128_t>::min();
+  const static auto int128_max = std::numeric_limits<__int128_t>::max();
+  const static auto int128_min = std::numeric_limits<__int128_t>::min();
+};
+
+int main() {
+  A a;
+
+  auto int128_max = A::int128_max;
+  auto uint128_max = A::uint128_max;
+  auto int128_min = A::int128_min;
+  auto uint128_min = A::uint128_min;
+  return 0; // break here
+}

diff  --git a/lldb/unittests/SymbolFile/DWARF/CMakeLists.txt b/lldb/unittests/SymbolFile/DWARF/CMakeLists.txt
index 16c38c4ab2190..6af866153a138 100644
--- a/lldb/unittests/SymbolFile/DWARF/CMakeLists.txt
+++ b/lldb/unittests/SymbolFile/DWARF/CMakeLists.txt
@@ -17,6 +17,7 @@ add_lldb_unittest(SymbolFileDWARFTests
     lldbPluginPlatformMacOSX
     lldbUtilityHelpers
     lldbSymbolHelpers
+    LLVMTestingSupport
   LINK_COMPONENTS
     Support
     DebugInfoPDB

diff  --git a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
index ed46db8e6be6c..3903aa75f1ce5 100644
--- a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
+++ b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
@@ -273,3 +273,128 @@ TEST_F(DWARFASTParserClangTests, TestCallingConventionParsing) {
   };
   ASSERT_EQ(found_function_types, expected_function_types);
 }
+
+struct ExtractIntFromFormValueTest : public testing::Test {
+  SubsystemRAII<FileSystem, HostInfo> subsystems;
+  TypeSystemClang ts;
+  DWARFASTParserClang parser;
+  ExtractIntFromFormValueTest()
+      : ts("dummy ASTContext", HostInfoBase::GetTargetTriple()), parser(ts) {}
+
+  /// Takes the given integer value, stores it in a DWARFFormValue and then
+  /// tries to extract the value back via
+  /// DWARFASTParserClang::ExtractIntFromFormValue.
+  /// Returns the string representation of the extracted value or the error
+  /// that was returned from ExtractIntFromFormValue.
+  llvm::Expected<std::string> Extract(clang::QualType qt, uint64_t value) {
+    DWARFFormValue form_value;
+    form_value.SetUnsigned(value);
+    llvm::Expected<llvm::APInt> result =
+        parser.ExtractIntFromFormValue(ts.GetType(qt), form_value);
+    if (!result)
+      return result.takeError();
+    llvm::SmallString<16> result_str;
+    result->toStringUnsigned(result_str);
+    return std::string(result_str.str());
+  }
+
+  /// Same as ExtractIntFromFormValueTest::Extract but takes a signed integer
+  /// and treats the result as a signed integer.
+  llvm::Expected<std::string> ExtractS(clang::QualType qt, int64_t value) {
+    DWARFFormValue form_value;
+    form_value.SetSigned(value);
+    llvm::Expected<llvm::APInt> result =
+        parser.ExtractIntFromFormValue(ts.GetType(qt), form_value);
+    if (!result)
+      return result.takeError();
+    llvm::SmallString<16> result_str;
+    result->toStringSigned(result_str);
+    return std::string(result_str.str());
+  }
+};
+
+TEST_F(ExtractIntFromFormValueTest, TestBool) {
+  using namespace llvm;
+  clang::ASTContext &ast = ts.getASTContext();
+
+  EXPECT_THAT_EXPECTED(Extract(ast.BoolTy, 0), HasValue("0"));
+  EXPECT_THAT_EXPECTED(Extract(ast.BoolTy, 1), HasValue("1"));
+  EXPECT_THAT_EXPECTED(Extract(ast.BoolTy, 2), Failed());
+  EXPECT_THAT_EXPECTED(Extract(ast.BoolTy, 3), Failed());
+}
+
+TEST_F(ExtractIntFromFormValueTest, TestInt) {
+  using namespace llvm;
+
+  clang::ASTContext &ast = ts.getASTContext();
+
+  // Find the min/max values for 'int' on the current host target.
+  constexpr int64_t int_max = std::numeric_limits<int>::max();
+  constexpr int64_t int_min = std::numeric_limits<int>::min();
+
+  // Check that the bit width of int matches the int width in our type system.
+  ASSERT_EQ(sizeof(int) * 8, ast.getIntWidth(ast.IntTy));
+
+  // Check values around int_min.
+  EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_min - 2), llvm::Failed());
+  EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_min - 1), llvm::Failed());
+  EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_min),
+                       HasValue(std::to_string(int_min)));
+  EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_min + 1),
+                       HasValue(std::to_string(int_min + 1)));
+  EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_min + 2),
+                       HasValue(std::to_string(int_min + 2)));
+
+  // Check values around 0.
+  EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, -128), HasValue("-128"));
+  EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, -10), HasValue("-10"));
+  EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, -1), HasValue("-1"));
+  EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, 0), HasValue("0"));
+  EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, 1), HasValue("1"));
+  EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, 10), HasValue("10"));
+  EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, 128), HasValue("128"));
+
+  // Check values around int_max.
+  EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_max - 2),
+                       HasValue(std::to_string(int_max - 2)));
+  EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_max - 1),
+                       HasValue(std::to_string(int_max - 1)));
+  EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_max),
+                       HasValue(std::to_string(int_max)));
+  EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_max + 1), llvm::Failed());
+  EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_max + 5), llvm::Failed());
+
+  // Check some values not near an edge case.
+  EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_max / 2),
+                       HasValue(std::to_string(int_max / 2)));
+  EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_min / 2),
+                       HasValue(std::to_string(int_min / 2)));
+}
+
+TEST_F(ExtractIntFromFormValueTest, TestUnsignedInt) {
+  using namespace llvm;
+
+  clang::ASTContext &ast = ts.getASTContext();
+  constexpr uint64_t uint_max = std::numeric_limits<uint32_t>::max();
+
+  // Check values around 0.
+  EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, 0), HasValue("0"));
+  EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, 1), HasValue("1"));
+  EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, 1234), HasValue("1234"));
+
+  // Check some values not near an edge case.
+  EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, uint_max / 2),
+                       HasValue(std::to_string(uint_max / 2)));
+
+  // Check values around uint_max.
+  EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, uint_max - 2),
+                       HasValue(std::to_string(uint_max - 2)));
+  EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, uint_max - 1),
+                       HasValue(std::to_string(uint_max - 1)));
+  EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, uint_max),
+                       HasValue(std::to_string(uint_max)));
+  EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, uint_max + 1),
+                       llvm::Failed());
+  EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, uint_max + 2),
+                       llvm::Failed());
+}


        


More information about the lldb-commits mailing list