Skip to content

Conversation

@DrSergei
Copy link
Contributor

@DrSergei DrSergei commented Jan 2, 2026

I want to propose adding summary providers for std::*_ordering types introduced in C++20. GDB already has pretty-printers for them, so I think it will be useful.

@llvmbot
Copy link
Member

llvmbot commented Jan 2, 2026

@llvm/pr-subscribers-lldb

Author: Sergei Druzhkov (DrSergei)

Changes

I want to propose adding summary providers for std::*_ordering types introduced in C++20. GDB already has pretty-printers for them, so I think it will be useful.


Full diff: https://github.com/llvm/llvm-project/pull/174195.diff

8 Files Affected:

  • (modified) lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp (+33)
  • (modified) lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp (+93-7)
  • (modified) lldb/source/Plugins/Language/CPlusPlus/LibCxx.h (+12)
  • (modified) lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp (+88-1)
  • (modified) lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h (+12)
  • (added) lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/ordering/Makefile (+4)
  • (added) lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/ordering/TestDataFormatterStdOrdering.py (+42)
  • (added) lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/ordering/main.cpp (+16)
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index dd3b84e47dec3..3c1464485bbe0 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -1286,6 +1286,22 @@ static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
       TypeSummaryImplSP(new StringSummaryFormat(
           eTypeOptionHideChildren | eTypeOptionHideValue,
           "${var.__y_} ${var.__m_} ${var.__wdl_}")));
+
+  AddCXXSummary(cpp_category_sp,
+                lldb_private::formatters::LibcxxPartialOrderingSummaryProvider,
+                "libc++ std::partial_ordering summary provider",
+                "^std::__[[:alnum:]]+::partial_ordering$",
+                eTypeOptionHideChildren | eTypeOptionHideValue, true);
+  AddCXXSummary(cpp_category_sp,
+                lldb_private::formatters::LibcxxWeakOrderingSummaryProvider,
+                "libc++ std::weak_ordering summary provider",
+                "^std::__[[:alnum:]]+::weak_ordering$",
+                eTypeOptionHideChildren | eTypeOptionHideValue, true);
+  AddCXXSummary(cpp_category_sp,
+                lldb_private::formatters::LibcxxStrongOrderingSummaryProvider,
+                "libc++ std::strong_ordering summary provider",
+                "^std::__[[:alnum:]]+::strong_ordering$",
+                eTypeOptionHideChildren | eTypeOptionHideValue, true);
 }
 
 static void RegisterStdStringSummaryProvider(
@@ -1515,6 +1531,23 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
                 lldb_private::formatters::ContainerSizeSummaryProvider,
                 "libstdc++ std::span summary provider", "^std::span<.+>$",
                 stl_summary_flags, true);
+
+  AddCXXSummary(
+      cpp_category_sp,
+      lldb_private::formatters::LibStdcppPartialOrderingSummaryProvider,
+      "libstdc++ std::partial_ordering summary provider",
+      "std::partial_ordering", eTypeOptionHideChildren | eTypeOptionHideValue,
+      false);
+  AddCXXSummary(cpp_category_sp,
+                lldb_private::formatters::LibStdcppWeakOrderingSummaryProvider,
+                "libstdc++ std::weak_ordering summary provider",
+                "std::weak_ordering",
+                eTypeOptionHideChildren | eTypeOptionHideValue, false);
+  AddCXXSummary(
+      cpp_category_sp,
+      lldb_private::formatters::LibStdcppStrongOrderingSummaryProvider,
+      "libstdc++ std::strong_ordering summary provider", "std::strong_ordering",
+      eTypeOptionHideChildren | eTypeOptionHideValue, false);
 }
 
 static lldb_private::SyntheticChildrenFrontEnd *
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
index 141c5c9a2caf9..914abd9590c95 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
@@ -256,6 +256,92 @@ bool lldb_private::formatters::LibcxxUniquePointerSummaryProvider(
   return true;
 }
 
