diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 39aacdb58e694..51d5222c45319 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -6690,9 +6690,21 @@ uint32_t TypeSystemClang::GetIndexForRecordBase( return UINT32_MAX; } -uint32_t TypeSystemClang::GetIndexForRecordChild( +uint32_t +TypeSystemClang::GetIndexForRecordChild(const clang::RecordDecl *record_decl, + clang::NamedDecl *canonical_decl, + bool omit_empty_base_classes) { + RecordChildResult result; + // For script backward-compatibility, we don't check anonymous bases here. + bool success = GetIndexForRecordChild(record_decl, canonical_decl, + omit_empty_base_classes, false, result); + return success ? result.index : UINT32_MAX; +} + +bool TypeSystemClang::GetIndexForRecordChild( const clang::RecordDecl *record_decl, clang::NamedDecl *canonical_decl, - bool omit_empty_base_classes) { + bool omit_empty_base_classes, bool check_anonymous_bases, + RecordChildResult &out) { uint32_t child_idx = TypeSystemClang::GetNumBaseClasses( llvm::dyn_cast(record_decl), omit_empty_base_classes); @@ -6700,11 +6712,72 @@ uint32_t TypeSystemClang::GetIndexForRecordChild( clang::RecordDecl::field_iterator field, field_end; for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field, ++child_idx) { - if (field->getCanonicalDecl() == canonical_decl) - return child_idx; + out = RecordChildResult{}; + + // If finding an `IndirectFieldDecl x` from `struct { int x; ... };` + if (auto *IFD = llvm::dyn_cast(canonical_decl)) { + if (!check_anonymous_bases) + continue; + // Only meaningful is the field is an anonymous struct/union. + if (!field->isAnonymousStructOrUnion()) + continue; + + // Find the anonymous aggregate record that is a direct field of + // `record_decl`, and the target field inside that aggregate, we need the + // latter to compute the correct child index to the anonymous field. + const clang::FieldDecl *anon_aggregate = nullptr; + const clang::FieldDecl *target_field_in_anon = nullptr; + + for (clang::NamedDecl *ND : IFD->chain()) { + if (auto *FD = llvm::dyn_cast(ND)) { + if (FD->isAnonymousStructOrUnion() && + FD->getDeclContext() == record_decl) + anon_aggregate = FD; + if (!FD->isAnonymousStructOrUnion()) + target_field_in_anon = FD; + } + } + if (!anon_aggregate || !target_field_in_anon) + continue; + + // If this field is not the anonymous aggregate, skip. + if (field->getCanonicalDecl() != anon_aggregate->getCanonicalDecl()) + continue; + + // the child_idx now points to the anonymous aggregate. + out.index = child_idx; + + // Compute inner slot: within the anonymous aggregate's record. + auto inner_rd = anon_aggregate->getType() + ->getAsCXXRecordDecl() + ->getDefinitionOrSelf(); + if (!inner_rd) { + out.has_inner = false; + return true; + } + + uint32_t inner_idx = + TypeSystemClang::GetNumBaseClasses(inner_rd, omit_empty_base_classes); + + for (auto inner_f = inner_rd->field_begin(), + inner_e = inner_rd->field_end(); + inner_f != inner_e; ++inner_f, ++inner_idx) { + if (inner_f->getCanonicalDecl() == + target_field_in_anon->getCanonicalDecl()) { + out.has_inner = true; + out.inner_index = inner_idx; + return true; + } + } + + } else if (field->getCanonicalDecl() == canonical_decl) { + out.index = child_idx; + out.has_inner = false; + return true; + } } - return UINT32_MAX; + return false; } // Look for a child member (doesn't include base classes, but it does include @@ -6785,7 +6858,7 @@ size_t TypeSystemClang::GetIndexOfChildMemberWithName( } } - if (cxx_record_decl) { + if (cxx_record_decl && !cxx_record_decl->isAnonymousStructOrUnion()) { const clang::RecordDecl *parent_record_decl = cxx_record_decl; // Didn't find things easily, lets let clang do its thang... @@ -6797,7 +6870,7 @@ size_t TypeSystemClang::GetIndexOfChildMemberWithName( [decl_name](const clang::CXXBaseSpecifier *specifier, clang::CXXBasePath &path) { CXXRecordDecl *record = - specifier->getType()->getAsCXXRecordDecl(); + specifier->getType()->getAsCXXRecordDecl(); auto r = record->lookup(decl_name); path.Decls = r.begin(); return !r.empty(); @@ -6825,13 +6898,17 @@ size_t TypeSystemClang::GetIndexOfChildMemberWithName( } for (clang::DeclContext::lookup_iterator I = path->Decls, E; I != E; ++I) { - child_idx = GetIndexForRecordChild( - parent_record_decl, *I, omit_empty_base_classes); - if (child_idx == UINT32_MAX) { + RecordChildResult result{}; + bool success = GetIndexForRecordChild(parent_record_decl, *I, + omit_empty_base_classes, + true, result); + if (!success) { child_indexes.clear(); return 0; } else { - child_indexes.push_back(child_idx); + child_indexes.push_back(result.index); + if (result.has_inner) + child_indexes.push_back(result.inner_index); } } } diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 709f89590ba3b..1bc6167462938 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -98,6 +98,16 @@ class TypePayloadClang { /// \} }; +/// Return result of GetIndexForRecordChild +struct RecordChildResult { + /// child index in `record_decl` + uint32_t index = UINT32_MAX; + /// true if canonical_decl is an IndirectFieldDecl via an anonymous aggregate + bool has_inner = false; + /// if has_inner is true, this is the index within the anonymous aggregate + uint32_t inner_index = UINT32_MAX; +}; + /// A TypeSystem implementation based on Clang. /// /// This class uses a single clang::ASTContext as the backend for storing @@ -316,6 +326,12 @@ class TypeSystemClang : public TypeSystem { clang::NamedDecl *canonical_decl, bool omit_empty_base_classes); + bool GetIndexForRecordChild(const clang::RecordDecl *record_decl, + clang::NamedDecl *canonical_decl, + bool omit_empty_base_classes, + bool check_anonymous_bases, + RecordChildResult &result); + uint32_t GetIndexForRecordBase(const clang::RecordDecl *record_decl, const clang::CXXBaseSpecifier *base_spec, bool omit_empty_base_classes); 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..d2cf4a71fbc86 --- /dev/null +++ b/lldb/test/API/lang/cpp/type_lookup_anon_base_member/TestCppTypeLookupAnonBaseMember.py @@ -0,0 +1,42 @@ +""" +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) + + # y from anonymous struct (inside Base) + x = d.GetChildMemberWithName("y") + self.assertTrue(x.IsValid()) + self.assertEqual(x.GetValueAsSigned(), 4) + + # 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..bff34a5895737 --- /dev/null +++ b/lldb/test/API/lang/cpp/type_lookup_anon_base_member/main.cpp @@ -0,0 +1,20 @@ +struct Base { + int b; + struct { + int x; + int y; + }; +}; + +struct Derived : public Base { + int d; +}; + +int main() { + Derived d; + d.b = 1; + d.x = 2; + d.d = 3; + d.y = 4; + return 0; // Set breakpoint here +}