[libcxx-commits] [libcxx] [libc++][lldb] Add initial LLDB data-formatters (PR #187677)

via libcxx-commits libcxx-commits at lists.llvm.org
Thu Jun 18 09:40:52 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Michael Buch (Michael137)

<details>
<summary>Changes</summary>

Depends on:
* https://github.com/llvm/llvm-project/pull/165769
* https://github.com/llvm/llvm-project/pull/188891

Adds synthetic child providers for `std::map` and `std::vector`. These
were translated from C++ into Python (with the help of Claude).

---

Patch is 31.21 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/187677.diff


8 Files Affected:

- (added) libcxx/test/libcxx/lldb/map_formatter.sh.test (+37) 
- (added) libcxx/test/libcxx/lldb/vector_bool_formatter.sh.test (+35) 
- (added) libcxx/test/libcxx/lldb/vector_formatter.sh.test (+160) 
- (modified) libcxx/utils/libcxx/test/features/__init__.py (+11-1) 
- (added) libcxx/utils/libcxx/test/features/lldb.py (+46) 
- (added) libcxx/utils/lldb/libcxx/libcxx.py (+52) 
- (added) libcxx/utils/lldb/libcxx/libcxx_map_formatter.py (+416) 
- (added) libcxx/utils/lldb/libcxx/libcxx_vector_formatter.py (+225) 


``````````diff
diff --git a/libcxx/test/libcxx/lldb/map_formatter.sh.test b/libcxx/test/libcxx/lldb/map_formatter.sh.test
new file mode 100644
index 0000000000000..a1aefdf405b15
--- /dev/null
+++ b/libcxx/test/libcxx/lldb/map_formatter.sh.test
@@ -0,0 +1,37 @@
+# REQUIRES: host-has-lldb-with-python
+# REQUIRES: has-filecheck
+# REQUIRES: has-splitfile
+# REQUIRES: optimization=none
+
+# RUN: split-file %s %{temp}
+# RUN: %{cxx} %{flags} %{temp}/main.cpp -o %t.out %{compile_flags} -g %{link_flags}
+
+# RUN: %{lldb} --no-lldbinit --batch -o "settings set target.load-script-from-symbol-file false" \
+# RUN:         -o "settings set interpreter.stop-command-source-on-error false" \
+# RUN:         -o "command script import %S/../../../utils/lldb/libcxx/libcxx.py" \
+# RUN:         -s %{temp}/lldb.commands %t.out | FileCheck %s
+#
+# CHECK:      (lldb) frame var mii
+# CHECK-NEXT: (std::map<int, int>) mii = size=2 {
+# CHECK-NEXT:   [0] = (first = 1, second = 2)
+# CHECK-NEXT:   [1] = (first = 3, second = 4)
+# CHECK-NEXT: }
+
+#--- main.cpp
+
+#include <map>
+
+int main(int, char**) {
+  std::map<int, int> mii = {
+    {1, 2},
+    {3, 4},
+  };
+
+  return 0;
+}
+
+#--- lldb.commands
+
+break set -p return -X main
+run
+frame var mii
diff --git a/libcxx/test/libcxx/lldb/vector_bool_formatter.sh.test b/libcxx/test/libcxx/lldb/vector_bool_formatter.sh.test
new file mode 100644
index 0000000000000..45624ae2262d1
--- /dev/null
+++ b/libcxx/test/libcxx/lldb/vector_bool_formatter.sh.test
@@ -0,0 +1,35 @@
+# REQUIRES: host-has-lldb-with-python
+# REQUIRES: has-filecheck
+# REQUIRES: has-splitfile
+# REQUIRES: optimization=none
+
+# RUN: split-file %s %{temp}
+# RUN: %{cxx} %{flags} %{temp}/main.cpp -o %t.out %{compile_flags} -g %{link_flags}
+
+# RUN: %{lldb} --no-lldbinit --batch -o "settings set target.load-script-from-symbol-file false" \
+# RUN:         -o "settings set interpreter.stop-command-source-on-error false" \
+# RUN:         -o "command script import %S/../../../utils/lldb/libcxx/libcxx.py" \
+# RUN:         -s %{temp}/lldb.commands %t.out | FileCheck %s
+
+# CHECK: (lldb) frame var vb
+# CHECK-NEXT: (std::vector<bool>) vb = size=3 {
+# CHECK-NEXT:   [0] = true
+# CHECK-NEXT:   [1] = false
+# CHECK-NEXT:   [2] = false
+# CHECK-NEXT: }
+
+#--- main.cpp
+
+#include <vector>
+
+int main(int, char**) {
+  std::vector<bool> vb = {true, false, false};
+
+  return 0;
+}
+
+#--- lldb.commands
+
+break set -p return -X main
+run
+frame var vb
diff --git a/libcxx/test/libcxx/lldb/vector_formatter.sh.test b/libcxx/test/libcxx/lldb/vector_formatter.sh.test
new file mode 100644
index 0000000000000..518d946c58900
--- /dev/null
+++ b/libcxx/test/libcxx/lldb/vector_formatter.sh.test
@@ -0,0 +1,160 @@
+# REQUIRES: host-has-lldb-with-python
+# REQUIRES: has-filecheck
+# REQUIRES: has-splitfile
+# REQUIRES: optimization=none
+
+# RUN: split-file %s %{temp}
+# RUN: %{cxx} %{flags} %{temp}/main.cpp -o %t.out %{compile_flags} -g %{link_flags}
+
+# RUN: %{lldb} --no-lldbinit --batch -o "settings set target.load-script-from-symbol-file false" \
+# RUN:         -o "settings set interpreter.stop-command-source-on-error false" \
+# RUN:         -o "command script import %S/../../../utils/lldb/libcxx/libcxx.py" \
+# RUN:         -s %{temp}/lldb.commands %t.out 2>&1 | FileCheck %s
+
+# CHECK: (lldb) frame var empty
+# CHECK-NEXT: (std::vector<float>) empty = size=0 {}
+
+# CHECK: (lldb) frame var vi
+# CHECK-NEXT: (std::vector<int>) vi = size=2 {
+# CHECK-NEXT:   [0] = 1
+# CHECK-NEXT:   [1] = 2
+# CHECK-NEXT: }
+
+# CHECK: (lldb) frame var empty[0]
+# CHECK: array index 0 is not valid
+
+# CHECK: (lldb) continue
+# CHECK: (lldb) frame var vi
+# CHECK-NEXT: (std::vector<int>) vi = size=5 {
+# CHECK-NEXT:   [0] = 1
+# CHECK-NEXT:   [1] = 2
+# CHECK-NEXT:   [2] = 5
+# CHECK-NEXT:   [3] = 4
+# CHECK-NEXT:   [4] = 0
+# CHECK-NEXT: }
+
+# CHECK: (lldb) frame var vi[2]
+# CHECK-NEXT: (int) vi[2] = 5
+
+# CHECK: (lldb) frame var vi[0]
+# CHECK-NEXT: (int) vi[0] = 1
+
+# CHECK: (lldb) continue
+# CHECK: (lldb) frame var vi[0]
+# CHECK-NEXT: array index 0 is not valid
+
+# CHECK: (lldb) frame var vi
+# CHECK-NEXT: (std::vector<int>) vi = size=0 {}
+
+# CHECK: (lldb) continue
+# CHECK: (lldb) frame var vec_of_vec
+# CHECK-NEXT: (std::vector<std::vector<int> >) vec_of_vec = size=1 {
+# CHECK-NEXT:   [0] = size=2 {
+# CHECK-NEXT:     [0] = 10
+# CHECK-NEXT:     [1] = 11
+# CHECK-NEXT:   }
+# CHECK-NEXT: }
+
+# CHECK: (lldb) frame var vec_of_vec[0]
+# CHECK-NEXT: (std::vector<int>) vec_of_vec[0] = size=2 {
+# CHECK-NEXT:   [0] = 10
+# CHECK-NEXT:   [1] = 11
+# CHECK-NEXT: }
+
+# CHECK: (lldb) continue
+# CHECK: (lldb) frame var vs
+# CHECK-NEXT: (std::vector<std::string>) vs = size=2 {
+# CHECK-NEXT:   [0] = "Foo"
+# CHECK-NEXT:   [1] = "Bar"
+# CHECK-NEXT: }
+
+# CHECK: (lldb) frame var vs[1]
+# CHECK-NEXT: (std::string) vs[1] = "Bar"
+
+# CHECK: (lldb) continue
+# CHECK: (lldb) frame var vec_ref
+# CHECK-NEXT: (const std::vector<int> &) vec_ref = 0x{{.*}} size=2: {
+# CHECK-NEXT:   [0] = 10
+# CHECK-NEXT:   [1] = 11
+# CHECK-NEXT: }
+
+# CHECK: (lldb) frame var vec_ref[1]
+# CHECK-NEXT: (int) vec_ref[1] = 11
+
+# CHECK: (lldb) frame var vec_ptr
+# CHECK-NEXT: (const std::vector<int> *) vec_ptr = 0x{{.*}} size=2
+
+# CHECK: (lldb) frame var vec_ptr[1]
+# CHECK-NEXT: (int) vec_ptr[1] = 11
+
+#--- main.cpp
+
+#include <vector>
+#include <string>
+
+void break_here() {}
+
+int main() {
+  std::vector<float> empty;
+  std::vector<int> vi = {1, 2};
+
+  break_here();
+
+  vi.push_back(5);
+  vi.push_back(4);
+  vi.push_back(0);
+
+  break_here();
+
+  vi.clear();
+
+  break_here();
+
+  vi.push_back(10);
+  vi.push_back(11);
+  std::vector<std::vector<int>> vec_of_vec{vi};
+
+  break_here();
+
+  std::vector<std::string>  vs{
+    "Foo",
+    "Bar"
+  };
+
+  break_here();
+
+  [[maybe_unused]] const auto &vec_ref = vi;
+  [[maybe_unused]] const auto *vec_ptr = &vi;
+
+  break_here();
+}
+
+#--- lldb.commands
+break set -p break_here -X main
+run
+frame var empty
+frame var vi
+frame var empty[0]
+
+continue
+frame var vi
+frame var vi[2]
+frame var vi[0]
+
+continue
+frame var vi[0]
+frame var vi
+
+continue
+frame var vec_of_vec
+frame var vec_of_vec[0]
+
+continue
+frame var vs
+frame var vs[1]
+
+continue
+frame var vec_ref
+frame var vec_ref[1]
+frame var vec_ptr
+frame var vec_ptr[1]
diff --git a/libcxx/utils/libcxx/test/features/__init__.py b/libcxx/utils/libcxx/test/features/__init__.py
index 5c0d1f3aaafc6..cb15a0d04fc8d 100644
--- a/libcxx/utils/libcxx/test/features/__init__.py
+++ b/libcxx/utils/libcxx/test/features/__init__.py
@@ -6,7 +6,16 @@
 #
 # ===----------------------------------------------------------------------===##
 
-from . import availability, compiler, gdb, libcxx_macros, localization, misc, platform
+from . import (
+    availability,
+    compiler,
+    gdb,
+    lldb,
+    libcxx_macros,
+    localization,
+    misc,
+    platform,
+)
 
 # Lit features are evaluated in order. Some features depend on other features, so
 # we are careful to define them in the correct order. For example, several features
@@ -17,5 +26,6 @@
 DEFAULT_FEATURES += platform.features
 DEFAULT_FEATURES += localization.features
 DEFAULT_FEATURES += gdb.features
+DEFAULT_FEATURES += lldb.features
 DEFAULT_FEATURES += misc.features
 DEFAULT_FEATURES += availability.features
diff --git a/libcxx/utils/libcxx/test/features/lldb.py b/libcxx/utils/libcxx/test/features/lldb.py
new file mode 100644
index 0000000000000..5d9c41a31a4e1
--- /dev/null
+++ b/libcxx/utils/libcxx/test/features/lldb.py
@@ -0,0 +1,46 @@
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+from libcxx.test.dsl import Feature, AddSubstitution
+import shutil
+import subprocess
+
+# Detect whether LLDB is on the system and has Python scripting support.
+# If so add a substitution to access it.
+
+
+def check_lldb(cfg):
+    lldb_path = shutil.which("lldb")
+    if lldb_path is None:
+        return False
+
+    try:
+        stdout = subprocess.check_output(
+            [
+                lldb_path,
+                "--batch",
+                "-o",
+                'script -l python -- print("Has", "Python", "!")',
+            ],
+            stderr=subprocess.DEVNULL,
+            universal_newlines=True,
+        )
+    except subprocess.CalledProcessError:
+        return False
+
+    # Check we actually ran the Python
+    return "Has Python !" in stdout
+
+
+features = [
+    Feature(
+        name="host-has-lldb-with-python",
+        when=check_lldb,
+        actions=[AddSubstitution("%{lldb}", lambda cfg: shutil.which("lldb"))],
+    )
+]
diff --git a/libcxx/utils/lldb/libcxx/libcxx.py b/libcxx/utils/lldb/libcxx/libcxx.py
new file mode 100644
index 0000000000000..e78d708b7da2d
--- /dev/null
+++ b/libcxx/utils/lldb/libcxx/libcxx.py
@@ -0,0 +1,52 @@
+# 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
+
+# Entry point for LLDB to register data formatters.
+
+import lldb
+from libcxx_map_formatter import *
+from libcxx_vector_formatter import *
+
+
+def register_synthetic(
+    debugger: lldb.SBDebugger, regex: str, class_name: str, extra_flags: str = ""
+):
+    debugger.HandleCommand(
+        f'type synthetic add {extra_flags} -x "{regex}" -l {__name__}.{class_name} -w "cplusplus-py"'
+    )
+
+
+# Entry point for LLDB when importing scripts.
+def __lldb_init_module(debugger, dict):
+    register_synthetic(
+        debugger, "^std::__[[:alnum:]]+::map<.+> >$", "LibcxxStdMapSyntheticProvider"
+    )
+    register_synthetic(
+        debugger, "^std::__[[:alnum:]]+::set<.+> >$", "LibcxxStdMapSyntheticProvider"
+    )
+    register_synthetic(
+        debugger,
+        "^std::__[[:alnum:]]+::multiset<.+> >$",
+        "LibcxxStdMapSyntheticProvider",
+    )
+    register_synthetic(
+        debugger,
+        "^std::__[[:alnum:]]+::multimap<.+> >$",
+        "LibcxxStdMapSyntheticProvider",
+    )
+    register_synthetic(
+        debugger,
+        "^std::__[[:alnum:]]+::__map_(const_)?iterator<.+>$",
+        "LibCxxMapIteratorSyntheticProvider",
+    )
+    register_synthetic(
+        debugger,
+        "^std::__[[:alnum:]]+::vector<.+>$",
+        "LibCxxStdVectorSyntheticFrontendCreator",
+        "--wants-dereference",
+    )
+
+    # Enables registered formatters in LLDB. The builtin LLDB formatters are registered in the `cplusplus` category.
+    # We don't want to override/clash with those.
+    debugger.HandleCommand("type category enable cplusplus-py")
diff --git a/libcxx/utils/lldb/libcxx/libcxx_map_formatter.py b/libcxx/utils/lldb/libcxx/libcxx_map_formatter.py
new file mode 100644
index 0000000000000..22f26ff7830ae
--- /dev/null
+++ b/libcxx/utils/lldb/libcxx/libcxx_map_formatter.py
@@ -0,0 +1,416 @@
+# 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
+
+# LLDB data formatters for libc++ std::(multi)map and std::(multi)set
+
+import lldb
+
+
+class MapEntry:
+    """Wrapper around an LLDB ValueObject representing a tree node entry."""
+
+    def __init__(self, entry_sp=None):
+        self.m_entry_sp = entry_sp
+
+    def left(self):
+        """Get the left child pointer (offset 0)."""
+        if not self.m_entry_sp:
+            return None
+        return self.m_entry_sp.CreateChildAtOffset("", 0, self.m_entry_sp.GetType())
+
+    def right(self):
+        if not self.m_entry_sp:
+            return None
+        addr_size = self.m_entry_sp.GetProcess().GetAddressByteSize()
+        return self.m_entry_sp.CreateChildAtOffset(
+            "", addr_size, self.m_entry_sp.GetType()
+        )
+
+    def parent(self):
+        if not self.m_entry_sp:
+            return None
+        addr_size = self.m_entry_sp.GetProcess().GetAddressByteSize()
+        return self.m_entry_sp.CreateChildAtOffset(
+            "", 2 * addr_size, self.m_entry_sp.GetType()
+        )
+
+    def value(self):
+        """Get the unsigned integer value of the entry (pointer address)."""
+        if not self.m_entry_sp:
+            return 0
+        return self.m_entry_sp.GetValueAsUnsigned(0)
+
+    def error(self):
+        if not self.m_entry_sp:
+            return True
+        return self.m_entry_sp.GetError().Fail()
+
+    def null(self):
+        return self.value() == 0
+
+    def get_entry(self):
+        return self.m_entry_sp
+
+    def set_entry(self, entry):
+        self.m_entry_sp = entry
+
+    def __eq__(self, other):
+        if not isinstance(other, MapEntry):
+            return False
+        if self.m_entry_sp is None and other.m_entry_sp is None:
+            return True
+        if self.m_entry_sp is None or other.m_entry_sp is None:
+            return False
+        return self.m_entry_sp.GetLoadAddress() == other.m_entry_sp.GetLoadAddress()
+
+
+class MapIterator:
+    """Iterator for traversing the tree backing std::map."""
+
+    def __init__(self, entry=None, depth=0):
+        self.m_entry = MapEntry(entry) if entry else MapEntry()
+        self.m_max_depth = depth
+        self.m_error = False
+
+    def value(self):
+        return self.m_entry.get_entry()
+
+    def advance(self, count):
+        """Advance the iterator by count steps and return the entry."""
+        if self.m_error:
+            return None
+
+        steps = 0
+        while count > 0:
+            self._next()
+            count -= 1
+            steps += 1
+            if self.m_error or self.m_entry.null() or (steps > self.m_max_depth):
+                return None
+
+        return self.m_entry.get_entry()
+
+    def _next(self):
+        """
+        Mimics libc++'s __tree_next algorithm, which libc++ uses
+        in its __tree_iterator::operator++.
+        """
+        if self.m_entry.null():
+            return
+
+        right = MapEntry(self.m_entry.right())
+        if not right.null():
+            self.m_entry = self._tree_min(right)
+            return
+
+        steps = 0
+        while not self._is_left_child(self.m_entry):
+            if self.m_entry.error():
+                self.m_error = True
+                return
+            self.m_entry.set_entry(self.m_entry.parent())
+            steps += 1
+            if steps > self.m_max_depth:
+                self.m_entry = MapEntry()
+                return
+
+        self.m_entry = MapEntry(self.m_entry.parent())
+
+    def _tree_min(self, x):
+        """Mimics libc++'s __tree_min algorithm."""
+        if x.null():
+            return MapEntry()
+
+        left = MapEntry(x.left())
+        steps = 0
+        while not left.null():
+            if left.error():
+                self.m_error = True
+                return MapEntry()
+            x = MapEntry(left.get_entry())
+            left = MapEntry(x.left())
+            steps += 1
+            if steps > self.m_max_depth:
+                return MapEntry()
+
+        return x
+
+    def _is_left_child(self, x):
+        """Check if x is a left child of its parent."""
+        if x.null():
+            return False
+        rhs = MapEntry(x.parent())
+        rhs.set_entry(rhs.left())
+        return x.value() == rhs.value()
+
+
+def _get_first_value_of_libcxx_compressed_pair(pair):
+    """
+    Get the first value from a libc++ compressed pair.
+    Handles both old and new layouts.
+    """
+    # Try __value_ first (newer layout)
+    value_sp = pair.GetChildMemberWithName("__value_")
+    if value_sp and value_sp.IsValid():
+        return value_sp
+
+    # Try __first_ (older layout)
+    first_sp = pair.GetChildMemberWithName("__first_")
+    if first_sp and first_sp.IsValid():
+        return first_sp
+
+    return None
+
+
+def _get_value_or_old_compressed_pair(tree, value_name, pair_name):
+    """
+    Try to get a value member directly, or fall back to compressed pair layout.
+    Returns (value_sp, is_compressed_pair).
+    """
+    # Try new layout first (direct member)
+    value_sp = tree.GetChildMemberWithName(value_name)
+    if value_sp and value_sp.IsValid():
+        return (value_sp, False)
+
+    # Fall back to old compressed pair layout
+    pair_sp = tree.GetChildMemberWithName(pair_name)
+    if pair_sp and pair_sp.IsValid():
+        return (pair_sp, True)
+
+    return (None, False)
+
+
+class LibcxxStdMapSyntheticProvider:
+    """Synthetic children provider for libc++ std::map."""
+
+    def __init__(self, valobj, internal_dict):
+        self.valobj = valobj
+        self.m_tree = None
+        self.m_root_node = None
+        self.m_node_ptr_type = None
+        self.m_count = None
+        self.m_iterators = {}
+
+    def num_children(self):
+        if self.m_count is not None:
+            return self.m_count
+
+        if self.m_tree is None:
+            return 0
+
+        # Try new layout (__size_) or old compressed pair layout (__pair3_)
+        size_sp, is_compressed_pair = _get_value_or_old_compressed_pair(
+            self.m_tree, "__size_", "__pair3_"
+        )
+
+        if not size_sp:
+            return 0
+
+        if is_compressed_pair:
+            return self._calculate_num_children_for_old_compressed_pair_layout(size_sp)
+
+        self.m_count = size_sp.GetValueAsUnsigned(0)
+        return self.m_count
+
+    def _calculate_num_children_for_old_compressed_pair_layout(self, pair):
+        """Handle old libc++ compressed pair layout."""
+        node_sp = _get_first_value_of_libcxx_compressed_pair(pair)
+
+        if not node_sp:
+            return 0
+
+        self.m_count = node_sp.GetValueAsUnsigned(0)
+        return self.m_count
+
+    def get_child_index(self, name):
+        """Get the index of a child with the given name (e.g., "[0]" -> 0)."""
+        try:
+            if name.startswith("[") and name.endswith("]"):
+                return int(name[1:-1])
+        except ValueError:
+            pass
+        return None
+
+    def get_child_at_index(self, index):
+        num_children = self.num_children()
+        if index >= num_children:
+            return None
+
+        if self.m_tree is None or self.m_root_node is None:
+            return None
+
+        key_val_sp = self._get_key_value_pair(index, num_children)
+        if not key_val_sp:
+            # This will stop all future searches until an update() happens
+            self.m_tree = None
+            return None
+
+        name = "[%d]" % index
+        potential_child_sp = key_val_sp.Clone(name)
+
+        if potential_child_sp and potential_child_sp.IsValid():
+            num_child_children = potential_child_sp.GetNumChildren()
+
+            # Handle __cc_ or __cc wrapper (1 child case)
+            if num_child_children == 1:
+                child0_sp = potential_child_sp.GetChildAtIndex(0)
+                if child0_sp:
+                    child_name = child0_sp.GetName()
+                    if child_name in ("__cc_", "__cc"):
+                        potential_child_sp = child0_sp.Clone(name)
+
+            # Handle __cc_ and __nc wrapper (2 children case)
+            elif num_child_children == 2:
+                child0_sp = potential_child_sp.GetChildAtIndex(0)
+                child1_sp = potential_child_sp.GetChildAtIndex(1)
+                if child0_sp and child1_sp:
+                    child0_name = child0_sp.GetName()
+                    child1_name = child1_sp.GetName()
+                    if child0_name in ("__cc_", "__cc") and child1_name == "__nc":
+                        potential_child_sp = child0_sp.Clone(name)
+
+        return potential_child_sp
+
+    def update(self):
+        """Update the cached state. Called when the underlying value changes."""
+        self.m_count = None
+        self.m_tree = None
+        self.m_root_node = None
+        self.m_iterators.clear()
+
+        self.m_tree = self.valobj.GetChildMemberWithName("__tree_")
+        if not self.m_tree or not self.m_tree.IsValid():
+            return False
+
+        self.m_root_node = self.m_tree.GetChildMem...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/187677


More information about the libcxx-commits mailing list