+static std::optional<int64_t> LibcxxExtractOrderingValue(ValueObject &valobj) {
+  lldb::ValueObjectSP value_sp = valobj.GetChildMemberWithName("__value_");
+  if (!value_sp)
+    return std::nullopt;
+  bool success;
+  int64_t value = value_sp->GetValueAsSigned(0, &success);
+  if (!success)
+    return std::nullopt;
+  return value;
+}
+
+bool lldb_private::formatters::LibcxxPartialOrderingSummaryProvider(
+    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+  std::optional<int64_t> value = LibcxxExtractOrderingValue(valobj);
+  if (!value) {
+    stream << "Summary Unavailable";
+    return true;
+  }
+  switch (*value) {
+  case -1:
+    stream << "less";
+    break;
+  case 0:
+    stream << "equivalent";
+    break;
+  case 1:
+    stream << "greater";
+    break;
+  case -127:
+    stream << "unordered";
+    break;
+  default:
+    stream << "Invalid partial ordering value";
+    break;
+  }
+  return true;
+}
+
+bool lldb_private::formatters::LibcxxWeakOrderingSummaryProvider(
+    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+  std::optional<int64_t> value = LibcxxExtractOrderingValue(valobj);
+  if (!value) {
+    stream << "Summary Unavailable";
+    return true;
+  }
+  switch (*value) {
+  case -1:
+    stream << "less";
+    break;
+  case 0:
+    stream << "equivalent";
+    break;
+  case 1:
+    stream << "greater";
+    break;
+  default:
+    stream << "Invalid weak ordering value";
+    break;
+  }
+  return true;
+}
+
+bool lldb_private::formatters::LibcxxStrongOrderingSummaryProvider(
+    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+  std::optional<int64_t> value = LibcxxExtractOrderingValue(valobj);
+  if (!value) {
+    stream << "Summary Unavailable";
+    return true;
+  }
+  switch (*value) {
+  case -1:
+    stream << "less";
+    break;
+  case 0:
+    stream << "equal";
+    break;
+  case 1:
+    stream << "greater";
+    break;
+  default:
+    stream << "Invalid strong ordering value";
+    break;
+  }
+  return true;
+}
+
 /*
  (lldb) fr var ibeg --raw --ptr-depth 1 -T
  (std::__1::__wrap_iter<int *>) ibeg = {
@@ -495,8 +581,8 @@ ExtractLibcxxStringInfo(ValueObject &valobj) {
   StringLayout layout =
       *index_or_err == 0 ? StringLayout::DSC : StringLayout::CSD;
 
-  bool short_mode = false; // this means the string is in short-mode and the
-                           // data is stored inline
+  bool short_mode = false;    // this means the string is in short-mode and the
+                              // data is stored inline
   bool using_bitmasks = true; // Whether the class uses bitmasks for the mode
                               // flag (pre-D123580).
   uint64_t size;
@@ -639,23 +725,23 @@ bool lldb_private::formatters::LibcxxStringSummaryProviderUTF32(
 }
 
 static std::tuple<bool, ValueObjectSP, size_t>
-LibcxxExtractStringViewData(ValueObject& valobj) {
+LibcxxExtractStringViewData(ValueObject &valobj) {
   auto dataobj = GetChildMemberWithName(
       valobj, {ConstString("__data_"), ConstString("__data")});
   auto sizeobj = GetChildMemberWithName(
       valobj, {ConstString("__size_"), ConstString("__size")});
   if (!dataobj || !sizeobj)
-    return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {});
+    return std::make_tuple<bool, ValueObjectSP, size_t>(false, {}, {});
 
   if (!dataobj->GetError().Success() || !sizeobj->GetError().Success())
-    return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {});
+    return std::make_tuple<bool, ValueObjectSP, size_t>(false, {}, {});
 
   bool success{false};
   uint64_t size = sizeobj->GetValueAsUnsigned(0, &success);
   if (!success)
-    return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {});
+    return std::make_tuple<bool, ValueObjectSP, size_t>(false, {}, {});
 
-  return std::make_tuple(true,dataobj,size);
+  return std::make_tuple(true, dataobj, size);
 }
 
 template <StringPrinter::StringElementType element_type>
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
index 8fd29288da35f..69cca8ee621d9 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
@@ -92,6 +92,18 @@ bool LibcxxFunctionSummaryProvider(
     ValueObject &valobj, Stream &stream,
     const TypeSummaryOptions &options); // libc++ std::function<>
 
+bool LibcxxPartialOrderingSummaryProvider(
+    ValueObject &valobj, Stream &stream,
+    const TypeSummaryOptions &options); // libc++ std::partial_ordering
+
+bool LibcxxWeakOrderingSummaryProvider(
+    ValueObject &valobj, Stream &stream,
+    const TypeSummaryOptions &options); // libc++ std::weak_ordering
+
+bool LibcxxStrongOrderingSummaryProvider(
+    ValueObject &valobj, Stream &stream,
+    const TypeSummaryOptions &options); // libc++ std::strong_ordering
+
 SyntheticChildrenFrontEnd *
 LibcxxVectorBoolSyntheticFrontEndCreator(CXXSyntheticChildren *,
                                          lldb::ValueObjectSP);
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp
index 86f0a5ad78a9a..c1e06850be9b3 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp
@@ -73,7 +73,6 @@ class LibStdcppSharedPtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
   llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
 
 private:
-
   // The lifetime of a ValueObject and all its derivative ValueObjects
   // (children, clones, etc.) is managed by a ClusterManager. These
   // objects are only destroyed when every shared pointer to any of them
@@ -410,3 +409,91 @@ bool formatters::LibStdcppVariantSummaryProvider(
   stream << " Active Type = " << active_type.GetDisplayTypeName() << " ";
   return true;
 }
+
+static std::optional<int64_t>
+LibStdcppExtractOrderingValue(ValueObject &valobj) {
+  lldb::ValueObjectSP value_sp = valobj.GetChildMemberWithName("_M_value");
+  if (!value_sp)
+    return std::nullopt;
+  bool success;
+  int64_t value = value_sp->GetValueAsSigned(0, &success);
+  if (!success)
+    return std::nullopt;
+  return value;
+}
+
+bool lldb_private::formatters::LibStdcppPartialOrderingSummaryProvider(
+    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+  std::optional<int64_t> value = LibStdcppExtractOrderingValue(valobj);
+  if (!value) {
+    stream << "Summary Unavailable";
+    return true;
+  }
+  switch (*value) {
+  case -1:
+    stream << "less";
+    break;
+  case 0:
+    stream << "equivalent";
+    break;
+  case 1:
+    stream << "greater";
+    break;
+  case -128:
+  case 2:
+    stream << "unordered";
+    break;
+  default:
+    stream << "Invalid partial ordering value";
+    break;
+  }
+  return true;
+}
+
+bool lldb_private::formatters::LibStdcppWeakOrderingSummaryProvider(
+    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+  std::optional<int64_t> value = LibStdcppExtractOrderingValue(valobj);
+  if (!value) {
+    stream << "Summary Unavailable";
+    return true;
+  }
+  switch (*value) {
+  case -1:
+    stream << "less";
+    break;
+  case 0:
+    stream << "equivalent";
+    break;
+  case 1:
+    stream << "greater";
+    break;
+  default:
+    stream << "Invalid weak ordering value";
+    break;
+  }
+  return true;
+}
+
+bool lldb_private::formatters::LibStdcppStrongOrderingSummaryProvider(
+    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+  std::optional<int64_t> value = LibStdcppExtractOrderingValue(valobj);
+  if (!value) {
+    stream << "Summary Unavailable";
+    return true;
+  }
+  switch (*value) {
+  case -1:
+    stream << "less";
+    break;
+  case 0:
+    stream << "equal";
+    break;
+  case 1:
+    stream << "greater";
+    break;
+  default:
+    stream << "Invalid strong ordering value";
+    break;
+  }
+  return true;
+}
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h
index 8d2c81f2bbcbb..94d4968b45175 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h
@@ -33,6 +33,18 @@ bool LibStdcppVariantSummaryProvider(
     ValueObject &valobj, Stream &stream,
     const TypeSummaryOptions &options); // libstdc++ std::variant<>
 
+bool LibStdcppPartialOrderingSummaryProvider(
+    ValueObject &valobj, Stream &stream,
+    const TypeSummaryOptions &options); // libstdc++ std::partial_ordering
+
+bool LibStdcppWeakOrderingSummaryProvider(
+    ValueObject &valobj, Stream &stream,
+    const TypeSummaryOptions &options); // libstdc++ std::weak_ordering
+
+bool LibStdcppStrongOrderingSummaryProvider(
+    ValueObject &valobj, Stream &stream,
+    const TypeSummaryOptions &options); // libstdc++ std::strong_ordering
+
 SyntheticChildrenFrontEnd *
 LibstdcppMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,
                                              lldb::ValueObjectSP);
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/ordering/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/ordering/Makefile
new file mode 100644
index 0000000000000..4f79c0a900c3a
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/ordering/Makefile
@@ -0,0 +1,4 @@
+CXX_SOURCES := main.cpp
+CXXFLAGS_EXTRAS := -std=c++20
+
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/ordering/TestDataFormatterStdOrdering.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/ordering/TestDataFormatterStdOrdering.py
new file mode 100644
index 0000000000000..aa0b08249030a
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/ordering/TestDataFormatterStdOrdering.py
@@ -0,0 +1,42 @@
+"""
+Test std::*_ordering summary.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class StdOrderingTestCase(TestBase):
+    def do_test(self):
+        lldbutil.run_to_source_breakpoint(
+            self, "// break here", lldb.SBFileSpec("main.cpp")
+        )
+
+        self.expect(
+            "frame variable",
+            substrs=[
+                "(std::partial_ordering) po_less = less",
+                "(std::partial_ordering) po_equivalent = equivalent",
+                "(std::partial_ordering) po_greater = greater",
+                "(std::partial_ordering) po_unordered = unordered",
+                "(std::weak_ordering) wo_less = less",
+                "(std::weak_ordering) wo_equivalent = equivalent",
+                "(std::weak_ordering) wo_greater = greater",
+                "(std::strong_ordering) so_less = less",
+                "(std::strong_ordering) so_equal = equal",
+                "(std::strong_ordering) so_equivalent = equal",
+                "(std::strong_ordering) so_greater = greater",
+            ],
+        )
+
+    @add_test_categories(["libc++"])
+    def test_libcxx(self):
+        self.build(dictionary={"USE_LIBCPP": 1})
+        self.do_test()
+
+    @add_test_categories(["libstdcxx"])
+    def test_libstdcxx(self):
+        self.build(dictionary={"USE_LIBSTDCPP": 1})
+        self.do_test()
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/ordering/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/ordering/main.cpp
new file mode 100644
index 0000000000000..2135d5558c1e7
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/ordering/main.cpp
@@ -0,0 +1,16 @@
+#include <compare>
+
+int main() {
+  auto po_less = std::partial_ordering::less;
+  auto po_equivalent = std::partial_ordering::equivalent;
+  auto po_greater = std::partial_ordering::greater;
+  auto po_unordered = std::partial_ordering::unordered;
+  auto wo_less = std::weak_ordering::less;
+  auto wo_equivalent = std::weak_ordering::equivalent;
+  auto wo_greater = std::weak_ordering::greater;
+  auto so_less = std::strong_ordering::less;
+  auto so_equal = std::strong_ordering::equal;
+  auto so_equivalent = std::strong_ordering::equivalent;
+  auto so_greater = std::strong_ordering::greater;
+  return 0; // break here
+}

Copy link
Contributor

@Nerixyz Nerixyz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The values from the MSVC STL are similar (https://github.com/microsoft/STL/blob/cbe2ee99caf122555a305d18f69e57c75b3fe5ec/stl/debugger/STL.natvis#L2012-L2032).
If you want to add them here, I can run the test for you before a merge, since we don't have a Windows premerge CI.

Comment on lines 584 to 585
bool short_mode = false; // this means the string is in short-mode and the
// data is stored inline
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the changes below are unrelated (probably from format-on-save?).

Comment on lines +28 to +29
"(std::strong_ordering) so_equal = equal",
"(std::strong_ordering) so_equivalent = equal",
Copy link
Contributor

@da-viper da-viper Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we distinguish strong_ordering::equal from strong_ordering::equivalent even though they are semantically the same ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They have the same value representation (0). Both of GDB pretty-printers and MSVC Natvis use equal in this case, so I prefer to keep existing behavior.

Copy link
Member

@Michael137 Michael137 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be great if you could split this into separate PRs per STL. Otherwise LGTM (modulo the existing comments)

@DrSergei
Copy link
Contributor Author

DrSergei commented Jan 2, 2026

The values from the MSVC STL are similar (https://github.com/microsoft/STL/blob/cbe2ee99caf122555a305d18f69e57c75b3fe5ec/stl/debugger/STL.natvis#L2012-L2032). If you want to add them here, I can run the test for you before a merge, since we don't have a Windows premerge CI.

Thanks, I will add implementation for MSVC stl and libc++ in separate PRs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants