-
Notifications
You must be signed in to change notification settings - Fork 13.4k
[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
base: main
Are you sure you want to change the base?
Conversation
@labath @jimingham |
I think something like
I'm not worried about that with a patch like this since the problem remains contained within |
The problem I encountered is that
I think the change in logic is allowing an array type pass the type check and calling |
Why does it need to do that? Is it so to control which error message gets printed? If so, I think we could tweak that text so that we don't need that. The string "not a pointer or reference type" is not correct anymore anyway since we don't know what is the criteria that a type system will use to determine if something is dereferencable. Maybe the type system should return an llvm::Error/Expected, and then caller figures out how (and whether) to print that? Also, could we reduce the number of arguments to that function? I'd hope that we don't need all of them since this is a very specific operation. E.g, I think the caller should be able to assume that the returned object is a "dereference" (whatever that means) of the parent instead of passing that via
I see. I'm still not worried about that because it means the array gets the same treatment as pointers. I think it's reasonable (though probably not very useful) that if a user registers a data formatter for |
From what I could understand, if we try to find a
|
e5d6984
to
0d5348a
Compare
@llvm/pr-subscribers-lldb Author: Ilia Kuklin (kuilpd) ChangesAdd a function Full diff: https://github.com/llvm/llvm-project/pull/135843.diff 7 Files Affected:
diff --git a/lldb/include/lldb/Symbol/CompilerType.h b/lldb/include/lldb/Symbol/CompilerType.h
index 671b5314c24a2..f02a415afd12c 100644
--- a/lldb/include/lldb/Symbol/CompilerType.h
+++ b/lldb/include/lldb/Symbol/CompilerType.h
@@ -433,6 +433,14 @@ class CompilerType {
CompilerDecl GetStaticFieldWithName(llvm::StringRef name) const;
+ llvm::Expected<CompilerType>
+ GetDereferencedType(ExecutionContext *exe_ctx, std::string &child_name,
+ uint32_t &child_byte_size, int32_t &child_byte_offset,
+ uint32_t &child_bitfield_bit_size,
+ uint32_t &child_bitfield_bit_offset,
+ bool &child_is_base_class, ValueObject *valobj,
+ uint64_t &language_flags, bool &type_valid) const;
+
llvm::Expected<CompilerType> GetChildCompilerTypeAtIndex(
ExecutionContext *exe_ctx, size_t idx, bool transparent_pointers,
bool omit_empty_base_classes, bool ignore_array_bounds,
diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h
index 9e9edc09a0846..7c58805342993 100644
--- a/lldb/include/lldb/Symbol/TypeSystem.h
+++ b/lldb/include/lldb/Symbol/TypeSystem.h
@@ -364,6 +364,13 @@ class TypeSystem : public PluginInterface,
return CompilerDecl();
}
+ virtual llvm::Expected<CompilerType> GetDereferencedType(
+ lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx,
+ std::string &child_name, uint32_t &child_byte_size,
+ int32_t &child_byte_offset, uint32_t &child_bitfield_bit_size,
+ uint32_t &child_bitfield_bit_offset, bool &child_is_base_class,
+ ValueObject *valobj, uint64_t &language_flags, bool &type_valid) = 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,
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 59292f4b24af3..5c1b98509892e 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -6201,6 +6201,23 @@ 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 &child_name, uint32_t &child_byte_size,
+ int32_t &child_byte_offset, uint32_t &child_bitfield_bit_size,
+ uint32_t &child_bitfield_bit_offset, bool &child_is_base_class,
+ ValueObject *valobj, uint64_t &language_flags, bool &type_valid) {
+ type_valid = IsPointerOrReferenceType(type, nullptr) ||
+ IsArrayType(type, nullptr, nullptr, nullptr);
+ if (!type_valid)
+ return llvm::createStringError("not a pointer, reference or array type");
+ bool child_is_deref_of_parent;
+ return GetChildCompilerTypeAtIndex(
+ type, exe_ctx, 0, false, true, false, 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, 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,
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
index 442f88a5b79ae..ab74027cc75c4 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
@@ -889,6 +889,13 @@ 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 &child_name, uint32_t &child_byte_size,
+ int32_t &child_byte_offset, uint32_t &child_bitfield_bit_size,
+ uint32_t &child_bitfield_bit_offset, bool &child_is_base_class,
+ ValueObject *valobj, uint64_t &language_flags, bool &type_valid) 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,
diff --git a/lldb/source/Symbol/CompilerType.cpp b/lldb/source/Symbol/CompilerType.cpp
index 8e89d006d08d3..998822708fe21 100644
--- a/lldb/source/Symbol/CompilerType.cpp
+++ b/lldb/source/Symbol/CompilerType.cpp
@@ -893,6 +893,21 @@ CompilerDecl CompilerType::GetStaticFieldWithName(llvm::StringRef name) const {
return CompilerDecl();
}
+llvm::Expected<CompilerType> CompilerType::GetDereferencedType(
+ ExecutionContext *exe_ctx, std::string &child_name,
+ uint32_t &child_byte_size, int32_t &child_byte_offset,
+ uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset,
+ bool &child_is_base_class, ValueObject *valobj, uint64_t &language_flags,
+ bool &type_valid) const {
+ if (IsValid())
+ if (auto type_system_sp = GetTypeSystem())
+ return type_system_sp->GetDereferencedType(
+ m_type, exe_ctx, child_name, child_byte_size, child_byte_offset,
+ child_bitfield_bit_size, child_bitfield_bit_offset,
+ child_is_base_class, valobj, language_flags, type_valid);
+ 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,
diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp
index 8741cb7343166..b023dbf118d78 100644
--- a/lldb/source/ValueObject/ValueObject.cpp
+++ b/lldb/source/ValueObject/ValueObject.cpp
@@ -2850,37 +2850,37 @@ 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 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;
+ CompilerType compiler_type = GetCompilerType();
+ uint64_t language_flags = 0;
+ bool is_valid_dereference_type = false;
- 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)
+ CompilerType child_compiler_type;
+ auto child_compiler_type_or_err = compiler_type.GetDereferencedType(
+ &exe_ctx, child_name_str, child_byte_size, child_byte_offset,
+ child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class,
+ this, language_flags, is_valid_dereference_type);
+
+ std::string deref_error;
+ if (!child_compiler_type_or_err) {
+ auto err = child_compiler_type_or_err.takeError();
+ if (err.isA<llvm::StringError>()) {
+ deref_error = llvm::toString(std::move(err));
LLDB_LOG_ERROR(GetLog(LLDBLog::Types),
- child_compiler_type_or_err.takeError(),
+ llvm::createStringError(deref_error),
"could not find child: {0}");
- else
- child_compiler_type = *child_compiler_type_or_err;
+ }
+ } else
+ child_compiler_type = *child_compiler_type_or_err;
+ if (is_valid_dereference_type) {
if (child_compiler_type && child_byte_size) {
ConstString child_name;
if (!child_name_str.empty())
@@ -2889,8 +2889,7 @@ ValueObjectSP ValueObject::Dereference(Status &error) {
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);
+ child_is_base_class, true, eAddressTypeInvalid, language_flags);
}
// In case of incomplete child compiler type, use the pointee type and try
@@ -2910,12 +2909,11 @@ ValueObjectSP ValueObject::Dereference(Status &error) {
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);
+ child_bitfield_bit_offset, child_is_base_class, true,
+ eAddressTypeInvalid, language_flags);
}
}
}
-
} else if (HasSyntheticValue()) {
m_deref_valobj =
GetSyntheticValue()->GetChildMemberWithName("$$dereference$$").get();
@@ -2930,13 +2928,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();
}
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py
index 7dc656a7ae225..8f36edea7d727 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py
@@ -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"],
)
@add_test_categories(["libc++"])
|
Thank you for looking into this. I can see how something like this might happen, but that's not the conclusion I would draw from this, at least not without more data. I think we need to know how exactly is that recursion happening (e.g., the call stack). Since there is python code involved, it could be there's a bug there. The data formatters we write for tests aren't exactly battle-tested, and it's certainly possible to cause an infinite recursion with a bad data formatter. It's also possible that the implementation of (Also, to avoid any confusion, I want to add that I don't consider this to be a requirement for merging #134428. I'm just waiting on that PR as you haven't responded to all of my comments.) |
This seems to fix the issue, I was able to remove the type check and rely just on the success of |
…peSystem to allow arrays to be dereferenced in C/C++.
0d5348a
to
a0ebf36
Compare
uint32_t child_bitfield_bit_size = 0; | ||
uint32_t child_bitfield_bit_offset = 0; | ||
bool child_is_base_class = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think child_is_base_class
should go away as well, and maybe the bitfield fields as well (in principle I can imagine a pointer to a bitfield, but I don't think any current language has those).
@@ -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"], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the full error message here? Would be nice to check that it's reasonable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a thought that a message might be different since it comes from TypeSystem
, but now I realize it's C++ test and we know exactly which type system.
auto err = child_compiler_type_or_err.takeError(); | ||
if (err.isA<llvm::StringError>()) { | ||
deref_error = llvm::toString(std::move(err)); | ||
LLDB_LOG_ERROR(GetLog(LLDBLog::Types), | ||
llvm::createStringError(deref_error), | ||
"could not find child: {0}"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any error can be converted to a string.
auto err = child_compiler_type_or_err.takeError(); | |
if (err.isA<llvm::StringError>()) { | |
deref_error = llvm::toString(std::move(err)); | |
LLDB_LOG_ERROR(GetLog(LLDBLog::Types), | |
llvm::createStringError(deref_error), | |
"could not find child: {0}"); | |
} | |
deref_error = llvm::toString(child_compiler_type_or_err.takeError()); | |
LLDB_LOG(GetLog(LLDBLog::Types), | |
"could not find child: {0}", deref_error); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. I think this is fine now. @jimingham, @adrian-prantl, do you have anything to add.
Heads-up: I believe you will need to implement this method in the Swift type system in order to keep dereferencing working.
CompilerType compiler_type = GetCompilerType(); | ||
uint64_t language_flags = 0; | ||
std::string child_name_str; | ||
uint32_t child_byte_size = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a tiny nit, but I find this flow harder to read because I don't understand why you use both "child" and "deref" as prefixes related to the defererenced object. I had to think a bit to realize that this distinction was without difference...
It is true that in strict lldb terms, the deference valobj is a child of the valobj it is the dereference of, but that's the least interesting aspect of it here.
For instance, it's odd that child_name_str
is the name that you are going to give to m_deref_valobj
, or when you assign deref_error
from child_type_or_err
.
This looks fine to me as well. I had a small typographical nit which you can act on or not as you feel moved to. |
Add a function
IsValidDereferenceType
to TypeSystem.TypeSystemClang now allows arrays to be dereferenced.