Author: Daedie-git
Date: 2026-08-22T11:59:18+02:00
New Revision: b8193ac24fe88f5e41fed451fbfcc7f92589fb47

URL: 
https://github.com/llvm/llvm-project/commit/b8193ac24fe88f5e41fed451fbfcc7f92589fb47
DIFF: 
https://github.com/llvm/llvm-project/commit/b8193ac24fe88f5e41fed451fbfcc7f92589fb47.diff

LOG: [lldb] Add MSVC STL formatter for std::valarray (#217242)

Pretty-print MSVC STL `std::valarray` from `_Myptr` / `_Mysize`. The
size summary is emitted only when that layout is present so libstdc++
valarrays (same `std::` name, no inline namespace) are not stolen.
References are dereferenced and the element type is taken from `_Myptr`.

Tests: generic valarray suite's MSVC STL category (Windows), covering
empty arrays, references, exact synthetic children, and the libstdc++
dispatch guard.

Part of #24834

Assisted-by: Grok 4.6
Assisted-by: codex-5.6-high

---------

Co-authored-by: Bjorn Schobben <[email protected]>

Added: 
    lldb/source/Plugins/Language/CPlusPlus/MsvcStlValarray.cpp

Modified: 
    lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
    lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
    lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
    
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/valarray/TestDataFormatterStdValarray.py
    
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/valarray/main.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt 
b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
index 1a4717fe5fe9c..2118b614dd228 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
+++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
@@ -49,6 +49,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
   MsvcStlTree.cpp
   MsvcStlTuple.cpp
   MsvcStlUnordered.cpp
+  MsvcStlValarray.cpp
   MsvcStlVariant.cpp
   MsvcStlVector.cpp
   MSVCUndecoratedNameParser.cpp

diff  --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp 
b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index ca5aeed182dda..c5b277311eadc 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -1753,6 +1753,23 @@ static bool GenericExpectedSummaryProvider(ValueObject 
&valobj, Stream &stream,
   return false;
 }
 
+static SyntheticChildrenFrontEnd *
+GenericValarraySyntheticFrontEndCreator(CXXSyntheticChildren *children,
+                                        lldb::ValueObjectSP valobj_sp) {
+  if (!valobj_sp)
+    return nullptr;
+  if (IsMsvcStlValarray(*valobj_sp))
+    return MsvcStlValarraySyntheticFrontEndCreator(children, valobj_sp);
+  return nullptr;
+}
+
+static bool GenericValarraySummaryProvider(ValueObject &valobj, Stream &stream,
+                                           const TypeSummaryOptions &options) {
+  if (!IsMsvcStlValarray(valobj))
+    return false;
+  return ContainerSizeSummaryProvider(valobj, stream, options);
+}
+
 /// Load formatters that are formatting types from more than one STL
 static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
   if (!cpp_category_sp)
@@ -2024,6 +2041,13 @@ static void 
LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
   AddCXXSummary(cpp_category_sp, GenericExpectedSummaryProvider,
                 "MSVC STL std::expected summary provider",
                 "^std::expected<.+>(( )?&)?$", stl_summary_flags, true);
+
+  AddCXXSynthetic(cpp_category_sp, GenericValarraySyntheticFrontEndCreator,
+                  "MSVC STL std::valarray synthetic children",
+                  "^std::valarray<.+>(( )?&)?$", stl_deref_flags, true);
+  AddCXXSummary(cpp_category_sp, GenericValarraySummaryProvider,
+                "MSVC STL std::valarray summary provider",
+                "^std::valarray<.+>(( )?&)?$", stl_summary_flags, true);
 }
 
 static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {

diff  --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h 
b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
index b1f6fce2b1da4..d488a6e7c1d32 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
@@ -159,6 +159,12 @@ SyntheticChildrenFrontEnd *
 MsvcStlExpectedSyntheticFrontEndCreator(CXXSyntheticChildren *,
                                         lldb::ValueObjectSP valobj_sp);
 
+// MSVC STL std::valarray<>
+bool IsMsvcStlValarray(ValueObject &valobj);
+SyntheticChildrenFrontEnd *
+MsvcStlValarraySyntheticFrontEndCreator(CXXSyntheticChildren *,
+                                        lldb::ValueObjectSP valobj_sp);
+
 } // namespace formatters
 } // namespace lldb_private
 

