Fix LLDB data formatter for llvm::Expected<T> with non-reference types (#179294)
This patch fixes LLDB data formatter support for llvm::Expected<T> 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<T>. 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<int> and Expected<int*>. --------- Co-authored-by: Jeffrey Tan <jeffreytan@fb.com>
This commit is contained in:
parent
9eca0a3b8b
commit
d6ae568d58
@ -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)
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
// Test llvm::Expected<T> data formatters.
|
||||
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include <cstdio>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
int main() {
|
||||
// Test primitive type (storage is T directly).
|
||||
Expected<int> ExpectedInt = 42;
|
||||
(void)static_cast<bool>(ExpectedInt);
|
||||
|
||||
// Test pointer type (storage is T* directly).
|
||||
int x = 10;
|
||||
Expected<int *> ExpectedPtr = &x;
|
||||
(void)static_cast<bool>(ExpectedPtr);
|
||||
|
||||
// Test reference type (storage is std::reference_wrapper<T>).
|
||||
int y = 100;
|
||||
Expected<int &> ExpectedRef = y;
|
||||
(void)static_cast<bool>(ExpectedRef);
|
||||
|
||||
// Test templated type (storage is the template type directly).
|
||||
Expected<SmallVector<int, 2>> ExpectedVec = SmallVector<int, 2>{1, 2};
|
||||
(void)static_cast<bool>(ExpectedVec);
|
||||
|
||||
// Test templated reference type (storage is std::reference_wrapper<T>).
|
||||
SmallVector<int, 2> vec{3, 4};
|
||||
Expected<SmallVector<int, 2> &> ExpectedVecRef = vec;
|
||||
(void)static_cast<bool>(ExpectedVecRef);
|
||||
|
||||
puts("Break here");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -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<int>) ExpectedInt = {
|
||||
# CHECK-NEXT: (llvm::Expected<int>::storage_type) value = 42
|
||||
# CHECK-NEXT: }
|
||||
|
||||
# CHECK: (lldb) v -T ExpectedPtr
|
||||
# CHECK-NEXT: (llvm::Expected<int *>) ExpectedPtr = {
|
||||
# CHECK-NEXT: (llvm::Expected<int *>::storage_type) value = 0x{{[0-9a-fA-F]+}}
|
||||
# CHECK-NEXT: }
|
||||
|
||||
# Reference types are unwrapped from std::reference_wrapper<T> to T.
|
||||
# CHECK: (lldb) v -T ExpectedRef
|
||||
# CHECK-NEXT: (llvm::Expected<int &>) ExpectedRef = {
|
||||
# CHECK-NEXT: (int) value = 100
|
||||
# CHECK-NEXT: }
|
||||
|
||||
# CHECK: (lldb) v -T ExpectedVec
|
||||
# CHECK-NEXT: (llvm::Expected<llvm::SmallVector<int, 2> >) ExpectedVec = {
|
||||
# CHECK-NEXT: (llvm::Expected<llvm::SmallVector<int, 2> >::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<T> to T.
|
||||
# CHECK: (lldb) v -T ExpectedVecRef
|
||||
# CHECK-NEXT: (llvm::Expected<llvm::SmallVector<int, 2> &>) ExpectedVecRef = {
|
||||
# CHECK-NEXT: (llvm::SmallVector<int, 2>) value = size=2 {
|
||||
# CHECK-NEXT: (int) [0] = 3
|
||||
# CHECK-NEXT: (int) [1] = 4
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: }
|
||||
@ -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
|
||||
|
||||
@ -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<T>, 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<T> 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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user