Skip to content

[lldb][TypeSystemClang] Allow arrays to be dereferenced in C/C++. #135843

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lldb/include/lldb/Symbol/CompilerType.h
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,11 @@ class CompilerType {

CompilerDecl GetStaticFieldWithName(llvm::StringRef name) const;

llvm::Expected<CompilerType>
GetDereferencedType(ExecutionContext *exe_ctx, std::string &deref_name,
uint32_t &deref_byte_size, int32_t &deref_byte_offset,
ValueObject *valobj, uint64_t &language_flags) const;

llvm::Expected<CompilerType> GetChildCompilerTypeAtIndex(
ExecutionContext *exe_ctx, size_t idx, bool transparent_pointers,
bool omit_empty_base_classes, bool ignore_array_bounds,
Expand Down
6 changes: 6 additions & 0 deletions lldb/include/lldb/Symbol/TypeSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,12 @@ class TypeSystem : public PluginInterface,
return CompilerDecl();
}

virtual llvm::Expected<CompilerType>
GetDereferencedType(lldb::opaque_compiler_type_t type,
ExecutionContext *exe_ctx, std::string &deref_name,
uint32_t &deref_byte_size, int32_t &deref_byte_offset,
ValueObject *valobj, uint64_t &language_flags) = 0;

virtual llvm::Expected<CompilerType> GetChildCompilerTypeAtIndex(
lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, size_t idx,
bool transparent_pointers, bool omit_empty_base_classes,
Expand Down
18 changes: 18 additions & 0 deletions lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6184,6 +6184,24 @@ uint32_t TypeSystemClang::GetNumPointeeChildren(clang::QualType type) {
return 0;
}

llvm::Expected<CompilerType> TypeSystemClang::GetDereferencedType(
lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx,
std::string &deref_name, uint32_t &deref_byte_size,
int32_t &deref_byte_offset, ValueObject *valobj, uint64_t &language_flags) {
bool type_valid = IsPointerOrReferenceType(type, nullptr) ||
IsArrayType(type, nullptr, nullptr, nullptr);
if (!type_valid)
return llvm::createStringError("not a pointer, reference or array type");
uint32_t child_bitfield_bit_size = 0;
uint32_t child_bitfield_bit_offset = 0;
bool child_is_base_class;
bool child_is_deref_of_parent;
return GetChildCompilerTypeAtIndex(
type, exe_ctx, 0, false, true, false, deref_name, deref_byte_size,
deref_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset,
child_is_base_class, child_is_deref_of_parent, valobj, language_flags);
}

llvm::Expected<CompilerType> TypeSystemClang::GetChildCompilerTypeAtIndex(
lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, size_t idx,
bool transparent_pointers, bool omit_empty_base_classes,
Expand Down
6 changes: 6 additions & 0 deletions lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,12 @@ class TypeSystemClang : public TypeSystem {

static uint32_t GetNumPointeeChildren(clang::QualType type);

llvm::Expected<CompilerType>
GetDereferencedType(lldb::opaque_compiler_type_t type,
ExecutionContext *exe_ctx, std::string &deref_name,
uint32_t &deref_byte_size, int32_t &deref_byte_offset,
ValueObject *valobj, uint64_t &language_flags) override;

llvm::Expected<CompilerType> GetChildCompilerTypeAtIndex(
lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, size_t idx,
bool transparent_pointers, bool omit_empty_base_classes,
Expand Down
12 changes: 12 additions & 0 deletions lldb/source/Symbol/CompilerType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,18 @@ CompilerDecl CompilerType::GetStaticFieldWithName(llvm::StringRef name) const {
return CompilerDecl();
}

llvm::Expected<CompilerType> CompilerType::GetDereferencedType(
ExecutionContext *exe_ctx, std::string &deref_name,
uint32_t &deref_byte_size, int32_t &deref_byte_offset, ValueObject *valobj,
uint64_t &language_flags) const {
if (IsValid())
if (auto type_system_sp = GetTypeSystem())
return type_system_sp->GetDereferencedType(
m_type, exe_ctx, deref_name, deref_byte_size, deref_byte_offset,
valobj, language_flags);
return CompilerType();
}

llvm::Expected<CompilerType> CompilerType::GetChildCompilerTypeAtIndex(
ExecutionContext *exe_ctx, size_t idx, bool transparent_pointers,
bool omit_empty_base_classes, bool ignore_array_bounds,
Expand Down
94 changes: 40 additions & 54 deletions lldb/source/ValueObject/ValueObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2794,74 +2794,60 @@ ValueObjectSP ValueObject::Dereference(Status &error) {
if (m_deref_valobj)
return m_deref_valobj->GetSP();

const bool is_pointer_or_reference_type = IsPointerOrReferenceType();
if (is_pointer_or_reference_type) {
bool omit_empty_base_classes = true;
bool ignore_array_bounds = false;

std::string child_name_str;
uint32_t child_byte_size = 0;
int32_t child_byte_offset = 0;
uint32_t child_bitfield_bit_size = 0;
uint32_t child_bitfield_bit_offset = 0;
bool child_is_base_class = false;
bool child_is_deref_of_parent = false;
const bool transparent_pointers = false;
CompilerType compiler_type = GetCompilerType();
uint64_t language_flags = 0;
std::string deref_name_str;
uint32_t deref_byte_size = 0;
int32_t deref_byte_offset = 0;
CompilerType compiler_type = GetCompilerType();
uint64_t language_flags = 0;

ExecutionContext exe_ctx(GetExecutionContextRef());
ExecutionContext exe_ctx(GetExecutionContextRef());

CompilerType child_compiler_type;
auto child_compiler_type_or_err = compiler_type.GetChildCompilerTypeAtIndex(
&exe_ctx, 0, transparent_pointers, omit_empty_base_classes,
ignore_array_bounds, child_name_str, child_byte_size, child_byte_offset,
child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class,
child_is_deref_of_parent, this, language_flags);
if (!child_compiler_type_or_err)
LLDB_LOG_ERROR(GetLog(LLDBLog::Types),
child_compiler_type_or_err.takeError(),
"could not find child: {0}");
else
child_compiler_type = *child_compiler_type_or_err;

if (child_compiler_type && child_byte_size) {
ConstString child_name;
if (!child_name_str.empty())
child_name.SetCString(child_name_str.c_str());

m_deref_valobj = new ValueObjectChild(
*this, child_compiler_type, child_name, child_byte_size,
child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset,
child_is_base_class, child_is_deref_of_parent, eAddressTypeInvalid,
language_flags);
CompilerType deref_compiler_type;
auto deref_compiler_type_or_err = compiler_type.GetDereferencedType(
&exe_ctx, deref_name_str, deref_byte_size, deref_byte_offset, this,
language_flags);

std::string deref_error;
if (deref_compiler_type_or_err) {
deref_compiler_type = *deref_compiler_type_or_err;
if (deref_compiler_type && deref_byte_size) {
ConstString deref_name;
if (!deref_name_str.empty())
deref_name.SetCString(deref_name_str.c_str());

m_deref_valobj =
new ValueObjectChild(*this, deref_compiler_type, deref_name,
deref_byte_size, deref_byte_offset, 0, 0, false,
true, eAddressTypeInvalid, language_flags);
}

// In case of incomplete child compiler type, use the pointee type and try
// In case of incomplete deref compiler type, use the pointee type and try
// to recreate a new ValueObjectChild using it.
if (!m_deref_valobj) {
// FIXME(#59012): C++ stdlib formatters break with incomplete types (e.g.
// `std::vector<int> &`). Remove ObjC restriction once that's resolved.
if (Language::LanguageIsObjC(GetPreferredDisplayLanguage()) &&
HasSyntheticValue()) {
child_compiler_type = compiler_type.GetPointeeType();
deref_compiler_type = compiler_type.GetPointeeType();

if (child_compiler_type) {
ConstString child_name;
if (!child_name_str.empty())
child_name.SetCString(child_name_str.c_str());
if (deref_compiler_type) {
ConstString deref_name;
if (!deref_name_str.empty())
deref_name.SetCString(deref_name_str.c_str());

m_deref_valobj = new ValueObjectChild(
*this, child_compiler_type, child_name, child_byte_size,
child_byte_offset, child_bitfield_bit_size,
child_bitfield_bit_offset, child_is_base_class,
child_is_deref_of_parent, eAddressTypeInvalid, language_flags);
*this, deref_compiler_type, deref_name, deref_byte_size,
deref_byte_offset, 0, 0, false, true, eAddressTypeInvalid,
language_flags);
}
}
}

} else if (IsSynthetic()) {
m_deref_valobj = GetChildMemberWithName("$$dereference$$").get();
} else {
deref_error = llvm::toString(deref_compiler_type_or_err.takeError());
LLDB_LOG(GetLog(LLDBLog::Types), "could not find child: {0}", deref_error);
if (IsSynthetic()) {
m_deref_valobj = GetChildMemberWithName("$$dereference$$").get();
}
}

if (m_deref_valobj) {
Expand All @@ -2871,13 +2857,13 @@ ValueObjectSP ValueObject::Dereference(Status &error) {
StreamString strm;
GetExpressionPath(strm);

if (is_pointer_or_reference_type)
if (deref_error.empty())
error = Status::FromErrorStringWithFormat(
"dereference failed: (%s) %s",
GetTypeName().AsCString("<invalid type>"), strm.GetData());
else
error = Status::FromErrorStringWithFormat(
"not a pointer or reference type: (%s) %s",
"dereference failed: %s: (%s) %s", deref_error.c_str(),
GetTypeName().AsCString("<invalid type>"), strm.GetData());
return ValueObjectSP();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,7 @@ def test_dereference(self):
self.expect_var_path("*offset_pref", True, type="int *")
self.expect_var_path("**pp_int0", value="0")
self.expect_var_path("&**pp_int0", type="int *")
self.expect(
"frame var '*array'",
error=True,
substrs=["not a pointer or reference type"],
)
self.expect_var_path("*array", value="0")
self.expect(
"frame var '&*p_null'",
error=True,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def cleanup():
self.expect(
"frame variable *number_not_engaged",
error=True,
substrs=["not a pointer or reference type"],
substrs=["dereference failed: not a pointer, reference or array type"],
)

@add_test_categories(["libc++"])
Expand Down
Loading