diff  --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlValarray.cpp 
b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlValarray.cpp
new file mode 100644
index 0000000000000..89c37b803e0f5
--- /dev/null
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlValarray.cpp
@@ -0,0 +1,112 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MsvcStl.h"
+
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/ValueObject/ValueObject.h"
+#include "llvm/Support/ErrorExtras.h"
+#include <cinttypes>
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+namespace {
+class MsvcStlValarraySyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+  MsvcStlValarraySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+      : SyntheticChildrenFrontEnd(*valobj_sp) {
+    if (valobj_sp)
+      Update();
+  }
+
+  llvm::Expected<uint32_t> CalculateNumChildren() override {
+    if (!m_start || m_element_size == 0)
+      return 0;
+    return m_count;
+  }
+
+  lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override {
+    if (!m_start || idx >= m_count)
+      return {};
+
+    uint64_t offset = m_start->GetValueAsUnsigned(0) +
+                      static_cast<uint64_t>(idx) * m_element_size;
+    StreamString name;
+    name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+    return CreateChildValueObjectFromAddress(name.GetString(), offset,
+                                             
m_backend.GetExecutionContextRef(),
+                                             m_element_type);
+  }
+
+  lldb::ChildCacheState Update() override {
+    m_start = nullptr;
+    m_count = 0;
+    m_element_size = 0;
+
+    ValueObjectSP start = m_backend.GetChildMemberWithName("_Myptr");
+    ValueObjectSP size_sp = m_backend.GetChildMemberWithName("_Mysize");
+    if (!start || !size_sp)
+      return ChildCacheState::eRefetch;
+
+    // Prefer the pointer's pointee type: template arguments are missing on
+    // references unless the frontend asked for a dereference, and PDB may
+    // omit them entirely.
+    m_element_type = start->GetCompilerType().GetPointeeType();
+    if (!m_element_type.IsValid()) {
+      CompilerType type = m_backend.GetCompilerType().GetNonReferenceType();
+      m_element_type = type.GetTypeTemplateArgument(0);
+    }
+    if (!m_element_type.IsValid())
+      return ChildCacheState::eRefetch;
+
+    if (std::optional<uint64_t> size =
+            llvm::expectedToOptional(m_element_type.GetByteSize(nullptr)))
+      m_element_size = *size;
+
+    if (m_element_size == 0)
+      return ChildCacheState::eRefetch;
+
+    m_start = start.get();
+    m_count = size_sp->GetValueAsUnsigned(0);
+    return ChildCacheState::eRefetch;
+  }
+
+  llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
+    if (!m_start)
+      return llvm::createStringErrorV("type has no child named '{0}'", name);
+    auto optional_idx = ExtractIndexFromString(name.GetCString());
+    if (!optional_idx)
+      return llvm::createStringErrorV("type has no child named '{0}'", name);
+    return *optional_idx;
+  }
+
+private:
+  /// A non-owning pointer to valarray's _Myptr member.
+  ValueObject *m_start = nullptr;
+  CompilerType m_element_type;
+  uint32_t m_element_size = 0;
+  uint64_t m_count = 0;
+};
+} // namespace
+
+bool formatters::IsMsvcStlValarray(ValueObject &valobj) {
+  if (auto valobj_sp = valobj.GetNonSyntheticValue())
+    return valobj_sp->GetChildMemberWithName("_Myptr") != nullptr;
+  return false;
+}
+
+SyntheticChildrenFrontEnd *formatters::MsvcStlValarraySyntheticFrontEndCreator(
+    CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+  if (!valobj_sp)
+    return nullptr;
+  return new MsvcStlValarraySyntheticFrontEnd(valobj_sp);
+}

diff  --git 
a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/valarray/TestDataFormatterStdValarray.py
 
b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/valarray/TestDataFormatterStdValarray.py
index 6386e431f80c6..fd12d8abed2fd 100644
--- 
a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/valarray/TestDataFormatterStdValarray.py
+++ 
b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/valarray/TestDataFormatterStdValarray.py
@@ -10,12 +10,9 @@
 
 class StdValarrayDataFormatterTestCase(TestBase):
     SHARED_BUILD_TESTCASE = False
+    TEST_WITH_PDB_DEBUG_INFO = True
 
-    def do_test(self):
-        (self.target, process, thread, bkpt) = 
lldbutil.run_to_source_breakpoint(
-            self, "break here", lldb.SBFileSpec("main.cpp", False)
-        )
-
+    def check_valarray(self, process, bkpt):
         #
         # std::valarray
         #
@@ -31,6 +28,18 @@ def do_test(self):
                 "}",
             ],
         )
+        self.expect("frame variable va_empty", substrs=["va_empty = size=0"])
+        self.expect(
+            "frame variable va_ref",
+            patterns=[r"va_ref = (0x[0-9a-fA-F]+ )?size=4"],
+            substrs=[
+                "[0] = 0",
+                "[1] = 0",
+                "[2] = 0",
+                "[3] = 0",
+                "}",
+            ],
+        )
 
         lldbutil.continue_to_breakpoint(process, bkpt)
         self.expect(
@@ -44,6 +53,17 @@ def do_test(self):
                 "}",
             ],
         )
+        self.expect(
+            "frame variable va_ref",
+            patterns=[r"va_ref = (0x[0-9a-fA-F]+ )?size=4"],
+            substrs=[
+                "[0] = 1",
+                "[1] = 12",
+                "[2] = 123",
+                "[3] = 1234",
+                "}",
+            ],
+        )
 
         # check access-by-index
         self.expect("frame variable va_int[0]", substrs=["1"])
@@ -79,6 +99,12 @@ def do_test(self):
             substrs=['array index 4 is not valid for "(valarray'],
         )
 
+    def do_test(self):
+        (self.target, process, thread, bkpt) = 
lldbutil.run_to_source_breakpoint(
+            self, "break here", lldb.SBFileSpec("main.cpp", False)
+        )
+        self.check_valarray(process, bkpt)
+
         #
         # std::slice_array
         #
@@ -182,3 +208,21 @@ def do_test(self):
     def test_libcxx(self):
         self.build(dictionary={"USE_LIBCPP": 1})
         self.do_test()
+
+    @add_test_categories(["libstdcxx"])
+    def test_libstdcxx_not_stolen_by_msvc(self):
+        """libstdc++ valarray must not get the MSVC size=raw-field-count 
summary."""
+        self.build(dictionary={"USE_LIBSTDCPP": 1})
+        lldbutil.run_to_source_breakpoint(
+            self, "break here", lldb.SBFileSpec("main.cpp", False)
+        )
+        va = self.expect_var_path("va_int")
+        self.assertNotEqual(va.summary, "size=2")
+
+    @add_test_categories(["msvcstl"])
+    def test_msvcstl(self):
+        self.build()
+        (self.target, process, thread, bkpt) = 
lldbutil.run_to_source_breakpoint(
+            self, "break here", lldb.SBFileSpec("main.cpp", False)
+        )
+        self.check_valarray(process, bkpt)

diff  --git 
a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/valarray/main.cpp
 
b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/valarray/main.cpp
index d31951c755eac..a6e5e18eae853 100644
--- 
a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/valarray/main.cpp
+++ 
b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/valarray/main.cpp
@@ -4,6 +4,8 @@
 int main() {
 
   std::valarray<int> va_int(4);
+  std::valarray<int> va_empty;
+  std::valarray<int> &va_ref = va_int;
   std::cout << "break here";
 
   va_int[0] = 1;


        
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to