Skip to content

Conversation

@Michael137
Copy link
Member

This patch adjusts the libcxx and libstdcxx std::shared_ptr formatters to look the same.

Changes to libcxx:

  • Now creates a synthetic child called pointer (like we already do for std::unique_ptr)

Changes to libstdcxx:

  • When asked to dereference the pointer, cast the type of the result ValueObject to the element type (which we get from the template argument to std::shared_ptr).
    Before:
(std::__shared_ptr<int, __gnu_cxx::_S_atomic>::element_type) *foo = 123

After:

(int) *foo = 123

Tested in #147141

@Michael137 Michael137 requested a review from labath July 5, 2025 22:47
@Michael137 Michael137 requested a review from JDevlieghere as a code owner July 5, 2025 22:47
@llvmbot llvmbot added the lldb label Jul 5, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 5, 2025

@llvm/pr-subscribers-lldb

Author: Michael Buch (Michael137)

Changes

This patch adjusts the libcxx and libstdcxx std::shared_ptr formatters to look the same.

Changes to libcxx:

  • Now creates a synthetic child called pointer (like we already do for std::unique_ptr)

Changes to libstdcxx:

  • When asked to dereference the pointer, cast the type of the result ValueObject to the element type (which we get from the template argument to std::shared_ptr).
    Before:
(std::__shared_ptr&lt;int, __gnu_cxx::_S_atomic&gt;::element_type) *foo = 123

After:

(int) *foo = 123

Tested in #147141


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

3 Files Affected:

  • (modified) lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp (+23-16)
  • (modified) lldb/source/Plugins/Language/CPlusPlus/LibCxx.h (+1)
  • (modified) lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp (+16-11)
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
index 104521b8c3e71..aa13e66a1b550 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
@@ -238,7 +238,8 @@ lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator(
 
 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
     LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
-    : SyntheticChildrenFrontEnd(*valobj_sp), m_cntrl(nullptr) {
+    : SyntheticChildrenFrontEnd(*valobj_sp), m_cntrl(nullptr),
+      m_ptr_obj(nullptr) {
   if (valobj_sp)
     Update();
 }
@@ -251,7 +252,7 @@ llvm::Expected<uint32_t> lldb_private::formatters::
 lldb::ValueObjectSP
 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex(
     uint32_t idx) {
-  if (!m_cntrl)
+  if (!m_cntrl || !m_ptr_obj)
     return lldb::ValueObjectSP();
 
   ValueObjectSP valobj_sp = m_backend.GetSP();
@@ -259,20 +260,17 @@ lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex(
     return lldb::ValueObjectSP();
 
   if (idx == 0)
-    return valobj_sp->GetChildMemberWithName("__ptr_");
+    return m_ptr_obj->GetSP();
 
   if (idx == 1) {
-    if (auto ptr_sp = valobj_sp->GetChildMemberWithName("__ptr_")) {
-      Status status;
-      auto value_type_sp =
-            valobj_sp->GetCompilerType()
-              .GetTypeTemplateArgument(0).GetPointerType();
-      ValueObjectSP cast_ptr_sp = ptr_sp->Cast(value_type_sp);
-      ValueObjectSP value_sp = cast_ptr_sp->Dereference(status);
-      if (status.Success()) {
-        return value_sp;
-      }
-    }
+    Status status;
+    auto value_type_sp = valobj_sp->GetCompilerType()
+                             .GetTypeTemplateArgument(0)
+                             .GetPointerType();
+    ValueObjectSP cast_ptr_sp = m_ptr_obj->Cast(value_type_sp);
+    ValueObjectSP value_sp = cast_ptr_sp->Dereference(status);
+    if (status.Success())
+      return value_sp;
   }
 
   return lldb::ValueObjectSP();
@@ -281,6 +279,7 @@ lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex(
 lldb::ChildCacheState
 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() {
   m_cntrl = nullptr;
+  m_ptr_obj = nullptr;
 
   ValueObjectSP valobj_sp = m_backend.GetSP();
   if (!valobj_sp)
@@ -290,6 +289,12 @@ lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() {
   if (!target_sp)
     return lldb::ChildCacheState::eRefetch;
 
+  auto ptr_obj_sp = valobj_sp->GetChildMemberWithName("__ptr_");
+  if (!ptr_obj_sp)
+    return lldb::ChildCacheState::eRefetch;
+
+  m_ptr_obj = ptr_obj_sp->Clone(ConstString("pointer")).get();
+
   lldb::ValueObjectSP cntrl_sp(valobj_sp->GetChildMemberWithName("__cntrl_"));
 
   m_cntrl = cntrl_sp.get(); // need to store the raw pointer to avoid a circular
@@ -300,10 +305,12 @@ lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() {
 llvm::Expected<size_t>
 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
     GetIndexOfChildWithName(ConstString name) {
-  if (name == "__ptr_")
+  if (name == "__ptr_" || name == "pointer")
     return 0;
-  if (name == "$$dereference$$")
+
+  if (name == "object" || name == "$$dereference$$")
     return 1;
+
   return llvm::createStringError("Type has no child named '%s'",
                                  name.AsCString());
 }
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
index 56501302d116f..6115ccde38ad2 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
@@ -108,6 +108,7 @@ class LibcxxSharedPtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
 
 private:
   ValueObject *m_cntrl;
+  ValueObject *m_ptr_obj;
 };
 
 class LibcxxUniquePtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp
index 28b7c01ab1b5b..7668a87ce447e 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp
@@ -77,8 +77,7 @@ class LibStdcppSharedPtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
   // objects are only destroyed when every shared pointer to any of them
   // is destroyed, so we must not store a shared pointer to any ValueObject
   // derived from our backend ValueObject (since we're in the same cluster).
-  ValueObject* m_ptr_obj = nullptr; // Underlying pointer (held, not owned)
-  ValueObject* m_obj_obj = nullptr; // Underlying object (held, not owned)
+  ValueObject *m_ptr_obj = nullptr; // Underlying pointer (held, not owned)
 };
 
 } // end of anonymous namespace
@@ -266,15 +265,20 @@ LibStdcppSharedPtrSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
 
   if (idx == 0)
     return m_ptr_obj->GetSP();
+
   if (idx == 1) {
-    if (m_ptr_obj && !m_obj_obj) {
-      Status error;
-      ValueObjectSP obj_obj = m_ptr_obj->Dereference(error);
-      if (error.Success())
-        m_obj_obj = obj_obj->Clone(ConstString("object")).get();
-    }
-    if (m_obj_obj)
-      return m_obj_obj->GetSP();
+    ValueObjectSP valobj_sp = m_backend.GetSP();
+    if (!valobj_sp)
+      return nullptr;
+
+    Status status;
+    auto value_type_sp = valobj_sp->GetCompilerType()
+                             .GetTypeTemplateArgument(0)
+                             .GetPointerType();
+    ValueObjectSP cast_ptr_sp = m_ptr_obj->Cast(value_type_sp);
+    ValueObjectSP value_sp = cast_ptr_sp->Dereference(status);
+    if (status.Success())
+      return value_sp;
   }
   return lldb::ValueObjectSP();
 }
@@ -293,7 +297,6 @@ lldb::ChildCacheState LibStdcppSharedPtrSyntheticFrontEnd::Update() {
     return lldb::ChildCacheState::eRefetch;
 
   m_ptr_obj = ptr_obj_sp->Clone(ConstString("pointer")).get();
-  m_obj_obj = nullptr;
 
   return lldb::ChildCacheState::eRefetch;
 }
@@ -302,8 +305,10 @@ llvm::Expected<size_t>
 LibStdcppSharedPtrSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {
   if (name == "pointer")
     return 0;
+
   if (name == "object" || name == "$$dereference$$")
     return 1;
+
   return llvm::createStringError("Type has no child named '%s'",
                                  name.AsCString());
 }

Copy link
Collaborator

@labath labath left a comment

Choose a reason for hiding this comment

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

Ah, I see you've done that already.

Not required, but I noticed that the pointer child still contains the goo:

(lldb) v a.__ptr_
(std::shared_ptr<int>::element_type *) a.__ptr_ = 0x000055555555a2b8

If we do the casting centrally, we could have both of these appear the same way.

@Michael137
Copy link
Member Author

Ah, I see you've done that already.

Not required, but I noticed that the pointer child still contains the goo:

(lldb) v a.__ptr_
(std::shared_ptr<int>::element_type *) a.__ptr_ = 0x000055555555a2b8

If we do the casting centrally, we could have both of these appear the same way.

Good point. I'll address that separately once I merge the combined tests

@Michael137 Michael137 merged commit 6fec6a9 into llvm:main Jul 7, 2025
9 checks passed
@Michael137 Michael137 deleted the lldb/consolidate-stl-shared_ptr-formatters branch July 7, 2025 08:13
Michael137 added a commit that referenced this pull request Jul 7, 2025
… summary (#147166)

Depends on #147165

This adds weak/strong counts to the std::shared_ptr summary of the
libstdcxx formatters. We already do this for libcxx. This will make it
easier to consolidate the tests into a generic one (see
#147141).
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Jul 7, 2025
…:shared_ptr summary (#147166)

Depends on llvm/llvm-project#147165

This adds weak/strong counts to the std::shared_ptr summary of the
libstdcxx formatters. We already do this for libcxx. This will make it
easier to consolidate the tests into a generic one (see
llvm/llvm-project#147141).
Michael137 added a commit that referenced this pull request Jul 8, 2025
…hared_ptr formatters (#147340)

Follow-up to
#147165 (review)

Currently when we explicitly dereference a std::shared_ptr, both the
libstdc++ and libc++ formatters will cast the type of the synthetic
pointer child to whatever the `std::shared_ptr::element_type` is aliased
to. E.g.,
```
(lldb) v p
(std::shared_ptr<int>) p = 10 strong=1 weak=0 {
  pointer = 0x000000010016c6a0
}
(lldb) v *p
(int) *p = 10
```

However, when we print (or dereference) `p.pointer`, the type devolves
to something less user-friendly:
```
(lldb) v p.pointer
(std::shared_ptr<int>::element_type *) p.pointer = 0x000000010016c6a0
(lldb) v *p.pointer
(std::shared_ptr<int>::element_type) *p.pointer = 10
```

This patch changes both formatters to store the casted type. Then
`GetChildAtIndex` will consistently use the unwrapped type.
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Jul 8, 2025
…e in std::shared_ptr formatters (#147340)

Follow-up to
llvm/llvm-project#147165 (review)

Currently when we explicitly dereference a std::shared_ptr, both the
libstdc++ and libc++ formatters will cast the type of the synthetic
pointer child to whatever the `std::shared_ptr::element_type` is aliased
to. E.g.,
```
(lldb) v p
(std::shared_ptr<int>) p = 10 strong=1 weak=0 {
  pointer = 0x000000010016c6a0
}
(lldb) v *p
(int) *p = 10
```

However, when we print (or dereference) `p.pointer`, the type devolves
to something less user-friendly:
```
(lldb) v p.pointer
(std::shared_ptr<int>::element_type *) p.pointer = 0x000000010016c6a0
(lldb) v *p.pointer
(std::shared_ptr<int>::element_type) *p.pointer = 10
```

This patch changes both formatters to store the casted type. Then
`GetChildAtIndex` will consistently use the unwrapped type.
Michael137 added a commit to swiftlang/llvm-project that referenced this pull request Jul 25, 2025
…rs consistent with each other (llvm#147165)

This patch adjusts the libcxx and libstdcxx std::shared_ptr formatters
to look the same.

Changes to libcxx:
* Now creates a synthetic child called `pointer` (like we already do for
`std::unique_ptr`)

Changes to libstdcxx:
* When asked to dereference the pointer, cast the type of the result
ValueObject to the element type (which we get from the template argument
to std::shared_ptr).
Before:
```
(std::__shared_ptr<int, __gnu_cxx::_S_atomic>::element_type) *foo = 123
```
After:
```
(int) *foo = 123
```

Tested in llvm#147141

(cherry picked from commit 6fec6a9)
Michael137 added a commit to swiftlang/llvm-project that referenced this pull request Jul 25, 2025
… summary (llvm#147166)

Depends on llvm#147165

This adds weak/strong counts to the std::shared_ptr summary of the
libstdcxx formatters. We already do this for libcxx. This will make it
easier to consolidate the tests into a generic one (see
llvm#147141).

(cherry picked from commit 40fb90e)
Michael137 added a commit to swiftlang/llvm-project that referenced this pull request Jul 25, 2025
…hared_ptr formatters (llvm#147340)

Follow-up to
llvm#147165 (review)

Currently when we explicitly dereference a std::shared_ptr, both the
libstdc++ and libc++ formatters will cast the type of the synthetic
pointer child to whatever the `std::shared_ptr::element_type` is aliased
to. E.g.,
```
(lldb) v p
(std::shared_ptr<int>) p = 10 strong=1 weak=0 {
  pointer = 0x000000010016c6a0
}
(lldb) v *p
(int) *p = 10
```

However, when we print (or dereference) `p.pointer`, the type devolves
to something less user-friendly:
```
(lldb) v p.pointer
(std::shared_ptr<int>::element_type *) p.pointer = 0x000000010016c6a0
(lldb) v *p.pointer
(std::shared_ptr<int>::element_type) *p.pointer = 10
```

This patch changes both formatters to store the casted type. Then
`GetChildAtIndex` will consistently use the unwrapped type.

(cherry picked from commit f999918)
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.

3 participants