From d6ae568d58b31b1cdd0a5f8c84955330a35c3a38 Mon Sep 17 00:00:00 2001 From: jeffreytan81 Date: Mon, 9 Feb 2026 19:23:40 -0800 Subject: [PATCH] Fix LLDB data formatter for llvm::Expected with non-reference types (#179294) This patch fixes LLDB data formatter support for llvm::Expected with the following changes: llvm/utils/lldbDataFormatters.py: Fix ExpectedSynthProvider to handle non-templated storage types (e.g., int, int*). Previously the formatter only worked with templated storage types like std::reference_wrapper. cross-project-tests/lit.cfg.py: Fix get_lldb_version_string() to use locally-built LLDB on non-Darwin platforms instead of system LLDB Fix minimum version from "1900" to "19.0.0" (typo in original code) New test files: Added expected.cpp and expected.test to test the formatter with Expected and Expected. --------- Co-authored-by: Jeffrey Tan --- .../llvm-prettyprinters/lldb/CMakeLists.txt | 2 + .../llvm-prettyprinters/lldb/expected.cpp | 36 +++++++++++++ .../llvm-prettyprinters/lldb/expected.test | 52 +++++++++++++++++++ cross-project-tests/lit.cfg.py | 15 ++++-- llvm/utils/lldbDataFormatters.py | 17 +++++- 5 files changed, 117 insertions(+), 5 deletions(-) create mode 100644 cross-project-tests/debuginfo-tests/llvm-prettyprinters/lldb/expected.cpp create mode 100644 cross-project-tests/debuginfo-tests/llvm-prettyprinters/lldb/expected.test diff --git a/cross-project-tests/debuginfo-tests/llvm-prettyprinters/lldb/CMakeLists.txt b/cross-project-tests/debuginfo-tests/llvm-prettyprinters/lldb/CMakeLists.txt index c0873759e0ca..e2a9a080fd4c 100644 --- a/cross-project-tests/debuginfo-tests/llvm-prettyprinters/lldb/CMakeLists.txt +++ b/cross-project-tests/debuginfo-tests/llvm-prettyprinters/lldb/CMakeLists.txt @@ -10,11 +10,13 @@ macro(add_lldb_test target source) endmacro() add_lldb_test(check-lldb-llvm-support-arrayref arrayref.cpp) +add_lldb_test(check-lldb-llvm-support-expected expected.cpp) add_lldb_test(check-lldb-llvm-support-pointer-int-pair pointer-int-pair.cpp) add_lldb_test(check-lldb-llvm-support-pointer-union pointer-union.cpp) set(LLDB_FORMATTER_TESTS check-lldb-llvm-support-arrayref + check-lldb-llvm-support-expected check-lldb-llvm-support-pointer-int-pair check-lldb-llvm-support-pointer-union PARENT_SCOPE) diff --git a/cross-project-tests/debuginfo-tests/llvm-prettyprinters/lldb/expected.cpp b/cross-project-tests/debuginfo-tests/llvm-prettyprinters/lldb/expected.cpp new file mode 100644 index 000000000000..20dca3dc4ff5 --- /dev/null +++ b/cross-project-tests/debuginfo-tests/llvm-prettyprinters/lldb/expected.cpp @@ -0,0 +1,36 @@ +// Test llvm::Expected data formatters. + +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Error.h" +#include + +using namespace llvm; + +int main() { + // Test primitive type (storage is T directly). + Expected ExpectedInt = 42; + (void)static_cast(ExpectedInt); + + // Test pointer type (storage is T* directly). + int x = 10; + Expected ExpectedPtr = &x; + (void)static_cast(ExpectedPtr); + + // Test reference type (storage is std::reference_wrapper). + int y = 100; + Expected ExpectedRef = y; + (void)static_cast(ExpectedRef); + + // Test templated type (storage is the template type directly). + Expected> ExpectedVec = SmallVector{1, 2}; + (void)static_cast(ExpectedVec); + + // Test templated reference type (storage is std::reference_wrapper). + SmallVector vec{3, 4}; + Expected &> ExpectedVecRef = vec; + (void)static_cast(ExpectedVecRef); + + puts("Break here"); + + return 0; +} diff --git a/cross-project-tests/debuginfo-tests/llvm-prettyprinters/lldb/expected.test b/cross-project-tests/debuginfo-tests/llvm-prettyprinters/lldb/expected.test new file mode 100644 index 000000000000..3044c14662d7 --- /dev/null +++ b/cross-project-tests/debuginfo-tests/llvm-prettyprinters/lldb/expected.test @@ -0,0 +1,52 @@ +# REQUIRES: lldb-formatters-compatibility +# +# RUN: split-file %s %t +# RUN: lldb -x \ +# RUN: -o 'command script import %llvm_src_root/utils/lldbDataFormatters.py' \ +# RUN: -s %t/commands.input %llvm_tools_dir/check-lldb-llvm-support-expected \ +# RUN: -o quit \ +# RUN: | FileCheck %t/checks + +#--- commands.input +break set -p "Break here" +run + +v -T ExpectedInt +v -T ExpectedPtr +v -T ExpectedRef +v -T ExpectedVec +v -T ExpectedVecRef + +#--- checks +# CHECK: (lldb) v -T ExpectedInt +# CHECK-NEXT: (llvm::Expected) ExpectedInt = { +# CHECK-NEXT: (llvm::Expected::storage_type) value = 42 +# CHECK-NEXT: } + +# CHECK: (lldb) v -T ExpectedPtr +# CHECK-NEXT: (llvm::Expected) ExpectedPtr = { +# CHECK-NEXT: (llvm::Expected::storage_type) value = 0x{{[0-9a-fA-F]+}} +# CHECK-NEXT: } + +# Reference types are unwrapped from std::reference_wrapper to T. +# CHECK: (lldb) v -T ExpectedRef +# CHECK-NEXT: (llvm::Expected) ExpectedRef = { +# CHECK-NEXT: (int) value = 100 +# CHECK-NEXT: } + +# CHECK: (lldb) v -T ExpectedVec +# CHECK-NEXT: (llvm::Expected >) ExpectedVec = { +# CHECK-NEXT: (llvm::Expected >::storage_type) value = size=2 { +# CHECK-NEXT: (int) [0] = 1 +# CHECK-NEXT: (int) [1] = 2 +# CHECK-NEXT: } +# CHECK-NEXT: } + +# Templated reference types are also unwrapped from std::reference_wrapper to T. +# CHECK: (lldb) v -T ExpectedVecRef +# CHECK-NEXT: (llvm::Expected &>) ExpectedVecRef = { +# CHECK-NEXT: (llvm::SmallVector) value = size=2 { +# CHECK-NEXT: (int) [0] = 3 +# CHECK-NEXT: (int) [1] = 4 +# CHECK-NEXT: } +# CHECK-NEXT: } diff --git a/cross-project-tests/lit.cfg.py b/cross-project-tests/lit.cfg.py index 190f9e212f78..d702c17f2c83 100644 --- a/cross-project-tests/lit.cfg.py +++ b/cross-project-tests/lit.cfg.py @@ -285,9 +285,16 @@ def get_lldb_version_string(): --version output is formatted unexpectedly. """ try: - cmd = ["lldb", "--version"] if platform.system() == "Darwin": - cmd = ["xcrun"] + cmd + # On Darwin, use system lldb which has Apple-specific versioning. + cmd = ["xcrun", "lldb", "--version"] + else: + # On non-Darwin, use the locally-built lldb from llvm_tools_dir. + lldb_path = os.path.join(config.llvm_tools_dir, "lldb") + if not os.path.exists(lldb_path): + print(f"LLDB not found at {lldb_path}", file=sys.stderr) + return None + cmd = [lldb_path, "--version"] lldb_vers_lines = subprocess.check_output(cmd).decode().splitlines() except: @@ -311,7 +318,9 @@ def set_lldb_formatters_compatibility_feature(): # The Apple LLDB version doesn't follow the LLVM release versioning. min_required_lldb_version = "1700" else: - min_required_lldb_version = "1900" + # Minimum version required for SBType::FindDirectNestedType API + # which some LLVM data formatters depend on. + min_required_lldb_version = "19.0.0" try: from packaging import version diff --git a/llvm/utils/lldbDataFormatters.py b/llvm/utils/lldbDataFormatters.py index 93f90dbf3a35..92a8a6b4dcc1 100644 --- a/llvm/utils/lldbDataFormatters.py +++ b/llvm/utils/lldbDataFormatters.py @@ -488,8 +488,21 @@ class ExpectedSynthetic: # Anonymous union. union = self.expected.child[0] storage = union.GetChildMemberWithName(member) - stored_type = storage.type.template_args[0] - self.stored_value = storage.Cast(stored_type).Clone(name) + # For reference types, storage is std::reference_wrapper, so we + # unwrap to get T. For non-reference types, storage is T directly. + # Use GetCanonicalType() to resolve the typedef to the underlying type. + canonical_type_name = storage.type.GetCanonicalType().name + if "reference_wrapper<" in canonical_type_name: + # reference_wrapper stores a T* pointer to the referenced value. + # Get the first child (the pointer member) and dereference it. + ptr_member = storage.GetChildAtIndex(0) + if ptr_member and ptr_member.IsValid() and ptr_member.type.IsPointerType(): + self.stored_value = ptr_member.Dereference().Clone(name) + else: + # Fallback: just use storage as-is. + self.stored_value = storage.Clone(name) + else: + self.stored_value = storage.Clone(name) def num_children(self) -> int: return 1