kpdev42 created this revision. kpdev42 added reviewers: DavidSpickett, davide, clayborg, k8stone. kpdev42 added projects: LLVM, LLDB. Herald added subscribers: Michael137, JDevlieghere, martong. Herald added a reviewer: shafik. Herald added a reviewer: shafik. Herald added a project: All. kpdev42 requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
The no_unique_address attribute is currently a part of C++20 standard and is being used in both libstdc++ and libc++. This causes problems when debugging C++ code with lldb because the latter doesn't expect two non-empty base classes having the same offset and crashes with assertion. Patch attempts to infer this attribute by detecting two consecutive base classes with the same offset to derived class and marking first of those classes as empty and adding no_unique_address attribute to all of its fields. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D143347 Files: clang/include/clang/AST/DeclCXX.h clang/lib/AST/ASTImporter.cpp lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp lldb/test/API/types/TestEmptyBase.py lldb/test/API/types/empty_base_type.cpp
Index: lldb/test/API/types/empty_base_type.cpp =================================================================== --- /dev/null +++ lldb/test/API/types/empty_base_type.cpp @@ -0,0 +1,23 @@ +struct C +{ + long c,d; +}; + +struct D +{ +}; + +struct B +{ + [[no_unique_address]] D x; +}; + +struct A : B,C +{ + long a,b; +} _a; + + +int main() { + return _a.a; // Set breakpoint here. +} Index: lldb/test/API/types/TestEmptyBase.py =================================================================== --- /dev/null +++ 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") Index: lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp =================================================================== --- lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -1460,6 +1460,8 @@ if (!result) return; + const clang::CXXBaseSpecifier *prev_base = + base_classes.empty() ? nullptr : base_classes.back().get(); base_classes.push_back(std::move(result)); if (is_virtual) { @@ -1476,6 +1478,20 @@ // be removed from LayoutRecordType() in the external // AST source in clang. } else { + if (prev_base) { + clang::CXXRecordDecl *prev_base_decl = + prev_base->getType()->getAsCXXRecordDecl(); + if (prev_base_decl && !prev_base_decl->isEmpty()) { + auto it = layout_info.base_offsets.find(prev_base_decl); + assert(it != layout_info.base_offsets.end()); + if (it->second.getQuantity() == member_byte_offset) { + 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))); Index: lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp +++ lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp @@ -548,6 +548,7 @@ void ClangASTImporter::SetRecordLayout(clang::RecordDecl *decl, const LayoutInfo &layout) { + m_record_decl_to_layout_map.insert(std::make_pair(decl, layout)); } Index: clang/lib/AST/ASTImporter.cpp =================================================================== --- clang/lib/AST/ASTImporter.cpp +++ clang/lib/AST/ASTImporter.cpp @@ -3892,6 +3892,8 @@ D->getInClassInitStyle())) return ToField; + if (D->hasAttrs()) + ToField->setAttrs(D->getAttrs()); ToField->setAccess(D->getAccess()); ToField->setLexicalDeclContext(LexicalDC); if (ToInitializer) Index: clang/include/clang/AST/DeclCXX.h =================================================================== --- clang/include/clang/AST/DeclCXX.h +++ clang/include/clang/AST/DeclCXX.h @@ -1165,6 +1165,10 @@ /// /// \note This does NOT include a check for union-ness. bool isEmpty() const { return data().Empty; } + /// Marks this record as empty. This is used by DWARFASTParserClang + /// when parsing records with empty fields having [[no_unique_address]] + /// attribute + void markEmpty() { data().Empty = true; } void setInitMethod(bool Val) { data().HasInitMethod = Val; } bool hasInitMethod() const { return data().HasInitMethod; }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits