diff --git a/clang/include/clang/AST/ASTImporter.h b/clang/include/clang/AST/ASTImporter.h index 4ffd913846575..f851decd0965c 100644 --- a/clang/include/clang/AST/ASTImporter.h +++ b/clang/include/clang/AST/ASTImporter.h @@ -258,7 +258,6 @@ class TypeSourceInfo; FoundDeclsTy findDeclsInToCtx(DeclContext *DC, DeclarationName Name); void AddToLookupTable(Decl *ToD); - llvm::Error ImportAttrs(Decl *ToD, Decl *FromD); protected: /// Can be overwritten by subclasses to implement their own import logic. diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 0923736a95f97..bf6a5ce92d438 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -1188,10 +1188,6 @@ class CXXRecordDecl : public RecordDecl { /// /// \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; } diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 835dd9a20c09c..5900c0e66b3f8 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -4201,12 +4201,6 @@ ExpectedDecl ASTNodeImporter::VisitFieldDecl(FieldDecl *D) { D->getInClassInitStyle())) return ToField; - // We need [[no_unqiue_address]] attributes to be added to FieldDecl, before - // we add fields in CXXRecordDecl::addedMember, otherwise record will be - // marked as having non-zero size. - Err = Importer.ImportAttrs(ToField, D); - if (Err) - return std::move(Err); ToField->setAccess(D->getAccess()); ToField->setLexicalDeclContext(LexicalDC); ToField->setImplicit(D->isImplicit()); @@ -9423,19 +9417,6 @@ TranslationUnitDecl *ASTImporter::GetFromTU(Decl *ToD) { return FromDPos->second->getTranslationUnitDecl(); } -Error ASTImporter::ImportAttrs(Decl *ToD, Decl *FromD) { - if (!FromD->hasAttrs() || ToD->hasAttrs()) - return Error::success(); - for (const Attr *FromAttr : FromD->getAttrs()) { - auto ToAttrOrErr = Import(FromAttr); - if (ToAttrOrErr) - ToD->addAttr(*ToAttrOrErr); - else - return ToAttrOrErr.takeError(); - } - return Error::success(); -} - Expected ASTImporter::Import(Decl *FromD) { if (!FromD) return nullptr; @@ -9569,8 +9550,15 @@ Expected ASTImporter::Import(Decl *FromD) { } // Make sure that ImportImpl registered the imported decl. assert(ImportedDecls.count(FromD) != 0 && "Missing call to MapImported?"); - if (auto Error = ImportAttrs(ToD, FromD)) - return std::move(Error); + + if (FromD->hasAttrs()) + for (const Attr *FromAttr : FromD->getAttrs()) { + auto ToAttrOrErr = Import(FromAttr); + if (ToAttrOrErr) + ToD->addAttr(*ToAttrOrErr); + else + return ToAttrOrErr.takeError(); + } // Notify subclasses. Imported(FromD, ToD); diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp index 6d987cc7e9ec6..651683de0ccea 100644 --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -9376,29 +9376,6 @@ TEST_P(ASTImporterOptionSpecificTestBase, VaListCpp) { ToVaList->getUnderlyingType(), ToBuiltinVaList->getUnderlyingType())); } -TEST_P(ASTImporterOptionSpecificTestBase, - ImportDefinitionOfEmptyClassWithNoUniqueAddressField) { - Decl *FromTU = getTuDecl( - R"( - struct B {}; - struct A { B b; }; - )", - Lang_CXX20); - - CXXRecordDecl *FromD = FirstDeclMatcher().match( - FromTU, cxxRecordDecl(hasName("A"))); - - for (auto *FD : FromD->fields()) - FD->addAttr(clang::NoUniqueAddressAttr::Create(FromD->getASTContext(), - clang::SourceRange())); - FromD->markEmpty(); - - CXXRecordDecl *ToD = Import(FromD, Lang_CXX20); - EXPECT_TRUE(ToD->isEmpty()); - for (auto *FD : ToD->fields()) - EXPECT_EQ(true, FD->hasAttr()); -} - TEST_P(ASTImporterOptionSpecificTestBase, ImportExistingTypedefToRecord) { const char *Code = R"( diff --git a/lldb/examples/synthetic/libcxx.py b/lldb/examples/synthetic/libcxx.py index 474aaa428fa23..b078a4eb2f639 100644 --- a/lldb/examples/synthetic/libcxx.py +++ b/lldb/examples/synthetic/libcxx.py @@ -721,6 +721,12 @@ def _get_value_of_compressed_pair(self, pair): def update(self): logger = lldb.formatters.Logger.Logger() try: + has_compressed_pair_layout = True + alloc_valobj = self.valobj.GetChildMemberWithName("__alloc_") + size_valobj = self.valobj.GetChildMemberWithName("__size_") + if alloc_valobj.IsValid() and size_valobj.IsValid(): + has_compressed_pair_layout = False + # A deque is effectively a two-dim array, with fixed width. # 'map' contains pointers to the rows of this array. The # full memory area allocated by the deque is delimited @@ -734,9 +740,13 @@ def update(self): # variable tells which element in this NxM array is the 0th # one, and the 'size' element gives the number of elements # in the deque. - count = self._get_value_of_compressed_pair( - self.valobj.GetChildMemberWithName("__size_") - ) + if has_compressed_pair_layout: + count = self._get_value_of_compressed_pair( + self.valobj.GetChildMemberWithName("__size_") + ) + else: + count = size_valobj.GetValueAsUnsigned(0) + # give up now if we cant access memory reliably if self.block_size < 0: logger.write("block_size < 0") @@ -748,9 +758,15 @@ def update(self): self.map_begin = map_.GetChildMemberWithName("__begin_") map_begin = self.map_begin.GetValueAsUnsigned(0) map_end = map_.GetChildMemberWithName("__end_").GetValueAsUnsigned(0) - map_endcap = self._get_value_of_compressed_pair( - map_.GetChildMemberWithName("__end_cap_") - ) + + if has_compressed_pair_layout: + map_endcap = self._get_value_of_compressed_pair( + map_.GetChildMemberWithName("__end_cap_") + ) + else: + map_endcap = map_.GetChildMemberWithName( + "__end_cap_" + ).GetValueAsUnsigned(0) # check consistency if not map_first <= map_begin <= map_end <= map_endcap: diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h index 260a104f1acf6..8d6f090605628 100644 --- a/lldb/include/lldb/Symbol/SymbolFile.h +++ b/lldb/include/lldb/Symbol/SymbolFile.h @@ -217,7 +217,15 @@ class SymbolFile : public PluginInterface { /// The characteristics of an array type. struct ArrayInfo { int64_t first_index = 0; - llvm::SmallVector element_orders; + + ///< Each entry belongs to a distinct DW_TAG_subrange_type. + ///< For multi-dimensional DW_TAG_array_types we would have + ///< an entry for each dimension. An entry represents the + ///< optional element count of the subrange. + /// + ///< The order of entries follows the order of the DW_TAG_subrange_type + ///< children of this DW_TAG_array_type. + llvm::SmallVector, 1> element_orders; uint32_t byte_stride = 0; uint32_t bit_stride = 0; }; diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp index 3f22f1468a6f1..f42297b5b628d 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp @@ -27,6 +27,7 @@ #include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h" #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" #include "lldb/lldb-enumerations.h" +#include "lldb/lldb-forward.h" #include #include @@ -34,6 +35,32 @@ using namespace lldb; using namespace lldb_private; using namespace lldb_private::formatters; +static void consumeInlineNamespace(llvm::StringRef &name) { + // Delete past an inline namespace, if any: __[a-zA-Z0-9_]+:: + auto scratch = name; + if (scratch.consume_front("__") && std::isalnum(scratch[0])) { + scratch = scratch.drop_while([](char c) { return std::isalnum(c); }); + if (scratch.consume_front("::")) { + // Successfully consumed a namespace. + name = scratch; + } + } +} + +bool lldb_private::formatters::isOldCompressedPairLayout( + ValueObject &pair_obj) { + return isStdTemplate(pair_obj.GetTypeName(), "__compressed_pair"); +} + +bool lldb_private::formatters::isStdTemplate(ConstString type_name, + llvm::StringRef type) { + llvm::StringRef name = type_name.GetStringRef(); + // The type name may be prefixed with `std::__::`. + if (name.consume_front("std::")) + consumeInlineNamespace(name); + return name.consume_front(type) && name.starts_with("<"); +} + lldb::ValueObjectSP lldb_private::formatters::GetChildMemberWithName( ValueObject &obj, llvm::ArrayRef alternative_names) { for (ConstString name : alternative_names) { @@ -53,7 +80,7 @@ lldb_private::formatters::GetFirstValueOfLibCXXCompressedPair( if (first_child) value = first_child->GetChildMemberWithName("__value_"); if (!value) { - // pre-r300140 member name + // pre-c88580c member name value = pair.GetChildMemberWithName("__first_"); } return value; @@ -70,7 +97,7 @@ lldb_private::formatters::GetSecondValueOfLibCXXCompressedPair( } } if (!value) { - // pre-r300140 member name + // pre-c88580c member name value = pair.GetChildMemberWithName("__second_"); } return value; @@ -176,7 +203,9 @@ bool lldb_private::formatters::LibcxxUniquePointerSummaryProvider( if (!ptr_sp) return false; - ptr_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp); + if (isOldCompressedPairLayout(*ptr_sp)) + ptr_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp); + if (!ptr_sp) return false; @@ -363,13 +392,22 @@ lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::Update() { // Retrieve the actual pointer and the deleter, and clone them to give them // user-friendly names. - ValueObjectSP value_pointer_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp); - if (value_pointer_sp) - m_value_ptr_sp = value_pointer_sp->Clone(ConstString("pointer")); + if (isOldCompressedPairLayout(*ptr_sp)) { + if (ValueObjectSP value_pointer_sp = + GetFirstValueOfLibCXXCompressedPair(*ptr_sp)) + m_value_ptr_sp = value_pointer_sp->Clone(ConstString("pointer")); + + if (ValueObjectSP deleter_sp = + GetSecondValueOfLibCXXCompressedPair(*ptr_sp)) + m_deleter_sp = deleter_sp->Clone(ConstString("deleter")); + } else { + m_value_ptr_sp = ptr_sp->Clone(ConstString("pointer")); - ValueObjectSP deleter_sp = GetSecondValueOfLibCXXCompressedPair(*ptr_sp); - if (deleter_sp) - m_deleter_sp = deleter_sp->Clone(ConstString("deleter")); + if (ValueObjectSP deleter_sp = + valobj_sp->GetChildMemberWithName("__deleter_")) + if (deleter_sp->GetNumChildrenIgnoringErrors() > 0) + m_deleter_sp = deleter_sp->Clone(ConstString("deleter")); + } return lldb::ChildCacheState::eRefetch; } @@ -407,24 +445,27 @@ namespace { enum class StringLayout { CSD, DSC }; } +static ValueObjectSP ExtractLibCxxStringData(ValueObject &valobj) { + if (auto rep_sp = valobj.GetChildMemberWithName("__rep_")) + return rep_sp; + + ValueObjectSP valobj_r_sp = valobj.GetChildMemberWithName("__r_"); + if (!valobj_r_sp || !valobj_r_sp->GetError().Success()) + return nullptr; + + if (!isOldCompressedPairLayout(*valobj_r_sp)) + return nullptr; + + return GetFirstValueOfLibCXXCompressedPair(*valobj_r_sp); +} + /// Determine the size in bytes of \p valobj (a libc++ std::string object) and /// extract its data payload. Return the size + payload pair. // TODO: Support big-endian architectures. static std::optional> ExtractLibcxxStringInfo(ValueObject &valobj) { - ValueObjectSP valobj_r_sp = valobj.GetChildMemberWithName("__r_"); - if (!valobj_r_sp || !valobj_r_sp->GetError().Success()) - return {}; - - // __r_ is a compressed_pair of the actual data and the allocator. The data we - // want is in the first base class. - ValueObjectSP valobj_r_base_sp = valobj_r_sp->GetChildAtIndex(0); - if (!valobj_r_base_sp) - return {}; - - ValueObjectSP valobj_rep_sp = - valobj_r_base_sp->GetChildMemberWithName("__value_"); - if (!valobj_rep_sp) + ValueObjectSP valobj_rep_sp = ExtractLibCxxStringData(valobj); + if (!valobj_rep_sp || !valobj_rep_sp->GetError().Success()) return {}; ValueObjectSP l = valobj_rep_sp->GetChildMemberWithName("__l"); diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h index 159616c74be90..cb9ceaf093300 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h @@ -25,7 +25,8 @@ GetChildMemberWithName(ValueObject &obj, lldb::ValueObjectSP GetFirstValueOfLibCXXCompressedPair(ValueObject &pair); lldb::ValueObjectSP GetSecondValueOfLibCXXCompressedPair(ValueObject &pair); - +bool isOldCompressedPairLayout(ValueObject &pair_obj); +bool isStdTemplate(ConstString type_name, llvm::StringRef type); bool LibcxxStringSummaryProviderASCII( ValueObject &valobj, Stream &stream, diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp index f92f263c87311..f33b148249ab9 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp @@ -295,12 +295,17 @@ lldb::ChildCacheState ForwardListFrontEnd::Update() { ValueObjectSP impl_sp(m_backend.GetChildMemberWithName("__before_begin_")); if (!impl_sp) - return lldb::ChildCacheState::eRefetch; - impl_sp = GetFirstValueOfLibCXXCompressedPair(*impl_sp); + return ChildCacheState::eRefetch; + + if (isOldCompressedPairLayout(*impl_sp)) + impl_sp = GetFirstValueOfLibCXXCompressedPair(*impl_sp); + if (!impl_sp) - return lldb::ChildCacheState::eRefetch; + return ChildCacheState::eRefetch; + m_head = impl_sp->GetChildMemberWithName("__next_").get(); - return lldb::ChildCacheState::eRefetch; + + return ChildCacheState::eRefetch; } ListFrontEnd::ListFrontEnd(lldb::ValueObjectSP valobj_sp) @@ -314,34 +319,42 @@ llvm::Expected ListFrontEnd::CalculateNumChildren() { return m_count; if (!m_head || !m_tail || m_node_address == 0) return 0; - ValueObjectSP size_alloc(m_backend.GetChildMemberWithName("__size_alloc_")); - if (size_alloc) { - ValueObjectSP value = GetFirstValueOfLibCXXCompressedPair(*size_alloc); - if (value) { - m_count = value->GetValueAsUnsigned(UINT32_MAX); - } + + ValueObjectSP size_node_sp(m_backend.GetChildMemberWithName("__size_")); + if (!size_node_sp) { + size_node_sp = m_backend.GetChildMemberWithName( + "__size_alloc_"); // pre-compressed_pair rework + + if (!isOldCompressedPairLayout(*size_node_sp)) + return llvm::createStringError("Unexpected std::list layout: expected " + "old __compressed_pair layout."); + + size_node_sp = GetFirstValueOfLibCXXCompressedPair(*size_node_sp); } - if (m_count != UINT32_MAX) { + + if (size_node_sp) + m_count = size_node_sp->GetValueAsUnsigned(UINT32_MAX); + + if (m_count != UINT32_MAX) return m_count; - } else { - uint64_t next_val = m_head->GetValueAsUnsigned(0); - uint64_t prev_val = m_tail->GetValueAsUnsigned(0); - if (next_val == 0 || prev_val == 0) - return 0; - if (next_val == m_node_address) - return 0; - if (next_val == prev_val) - return 1; - uint64_t size = 2; - ListEntry current(m_head); - while (current.next() && current.next().value() != m_node_address) { - size++; - current = current.next(); - if (size > m_list_capping_size) - break; - } - return m_count = (size - 1); + + uint64_t next_val = m_head->GetValueAsUnsigned(0); + uint64_t prev_val = m_tail->GetValueAsUnsigned(0); + if (next_val == 0 || prev_val == 0) + return 0; + if (next_val == m_node_address) + return 0; + if (next_val == prev_val) + return 1; + uint64_t size = 2; + ListEntry current(m_head); + while (current.next() && current.next().value() != m_node_address) { + size++; + current = current.next(); + if (size > m_list_capping_size) + break; } + return m_count = (size - 1); } lldb::ValueObjectSP ListFrontEnd::GetChildAtIndex(uint32_t idx) { diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp index e62bff9bd19ac..ebaf60a16b069 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp @@ -202,6 +202,8 @@ class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd { size_t GetIndexOfChildWithName(ConstString name) override; private: + llvm::Expected CalculateNumChildrenForOldCompressedPairLayout(); + /// Returns the ValueObject for the __tree_node type that /// holds the key/value pair of the node at index \ref idx. /// @@ -254,6 +256,27 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: Update(); } +llvm::Expected +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: + CalculateNumChildrenForOldCompressedPairLayout() { + ValueObjectSP node_sp(m_tree->GetChildMemberWithName("__pair3_")); + if (!node_sp) + return 0; + + if (!isOldCompressedPairLayout(*node_sp)) + return llvm::createStringError("Unexpected std::map layout: expected " + "old __compressed_pair layout."); + + node_sp = GetFirstValueOfLibCXXCompressedPair(*node_sp); + + if (!node_sp) + return 0; + + m_count = node_sp->GetValueAsUnsigned(0); + + return m_count; +} + llvm::Expected lldb_private::formatters:: LibcxxStdMapSyntheticFrontEnd::CalculateNumChildren() { if (m_count != UINT32_MAX) @@ -262,17 +285,12 @@ llvm::Expected lldb_private::formatters:: if (m_tree == nullptr) return 0; - ValueObjectSP size_node(m_tree->GetChildMemberWithName("__pair3_")); - if (!size_node) - return 0; - - size_node = GetFirstValueOfLibCXXCompressedPair(*size_node); - - if (!size_node) - return 0; + if (auto node_sp = m_tree->GetChildMemberWithName("__size_")) { + m_count = node_sp->GetValueAsUnsigned(0); + return m_count; + } - m_count = size_node->GetValueAsUnsigned(0); - return m_count; + return CalculateNumChildrenForOldCompressedPairLayout(); } ValueObjectSP @@ -371,6 +389,7 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() { m_tree = m_backend.GetChildMemberWithName("__tree_").get(); if (!m_tree) return lldb::ChildCacheState::eRefetch; + m_root_node = m_tree->GetChildMemberWithName("__begin_node_").get(); m_node_ptr_type = m_tree->GetCompilerType().GetDirectNestedTypeWithName("__node_pointer"); diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp index 105ec6b9c4a3e..bf91fc42482f3 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp @@ -19,6 +19,7 @@ #include "lldb/ValueObject/ValueObject.h" #include "lldb/ValueObject/ValueObjectConstResult.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" using namespace lldb; using namespace lldb_private; @@ -44,6 +45,10 @@ class LibcxxStdUnorderedMapSyntheticFrontEnd size_t GetIndexOfChildWithName(ConstString name) override; private: + CompilerType GetNodeType(); + CompilerType GetElementType(CompilerType node_type); + llvm::Expected CalculateNumChildrenImpl(ValueObject &table); + CompilerType m_element_type; CompilerType m_node_type; ValueObject *m_tree = nullptr; @@ -91,29 +96,53 @@ llvm::Expected lldb_private::formatters:: return m_num_elements; } -static void consumeInlineNamespace(llvm::StringRef &name) { - // Delete past an inline namespace, if any: __[a-zA-Z0-9_]+:: - auto scratch = name; - if (scratch.consume_front("__") && std::isalnum(scratch[0])) { - scratch = scratch.drop_while([](char c) { return std::isalnum(c); }); - if (scratch.consume_front("::")) { - // Successfully consumed a namespace. - name = scratch; - } - } +static bool isUnorderedMap(ConstString type_name) { + return isStdTemplate(type_name, "unordered_map") || + isStdTemplate(type_name, "unordered_multimap"); } -static bool isStdTemplate(ConstString type_name, llvm::StringRef type) { - llvm::StringRef name = type_name.GetStringRef(); - // The type name may be prefixed with `std::__::`. - if (name.consume_front("std::")) - consumeInlineNamespace(name); - return name.consume_front(type) && name.starts_with("<"); +CompilerType lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: + GetElementType(CompilerType node_type) { + CompilerType element_type = node_type.GetTypeTemplateArgument(0); + + // This synthetic provider is used for both unordered_(multi)map and + // unordered_(multi)set. For unordered_map, the element type has an + // additional type layer, an internal struct (`__hash_value_type`) + // that wraps a std::pair. Peel away the internal wrapper type - whose + // structure is of no value to users, to expose the std::pair. This + // matches the structure returned by the std::map synthetic provider. + if (isUnorderedMap(m_backend.GetTypeName())) { + std::string name; + CompilerType field_type = + element_type.GetFieldAtIndex(0, name, nullptr, nullptr, nullptr); + CompilerType actual_type = field_type.GetTypedefedType(); + if (isStdTemplate(actual_type.GetTypeName(), "pair")) + element_type = actual_type; + } + + return element_type; } -static bool isUnorderedMap(ConstString type_name) { - return isStdTemplate(type_name, "unordered_map") || - isStdTemplate(type_name, "unordered_multimap"); +CompilerType lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: + GetNodeType() { + auto node_sp = m_backend.GetChildAtNamePath({"__table_", "__first_node_"}); + + if (!node_sp) { + auto p1_sp = m_backend.GetChildAtNamePath({"__table_", "__p1_"}); + if (!p1_sp) + return {}; + + if (!isOldCompressedPairLayout(*p1_sp)) + return {}; + + node_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp); + if (!node_sp) + return {}; + } + + assert(node_sp); + + return node_sp->GetCompilerType().GetTypeTemplateArgument(0).GetPointeeType(); } lldb::ValueObjectSP lldb_private::formatters:: @@ -136,36 +165,12 @@ lldb::ValueObjectSP lldb_private::formatters:: ValueObjectSP hash_sp = node_sp->GetChildMemberWithName("__hash_"); if (!hash_sp || !value_sp) { if (!m_element_type) { - auto p1_sp = m_backend.GetChildAtNamePath({"__table_", "__p1_"}); - if (!p1_sp) - return nullptr; - - ValueObjectSP first_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp); - if (!first_sp) + m_node_type = GetNodeType(); + if (!m_node_type) return nullptr; - m_element_type = first_sp->GetCompilerType(); - m_element_type = m_element_type.GetTypeTemplateArgument(0); - m_element_type = m_element_type.GetPointeeType(); - m_node_type = m_element_type; - m_element_type = m_element_type.GetTypeTemplateArgument(0); - // This synthetic provider is used for both unordered_(multi)map and - // unordered_(multi)set. For unordered_map, the element type has an - // additional type layer, an internal struct (`__hash_value_type`) - // that wraps a std::pair. Peel away the internal wrapper type - whose - // structure is of no value to users, to expose the std::pair. This - // matches the structure returned by the std::map synthetic provider. - if (isUnorderedMap(m_backend.GetTypeName())) { - std::string name; - CompilerType field_type = m_element_type.GetFieldAtIndex( - 0, name, nullptr, nullptr, nullptr); - CompilerType actual_type = field_type.GetTypedefedType(); - if (isStdTemplate(actual_type.GetTypeName(), "pair")) - m_element_type = actual_type; - } + m_element_type = GetElementType(m_node_type); } - if (!m_node_type) - return nullptr; node_sp = m_next_element->Cast(m_node_type.GetPointerType()) ->Dereference(error); if (!node_sp || error.Fail()) @@ -217,6 +222,49 @@ lldb::ValueObjectSP lldb_private::formatters:: m_element_type); } +llvm::Expected +lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: + CalculateNumChildrenImpl(ValueObject &table) { + if (auto size_sp = table.GetChildMemberWithName("__size_")) + return size_sp->GetValueAsUnsigned(0); + + ValueObjectSP p2_sp = table.GetChildMemberWithName("__p2_"); + if (!p2_sp) + return llvm::createStringError( + "Unexpected std::unordered_map layout: __p2_ member not found."); + + if (!isOldCompressedPairLayout(*p2_sp)) + return llvm::createStringError("Unexpected std::unordered_map layout: old " + "__compressed_pair layout not found."); + + ValueObjectSP num_elements_sp = GetFirstValueOfLibCXXCompressedPair(*p2_sp); + + if (!num_elements_sp) + return llvm::createStringError( + "Unexpected std::unordered_map layout: failed to retrieve first member " + "in old __compressed_pair layout."); + + return num_elements_sp->GetValueAsUnsigned(0); +} + +static ValueObjectSP GetTreePointer(ValueObject &table) { + ValueObjectSP tree_sp = table.GetChildMemberWithName("__first_node_"); + if (!tree_sp) { + ValueObjectSP p1_sp = table.GetChildMemberWithName("__p1_"); + if (!p1_sp) + return nullptr; + + if (!isOldCompressedPairLayout(*p1_sp)) + return nullptr; + + tree_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp); + if (!tree_sp) + return nullptr; + } + + return tree_sp->GetChildMemberWithName("__next_"); +} + lldb::ChildCacheState lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::Update() { m_num_elements = 0; @@ -226,27 +274,19 @@ lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::Update() { if (!table_sp) return lldb::ChildCacheState::eRefetch; - ValueObjectSP p2_sp = table_sp->GetChildMemberWithName("__p2_"); - if (!p2_sp) - return lldb::ChildCacheState::eRefetch; - - ValueObjectSP num_elements_sp = GetFirstValueOfLibCXXCompressedPair(*p2_sp); - if (!num_elements_sp) - return lldb::ChildCacheState::eRefetch; - - ValueObjectSP p1_sp = table_sp->GetChildMemberWithName("__p1_"); - if (!p1_sp) + ValueObjectSP tree_sp = GetTreePointer(*table_sp); + if (!tree_sp) return lldb::ChildCacheState::eRefetch; - ValueObjectSP value_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp); - if (!value_sp) - return lldb::ChildCacheState::eRefetch; + m_tree = tree_sp.get(); - m_tree = value_sp->GetChildMemberWithName("__next_").get(); - if (m_tree == nullptr) + if (auto num_elems_or_err = CalculateNumChildrenImpl(*table_sp)) + m_num_elements = *num_elems_or_err; + else { + LLDB_LOG_ERRORV(GetLog(LLDBLog::DataFormatters), + num_elems_or_err.takeError(), "{0}"); return lldb::ChildCacheState::eRefetch; - - m_num_elements = num_elements_sp->GetValueAsUnsigned(0); + } if (m_num_elements > 0) m_next_element = m_tree; diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp index 0e5137bf1a722..64231b5939b76 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp @@ -118,20 +118,30 @@ lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::GetChildAtIndex( m_element_type); } +static ValueObjectSP GetDataPointer(ValueObject &root) { + if (auto cap_sp = root.GetChildMemberWithName("__cap_")) + return cap_sp; + + ValueObjectSP cap_sp = root.GetChildMemberWithName("__end_cap_"); + if (!cap_sp) + return nullptr; + + if (!isOldCompressedPairLayout(*cap_sp)) + return nullptr; + + return GetFirstValueOfLibCXXCompressedPair(*cap_sp); +} + lldb::ChildCacheState lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::Update() { m_start = m_finish = nullptr; - ValueObjectSP data_type_finder_sp( - m_backend.GetChildMemberWithName("__end_cap_")); - if (!data_type_finder_sp) - return lldb::ChildCacheState::eRefetch; + ValueObjectSP data_sp(GetDataPointer(m_backend)); - data_type_finder_sp = - GetFirstValueOfLibCXXCompressedPair(*data_type_finder_sp); - if (!data_type_finder_sp) + if (!data_sp) return lldb::ChildCacheState::eRefetch; - m_element_type = data_type_finder_sp->GetCompilerType().GetPointeeType(); + m_element_type = data_sp->GetCompilerType().GetPointeeType(); + llvm::Expected size_or_err = m_element_type.GetByteSize(nullptr); if (!size_or_err) LLDB_LOG_ERRORV(GetLog(LLDBLog::Types), size_or_err.takeError(), "{0}"); @@ -222,17 +232,6 @@ lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::GetChildAtIndex( return retval_sp; } -/*(std::__1::vector >) vBool = { - __begin_ = 0x00000001001000e0 - __size_ = 56 - __cap_alloc_ = { - std::__1::__libcpp_compressed_pair_imp > = { - __first_ = 1 - } - } - }*/ - lldb::ChildCacheState lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::Update() { m_children.clear(); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp index 0cd2db86e5d7a..e53e930665a60 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp @@ -37,7 +37,7 @@ DWARFASTParser::ParseChildArrayInfo(const DWARFDIE &parent_die, if (attributes.Size() == 0) continue; - uint64_t num_elements = 0; + std::optional num_elements; uint64_t lower_bound = 0; uint64_t upper_bound = 0; bool upper_bound_valid = false; @@ -91,7 +91,7 @@ DWARFASTParser::ParseChildArrayInfo(const DWARFDIE &parent_die, } } - if (num_elements == 0) { + if (!num_elements || *num_elements == 0) { if (upper_bound_valid && upper_bound >= lower_bound) num_elements = upper_bound - lower_bound + 1; } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index f96ed38a2c0f8..93a47d748a4e5 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -1502,20 +1502,20 @@ DWARFASTParserClang::ParseArrayType(const DWARFDIE &die, uint64_t array_element_bit_stride = byte_stride * 8 + bit_stride; CompilerType clang_type; if (array_info && array_info->element_orders.size() > 0) { - uint64_t num_elements = 0; auto end = array_info->element_orders.rend(); for (auto pos = array_info->element_orders.rbegin(); pos != end; ++pos) { - num_elements = *pos; - clang_type = m_ast.CreateArrayType(array_element_type, num_elements, - attrs.is_vector); + clang_type = m_ast.CreateArrayType( + array_element_type, /*element_count=*/*pos, attrs.is_vector); + + uint64_t num_elements = pos->value_or(0); array_element_type = clang_type; array_element_bit_stride = num_elements ? array_element_bit_stride * num_elements : array_element_bit_stride; } } else { - clang_type = - m_ast.CreateArrayType(array_element_type, 0, attrs.is_vector); + clang_type = m_ast.CreateArrayType( + array_element_type, /*element_count=*/std::nullopt, attrs.is_vector); } ConstString empty_name; TypeSP type_sp = diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 5fdbf0af1a6f5..3dc6e391e5d83 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -2290,30 +2290,31 @@ TypeSystemClang::CreateBlockPointerType(const CompilerType &function_type) { #pragma mark Array Types -CompilerType TypeSystemClang::CreateArrayType(const CompilerType &element_type, - size_t element_count, - bool is_vector) { - if (element_type.IsValid()) { - ASTContext &ast = getASTContext(); +CompilerType +TypeSystemClang::CreateArrayType(const CompilerType &element_type, + std::optional element_count, + bool is_vector) { + if (!element_type.IsValid()) + return {}; - if (is_vector) { - return GetType(ast.getExtVectorType(ClangUtil::GetQualType(element_type), - element_count)); - } else { + ASTContext &ast = getASTContext(); - llvm::APInt ap_element_count(64, element_count); - if (element_count == 0) { - return GetType( - ast.getIncompleteArrayType(ClangUtil::GetQualType(element_type), - clang::ArraySizeModifier::Normal, 0)); - } else { - return GetType(ast.getConstantArrayType( - ClangUtil::GetQualType(element_type), ap_element_count, nullptr, - clang::ArraySizeModifier::Normal, 0)); - } - } - } - return CompilerType(); + // Unknown number of elements; this is an incomplete array + // (e.g., variable length array with non-constant bounds, or + // a flexible array member). + if (!element_count) + return GetType( + ast.getIncompleteArrayType(ClangUtil::GetQualType(element_type), + clang::ArraySizeModifier::Normal, 0)); + + if (is_vector) + return GetType(ast.getExtVectorType(ClangUtil::GetQualType(element_type), + *element_count)); + + llvm::APInt ap_element_count(64, *element_count); + return GetType(ast.getConstantArrayType(ClangUtil::GetQualType(element_type), + ap_element_count, nullptr, + clang::ArraySizeModifier::Normal, 0)); } CompilerType TypeSystemClang::CreateStructForIdentifier( @@ -4956,66 +4957,31 @@ TypeSystemClang::GetBitSize(lldb::opaque_compiler_type_t type, return llvm::createStringError( "could not complete type %s", GetTypeName(type, base_name_only).AsCString("")); - if (GetCompleteType(type)) { - clang::QualType qual_type(GetCanonicalQualType(type)); - const clang::Type::TypeClass type_class = qual_type->getTypeClass(); - switch (type_class) { - case clang::Type::Record: - if (GetCompleteType(type)) - return getASTContext().getTypeSize(qual_type); - else - return llvm::createStringError( - "could not complete type %s", - GetTypeName(type, base_name_only).AsCString("")); - break; - case clang::Type::ObjCInterface: - case clang::Type::ObjCObject: { - ExecutionContext exe_ctx(exe_scope); - Process *process = exe_ctx.GetProcessPtr(); - if (process) { - if (ObjCLanguageRuntime *objc_runtime = - ObjCLanguageRuntime::Get(*process)) { - if (std::optional bit_size = - objc_runtime->GetTypeBitSize(GetType(qual_type))) - return *bit_size; - } - } else { - static bool g_printed = false; - if (!g_printed) { - StreamString s; - DumpTypeDescription(type, s); - - llvm::outs() << "warning: trying to determine the size of type "; - llvm::outs() << s.GetString() << "\n"; - llvm::outs() << "without a valid ExecutionContext. this is not " - "reliable. please file a bug against LLDB.\n"; - llvm::outs() << "backtrace:\n"; - llvm::sys::PrintStackTrace(llvm::outs()); - llvm::outs() << "\n"; - g_printed = true; - } - } - } - [[fallthrough]]; - default: - const uint32_t bit_size = getASTContext().getTypeSize(qual_type); - if (bit_size == 0) { - if (qual_type->isIncompleteArrayType()) - return getASTContext().getTypeSize( - qual_type->getArrayElementTypeNoTypeQual() - ->getCanonicalTypeUnqualified()); - } - if (qual_type->isObjCObjectOrInterfaceType()) - return bit_size + - getASTContext().getTypeSize(getASTContext().ObjCBuiltinClassTy); - // Function types actually have a size of 0, that's not an error. - if (qual_type->isFunctionProtoType()) - return bit_size; - if (bit_size) - return bit_size; - } + clang::QualType qual_type(GetCanonicalQualType(type)); + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) { + case clang::Type::ConstantArray: + case clang::Type::FunctionProto: + case clang::Type::Record: + return getASTContext().getTypeSize(qual_type); + case clang::Type::ObjCInterface: + case clang::Type::ObjCObject: + return GetObjCBitSize(qual_type, exe_scope); + case clang::Type::IncompleteArray: { + const uint64_t bit_size = getASTContext().getTypeSize(qual_type); + if (bit_size == 0) + return getASTContext().getTypeSize( + qual_type->getArrayElementTypeNoTypeQual() + ->getCanonicalTypeUnqualified()); + + return bit_size; } + default: + if (const uint64_t bit_size = getASTContext().getTypeSize(qual_type)) + return bit_size; + } + return llvm::createStringError( "could not get size of type %s", GetTypeName(type, base_name_only).AsCString("")); @@ -5695,9 +5661,9 @@ TypeSystemClang::GetNumChildren(lldb::opaque_compiler_type_t type, case clang::Type::IncompleteArray: if (auto array_info = GetDynamicArrayInfo(*this, GetSymbolFile(), qual_type, exe_ctx)) - // Only 1-dimensional arrays are supported. + // FIXME: Only 1-dimensional arrays are supported. num_children = array_info->element_orders.size() - ? array_info->element_orders.back() + ? array_info->element_orders.back().value_or(0) : 0; break; diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 4abe4aa109611..778f444b9e1bc 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -535,7 +535,8 @@ class TypeSystemClang : public TypeSystem { // Array Types CompilerType CreateArrayType(const CompilerType &element_type, - size_t element_count, bool is_vector); + std::optional element_count, + bool is_vector); // Enumeration Types clang::EnumDecl *CreateEnumerationDecl( diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py index 270aab1b75122..8546b1f3b6431 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py @@ -60,9 +60,7 @@ def cleanup(): self.runCmd("type format add -f hex int") self.expect( - "frame variable numbers_list --raw", - matching=False, - substrs=["size=0", "{}"], + "frame variable numbers_list --raw", matching=False, substrs=["size=0"] ) if stdlib_type == USE_LIBSTDCPP: diff --git a/lldb/test/API/lang/c/struct_types/main.c b/lldb/test/API/lang/c/struct_types/main.c index e683c49108976..70217c57bec5f 100644 --- a/lldb/test/API/lang/c/struct_types/main.c +++ b/lldb/test/API/lang/c/struct_types/main.c @@ -1,3 +1,4 @@ +// clang-format off struct things_to_sum { int a; int b; @@ -18,7 +19,7 @@ int main (int argc, char const *argv[]) }; //% self.expect("frame variable pt.padding[0]", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["pt.padding[0] = "]) //% self.expect("frame variable pt.padding[1]", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["pt.padding[1] = "]) //% self.expect_expr("pt.padding[0]", result_type="char") - //% self.expect("image lookup -t point_tag", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ['padding[]']) + //% self.expect("image lookup -t point_tag", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ['padding[0]']) struct {} empty; //% self.expect("frame variable empty", substrs = ["empty = {}"]) diff --git a/lldb/test/Shell/SymbolFile/DWARF/vla.cpp b/lldb/test/Shell/SymbolFile/DWARF/vla.cpp new file mode 100644 index 0000000000000..47a4aa1836899 --- /dev/null +++ b/lldb/test/Shell/SymbolFile/DWARF/vla.cpp @@ -0,0 +1,83 @@ +// When linking with link.exe, -gdwarf still produces PDB instead. +// UNSUPPORTED: system-windows + +// RUN: %clangxx_host -gdwarf -std=c++11 -o %t %s +// RUN: %lldb %t \ +// RUN: -o run \ +// RUN: -o "frame var --show-types f" \ +// RUN: -o "frame var vla0" \ +// RUN: -o "frame var fla0" \ +// RUN: -o "frame var fla1" \ +// RUN: -o "frame var vla01" \ +// RUN: -o "frame var vla10" \ +// RUN: -o "frame var vlaN" \ +// RUN: -o "frame var vlaNM" \ +// RUN: -o exit | FileCheck %s + +struct Foo { + static constexpr int n = 1; + int m_vlaN[n]; + + int m_vla0[0]; +}; + +int main() { + Foo f; + f.m_vlaN[0] = 60; + + // CHECK: (lldb) frame var --show-types f + // CHECK-NEXT: (Foo) f = { + // CHECK-NEXT: (int[1]) m_vlaN = { + // CHECK-NEXT: (int) [0] = 60 + // CHECK-NEXT: } + // CHECK-NEXT: (int[0]) m_vla0 = {} + // CHECK-NEXT: } + + int vla0[0] = {}; + + // CHECK: (lldb) frame var vla0 + // CHECK-NEXT: (int[0]) vla0 = {} + + int fla0[] = {}; + + // CHECK: (lldb) frame var fla0 + // CHECK-NEXT: (int[0]) fla0 = {} + + int fla1[] = {42}; + + // CHECK: (lldb) frame var fla1 + // CHECK-NEXT: (int[1]) fla1 = ([0] = 42) + + int vla01[0][1]; + + // CHECK: (lldb) frame var vla01 + // CHECK-NEXT: (int[0][1]) vla01 = {} + + int vla10[1][0]; + + // CHECK: (lldb) frame var vla10 + // CHECK-NEXT: (int[1][0]) vla10 = ([0] = int[0] + + int n = 3; + int vlaN[n]; + for (int i = 0; i < n; ++i) + vlaN[i] = -i; + + // CHECK: (lldb) frame var vlaN + // CHECK-NEXT: (int[]) vlaN = ([0] = 0, [1] = -1, [2] = -2) + + int m = 2; + int vlaNM[n][m]; + for (int i = 0; i < n; ++i) + for (int j = 0; j < m; ++j) + vlaNM[i][j] = i + j; + + // FIXME: multi-dimensional VLAs aren't well supported + // CHECK: (lldb) frame var vlaNM + // CHECK-NEXT: (int[][]) vlaNM = { + // CHECK-NEXT: [0] = ([0] = 0, [1] = 1, [2] = 1) + // CHECK-NEXT: [1] = ([0] = 1, [1] = 1, [2] = 2) + // CHECK-NEXT: } + + __builtin_debugtrap(); +}