diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 39aacdb58e694..414349a919721 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -6707,6 +6707,53 @@ uint32_t TypeSystemClang::GetIndexForRecordChild( return UINT32_MAX; } +bool TypeSystemClang::FindInAnonRecordFields(const clang::RecordDecl *rd, + std::vector &path, + llvm::StringRef name, + bool omit_empty_base_classes) { + uint32_t local_idx = 0; + + // We need the visible base count to compute the child index offset + const clang::CXXRecordDecl *crd = llvm::dyn_cast(rd); + const uint32_t bases = + TypeSystemClang::GetNumBaseClasses(crd, omit_empty_base_classes); + + // We only treat anonymous record fields as transparent containers for further + // lookup. + for (auto it = rd->field_begin(), ie = rd->field_end(); it != ie; + ++it, ++local_idx) { + llvm::StringRef fname = it->getName(); + const bool is_anon = it->isAnonymousStructOrUnion() || fname.empty(); + + // named field, check for a match + if (!is_anon) { + if (fname == name) { + path.push_back(bases + local_idx); + return true; + } + continue; + } + + // anonymous field, look inside only if it is a record type + if (!it->getType()->isRecordType()) + continue; + + const auto *inner_rt = it->getType()->castAs(); + const clang::RecordDecl *inner_rd = + inner_rt->getOriginalDecl()->getDefinitionOrSelf(); + if (!inner_rd) + continue; + + // only descend into the "fields" of the anonymous record + // (do not traverse its bases here) + path.push_back(bases + local_idx); + if (FindInAnonRecordFields(inner_rd, path, name, omit_empty_base_classes)) + return true; + path.pop_back(); + } + return false; +} + // Look for a child member (doesn't include base classes, but it does include // their members) in the type hierarchy. Returns an index path into // "clang_type" on how to reach the appropriate member. @@ -6766,16 +6813,23 @@ size_t TypeSystemClang::GetIndexOfChildMemberWithName( field_end = record_decl->field_end(); field != field_end; ++field, ++child_idx) { llvm::StringRef field_name = field->getName(); - if (field_name.empty()) { - CompilerType field_type = GetType(field->getType()); - std::vector save_indices = child_indexes; - child_indexes.push_back( - child_idx + TypeSystemClang::GetNumBaseClasses( - cxx_record_decl, omit_empty_base_classes)); - if (field_type.GetIndexOfChildMemberWithName( - name, omit_empty_base_classes, child_indexes)) - return child_indexes.size(); - child_indexes = std::move(save_indices); + const bool is_anon = + field->isAnonymousStructOrUnion() || field_name.empty(); + if (is_anon) { + if (field->getType()->isRecordType()) { + const uint32_t this_slot = + child_idx + TypeSystemClang::GetNumBaseClasses( + cxx_record_decl, omit_empty_base_classes); + std::vector save_indices = child_indexes; + child_indexes.push_back(this_slot); + const auto *rt = field->getType()->castAs(); + const clang::RecordDecl *rd = + rt->getOriginalDecl()->getDefinitionOrSelf(); + if (rd && FindInAnonRecordFields(rd, child_indexes, name, + omit_empty_base_classes)) + return child_indexes.size(); + child_indexes = std::move(save_indices); + } } else if (field_name == name) { // We have to add on the number of base classes to this index! child_indexes.push_back( @@ -6786,6 +6840,24 @@ size_t TypeSystemClang::GetIndexOfChildMemberWithName( } if (cxx_record_decl) { + for (const clang::CXXBaseSpecifier &base_spec : + cxx_record_decl->bases()) { + uint32_t base_slot = GetIndexForRecordBase(record_decl, &base_spec, + omit_empty_base_classes); + if (base_slot == UINT32_MAX) + continue; + + std::vector save = child_indexes; + child_indexes.push_back(base_slot); + CompilerType base_type = GetType(base_spec.getType()); + if (GetIndexOfChildMemberWithName(base_type.GetOpaqueQualType(), + name, omit_empty_base_classes, + child_indexes)) { + return child_indexes.size(); + } + child_indexes = std::move(save); + } + const clang::RecordDecl *parent_record_decl = cxx_record_decl; // Didn't find things easily, lets let clang do its thang... diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 709f89590ba3b..8a6b364269f82 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -320,6 +320,10 @@ class TypeSystemClang : public TypeSystem { const clang::CXXBaseSpecifier *base_spec, bool omit_empty_base_classes); + bool FindInAnonRecordFields(const clang::RecordDecl *rd, + std::vector &path, llvm::StringRef name, + bool omit_empty_base_classes); + /// Synthesize a clang::Module and return its ID or a default-constructed ID. OptionalClangModuleID GetOrCreateClangModule(llvm::StringRef name, OptionalClangModuleID parent, diff --git a/lldb/test/API/lang/cpp/diamond/TestCppDiamond.py b/lldb/test/API/lang/cpp/diamond/TestCppDiamond.py index 27062c0666a1a..13ab8cc742551 100644 --- a/lldb/test/API/lang/cpp/diamond/TestCppDiamond.py +++ b/lldb/test/API/lang/cpp/diamond/TestCppDiamond.py @@ -107,7 +107,6 @@ def test(self): # Use variable paths to access the members. self.expect_var_path("j1.x", type="long", value="1") - @expectedFailureAll @no_debug_info_test def test_invalid_member(self): self.build() diff --git a/lldb/test/API/lang/cpp/reference-to-outer-type/TestCppReferenceToOuterClass.py b/lldb/test/API/lang/cpp/reference-to-outer-type/TestCppReferenceToOuterClass.py index a6e419b7fcdfa..cb28e2b31fad1 100644 --- a/lldb/test/API/lang/cpp/reference-to-outer-type/TestCppReferenceToOuterClass.py +++ b/lldb/test/API/lang/cpp/reference-to-outer-type/TestCppReferenceToOuterClass.py @@ -6,7 +6,6 @@ class TestCase(TestBase): - @unittest.expectedFailure # The fix for this was reverted due to llvm.org/PR52257 def test(self): self.build() self.dbg.CreateTarget(self.getBuildArtifact("a.out")) diff --git a/lldb/test/API/lang/cpp/type_lookup_anon_base_member/Makefile b/lldb/test/API/lang/cpp/type_lookup_anon_base_member/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/lang/cpp/type_lookup_anon_base_member/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/type_lookup_anon_base_member/TestCppTypeLookupAnonBaseMember.py b/lldb/test/API/lang/cpp/type_lookup_anon_base_member/TestCppTypeLookupAnonBaseMember.py new file mode 100644 index 0000000000000..dc538885c32dd --- /dev/null +++ b/lldb/test/API/lang/cpp/type_lookup_anon_base_member/TestCppTypeLookupAnonBaseMember.py @@ -0,0 +1,37 @@ +""" +Test that we properly print anonymous members in a base class. +""" + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +from lldbsuite.test import decorators + + +class TestTypeLookupAnonBaseMember(TestBase): + def test_lookup_anon_base_member(self): + self.build() + (target, process, thread, bp1) = lldbutil.run_to_source_breakpoint( + self, "// Set breakpoint here", lldb.SBFileSpec("main.cpp") + ) + + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + frame = thread.GetFrameAtIndex(0) + + d = frame.FindVariable("d") + self.assertTrue(d.IsValid()) + + # b from Base + b = d.GetChildMemberWithName("b") + self.assertTrue(b.IsValid()) + self.assertEqual(b.GetValueAsSigned(), 1) + + # x from anonymous struct (inside Base) + x = d.GetChildMemberWithName("x") + self.assertTrue(x.IsValid()) + self.assertEqual(x.GetValueAsSigned(), 2) + + # d from Derived + dd = d.GetChildMemberWithName("d") + self.assertTrue(dd.IsValid()) + self.assertEqual(dd.GetValueAsSigned(), 3) diff --git a/lldb/test/API/lang/cpp/type_lookup_anon_base_member/main.cpp b/lldb/test/API/lang/cpp/type_lookup_anon_base_member/main.cpp new file mode 100644 index 0000000000000..a0b71e8a72237 --- /dev/null +++ b/lldb/test/API/lang/cpp/type_lookup_anon_base_member/main.cpp @@ -0,0 +1,18 @@ +struct Base { + int b; + struct { + int x; + }; +}; + +struct Derived : public Base { + int d; +}; + +int main() { + Derived d; + d.b = 1; + d.x = 2; + d.d = 3; + return 0; // Set breakpoint here +}