diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -1460,8 +1460,6 @@ if (!result) return; - base_classes.push_back(std::move(result)); - if (is_virtual) { // Do not specify any offset for virtual inheritance. The DWARF // produced by clang doesn't give us a constant offset, but gives @@ -1476,10 +1474,33 @@ // be removed from LayoutRecordType() in the external // AST source in clang. } else { + // DWARF doesn't have any representation for [[no_unique_address]] + // attribute. Empty base classes with [[no_unique_address]] fields + // confuse lldb and prevent construction of object memory layout. + // To fix this we scan base classes in reverse order to determine + // overlapping offsets. Wnen found we consider such class as empty + // base with all fields having [[no_unique_address]] attribute. + for (auto it = base_classes.rbegin(); it != base_classes.rend(); ++it) { + clang::CXXRecordDecl *prev_base_decl = + (*it)->getType()->getAsCXXRecordDecl(); + // We've already marked this class, exit. + if (prev_base_decl->isEmpty()) + break; + auto it_layout = layout_info.base_offsets.find(prev_base_decl); + assert(it_layout != layout_info.base_offsets.end()); + // We found a normal base class, exit. + if (it_layout->second.getQuantity() < member_byte_offset) + break; + prev_base_decl->markEmpty(); + for (auto *field : prev_base_decl->fields()) + field->addAttr(clang::NoUniqueAddressAttr::Create( + ast->getASTContext(), clang::SourceRange())); + } layout_info.base_offsets.insert(std::make_pair( ast->GetAsCXXRecordDecl(base_class_clang_type.GetOpaqueQualType()), clang::CharUnits::fromQuantity(member_byte_offset))); } + base_classes.push_back(std::move(result)); } TypeSP DWARFASTParserClang::UpdateSymbolContextScopeForType( diff --git a/lldb/test/API/types/TestEmptyBase.py b/lldb/test/API/types/TestEmptyBase.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/types/TestEmptyBase.py @@ -0,0 +1,42 @@ +""" +Test that recursive types are handled correctly. +""" + + + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + + +class EmptyBaseTestCase(TestBase): + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + # Find the line number to break for main.c. + self.line = line_number('empty_base_type.cpp', + '// Set breakpoint here.') + + self.sources = { + 'CXX_SOURCES': 'empty_base_type.cpp'} + + def test(self): + """Test that recursive structs are displayed correctly.""" + self.build(dictionary=self.sources) + self.setTearDownCleanup(dictionary=self.sources) + self.run_expr() + + def run_expr(self): + self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line( + self, + "empty_base_type.cpp", + self.line, + num_expected_locations=-1, + loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + self.runCmd("expression _a") diff --git a/lldb/test/API/types/empty_base_type.cpp b/lldb/test/API/types/empty_base_type.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/types/empty_base_type.cpp @@ -0,0 +1,29 @@ +struct C +{ + long c,d; +}; + +struct D +{ +}; + +struct B +{ + [[no_unique_address]] D x; +}; + +struct E +{ + [[no_unique_address]] D x; +}; + + +struct A : B,E,C +{ + long a,b; +} _a; + + +int main() { + return _a.a; // Set breakpoint here. +}