https://github.com/cmtice created https://github.com/llvm/llvm-project/pull/154269
This attempts to fix the issues with the original PR (#151605), updating the DIL code for handling array subscripting to more closely match and handle all the casees from the original 'frame var' implementation. The first PR did not include special-case code for objc pointers, which apparently caused a test failure on the green-dragon buildbot. Hopefully this PR, which includes the objc pointer special code, fixes that issue. >From 6339648dada68de6fd1489b33c37f1e7c4a38569 Mon Sep 17 00:00:00 2001 From: Caroline Tice <cmt...@google.com> Date: Mon, 18 Aug 2025 22:00:46 -0700 Subject: [PATCH] [LLDB] Re-land 'Update DIL handling of array subscripting' This attempts to fix the issues with the original PR (#151605), updating the DIL code for handling array subscripting to more closely match and handle all the casees from the original 'frame var' implementation. The first PR did not include special-case code for objc pointers, which apparently caused a test failure on the green-dragon buildbot. Hopefully this PR, which includes the objc pointer special code, fixes that issue. --- lldb/source/ValueObject/DILEval.cpp | 149 ++++++++++++++---- .../TestFrameVarDILArraySubscript.py | 21 ++- .../var-dil/basics/ArraySubscript/main.cpp | 9 ++ .../ArraySubscript/myArraySynthProvider.py | 33 ++++ 4 files changed, 178 insertions(+), 34 deletions(-) create mode 100644 lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/myArraySynthProvider.py diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index 6f28434c646cd..e61b7e33f5fd8 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -330,40 +330,135 @@ Interpreter::Visit(const ArraySubscriptNode *node) { return lhs_or_err; lldb::ValueObjectSP base = *lhs_or_err; - // Check to see if 'base' has a synthetic value; if so, try using that. + StreamString var_expr_path_strm; uint64_t child_idx = node->GetIndex(); - if (lldb::ValueObjectSP synthetic = base->GetSyntheticValue()) { - llvm::Expected<uint32_t> num_children = - synthetic->GetNumChildren(child_idx + 1); - if (!num_children) - return llvm::make_error<DILDiagnosticError>( - m_expr, toString(num_children.takeError()), node->GetLocation()); - if (child_idx >= *num_children) { - std::string message = llvm::formatv( + lldb::ValueObjectSP child_valobj_sp; + + bool is_incomplete_array = false; + CompilerType base_type = base->GetCompilerType().GetNonReferenceType(); + base->GetExpressionPath(var_expr_path_strm); + + if (base_type.IsPointerType()) { + bool is_objc_pointer = true; + + if (base->GetCompilerType().GetMinimumLanguage() != lldb::eLanguageTypeObjC) + is_objc_pointer = false; + else if (!base->GetCompilerType().IsPointerType()) + is_objc_pointer = false; + + if (!m_use_synthetic && is_objc_pointer) { + std::string err_msg = + llvm::formatv("\"(%s) %s\" is an Objective-C pointer, and cannot be " + "subscripted", + base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation()); + } else if (is_objc_pointer) { + lldb::ValueObjectSP synthetic = base->GetSyntheticValue(); + if (!synthetic || synthetic == base) { + std::string err_msg = + llvm::formatv("\"(%s) %s\" is not an array type", + base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation()); + } else if (static_cast<uint32_t>(child_idx) >= + synthetic->GetNumChildrenIgnoringErrors()) { + std::string err_msg = llvm::formatv( + "array index %ld is not valid for \"(%s) %s\"", child_idx, + base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation()); + } else { + child_valobj_sp = synthetic->GetChildAtIndex(child_idx); + if (!child_valobj_sp) { + std::string err_msg = llvm::formatv( + "array index %ld is not valid for \"(%s) %s\"", child_idx, + base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>( + m_expr, std::move(err_msg), node->GetLocation()); + } + if (m_use_dynamic != lldb::eNoDynamicValues) { + if (auto dynamic_sp = child_valobj_sp->GetDynamicValue(m_use_dynamic)) + child_valobj_sp = std::move(dynamic_sp); + } + return child_valobj_sp; + } + } + + child_valobj_sp = base->GetSyntheticArrayMember(child_idx, true); + if (!child_valobj_sp) { + std::string err_msg = llvm::formatv( + "failed to use pointer as array for index {0} for " + "\"({1}) {2}\"", + child_idx, base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + if (base_type.IsPointerToVoid()) + err_msg = "subscript of pointer to incomplete type 'void'"; + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation()); + } + } else if (base_type.IsArrayType(nullptr, nullptr, &is_incomplete_array)) { + child_valobj_sp = base->GetChildAtIndex(child_idx); + if (!child_valobj_sp && (is_incomplete_array || m_use_synthetic)) + child_valobj_sp = base->GetSyntheticArrayMember(child_idx, true); + if (!child_valobj_sp) { + std::string err_msg = llvm::formatv( "array index {0} is not valid for \"({1}) {2}\"", child_idx, base->GetTypeName().AsCString("<invalid type>"), - base->GetName().AsCString()); - return llvm::make_error<DILDiagnosticError>(m_expr, message, + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), node->GetLocation()); } - if (lldb::ValueObjectSP child_valobj_sp = - synthetic->GetChildAtIndex(child_idx)) - return child_valobj_sp; + } else if (base_type.IsScalarType()) { + child_valobj_sp = + base->GetSyntheticBitFieldChild(child_idx, child_idx, true); + if (!child_valobj_sp) { + std::string err_msg = llvm::formatv( + "bitfield range {0}-{1} is not valid for \"({2}) {3}\"", child_idx, + child_idx, base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation(), 1); + } + } else { + lldb::ValueObjectSP synthetic = base->GetSyntheticValue(); + if (!m_use_synthetic || !synthetic || synthetic == base) { + std::string err_msg = + llvm::formatv("\"{0}\" is not an array type", + base->GetTypeName().AsCString("<invalid type>")); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation(), 1); + } + if (static_cast<uint32_t>(child_idx) >= + synthetic->GetNumChildrenIgnoringErrors(child_idx + 1)) { + std::string err_msg = llvm::formatv( + "array index {0} is not valid for \"({1}) {2}\"", child_idx, + base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation(), 1); + } + child_valobj_sp = synthetic->GetChildAtIndex(child_idx); + if (!child_valobj_sp) { + std::string err_msg = llvm::formatv( + "array index {0} is not valid for \"({1}) {2}\"", child_idx, + base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation(), 1); + } } - auto base_type = base->GetCompilerType().GetNonReferenceType(); - if (!base_type.IsPointerType() && !base_type.IsArrayType()) - return llvm::make_error<DILDiagnosticError>( - m_expr, "subscripted value is not an array or pointer", - node->GetLocation()); - if (base_type.IsPointerToVoid()) - return llvm::make_error<DILDiagnosticError>( - m_expr, "subscript of pointer to incomplete type 'void'", - node->GetLocation()); - - if (base_type.IsArrayType()) { - if (lldb::ValueObjectSP child_valobj_sp = base->GetChildAtIndex(child_idx)) - return child_valobj_sp; + if (child_valobj_sp) { + if (m_use_dynamic != lldb::eNoDynamicValues) { + if (auto dynamic_sp = child_valobj_sp->GetDynamicValue(m_use_dynamic)) + child_valobj_sp = std::move(dynamic_sp); + } + return child_valobj_sp; } int64_t signed_child_idx = node->GetIndex(); diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py index 0f56057189395..e3cfb878dd415 100644 --- a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py +++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py @@ -69,17 +69,18 @@ def test_subscript(self): substrs=["expected 'r_square', got: <'.'"], ) - # Base should be a "pointer to T" and index should be of an integral type. - self.expect( - "frame var 'idx_1[0]'", - error=True, - substrs=["subscripted value is not an array or pointer"], - ) + # Test accessing bits in scalar types. + self.expect_var_path("idx_1[0]", value="1") + self.expect_var_path("idx_1[1]", value="0") + + # Bit adcess not valid for a reference. self.expect( "frame var 'idx_1_ref[0]'", error=True, - substrs=["subscripted value is not an array or pointer"], + substrs=["bitfield range 0-0 is not valid"], ) + + # Base should be a "pointer to T" and index should be of an integral type. self.expect( "frame var 'int_arr[int_ptr]'", error=True, @@ -105,6 +106,8 @@ def test_subscript_synthetic(self): ) self.runCmd("settings set target.experimental.use-DIL true") + self.runCmd("script from myArraySynthProvider import *") + self.runCmd("type synth add -l myArraySynthProvider myArray") # Test synthetic value subscription self.expect_var_path("vector[1]", value="2") @@ -113,3 +116,7 @@ def test_subscript_synthetic(self): error=True, substrs=["array index 100 is not valid"], ) + self.expect( + "frame var 'ma_ptr[0]'", + substrs=["(myArray) ma_ptr[0] = ([0] = 7, [1] = 8, [2] = 9, [3] = 10)"], + ) diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp index a9a3612dfae5a..03ad3e872ca76 100644 --- a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp +++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp @@ -1,5 +1,11 @@ #include <vector> +class myArray { +public: + int m_array[4] = {7, 8, 9, 10}; + int m_arr_size = 4; +}; + int main(int argc, char **argv) { int int_arr[] = {1, 2, 3}; int *int_ptr = int_arr; @@ -29,5 +35,8 @@ int main(int argc, char **argv) { std::vector<int> vector = {1, 2, 3}; + myArray ma; + myArray *ma_ptr = &ma; + return 0; // Set a breakpoint here } diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/myArraySynthProvider.py b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/myArraySynthProvider.py new file mode 100644 index 0000000000000..167899bd3907c --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/myArraySynthProvider.py @@ -0,0 +1,33 @@ +import lldb + + +class myArraySynthProvider: + def __init__(self, valobj, dict): + self.valobj = valobj + + def num_children(self): + size_valobj = self.valobj.GetChildMemberWithName("m_arr_size") + if size_valobj: + return size_valobj.GetValueAsUnsigned(0) + return 0 + + def get_child_at_index(self, index): + size_valobj = self.valobj.GetChildMemberWithName("m_arr_size") + arr = self.valobj.GetChildMemberWithName("m_array") + if not size_valobj or not arr: + return None + max_idx = size_valobj.GetValueAsUnsigned(0) + if index >= max_idx: + return None + return arr.GetChildAtIndex(index) + + def get_child_index(self, name): + if name == "[0]": + return 0 + if name == "[1]": + return + if name == "[2]": + return 2 + if name == "[3]": + return 3 + return -1 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits