[LLDB] Update DIL handling of array subscripting. (#151605)

This updates the DIL code for handling array subscripting to more
closely match and handle all the cases from the original 'frame var'
implementation. Also updates the DIL array subscripting test. This
particularly fixes some issues with handling synthetic children, objc
pointers, and accessing specific bits within scalar data types.
This commit is contained in:
cmtice 2025-08-15 08:26:45 -07:00 committed by GitHub
parent 11c2240049
commit 6d3ad9d9fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 128 additions and 36 deletions

View File

@ -330,39 +330,82 @@ 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()) {
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))
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;
}

View File

@ -69,17 +69,18 @@ class TestFrameVarDILArraySubscript(TestBase):
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 @@ class TestFrameVarDILArraySubscript(TestBase):
)
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 @@ class TestFrameVarDILArraySubscript(TestBase):
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)"],
)

View File

@ -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
}

View File

@ -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