[Lldb-commits] [lldb] [lldb] Add MSVC STL formatter for std::expected (PR #217243)
via lldb-commits
lldb-commits at lists.llvm.org
Fri Aug 21 09:05:34 PDT 2026
https://github.com/Daedie-git updated https://github.com/llvm/llvm-project/pull/217243
>From 754120d02038acbd04ce45bb9c2a6914950b94b5 Mon Sep 17 00:00:00 2001
From: Bjorn Schobben <bjorn.schobben at aimsport.com>
Date: Wed, 19 Aug 2026 10:55:43 +0200
Subject: [PATCH 1/4] [lldb] Add MSVC STL formatter for std::expected
Pretty-print engaged values and unexpected alternatives, including
expected<void, E>. Walk the anonymous union the same way as optional
so PDB debug info still finds _Value/_Unexpected.
Part of #24834
---
.../Plugins/Language/CPlusPlus/CMakeLists.txt | 1 +
.../Language/CPlusPlus/CPlusPlusLanguage.cpp | 24 +++
.../Plugins/Language/CPlusPlus/MsvcStl.h | 8 +
.../Language/CPlusPlus/MsvcStlExpected.cpp | 137 ++++++++++++++++++
.../msvc-simulators/expected/Makefile | 3 +
...stDataFormatterMsvcStlExpectedSimulator.py | 36 +++++
.../msvc-simulators/expected/main.cpp | 32 ++++
7 files changed, 241 insertions(+)
create mode 100644 lldb/source/Plugins/Language/CPlusPlus/MsvcStlExpected.cpp
create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/Makefile
create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/TestDataFormatterMsvcStlExpectedSimulator.py
create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/main.cpp
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
index c34f63637dbb4..1a4717fe5fe9c 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
+++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
@@ -43,6 +43,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
MsvcStl.cpp
MsvcStlAtomic.cpp
MsvcStlDeque.cpp
+ MsvcStlExpected.cpp
MsvcStlSmartPointer.cpp
MsvcStlSpan.cpp
MsvcStlTree.cpp
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index 30050e0878e6e..4e1a499594117 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -1725,6 +1725,23 @@ GenericStrongOrderingSummaryProvider(ValueObject &valobj, Stream &stream,
return LibStdcppStrongOrderingSummaryProvider(valobj, stream, options);
}
+static SyntheticChildrenFrontEnd *
+GenericExpectedSyntheticFrontEndCreator(CXXSyntheticChildren *children,
+ lldb::ValueObjectSP valobj_sp) {
+ if (!valobj_sp)
+ return nullptr;
+ if (IsMsvcStlExpected(*valobj_sp))
+ return MsvcStlExpectedSyntheticFrontEndCreator(children, valobj_sp);
+ return nullptr;
+}
+
+static bool GenericExpectedSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options) {
+ if (IsMsvcStlExpected(valobj))
+ return MsvcStlExpectedSummaryProvider(valobj, stream, options);
+ return false;
+}
+
/// Load formatters that are formatting types from more than one STL
static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
if (!cpp_category_sp)
@@ -1983,6 +2000,13 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
"MSVC STL/libstdc++ std::error_code/error_condition "
"synthetic children",
"^std::error_(code|condition)$", stl_synth_flags, true);
+
+ AddCXXSynthetic(cpp_category_sp, GenericExpectedSyntheticFrontEndCreator,
+ "MSVC STL std::expected synthetic children",
+ "^std::expected<.+>(( )?&)?$", stl_synth_flags, true);
+ AddCXXSummary(cpp_category_sp, GenericExpectedSummaryProvider,
+ "MSVC STL std::expected summary provider",
+ "^std::expected<.+>(( )?&)?$", 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 0bdd548dd99e3..3edb47ff800b6 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
@@ -142,6 +142,14 @@ SyntheticChildrenFrontEnd *
MsvcStlSpanSyntheticFrontEndCreator(CXXSyntheticChildren *,
lldb::ValueObjectSP valobj_sp);
+// MSVC STL std::expected<>
+bool IsMsvcStlExpected(ValueObject &valobj);
+bool MsvcStlExpectedSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+SyntheticChildrenFrontEnd *
+MsvcStlExpectedSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp);
+
} // namespace formatters
} // namespace lldb_private
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlExpected.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlExpected.cpp
new file mode 100644
index 0000000000000..ac2bef91ea4b5
--- /dev/null
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlExpected.cpp
@@ -0,0 +1,137 @@
+//===-- MsvcStlExpected.cpp -----------------------------------------------===//
+//
+// 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 "llvm/Support/ErrorExtras.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+namespace {
+
+/// `_Value` / `_Unexpected` live in an anonymous union. Direct
+/// GetChildMemberWithName from the class works on DWARF; PDB often needs the
+/// same parent/index walk GenericOptional uses for `_Value`.
+ValueObjectSP GetExpectedUnionMember(ValueObject &ns, const char *name) {
+ if (ValueObjectSP direct = ns.GetChildMemberWithName(name))
+ return direct;
+
+ ValueObjectSP has_sp = ns.GetChildMemberWithName("_Has_value");
+ if (!has_sp)
+ return {};
+ ValueObject *parent = has_sp->GetParent();
+ if (!parent)
+ return {};
+ ValueObjectSP first_sp = parent->GetChildAtIndex(0);
+ if (!first_sp)
+ return {};
+ if (ValueObjectSP nested = first_sp->GetChildMemberWithName(name))
+ return nested;
+ if (first_sp->GetName() == name)
+ return first_sp;
+ return {};
+}
+
+class MsvcStlExpectedFrontend : public SyntheticChildrenFrontEnd {
+public:
+ MsvcStlExpectedFrontend(ValueObject &valobj)
+ : SyntheticChildrenFrontEnd(valobj) {
+ if (valobj.GetTargetSP())
+ Update();
+ }
+
+ llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
+ if (!m_engaged)
+ return llvm::createStringErrorV("type has no child named '{0}'", name);
+ if (m_has_value) {
+ if (name == "Value" || name == "$$dereference$$")
+ return 0;
+ } else if (name == "Unexpected") {
+ return 0;
+ }
+ return llvm::createStringErrorV("type has no child named '{0}'", name);
+ }
+
+ llvm::Expected<uint32_t> CalculateNumChildren() override {
+ return m_engaged ? 1U : 0U;
+ }
+
+ ValueObjectSP GetChildAtIndex(uint32_t idx) override {
+ if (!m_engaged || idx != 0)
+ return {};
+
+ ValueObjectSP ns = m_backend.GetNonSyntheticValue();
+ if (!ns)
+ return {};
+
+ if (m_has_value) {
+ if (ValueObjectSP val_sp = GetExpectedUnionMember(*ns, "_Value"))
+ return val_sp->Clone("Value");
+ return {};
+ }
+
+ if (ValueObjectSP err_sp = GetExpectedUnionMember(*ns, "_Unexpected"))
+ return err_sp->Clone("Unexpected");
+ return {};
+ }
+
+ lldb::ChildCacheState Update() override {
+ m_engaged = false;
+ m_has_value = false;
+ ValueObjectSP ns = m_backend.GetNonSyntheticValue();
+ if (!ns)
+ return lldb::ChildCacheState::eRefetch;
+
+ ValueObjectSP has_sp = ns->GetChildMemberWithName("_Has_value");
+ if (!has_sp)
+ return lldb::ChildCacheState::eRefetch;
+
+ m_has_value = has_sp->GetValueAsUnsigned(0) != 0;
+ // expected<void, E> has no value child when engaged.
+ if (m_has_value)
+ m_engaged = GetExpectedUnionMember(*ns, "_Value") != nullptr;
+ else
+ m_engaged = GetExpectedUnionMember(*ns, "_Unexpected") != nullptr;
+ return lldb::ChildCacheState::eRefetch;
+ }
+
+private:
+ bool m_engaged = false;
+ bool m_has_value = false;
+};
+} // namespace
+
+bool formatters::IsMsvcStlExpected(ValueObject &valobj) {
+ if (auto valobj_sp = valobj.GetNonSyntheticValue())
+ return valobj_sp->GetChildMemberWithName("_Has_value") != nullptr;
+ return false;
+}
+
+bool formatters::MsvcStlExpectedSummaryProvider(ValueObject &valobj,
+ Stream &stream,
+ const TypeSummaryOptions &) {
+ ValueObjectSP ns = valobj.GetNonSyntheticValue();
+ if (!ns)
+ return false;
+ ValueObjectSP has_sp = ns->GetChildMemberWithName("_Has_value");
+ if (!has_sp)
+ return false;
+ stream.Printf(" Has Value=%s ",
+ has_sp->GetValueAsUnsigned(0) ? "true" : "false");
+ return true;
+}
+
+SyntheticChildrenFrontEnd *formatters::MsvcStlExpectedSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ if (valobj_sp)
+ return new MsvcStlExpectedFrontend(*valobj_sp);
+ return nullptr;
+}
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/Makefile
new file mode 100644
index 0000000000000..2698b0794a3ff
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+override CXXFLAGS_EXTRAS += -std=c++17
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/TestDataFormatterMsvcStlExpectedSimulator.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/TestDataFormatterMsvcStlExpectedSimulator.py
new file mode 100644
index 0000000000000..b2c349ca76ad0
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/TestDataFormatterMsvcStlExpectedSimulator.py
@@ -0,0 +1,36 @@
+"""Simulate MSVC STL std::expected and check the formatter."""
+
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class MsvcStlExpectedSimulatorTestCase(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "break here", lldb.SBFileSpec("main.cpp")
+ )
+
+ ok = self.frame().FindVariable("ok")
+ self.assertEqual(ok.GetNumChildren(), 1)
+ self.assertEqual(ok.GetChildAtIndex(0).GetName(), "Value")
+ self.assertEqual(ok.GetChildAtIndex(0).GetValueAsSigned(), 7)
+ self.assertFalse(ok.GetChildMemberWithName("Unexpected").IsValid())
+ self.expect("frame variable ok", substrs=["Has Value=true", "Value = 7"])
+
+ err = self.frame().FindVariable("err")
+ self.assertEqual(err.GetNumChildren(), 1)
+ self.assertEqual(err.GetChildAtIndex(0).GetName(), "Unexpected")
+ self.assertFalse(err.GetChildMemberWithName("Value").IsValid())
+ self.expect("frame variable err", substrs=["Has Value=false", "boom"])
+
+ void_ok = self.frame().FindVariable("void_ok")
+ self.assertEqual(void_ok.GetNumChildren(), 0)
+ self.expect("frame variable void_ok", substrs=["Has Value=true"])
+
+ void_err = self.frame().FindVariable("void_err")
+ self.assertEqual(void_err.GetNumChildren(), 1)
+ self.assertEqual(void_err.GetChildAtIndex(0).GetValueAsSigned(), 11)
+ self.expect("frame variable void_err", substrs=["Has Value=false"])
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/main.cpp
new file mode 100644
index 0000000000000..93f05bf2e514a
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/main.cpp
@@ -0,0 +1,32 @@
+#include <stddef.h>
+
+namespace std {
+template <class T, class E> class expected {
+public:
+ expected(const T &value) : _Value(value), _Has_value(true) {}
+ expected(const E &error, bool) : _Unexpected(error), _Has_value(false) {}
+ union {
+ T _Value;
+ E _Unexpected;
+ };
+ bool _Has_value;
+};
+
+template <class E> class expected<void, E> {
+public:
+ expected() : _Has_value(true) {}
+ expected(const E &error, bool) : _Unexpected(error), _Has_value(false) {}
+ union {
+ E _Unexpected;
+ };
+ bool _Has_value;
+};
+} // namespace std
+
+int main() {
+ std::expected<int, const char *> ok(7);
+ std::expected<int, const char *> err("boom", true);
+ std::expected<void, int> void_ok;
+ std::expected<void, int> void_err(11, true);
+ return 0; // break here
+}
>From 0524631bf5918f1822835efb2f1dc1f4d19b5de1 Mon Sep 17 00:00:00 2001
From: Bjorn Schobben <bjorn.schobben at aimsport.com>
Date: Thu, 20 Aug 2026 19:25:12 +0200
Subject: [PATCH 2/4] [lldb] Test the MSVC expected formatter with the real STL
---
.../Language/CPlusPlus/CPlusPlusLanguage.cpp | 2 +-
.../expected/Makefile | 3 +-
.../expected/TestDataFormatterStdExpected.py | 46 +++++++++++++++++++
.../generic/expected/main.cpp | 12 +++++
...stDataFormatterMsvcStlExpectedSimulator.py | 36 ---------------
.../msvc-simulators/expected/main.cpp | 32 -------------
6 files changed, 61 insertions(+), 70 deletions(-)
rename lldb/test/API/functionalities/data-formatter/data-formatter-stl/{msvc-simulators => generic}/expected/Makefile (54%)
create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/TestDataFormatterStdExpected.py
create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/main.cpp
delete mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/TestDataFormatterMsvcStlExpectedSimulator.py
delete mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/main.cpp
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index 4e1a499594117..ec13c473449a4 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -2003,7 +2003,7 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
AddCXXSynthetic(cpp_category_sp, GenericExpectedSyntheticFrontEndCreator,
"MSVC STL std::expected synthetic children",
- "^std::expected<.+>(( )?&)?$", stl_synth_flags, true);
+ "^std::expected<.+>(( )?&)?$", stl_deref_flags, true);
AddCXXSummary(cpp_category_sp, GenericExpectedSummaryProvider,
"MSVC STL std::expected summary provider",
"^std::expected<.+>(( )?&)?$", stl_summary_flags, true);
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/Makefile
similarity index 54%
rename from lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/Makefile
rename to lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/Makefile
index 2698b0794a3ff..96c46af546658 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/Makefile
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/Makefile
@@ -1,3 +1,4 @@
CXX_SOURCES := main.cpp
-override CXXFLAGS_EXTRAS += -std=c++17
+CXXFLAGS_EXTRAS := -std=c++23
+
include Makefile.rules
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/TestDataFormatterStdExpected.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/TestDataFormatterStdExpected.py
new file mode 100644
index 0000000000000..c465f8998d6c4
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/TestDataFormatterStdExpected.py
@@ -0,0 +1,46 @@
+"""Test the MSVC STL std::expected formatter."""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class StdExpectedDataFormatterTestCase(TestBase):
+ SHARED_BUILD_TESTCASE = False
+ TEST_WITH_PDB_DEBUG_INFO = True
+
+ @add_test_categories(["msvcstl"])
+ def test_msvcstl(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "// break here", lldb.SBFileSpec("main.cpp")
+ )
+
+ self.expect_var_path(
+ "ok",
+ summary=" Has Value=true ",
+ children=[ValueCheck(name="Value", value="7")],
+ )
+ self.expect_var_path(
+ "err",
+ summary=" Has Value=false ",
+ children=[ValueCheck(name="Unexpected", value="42")],
+ )
+ self.expect_var_path("void_ok", summary=" Has Value=true ", children=[])
+ self.expect_var_path(
+ "void_err",
+ summary=" Has Value=false ",
+ children=[ValueCheck(name="Unexpected", value="11")],
+ )
+
+ self.expect_var_path(
+ "ok_ref",
+ summary=" Has Value=true ",
+ children=[ValueCheck(name="Value", value="7")],
+ )
+ self.expect_var_path(
+ "err_ref",
+ summary=" Has Value=false ",
+ children=[ValueCheck(name="Unexpected", value="42")],
+ )
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/main.cpp
new file mode 100644
index 0000000000000..5079781c1017b
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/expected/main.cpp
@@ -0,0 +1,12 @@
+#include <expected>
+
+int main() {
+ std::expected<int, int> ok = 7;
+ std::expected<int, int> err = std::unexpected(42);
+ std::expected<void, int> void_ok;
+ std::expected<void, int> void_err = std::unexpected(11);
+ std::expected<int, int> &ok_ref = ok;
+ std::expected<int, int> &err_ref = err;
+
+ return ok.value(); // break here
+}
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/TestDataFormatterMsvcStlExpectedSimulator.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/TestDataFormatterMsvcStlExpectedSimulator.py
deleted file mode 100644
index b2c349ca76ad0..0000000000000
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/TestDataFormatterMsvcStlExpectedSimulator.py
+++ /dev/null
@@ -1,36 +0,0 @@
-"""Simulate MSVC STL std::expected and check the formatter."""
-
-from lldbsuite.test.lldbtest import *
-from lldbsuite.test import lldbutil
-
-
-class MsvcStlExpectedSimulatorTestCase(TestBase):
- NO_DEBUG_INFO_TESTCASE = True
-
- def test(self):
- self.build()
- lldbutil.run_to_source_breakpoint(
- self, "break here", lldb.SBFileSpec("main.cpp")
- )
-
- ok = self.frame().FindVariable("ok")
- self.assertEqual(ok.GetNumChildren(), 1)
- self.assertEqual(ok.GetChildAtIndex(0).GetName(), "Value")
- self.assertEqual(ok.GetChildAtIndex(0).GetValueAsSigned(), 7)
- self.assertFalse(ok.GetChildMemberWithName("Unexpected").IsValid())
- self.expect("frame variable ok", substrs=["Has Value=true", "Value = 7"])
-
- err = self.frame().FindVariable("err")
- self.assertEqual(err.GetNumChildren(), 1)
- self.assertEqual(err.GetChildAtIndex(0).GetName(), "Unexpected")
- self.assertFalse(err.GetChildMemberWithName("Value").IsValid())
- self.expect("frame variable err", substrs=["Has Value=false", "boom"])
-
- void_ok = self.frame().FindVariable("void_ok")
- self.assertEqual(void_ok.GetNumChildren(), 0)
- self.expect("frame variable void_ok", substrs=["Has Value=true"])
-
- void_err = self.frame().FindVariable("void_err")
- self.assertEqual(void_err.GetNumChildren(), 1)
- self.assertEqual(void_err.GetChildAtIndex(0).GetValueAsSigned(), 11)
- self.expect("frame variable void_err", substrs=["Has Value=false"])
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/main.cpp
deleted file mode 100644
index 93f05bf2e514a..0000000000000
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/msvc-simulators/expected/main.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-#include <stddef.h>
-
-namespace std {
-template <class T, class E> class expected {
-public:
- expected(const T &value) : _Value(value), _Has_value(true) {}
- expected(const E &error, bool) : _Unexpected(error), _Has_value(false) {}
- union {
- T _Value;
- E _Unexpected;
- };
- bool _Has_value;
-};
-
-template <class E> class expected<void, E> {
-public:
- expected() : _Has_value(true) {}
- expected(const E &error, bool) : _Unexpected(error), _Has_value(false) {}
- union {
- E _Unexpected;
- };
- bool _Has_value;
-};
-} // namespace std
-
-int main() {
- std::expected<int, const char *> ok(7);
- std::expected<int, const char *> err("boom", true);
- std::expected<void, int> void_ok;
- std::expected<void, int> void_err(11, true);
- return 0; // break here
-}
>From cb22297e91b9982c3537532d6f3d52fd819bf0e6 Mon Sep 17 00:00:00 2001
From: Bjorn Schobben <bjorn.schobben at aimsport.com>
Date: Thu, 20 Aug 2026 22:32:15 +0200
Subject: [PATCH 3/4] [lldb] Cache the active MSVC expected member
---
.../Language/CPlusPlus/MsvcStlExpected.cpp | 54 +++----------------
1 file changed, 8 insertions(+), 46 deletions(-)
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlExpected.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlExpected.cpp
index ac2bef91ea4b5..565573b7cd167 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlExpected.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlExpected.cpp
@@ -17,29 +17,6 @@ using namespace lldb_private::formatters;
namespace {
-/// `_Value` / `_Unexpected` live in an anonymous union. Direct
-/// GetChildMemberWithName from the class works on DWARF; PDB often needs the
-/// same parent/index walk GenericOptional uses for `_Value`.
-ValueObjectSP GetExpectedUnionMember(ValueObject &ns, const char *name) {
- if (ValueObjectSP direct = ns.GetChildMemberWithName(name))
- return direct;
-
- ValueObjectSP has_sp = ns.GetChildMemberWithName("_Has_value");
- if (!has_sp)
- return {};
- ValueObject *parent = has_sp->GetParent();
- if (!parent)
- return {};
- ValueObjectSP first_sp = parent->GetChildAtIndex(0);
- if (!first_sp)
- return {};
- if (ValueObjectSP nested = first_sp->GetChildMemberWithName(name))
- return nested;
- if (first_sp->GetName() == name)
- return first_sp;
- return {};
-}
-
class MsvcStlExpectedFrontend : public SyntheticChildrenFrontEnd {
public:
MsvcStlExpectedFrontend(ValueObject &valobj)
@@ -49,7 +26,7 @@ class MsvcStlExpectedFrontend : public SyntheticChildrenFrontEnd {
}
llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
- if (!m_engaged)
+ if (!m_active_sp)
return llvm::createStringErrorV("type has no child named '{0}'", name);
if (m_has_value) {
if (name == "Value" || name == "$$dereference$$")
@@ -61,30 +38,17 @@ class MsvcStlExpectedFrontend : public SyntheticChildrenFrontEnd {
}
llvm::Expected<uint32_t> CalculateNumChildren() override {
- return m_engaged ? 1U : 0U;
+ return m_active_sp ? 1U : 0U;
}
ValueObjectSP GetChildAtIndex(uint32_t idx) override {
- if (!m_engaged || idx != 0)
+ if (!m_active_sp || idx != 0)
return {};
-
- ValueObjectSP ns = m_backend.GetNonSyntheticValue();
- if (!ns)
- return {};
-
- if (m_has_value) {
- if (ValueObjectSP val_sp = GetExpectedUnionMember(*ns, "_Value"))
- return val_sp->Clone("Value");
- return {};
- }
-
- if (ValueObjectSP err_sp = GetExpectedUnionMember(*ns, "_Unexpected"))
- return err_sp->Clone("Unexpected");
- return {};
+ return m_active_sp->Clone(m_has_value ? "Value" : "Unexpected");
}
lldb::ChildCacheState Update() override {
- m_engaged = false;
+ m_active_sp.reset();
m_has_value = false;
ValueObjectSP ns = m_backend.GetNonSyntheticValue();
if (!ns)
@@ -96,15 +60,13 @@ class MsvcStlExpectedFrontend : public SyntheticChildrenFrontEnd {
m_has_value = has_sp->GetValueAsUnsigned(0) != 0;
// expected<void, E> has no value child when engaged.
- if (m_has_value)
- m_engaged = GetExpectedUnionMember(*ns, "_Value") != nullptr;
- else
- m_engaged = GetExpectedUnionMember(*ns, "_Unexpected") != nullptr;
+ m_active_sp =
+ ns->GetChildMemberWithName(m_has_value ? "_Value" : "_Unexpected");
return lldb::ChildCacheState::eRefetch;
}
private:
- bool m_engaged = false;
+ ValueObjectSP m_active_sp;
bool m_has_value = false;
};
} // namespace
>From 03bace66ebde7a24c51e8d036f4d7bd32160e99c Mon Sep 17 00:00:00 2001
From: Daedie-git <HC_Daedalus at hotmail.com>
Date: Fri, 21 Aug 2026 18:05:23 +0200
Subject: [PATCH 4/4] Update
lldb/source/Plugins/Language/CPlusPlus/MsvcStlExpected.cpp
Co-authored-by: Nerixyz <nero.9 at hotmail.de>
---
lldb/source/Plugins/Language/CPlusPlus/MsvcStlExpected.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlExpected.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlExpected.cpp
index 565573b7cd167..7f3ccd2420435 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlExpected.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlExpected.cpp
@@ -1,4 +1,4 @@
-//===-- MsvcStlExpected.cpp -----------------------------------------------===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
More information about the lldb-commits
mailing list