[Lldb-commits] [lldb] [LLDB] Re-land 'Update DIL handling of array subscripting' (PR #154269)
via lldb-commits
lldb-commits at lists.llvm.org
Mon Aug 18 22:04:53 PDT 2025
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 <cmtice at 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
More information about the lldb-commits
mailing list