Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 88 additions & 11 deletions lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6690,21 +6690,94 @@ 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<clang::CXXRecordDecl>(record_decl),
omit_empty_base_classes);

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<clang::IndirectFieldDecl>(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<clang::FieldDecl>(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
Expand Down Expand Up @@ -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...
Expand All @@ -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();
Expand Down Expand Up @@ -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);
}
}
}
Expand Down
16 changes: 16 additions & 0 deletions lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CXX_SOURCES := main.cpp

include Makefile.rules
Original file line number Diff line number Diff line change
@@ -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)
20 changes: 20 additions & 0 deletions lldb/test/API/lang/cpp/type_lookup_anon_base_member/main.cpp
Original file line number Diff line number Diff line change
@@ -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
}
Loading