diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 37fb16d4e0351..4366324738166 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -2482,8 +2482,9 @@ struct MemberAttributes { DWARFFormValue encoding_form; /// Indicates the byte offset of the word from the base address of the /// structure. - uint32_t member_byte_offset; + uint32_t member_byte_offset = UINT32_MAX; bool is_artificial = false; + bool is_declaration = false; }; /// Parsed form of all attributes that are relevant for parsing Objective-C @@ -2656,8 +2657,6 @@ DiscriminantValue &VariantPart::discriminant() { return this->_discriminant; } MemberAttributes::MemberAttributes(const DWARFDIE &die, const DWARFDIE &parent_die, ModuleSP module_sp) { - member_byte_offset = (parent_die.Tag() == DW_TAG_union_type) ? 0 : UINT32_MAX; - DWARFAttributes attributes = die.GetAttributes(); for (size_t i = 0; i < attributes.Size(); ++i) { const dw_attr_t attr = attributes.AttributeAtIndex(i); @@ -2717,6 +2716,9 @@ MemberAttributes::MemberAttributes(const DWARFDIE &die, case DW_AT_artificial: is_artificial = form_value.Boolean(); break; + case DW_AT_declaration: + is_declaration = form_value.Boolean(); + break; default: break; } @@ -2923,10 +2925,18 @@ void DWARFASTParserClang::ParseSingleMember( if (class_is_objc_object_or_interface) attrs.accessibility = eAccessNone; - // Handle static members, which is any member that doesn't have a bit or a - // byte member offset. + // Handle static members, which are typically members without + // locations. However, GCC *never* emits DW_AT_data_member_location + // for static data members of unions. + // Non-normative text pre-DWARFv5 recommends marking static + // data members with an DW_AT_external flag. Clang emits this consistently + // whereas GCC emits it only for static data members if not part of an + // anonymous namespace. The flag that is consistently emitted for static + // data members is DW_AT_declaration, so we check it instead. + // FIXME: Since DWARFv5, static data members are marked DW_AT_variable so we + // can consistently detect them on both GCC and Clang without below heuristic. if (attrs.member_byte_offset == UINT32_MAX && - attrs.data_bit_offset == UINT64_MAX) { + attrs.data_bit_offset == UINT64_MAX && attrs.is_declaration) { Type *var_type = die.ResolveTypeUID(attrs.encoding_form.Reference()); if (var_type) { diff --git a/lldb/test/API/lang/cpp/union-static-data-members/Makefile b/lldb/test/API/lang/cpp/union-static-data-members/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/lang/cpp/union-static-data-members/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/union-static-data-members/TestCppUnionStaticMembers.py b/lldb/test/API/lang/cpp/union-static-data-members/TestCppUnionStaticMembers.py new file mode 100644 index 0000000000000..47166636b1264 --- /dev/null +++ b/lldb/test/API/lang/cpp/union-static-data-members/TestCppUnionStaticMembers.py @@ -0,0 +1,43 @@ +""" +Tests that frame variable and expr work for +C++ unions and their static data members. +""" +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +import lldbsuite.test.lldbutil as lldbutil + +class CppUnionStaticMembersTestCase(TestBase): + def test(self): + """Tests that frame variable and expr work + for union static data members""" + self.build() + + (target, process, main_thread, _) = lldbutil.run_to_source_breakpoint( + self, "return 0", lldb.SBFileSpec("main.cpp") + ) + + self.expect("frame variable foo", substrs=["val = 42"]) + self.expect("frame variable bar", substrs=["val = 137"]) + + self.expect_expr("foo", result_type="Foo", result_children=[ValueCheck( + name="val", value="42" + )]) + self.expect_expr("bar", result_type="Bar", result_children=[ValueCheck( + name="val", value="137" + )]) + + self.expect_expr("Foo::sVal1", result_type="const int", result_value="-42") + self.expect_expr("Foo::sVal2", result_type="Foo", result_children=[ValueCheck( + name="val", value="42" + )]) + + @expectedFailureAll + def test_union_in_anon_namespace(self): + """Tests that frame variable and expr work + for union static data members in anonymous + namespaces""" + self.expect_expr("Bar::sVal1", result_type="const int", result_value="-137") + self.expect_expr("Bar::sVal2", result_type="Bar", result_children=[ValueCheck( + name="val", value="137" + )]) diff --git a/lldb/test/API/lang/cpp/union-static-data-members/main.cpp b/lldb/test/API/lang/cpp/union-static-data-members/main.cpp new file mode 100644 index 0000000000000..8ba0312cd3a61 --- /dev/null +++ b/lldb/test/API/lang/cpp/union-static-data-members/main.cpp @@ -0,0 +1,25 @@ +union Foo { + int val = 42; + static const int sVal1 = -42; + static Foo sVal2; +}; + +Foo Foo::sVal2{}; + +namespace { +union Bar { + int val = 137; + static const int sVal1 = -137; + static Bar sVal2; +}; + +Bar Bar::sVal2{}; +} // namespace + +int main() { + Foo foo; + Bar bar; + auto sum = Bar::sVal1 + Foo::sVal1 + Foo::sVal2.val + Bar::sVal2.val; + + return 0; +}