Index: lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp +++ lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp @@ -196,6 +196,24 @@ 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) { + 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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); @@ -303,7 +321,7 @@ clang::VarDecl *result_decl = nullptr; - if (is_lvalue) { + if (is_lvalue && CanTakeAddressOfLValue(last_expr)) { IdentifierInfo *result_ptr_id; if (expr_type->isFunctionType()) Index: lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h =================================================================== --- lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h +++ lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h @@ -67,6 +67,10 @@ lldb_private::ClangASTImporter &GetClangASTImporter(); + llvm::Expected + ExtractIntFromFormValue(const lldb_private::CompilerType &ct, + const DWARFFormValue &form_value) const; + protected: /// Protected typedefs and members. /// @{ Index: lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp =================================================================== --- lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -2383,6 +2383,45 @@ return nullptr; } +llvm::Expected DWARFASTParserClang::ExtractIntFromFormValue( + const CompilerType &ct, const DWARFFormValue &form_value) const { + clang::QualType qt = ClangUtil::GetQualType(ct); + assert(qt->isIntegralOrEnumerationType()); + TypeSystemClang &ts = *llvm::cast(ct.GetTypeSystem()); + clang::ASTContext &ast = ts.getASTContext(); + + const bool is_unsigned = qt->isUnsignedIntegerType(); + const unsigned type_bits = ast.getIntWidth(qt); + + llvm::APSInt result( + llvm::APInt(sizeof(uint64_t) * 8, form_value.Unsigned(), !is_unsigned), + is_unsigned); + + bool failed = false; + if (is_unsigned) { + failed = result.getActiveBits() > type_bits; + } else { + failed = result.getMinSignedBits() > type_bits; + } + if (failed) { + std::string value_as_str = is_unsigned + ? std::to_string(form_value.Unsigned()) + : std::to_string(form_value.Signed()); + llvm::errs() << "##########\n"; + llvm::errs() << "Active " << result.getActiveBits() << "\n"; + llvm::errs() << "getMinSignedBits " << result.getMinSignedBits() << "\n"; + llvm::errs() << "Value: " << value_as_str << "\n"; + 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()); + } + 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, @@ -2392,6 +2431,9 @@ DelayedPropertyList &delayed_properties, lldb_private::ClangASTImporter::LayoutInfo &layout_info, FieldInfo &last_field_info) { + Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_TYPE_COMPLETION | + DWARF_LOG_LOOKUPS); + ModuleSP module_sp = parent_die.GetDWARF()->GetObjectFile()->GetModule(); const dw_tag_t tag = die.Tag(); // Get the parent byte size so we can verify any members will fit @@ -2418,6 +2460,7 @@ int64_t bit_offset = 0; uint64_t data_bit_offset = UINT64_MAX; size_t bit_size = 0; + llvm::Optional const_value_form; bool is_external = false; // On DW_TAG_members, this means the member is static uint32_t i; @@ -2449,6 +2492,9 @@ 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; @@ -2572,9 +2618,21 @@ if (var_type) { if (accessibility == eAccessNone) accessibility = eAccessPublic; - TypeSystemClang::AddVariableToRecordType( - class_clang_type, name, var_type->GetLayoutCompilerType(), - accessibility); + CompilerType ct = var_type->GetLayoutCompilerType(); + clang::VarDecl *v = TypeSystemClang::AddVariableToRecordType( + class_clang_type, name, ct, accessibility); + + if (!const_value_form) + return; + llvm::Expected const_value_or_err = + ExtractIntFromFormValue(ct, *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::AddInitToVarDecl(v, *const_value_or_err); } return; } Index: lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h =================================================================== --- lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -873,6 +873,9 @@ const CompilerType &var_type, lldb::AccessType access); + static void AddInitToVarDecl(clang::VarDecl *v, + const llvm::APInt &init_value); + clang::CXXMethodDecl *AddMethodToCXXRecordType( lldb::opaque_compiler_type_t type, llvm::StringRef name, const char *mangled_name, const CompilerType &method_type, Index: lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp =================================================================== --- lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -7279,6 +7279,19 @@ return var_decl; } +void TypeSystemClang::AddInitToVarDecl(VarDecl *v, + const llvm::APInt &init_value) { + clang::ASTContext &ast = v->getASTContext(); + QualType qt = v->getType(); + assert(qt->isIntegralOrEnumerationType()); + if (const EnumType *enum_type = llvm::dyn_cast(qt.getTypePtr())) { + const EnumDecl *enum_decl = enum_type->getDecl(); + qt = enum_decl->getIntegerType(); + } + v->setInit(IntegerLiteral::Create(ast, init_value, qt.getUnqualifiedType(), + SourceLocation())); +} + clang::CXXMethodDecl *TypeSystemClang::AddMethodToCXXRecordType( lldb::opaque_compiler_type_t type, llvm::StringRef name, const char *mangled_name, const CompilerType &method_clang_type, Index: lldb/source/Utility/DataExtractor.cpp =================================================================== --- lldb/source/Utility/DataExtractor.cpp +++ lldb/source/Utility/DataExtractor.cpp @@ -33,6 +33,7 @@ #include #include +#include "llvm/Support/LEB128.h" #include #include #include @@ -877,26 +878,10 @@ if (src == nullptr) return 0; - const uint8_t *end = m_end; - - if (src < end) { - uint64_t result = *src++; - if (result >= 0x80) { - result &= 0x7f; - int shift = 7; - while (src < end) { - uint8_t byte = *src++; - result |= static_cast(byte & 0x7f) << shift; - if ((byte & 0x80) == 0) - break; - shift += 7; - } - } - *offset_ptr = src - m_start; - return result; - } - - return 0; + unsigned byte_count = 0; + uint64_t result = llvm::decodeULEB128(src, &byte_count, src + 9); + *offset_ptr += byte_count; + return result; } // Extracts an signed LEB128 number from this object's data starting at the @@ -910,35 +895,10 @@ if (src == nullptr) return 0; - const uint8_t *end = m_end; - - if (src < end) { - int64_t result = 0; - int shift = 0; - int size = sizeof(int64_t) * 8; - - uint8_t byte = 0; - int bytecount = 0; - - while (src < end) { - bytecount++; - byte = *src++; - result |= static_cast(byte & 0x7f) << shift; - shift += 7; - if ((byte & 0x80) == 0) - break; - } - - // Sign bit of byte is 2nd high order bit (0x40) - if (shift < size && (byte & 0x40)) { - // -(static_cast(1) << 63) errors on the negation with UBSan. - result |= -(static_cast(1) << shift); - } - - *offset_ptr += bytecount; - return result; - } - return 0; + unsigned byte_count = 0; + int64_t result = llvm::decodeSLEB128(src, &byte_count, src + 9); + *offset_ptr += byte_count; + return result; } // Skips a ULEB128 number (signed or unsigned) from this object's data starting Index: lldb/test/API/lang/cpp/const_static_integral_member/Makefile =================================================================== --- /dev/null +++ lldb/test/API/lang/cpp/const_static_integral_member/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules Index: lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py =================================================================== --- /dev/null +++ lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py @@ -0,0 +1,75 @@ +""" +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): + + mydir = TestBase.compute_mydir(__file__) + + 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") + + # Try to take the address of the data member. Should produce a linker + # error only if the member doesn't have an address. + self.expect("expr const int *i = &A::int_val; *i", error=True, + substrs=["Couldn't lookup symbols:"]) + self.expect_expr("const int *i = &A::int_val_with_address; *i", + 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") Index: lldb/test/API/lang/cpp/const_static_integral_member/main.cpp =================================================================== --- /dev/null +++ lldb/test/API/lang/cpp/const_static_integral_member/main.cpp @@ -0,0 +1,90 @@ +#include + +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::min(), + case1 = 1, + case2 = std::numeric_limits::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::max(); + const static auto uchar_max = std::numeric_limits::max(); + const static auto int_max = std::numeric_limits::max(); + const static auto uint_max = std::numeric_limits::max(); + const static auto long_max = std::numeric_limits::max(); + const static auto ulong_max = std::numeric_limits::max(); + const static auto longlong_max = std::numeric_limits::max(); + const static auto ulonglong_max = + std::numeric_limits::max(); + + const static auto char_min = std::numeric_limits::min(); + const static auto uchar_min = std::numeric_limits::min(); + const static auto int_min = std::numeric_limits::min(); + const static auto uint_min = std::numeric_limits::min(); + const static auto long_min = std::numeric_limits::min(); + const static auto ulong_min = std::numeric_limits::min(); + const static auto longlong_min = std::numeric_limits::min(); + const static auto ulonglong_min = + std::numeric_limits::min(); + + const static Enum enum_val = enum_case2; + const static Enum invalid_enum_val = static_cast(enum_case2 + 5); + const static ScopedEnum scoped_enum_val = ScopedEnum::scoped_enum_case2; + const static ScopedEnum invalid_scoped_enum_val = static_cast(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; + +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; + + 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 +} Index: lldb/unittests/SymbolFile/DWARF/CMakeLists.txt =================================================================== --- lldb/unittests/SymbolFile/DWARF/CMakeLists.txt +++ lldb/unittests/SymbolFile/DWARF/CMakeLists.txt @@ -13,6 +13,7 @@ lldbPluginTypeSystemClang lldbUtilityHelpers lldbPluginPlatformMacOSX + LLVMTestingSupport LINK_COMPONENTS Support DebugInfoPDB Index: lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp =================================================================== --- lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp +++ lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp @@ -17,7 +17,7 @@ using namespace lldb_private; namespace { -class DWARFASTParserClangTests : public testing::Test {}; +class DWARFASTParserClangStubTests : public testing::Test {}; class DWARFASTParserClangStub : public DWARFASTParserClang { public: @@ -35,7 +35,7 @@ // If your implementation needs to dereference the dummy pointers we are // defining here, causing this test to fail, feel free to delete it. -TEST_F(DWARFASTParserClangTests, +TEST_F(DWARFASTParserClangStubTests, EnsureAllDIEsInDeclContextHaveBeenParsedParsesOnlyMatchingEntries) { /// Auxiliary debug info. @@ -116,3 +116,95 @@ testing::UnorderedElementsAre(decl_ctxs[0], decl_ctxs[3])); } +struct ExtractIntFromFormValueTest : public testing::Test { + SubsystemRAII subsystems; + TypeSystemClang ts; + DWARFASTParserClang parser; + ExtractIntFromFormValueTest() + : ts("dummy ASTContext", HostInfoBase::GetTargetTriple()), parser(ts) {} + + llvm::Expected Extract(clang::QualType qt, uint64_t value) { + DWARFFormValue form_value; + form_value.SetUnsigned(value); + llvm::Expected 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()); + } + + llvm::Expected ExtractS(clang::QualType qt, int64_t value) { + DWARFFormValue form_value; + form_value.SetSigned(value); + llvm::Expected 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(); + const int64_t int_max = (static_cast(1) + << (ast.getIntWidth(ast.IntTy) - 1)) - 1U; + const int64_t int_min = -int_max - 1; + + 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))); + 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, 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()); +} + +TEST_F(ExtractIntFromFormValueTest, TestUnsignedInt) { + using namespace llvm; + + clang::ASTContext &ast = ts.getASTContext(); + const uint64_t uint_max = + (static_cast(1) << ast.getIntWidth(ast.UnsignedIntTy)) - 1U; + + 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")); + 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()); +}