diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 81d846ba2a25f..1e5de6cc36971 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -2839,10 +2839,9 @@ void DWARFASTParserClang::ParseSingleMember( die.GetCU()->Supports_unnamed_objc_bitfields(); if (detect_unnamed_bitfields) { - llvm::Optional unnamed_field_info; - uint64_t last_field_end = 0; - - last_field_end = last_field_info.bit_offset + last_field_info.bit_size; + std::optional unnamed_field_info; + uint64_t last_field_end = + last_field_info.bit_offset + last_field_info.bit_size; if (!last_field_info.IsBitfield()) { // The last field was not a bit-field... @@ -2853,20 +2852,8 @@ void DWARFASTParserClang::ParseSingleMember( last_field_end += word_width - (last_field_end % word_width); } - // If we have a gap between the last_field_end and the current - // field we have an unnamed bit-field. - // If we have a base class, we assume there is no unnamed - // bit-field if this is the first field since the gap can be - // attributed to the members from the base class. This assumption - // is not correct if the first field of the derived class is - // indeed an unnamed bit-field. We currently do not have the - // machinary to track the offset of the last field of classes we - // have seen before, so we are not handling this case. - if (this_field_info.bit_offset != last_field_end && - this_field_info.bit_offset > last_field_end && - !(last_field_info.bit_offset == 0 && - last_field_info.bit_size == 0 && - layout_info.base_offsets.size() != 0)) { + if (ShouldCreateUnnamedBitfield(last_field_info, last_field_end, + this_field_info, layout_info)) { unnamed_field_info = FieldInfo{}; unnamed_field_info->bit_size = this_field_info.bit_offset - last_field_end; @@ -2907,8 +2894,10 @@ void DWARFASTParserClang::ParseSingleMember( // artificial member with (unnamed bitfield) padding. // FIXME: This check should verify that this is indeed an artificial member // we are supposed to ignore. - if (attrs.is_artificial) + if (attrs.is_artificial) { + last_field_info.SetIsArtificial(true); return; + } if (!member_clang_type.IsCompleteType()) member_clang_type.GetCompleteType(); @@ -3660,3 +3649,35 @@ bool DWARFASTParserClang::CopyUniqueClassMethodTypes( return !failures.empty(); } + +bool DWARFASTParserClang::ShouldCreateUnnamedBitfield( + FieldInfo const &last_field_info, uint64_t last_field_end, + FieldInfo const &this_field_info, + lldb_private::ClangASTImporter::LayoutInfo const &layout_info) const { + // If we have a gap between the last_field_end and the current + // field we have an unnamed bit-field. + if (this_field_info.bit_offset <= last_field_end) + return false; + + // If we have a base class, we assume there is no unnamed + // bit-field if either of the following is true: + // (a) this is the first field since the gap can be + // attributed to the members from the base class. + // FIXME: This assumption is not correct if the first field of + // the derived class is indeed an unnamed bit-field. We currently + // do not have the machinary to track the offset of the last field + // of classes we have seen before, so we are not handling this case. + // (b) Or, the first member of the derived class was a vtable pointer. + // In this case we don't want to create an unnamed bitfield either + // since those will be inserted by clang later. + const bool have_base = layout_info.base_offsets.size() != 0; + const bool this_is_first_field = + last_field_info.bit_offset == 0 && last_field_info.bit_size == 0; + const bool first_field_is_vptr = + last_field_info.bit_offset == 0 && last_field_info.IsArtificial(); + + if (have_base && (this_is_first_field || first_field_is_vptr)) + return false; + + return true; +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h index d479a73705af3..092dd56b96ed8 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h @@ -206,12 +206,16 @@ class DWARFASTParserClang : public DWARFASTParser { uint64_t bit_size = 0; uint64_t bit_offset = 0; bool is_bitfield = false; + bool is_artificial = false; FieldInfo() = default; void SetIsBitfield(bool flag) { is_bitfield = flag; } bool IsBitfield() { return is_bitfield; } + void SetIsArtificial(bool flag) { is_artificial = flag; } + bool IsArtificial() const { return is_artificial; } + bool NextBitfieldOffsetIsValid(const uint64_t next_bit_offset) const { // Any subsequent bitfields must not overlap and must be at a higher // bit offset than any previous bitfield + size. @@ -219,6 +223,22 @@ class DWARFASTParserClang : public DWARFASTParser { } }; + /// Returns 'true' if we should create an unnamed bitfield + /// and add it to the parser's current AST. + /// + /// \param[in] last_field_info FieldInfo of the previous DW_TAG_member + /// we parsed. + /// \param[in] last_field_end Offset (in bits) where the last parsed field + /// ended. + /// \param[in] this_field_info FieldInfo of the current DW_TAG_member + /// being parsed. + /// \param[in] layout_info Layout information of all decls parsed by the + /// current parser. + bool ShouldCreateUnnamedBitfield( + FieldInfo const &last_field_info, uint64_t last_field_end, + FieldInfo const &this_field_info, + lldb_private::ClangASTImporter::LayoutInfo const &layout_info) const; + /// Parses a DW_TAG_APPLE_property DIE and appends the parsed data to the /// list of delayed Objective-C properties. /// diff --git a/lldb/test/API/lang/cpp/bitfields/TestCppBitfields.py b/lldb/test/API/lang/cpp/bitfields/TestCppBitfields.py index f38d0f6c7edf4..b85f23eb4bd0a 100644 --- a/lldb/test/API/lang/cpp/bitfields/TestCppBitfields.py +++ b/lldb/test/API/lang/cpp/bitfields/TestCppBitfields.py @@ -168,3 +168,14 @@ def test_bitfield_behind_vtable_ptr(self): result_children=with_vtable_and_unnamed_children) self.expect_var_path("with_vtable_and_unnamed", children=with_vtable_and_unnamed_children) + + derived_with_vtable_children = [ + ValueCheck(name="Base", children=[ + ValueCheck(name="b_a", value="2", type="uint32_t") + ]), + ValueCheck(name="a", value="1", type="unsigned int:1") + ] + self.expect_expr("derived_with_vtable", + result_children=derived_with_vtable_children) + self.expect_var_path("derived_with_vtable", + children=derived_with_vtable_children) diff --git a/lldb/test/API/lang/cpp/bitfields/main.cpp b/lldb/test/API/lang/cpp/bitfields/main.cpp index eb9db7271aae6..6e1d4ba63bf6d 100644 --- a/lldb/test/API/lang/cpp/bitfields/main.cpp +++ b/lldb/test/API/lang/cpp/bitfields/main.cpp @@ -113,6 +113,12 @@ struct HasBaseWithVTable : BaseWithVTable { }; HasBaseWithVTable base_with_vtable; +struct DerivedWithVTable : public Base { + virtual ~DerivedWithVTable() {} + unsigned a : 1; +}; +DerivedWithVTable derived_with_vtable; + int main(int argc, char const *argv[]) { lba.a = 2; @@ -153,5 +159,8 @@ int main(int argc, char const *argv[]) { base_with_vtable.b = 0; base_with_vtable.c = 5; + derived_with_vtable.b_a = 2; + derived_with_vtable.a = 1; + return 0; // break here }