[Lldb-commits] [lldb] [LLDB] Improve error handling in TypeSystemClang::GetChildCompilerTyp… (PR #170932)
Adrian Prantl via lldb-commits
lldb-commits at lists.llvm.org
Fri Dec 5 13:59:57 PST 2025
https://github.com/adrian-prantl created https://github.com/llvm/llvm-project/pull/170932
…eAtIndex
Convert the function to early exits, and add sensible error messages for all failing checks.
>From a8fb739fb004fb2c9b9a8bfbfa862950958ca984 Mon Sep 17 00:00:00 2001
From: Adrian Prantl <aprantl at apple.com>
Date: Fri, 5 Dec 2025 13:53:31 -0800
Subject: [PATCH] [LLDB] Improve error handling in
TypeSystemClang::GetChildCompilerTypeAtIndex
Convert the function to early exits, and add sensible error messages
for all failing checks.
---
.../TypeSystem/Clang/TypeSystemClang.cpp | 676 +++++++++---------
.../TestFrameVarDILPointerArithmetic.py | 2 +-
2 files changed, 337 insertions(+), 341 deletions(-)
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 2cb4a46130c84..d2606faeee8b8 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -6241,7 +6241,7 @@ llvm::Expected<CompilerType> TypeSystemClang::GetChildCompilerTypeAtIndex(
bool &child_is_base_class, bool &child_is_deref_of_parent,
ValueObject *valobj, uint64_t &language_flags) {
if (!type)
- return CompilerType();
+ return llvm::createStringError("invalid type");
auto get_exe_scope = [&exe_ctx]() {
return exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr;
@@ -6265,356 +6265,277 @@ llvm::Expected<CompilerType> TypeSystemClang::GetChildCompilerTypeAtIndex(
int32_t bit_offset;
switch (parent_type_class) {
case clang::Type::Builtin:
- if (idx_is_valid) {
- switch (llvm::cast<clang::BuiltinType>(parent_qual_type)->getKind()) {
- case clang::BuiltinType::ObjCId:
- case clang::BuiltinType::ObjCClass:
- child_name = "isa";
- child_byte_size =
- getASTContext().getTypeSize(getASTContext().ObjCBuiltinClassTy) /
- CHAR_BIT;
- return GetType(getASTContext().ObjCBuiltinClassTy);
+ if (!idx_is_valid)
+ return llvm::createStringError("invalid index");
- default:
- break;
- }
+ switch (llvm::cast<clang::BuiltinType>(parent_qual_type)->getKind()) {
+ case clang::BuiltinType::ObjCId:
+ case clang::BuiltinType::ObjCClass:
+ child_name = "isa";
+ child_byte_size =
+ getASTContext().getTypeSize(getASTContext().ObjCBuiltinClassTy) /
+ CHAR_BIT;
+ return GetType(getASTContext().ObjCBuiltinClassTy);
+
+ default:
+ break;
}
break;
+ case clang::Type::Record: {
+ if (!idx_is_valid)
+ return llvm::createStringError("invalid index");
+ if (!GetCompleteType(type))
+ return llvm::createStringError("cannot complete type");
- case clang::Type::Record:
- if (idx_is_valid && GetCompleteType(type)) {
- const clang::RecordType *record_type =
- llvm::cast<clang::RecordType>(parent_qual_type.getTypePtr());
- const clang::RecordDecl *record_decl =
- record_type->getDecl()->getDefinitionOrSelf();
- const clang::ASTRecordLayout &record_layout =
- getASTContext().getASTRecordLayout(record_decl);
- uint32_t child_idx = 0;
+ const clang::RecordType *record_type =
+ llvm::cast<clang::RecordType>(parent_qual_type.getTypePtr());
+ const clang::RecordDecl *record_decl =
+ record_type->getDecl()->getDefinitionOrSelf();
+ const clang::ASTRecordLayout &record_layout =
+ getASTContext().getASTRecordLayout(record_decl);
+ uint32_t child_idx = 0;
- const clang::CXXRecordDecl *cxx_record_decl =
- llvm::dyn_cast<clang::CXXRecordDecl>(record_decl);
- if (cxx_record_decl) {
- // We might have base classes to print out first
- clang::CXXRecordDecl::base_class_const_iterator base_class,
- base_class_end;
- for (base_class = cxx_record_decl->bases_begin(),
- base_class_end = cxx_record_decl->bases_end();
- base_class != base_class_end; ++base_class) {
- const clang::CXXRecordDecl *base_class_decl = nullptr;
+ const clang::CXXRecordDecl *cxx_record_decl =
+ llvm::dyn_cast<clang::CXXRecordDecl>(record_decl);
+ if (cxx_record_decl) {
+ // We might have base classes to print out first
+ clang::CXXRecordDecl::base_class_const_iterator base_class,
+ base_class_end;
+ for (base_class = cxx_record_decl->bases_begin(),
+ base_class_end = cxx_record_decl->bases_end();
+ base_class != base_class_end; ++base_class) {
+ const clang::CXXRecordDecl *base_class_decl = nullptr;
- // Skip empty base classes
- if (omit_empty_base_classes) {
+ // Skip empty base classes
+ if (omit_empty_base_classes) {
+ base_class_decl =
+ llvm::cast<clang::CXXRecordDecl>(
+ base_class->getType()->getAs<clang::RecordType>()->getDecl())
+ ->getDefinitionOrSelf();
+ if (!TypeSystemClang::RecordHasFields(base_class_decl))
+ continue;
+ }
+
+ if (idx == child_idx) {
+ if (base_class_decl == nullptr)
base_class_decl = llvm::cast<clang::CXXRecordDecl>(
base_class->getType()
->getAs<clang::RecordType>()
->getDecl())
->getDefinitionOrSelf();
- if (!TypeSystemClang::RecordHasFields(base_class_decl))
- continue;
- }
- if (idx == child_idx) {
- if (base_class_decl == nullptr)
- base_class_decl = llvm::cast<clang::CXXRecordDecl>(
- base_class->getType()
- ->getAs<clang::RecordType>()
- ->getDecl())
- ->getDefinitionOrSelf();
-
- if (base_class->isVirtual()) {
- bool handled = false;
- if (valobj) {
- clang::VTableContextBase *vtable_ctx =
- getASTContext().getVTableContext();
- if (vtable_ctx)
- handled = GetVBaseBitOffset(*vtable_ctx, *valobj,
- record_layout, cxx_record_decl,
- base_class_decl, bit_offset);
- }
- if (!handled)
- bit_offset = record_layout.getVBaseClassOffset(base_class_decl)
- .getQuantity() *
- 8;
- } else
- bit_offset = record_layout.getBaseClassOffset(base_class_decl)
+ if (base_class->isVirtual()) {
+ bool handled = false;
+ if (valobj) {
+ clang::VTableContextBase *vtable_ctx =
+ getASTContext().getVTableContext();
+ if (vtable_ctx)
+ handled = GetVBaseBitOffset(*vtable_ctx, *valobj, record_layout,
+ cxx_record_decl, base_class_decl,
+ bit_offset);
+ }
+ if (!handled)
+ bit_offset = record_layout.getVBaseClassOffset(base_class_decl)
.getQuantity() *
8;
-
- // Base classes should be a multiple of 8 bits in size
- child_byte_offset = bit_offset / 8;
- CompilerType base_class_clang_type = GetType(base_class->getType());
- child_name = base_class_clang_type.GetTypeName().AsCString("");
- auto size_or_err =
- base_class_clang_type.GetBitSize(get_exe_scope());
- if (!size_or_err)
- return llvm::joinErrors(
- llvm::createStringError("no size info for base class"),
- size_or_err.takeError());
-
- uint64_t base_class_clang_type_bit_size = *size_or_err;
-
- // Base classes bit sizes should be a multiple of 8 bits in size
- assert(base_class_clang_type_bit_size % 8 == 0);
- child_byte_size = base_class_clang_type_bit_size / 8;
- child_is_base_class = true;
- return base_class_clang_type;
- }
- // We don't increment the child index in the for loop since we might
- // be skipping empty base classes
- ++child_idx;
- }
- }
- // Make sure index is in range...
- uint32_t field_idx = 0;
- clang::RecordDecl::field_iterator field, field_end;
- for (field = record_decl->field_begin(),
- field_end = record_decl->field_end();
- field != field_end; ++field, ++field_idx, ++child_idx) {
- if (idx == child_idx) {
- // Print the member type if requested
- // Print the member name and equal sign
- child_name.assign(field->getNameAsString());
-
- // Figure out the type byte size (field_type_info.first) and
- // alignment (field_type_info.second) from the AST context.
- CompilerType field_clang_type = GetType(field->getType());
- assert(field_idx < record_layout.getFieldCount());
- auto size_or_err = field_clang_type.GetByteSize(get_exe_scope());
+ } else
+ bit_offset = record_layout.getBaseClassOffset(base_class_decl)
+ .getQuantity() *
+ 8;
+
+ // Base classes should be a multiple of 8 bits in size
+ child_byte_offset = bit_offset / 8;
+ CompilerType base_class_clang_type = GetType(base_class->getType());
+ child_name = base_class_clang_type.GetTypeName().AsCString("");
+ auto size_or_err = base_class_clang_type.GetBitSize(get_exe_scope());
if (!size_or_err)
return llvm::joinErrors(
- llvm::createStringError("no size info for field"),
+ llvm::createStringError("no size info for base class"),
size_or_err.takeError());
- child_byte_size = *size_or_err;
- const uint32_t child_bit_size = child_byte_size * 8;
-
- // Figure out the field offset within the current struct/union/class
- // type
- bit_offset = record_layout.getFieldOffset(field_idx);
- if (FieldIsBitfield(*field, child_bitfield_bit_size)) {
- child_bitfield_bit_offset = bit_offset % child_bit_size;
- const uint32_t child_bit_offset =
- bit_offset - child_bitfield_bit_offset;
- child_byte_offset = child_bit_offset / 8;
- } else {
- child_byte_offset = bit_offset / 8;
- }
+ uint64_t base_class_clang_type_bit_size = *size_or_err;
- return field_clang_type;
+ // Base classes bit sizes should be a multiple of 8 bits in size
+ assert(base_class_clang_type_bit_size % 8 == 0);
+ child_byte_size = base_class_clang_type_bit_size / 8;
+ child_is_base_class = true;
+ return base_class_clang_type;
}
+ // We don't increment the child index in the for loop since we might
+ // be skipping empty base classes
+ ++child_idx;
}
}
- break;
+ // Make sure index is in range...
+ uint32_t field_idx = 0;
+ clang::RecordDecl::field_iterator field, field_end;
+ for (field = record_decl->field_begin(),
+ field_end = record_decl->field_end();
+ field != field_end; ++field, ++field_idx, ++child_idx) {
+ if (idx == child_idx) {
+ // Print the member type if requested
+ // Print the member name and equal sign
+ child_name.assign(field->getNameAsString());
+
+ // Figure out the type byte size (field_type_info.first) and
+ // alignment (field_type_info.second) from the AST context.
+ CompilerType field_clang_type = GetType(field->getType());
+ assert(field_idx < record_layout.getFieldCount());
+ auto size_or_err = field_clang_type.GetByteSize(get_exe_scope());
+ if (!size_or_err)
+ return llvm::joinErrors(
+ llvm::createStringError("no size info for field"),
+ size_or_err.takeError());
+ child_byte_size = *size_or_err;
+ const uint32_t child_bit_size = child_byte_size * 8;
+
+ // Figure out the field offset within the current struct/union/class
+ // type
+ bit_offset = record_layout.getFieldOffset(field_idx);
+ if (FieldIsBitfield(*field, child_bitfield_bit_size)) {
+ child_bitfield_bit_offset = bit_offset % child_bit_size;
+ const uint32_t child_bit_offset =
+ bit_offset - child_bitfield_bit_offset;
+ child_byte_offset = child_bit_offset / 8;
+ } else {
+ child_byte_offset = bit_offset / 8;
+ }
+
+ return field_clang_type;
+ }
+ }
+ } break;
case clang::Type::ObjCObject:
- case clang::Type::ObjCInterface:
- if (idx_is_valid && GetCompleteType(type)) {
- const clang::ObjCObjectType *objc_class_type =
- llvm::dyn_cast<clang::ObjCObjectType>(parent_qual_type.getTypePtr());
- assert(objc_class_type);
- if (objc_class_type) {
- uint32_t child_idx = 0;
- clang::ObjCInterfaceDecl *class_interface_decl =
- objc_class_type->getInterface();
+ case clang::Type::ObjCInterface: {
+ if (!idx_is_valid)
+ return llvm::createStringError("invalid index");
+ if (!GetCompleteType(type))
+ return llvm::createStringError("cannot complete type");
- if (class_interface_decl) {
+ const clang::ObjCObjectType *objc_class_type =
+ llvm::dyn_cast<clang::ObjCObjectType>(parent_qual_type.getTypePtr());
+ assert(objc_class_type);
+ if (!objc_class_type)
+ return llvm::createStringError("unexpected object type");
- const clang::ASTRecordLayout &interface_layout =
- getASTContext().getASTObjCInterfaceLayout(class_interface_decl);
- clang::ObjCInterfaceDecl *superclass_interface_decl =
- class_interface_decl->getSuperClass();
- if (superclass_interface_decl) {
- if (omit_empty_base_classes) {
- CompilerType base_class_clang_type =
- GetType(getASTContext().getObjCInterfaceType(
- superclass_interface_decl));
- if (llvm::expectedToStdOptional(
- base_class_clang_type.GetNumChildren(
- omit_empty_base_classes, exe_ctx))
- .value_or(0) > 0) {
- if (idx == 0) {
- clang::QualType ivar_qual_type(
- getASTContext().getObjCInterfaceType(
- superclass_interface_decl));
-
- child_name.assign(
- superclass_interface_decl->getNameAsString());
-
- clang::TypeInfo ivar_type_info =
- getASTContext().getTypeInfo(ivar_qual_type.getTypePtr());
-
- child_byte_size = ivar_type_info.Width / 8;
- child_byte_offset = 0;
- child_is_base_class = true;
-
- return GetType(ivar_qual_type);
- }
+ uint32_t child_idx = 0;
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_class_type->getInterface();
- ++child_idx;
- }
- } else
- ++child_idx;
- }
+ if (!class_interface_decl)
+ return llvm::createStringError("cannot get interface decl");
- const uint32_t superclass_idx = child_idx;
+ const clang::ASTRecordLayout &interface_layout =
+ getASTContext().getASTObjCInterfaceLayout(class_interface_decl);
+ clang::ObjCInterfaceDecl *superclass_interface_decl =
+ class_interface_decl->getSuperClass();
+ if (superclass_interface_decl) {
+ if (omit_empty_base_classes) {
+ CompilerType base_class_clang_type = GetType(
+ getASTContext().getObjCInterfaceType(superclass_interface_decl));
+ if (llvm::expectedToStdOptional(base_class_clang_type.GetNumChildren(
+ omit_empty_base_classes, exe_ctx))
+ .value_or(0) > 0) {
+ if (idx == 0) {
+ clang::QualType ivar_qual_type(getASTContext().getObjCInterfaceType(
+ superclass_interface_decl));
- if (idx < (child_idx + class_interface_decl->ivar_size())) {
- clang::ObjCInterfaceDecl::ivar_iterator ivar_pos,
- ivar_end = class_interface_decl->ivar_end();
+ child_name.assign(superclass_interface_decl->getNameAsString());
- for (ivar_pos = class_interface_decl->ivar_begin();
- ivar_pos != ivar_end; ++ivar_pos) {
- if (child_idx == idx) {
- clang::ObjCIvarDecl *ivar_decl = *ivar_pos;
-
- clang::QualType ivar_qual_type(ivar_decl->getType());
-
- child_name.assign(ivar_decl->getNameAsString());
-
- clang::TypeInfo ivar_type_info =
- getASTContext().getTypeInfo(ivar_qual_type.getTypePtr());
-
- child_byte_size = ivar_type_info.Width / 8;
-
- // Figure out the field offset within the current
- // struct/union/class type For ObjC objects, we can't trust the
- // bit offset we get from the Clang AST, since that doesn't
- // account for the space taken up by unbacked properties, or
- // from the changing size of base classes that are newer than
- // this class. So if we have a process around that we can ask
- // about this object, do so.
- child_byte_offset = LLDB_INVALID_IVAR_OFFSET;
- Process *process = nullptr;
- if (exe_ctx)
- process = exe_ctx->GetProcessPtr();
- if (process) {
- ObjCLanguageRuntime *objc_runtime =
- ObjCLanguageRuntime::Get(*process);
- if (objc_runtime != nullptr) {
- CompilerType parent_ast_type = GetType(parent_qual_type);
- child_byte_offset = objc_runtime->GetByteOffsetForIvar(
- parent_ast_type, ivar_decl->getNameAsString().c_str());
- }
- }
+ clang::TypeInfo ivar_type_info =
+ getASTContext().getTypeInfo(ivar_qual_type.getTypePtr());
- // Setting this to INT32_MAX to make sure we don't compute it
- // twice...
- bit_offset = INT32_MAX;
+ child_byte_size = ivar_type_info.Width / 8;
+ child_byte_offset = 0;
+ child_is_base_class = true;
- if (child_byte_offset ==
- static_cast<int32_t>(LLDB_INVALID_IVAR_OFFSET)) {
- bit_offset = interface_layout.getFieldOffset(child_idx -
- superclass_idx);
- child_byte_offset = bit_offset / 8;
- }
+ return GetType(ivar_qual_type);
+ }
- // Note, the ObjC Ivar Byte offset is just that, it doesn't
- // account for the bit offset of a bitfield within its
- // containing object. So regardless of where we get the byte
- // offset from, we still need to get the bit offset for
- // bitfields from the layout.
+ ++child_idx;
+ }
+ } else
+ ++child_idx;
+ }
- if (FieldIsBitfield(ivar_decl, child_bitfield_bit_size)) {
- if (bit_offset == INT32_MAX)
- bit_offset = interface_layout.getFieldOffset(
- child_idx - superclass_idx);
+ const uint32_t superclass_idx = child_idx;
- child_bitfield_bit_offset = bit_offset % 8;
- }
- return GetType(ivar_qual_type);
- }
- ++child_idx;
+ if (idx < (child_idx + class_interface_decl->ivar_size())) {
+ clang::ObjCInterfaceDecl::ivar_iterator ivar_pos,
+ ivar_end = class_interface_decl->ivar_end();
+
+ for (ivar_pos = class_interface_decl->ivar_begin(); ivar_pos != ivar_end;
+ ++ivar_pos) {
+ if (child_idx == idx) {
+ clang::ObjCIvarDecl *ivar_decl = *ivar_pos;
+
+ clang::QualType ivar_qual_type(ivar_decl->getType());
+
+ child_name.assign(ivar_decl->getNameAsString());
+
+ clang::TypeInfo ivar_type_info =
+ getASTContext().getTypeInfo(ivar_qual_type.getTypePtr());
+
+ child_byte_size = ivar_type_info.Width / 8;
+
+ // Figure out the field offset within the current
+ // struct/union/class type For ObjC objects, we can't trust the
+ // bit offset we get from the Clang AST, since that doesn't
+ // account for the space taken up by unbacked properties, or
+ // from the changing size of base classes that are newer than
+ // this class. So if we have a process around that we can ask
+ // about this object, do so.
+ child_byte_offset = LLDB_INVALID_IVAR_OFFSET;
+ Process *process = nullptr;
+ if (exe_ctx)
+ process = exe_ctx->GetProcessPtr();
+ if (process) {
+ ObjCLanguageRuntime *objc_runtime =
+ ObjCLanguageRuntime::Get(*process);
+ if (objc_runtime != nullptr) {
+ CompilerType parent_ast_type = GetType(parent_qual_type);
+ child_byte_offset = objc_runtime->GetByteOffsetForIvar(
+ parent_ast_type, ivar_decl->getNameAsString().c_str());
}
}
- }
- }
- }
- break;
- case clang::Type::ObjCObjectPointer:
- if (idx_is_valid) {
- CompilerType pointee_clang_type(GetPointeeType(type));
+ // Setting this to INT32_MAX to make sure we don't compute it
+ // twice...
+ bit_offset = INT32_MAX;
- if (transparent_pointers && pointee_clang_type.IsAggregateType()) {
- child_is_deref_of_parent = false;
- bool tmp_child_is_deref_of_parent = false;
- return pointee_clang_type.GetChildCompilerTypeAtIndex(
- exe_ctx, idx, transparent_pointers, omit_empty_base_classes,
- ignore_array_bounds, child_name, child_byte_size, child_byte_offset,
- child_bitfield_bit_size, child_bitfield_bit_offset,
- child_is_base_class, tmp_child_is_deref_of_parent, valobj,
- language_flags);
- } else {
- child_is_deref_of_parent = true;
- const char *parent_name =
- valobj ? valobj->GetName().GetCString() : nullptr;
- if (parent_name) {
- child_name.assign(1, '*');
- child_name += parent_name;
- }
+ if (child_byte_offset ==
+ static_cast<int32_t>(LLDB_INVALID_IVAR_OFFSET)) {
+ bit_offset =
+ interface_layout.getFieldOffset(child_idx - superclass_idx);
+ child_byte_offset = bit_offset / 8;
+ }
- // We have a pointer to an simple type
- if (idx == 0 && pointee_clang_type.GetCompleteType()) {
- auto size_or_err = pointee_clang_type.GetByteSize(get_exe_scope());
- if (!size_or_err)
- return size_or_err.takeError();
- child_byte_size = *size_or_err;
- child_byte_offset = 0;
- return pointee_clang_type;
- }
- }
- }
- break;
+ // Note, the ObjC Ivar Byte offset is just that, it doesn't
+ // account for the bit offset of a bitfield within its
+ // containing object. So regardless of where we get the byte
+ // offset from, we still need to get the bit offset for
+ // bitfields from the layout.
- case clang::Type::Vector:
- case clang::Type::ExtVector:
- if (idx_is_valid) {
- const clang::VectorType *array =
- llvm::cast<clang::VectorType>(parent_qual_type.getTypePtr());
- if (array) {
- CompilerType element_type = GetType(array->getElementType());
- if (element_type.GetCompleteType()) {
- char element_name[64];
- ::snprintf(element_name, sizeof(element_name), "[%" PRIu64 "]",
- static_cast<uint64_t>(idx));
- child_name.assign(element_name);
- auto size_or_err = element_type.GetByteSize(get_exe_scope());
- if (!size_or_err)
- return size_or_err.takeError();
- child_byte_size = *size_or_err;
- child_byte_offset = (int32_t)idx * (int32_t)child_byte_size;
- return element_type;
- }
- }
- }
- break;
+ if (FieldIsBitfield(ivar_decl, child_bitfield_bit_size)) {
+ if (bit_offset == INT32_MAX)
+ bit_offset =
+ interface_layout.getFieldOffset(child_idx - superclass_idx);
- case clang::Type::ConstantArray:
- case clang::Type::IncompleteArray:
- if (ignore_array_bounds || idx_is_valid) {
- const clang::ArrayType *array = GetQualType(type)->getAsArrayTypeUnsafe();
- if (array) {
- CompilerType element_type = GetType(array->getElementType());
- if (element_type.GetCompleteType()) {
- child_name = std::string(llvm::formatv("[{0}]", idx));
- auto size_or_err = element_type.GetByteSize(get_exe_scope());
- if (!size_or_err)
- return size_or_err.takeError();
- child_byte_size = *size_or_err;
- child_byte_offset = (int32_t)idx * (int32_t)child_byte_size;
- return element_type;
+ child_bitfield_bit_offset = bit_offset % 8;
+ }
+ return GetType(ivar_qual_type);
}
+ ++child_idx;
}
}
- break;
+ } break;
- case clang::Type::Pointer: {
+ case clang::Type::ObjCObjectPointer: {
+ if (!idx_is_valid)
+ return llvm::createStringError("invalid index");
CompilerType pointee_clang_type(GetPointeeType(type));
- // Don't dereference "void *" pointers
- if (pointee_clang_type.IsVoidType())
- return CompilerType();
-
if (transparent_pointers && pointee_clang_type.IsAggregateType()) {
child_is_deref_of_parent = false;
bool tmp_child_is_deref_of_parent = false;
@@ -6626,7 +6547,6 @@ llvm::Expected<CompilerType> TypeSystemClang::GetChildCompilerTypeAtIndex(
language_flags);
} else {
child_is_deref_of_parent = true;
-
const char *parent_name =
valobj ? valobj->GetName().GetCString() : nullptr;
if (parent_name) {
@@ -6635,7 +6555,7 @@ llvm::Expected<CompilerType> TypeSystemClang::GetChildCompilerTypeAtIndex(
}
// We have a pointer to an simple type
- if (idx == 0) {
+ if (idx == 0 && pointee_clang_type.GetCompleteType()) {
auto size_or_err = pointee_clang_type.GetByteSize(get_exe_scope());
if (!size_or_err)
return size_or_err.takeError();
@@ -6644,51 +6564,127 @@ llvm::Expected<CompilerType> TypeSystemClang::GetChildCompilerTypeAtIndex(
return pointee_clang_type;
}
}
+ } break;
+
+ case clang::Type::Vector:
+ case clang::Type::ExtVector: {
+ if (!idx_is_valid)
+ return llvm::createStringError("invalid index");
+ const clang::VectorType *array =
+ llvm::cast<clang::VectorType>(parent_qual_type.getTypePtr());
+ if (!array)
+ return llvm::createStringError("unexpected vector type");
+
+ CompilerType element_type = GetType(array->getElementType());
+ if (!element_type.GetCompleteType())
+ return llvm::createStringError("cannot complete type");
+
+ char element_name[64];
+ ::snprintf(element_name, sizeof(element_name), "[%" PRIu64 "]",
+ static_cast<uint64_t>(idx));
+ child_name.assign(element_name);
+ auto size_or_err = element_type.GetByteSize(get_exe_scope());
+ if (!size_or_err)
+ return size_or_err.takeError();
+ child_byte_size = *size_or_err;
+ child_byte_offset = (int32_t)idx * (int32_t)child_byte_size;
+ return element_type;
+ }
+ case clang::Type::ConstantArray:
+ case clang::Type::IncompleteArray: {
+ if (!ignore_array_bounds && !idx_is_valid)
+ return llvm::createStringError("invalid index");
+ const clang::ArrayType *array = GetQualType(type)->getAsArrayTypeUnsafe();
+ if (!array)
+ return llvm::createStringError("unexpected array type");
+ CompilerType element_type = GetType(array->getElementType());
+ if (!element_type.GetCompleteType())
+ return llvm::createStringError("cannot complete type");
+
+ child_name = std::string(llvm::formatv("[{0}]", idx));
+ auto size_or_err = element_type.GetByteSize(get_exe_scope());
+ if (!size_or_err)
+ return size_or_err.takeError();
+ child_byte_size = *size_or_err;
+ child_byte_offset = (int32_t)idx * (int32_t)child_byte_size;
+ return element_type;
+ }
+ case clang::Type::Pointer: {
+ CompilerType pointee_clang_type(GetPointeeType(type));
+
+ // Don't dereference "void *" pointers
+ if (pointee_clang_type.IsVoidType())
+ return llvm::createStringError("cannot dereference void *");
+
+ if (transparent_pointers && pointee_clang_type.IsAggregateType()) {
+ child_is_deref_of_parent = false;
+ bool tmp_child_is_deref_of_parent = false;
+ return pointee_clang_type.GetChildCompilerTypeAtIndex(
+ exe_ctx, idx, transparent_pointers, omit_empty_base_classes,
+ ignore_array_bounds, child_name, child_byte_size, child_byte_offset,
+ child_bitfield_bit_size, child_bitfield_bit_offset,
+ child_is_base_class, tmp_child_is_deref_of_parent, valobj,
+ language_flags);
+ }
+ child_is_deref_of_parent = true;
+
+ const char *parent_name = valobj ? valobj->GetName().GetCString() : nullptr;
+ if (parent_name) {
+ child_name.assign(1, '*');
+ child_name += parent_name;
+ }
+
+ // We have a pointer to an simple type
+ if (idx == 0) {
+ auto size_or_err = pointee_clang_type.GetByteSize(get_exe_scope());
+ if (!size_or_err)
+ return size_or_err.takeError();
+ child_byte_size = *size_or_err;
+ child_byte_offset = 0;
+ return pointee_clang_type;
+ }
break;
}
case clang::Type::LValueReference:
- case clang::Type::RValueReference:
- if (idx_is_valid) {
- const clang::ReferenceType *reference_type =
- llvm::cast<clang::ReferenceType>(
- RemoveWrappingTypes(GetQualType(type)).getTypePtr());
- CompilerType pointee_clang_type =
- GetType(reference_type->getPointeeType());
- if (transparent_pointers && pointee_clang_type.IsAggregateType()) {
- child_is_deref_of_parent = false;
- bool tmp_child_is_deref_of_parent = false;
- return pointee_clang_type.GetChildCompilerTypeAtIndex(
- exe_ctx, idx, transparent_pointers, omit_empty_base_classes,
- ignore_array_bounds, child_name, child_byte_size, child_byte_offset,
- child_bitfield_bit_size, child_bitfield_bit_offset,
- child_is_base_class, tmp_child_is_deref_of_parent, valobj,
- language_flags);
- } else {
- const char *parent_name =
- valobj ? valobj->GetName().GetCString() : nullptr;
- if (parent_name) {
- child_name.assign(1, '&');
- child_name += parent_name;
- }
+ case clang::Type::RValueReference: {
+ if (!idx_is_valid)
+ return llvm::createStringError("invalid index");
+ const clang::ReferenceType *reference_type =
+ llvm::cast<clang::ReferenceType>(
+ RemoveWrappingTypes(GetQualType(type)).getTypePtr());
+ CompilerType pointee_clang_type = GetType(reference_type->getPointeeType());
+ if (transparent_pointers && pointee_clang_type.IsAggregateType()) {
+ child_is_deref_of_parent = false;
+ bool tmp_child_is_deref_of_parent = false;
+ return pointee_clang_type.GetChildCompilerTypeAtIndex(
+ exe_ctx, idx, transparent_pointers, omit_empty_base_classes,
+ ignore_array_bounds, child_name, child_byte_size, child_byte_offset,
+ child_bitfield_bit_size, child_bitfield_bit_offset,
+ child_is_base_class, tmp_child_is_deref_of_parent, valobj,
+ language_flags);
+ }
+ const char *parent_name = valobj ? valobj->GetName().GetCString() : nullptr;
+ if (parent_name) {
+ child_name.assign(1, '&');
+ child_name += parent_name;
+ }
- // We have a pointer to an simple type
- if (idx == 0) {
- auto size_or_err = pointee_clang_type.GetByteSize(get_exe_scope());
- if (!size_or_err)
- return size_or_err.takeError();
- child_byte_size = *size_or_err;
- child_byte_offset = 0;
- return pointee_clang_type;
- }
- }
+ // We have a pointer to an simple type
+ if (idx == 0) {
+ auto size_or_err = pointee_clang_type.GetByteSize(get_exe_scope());
+ if (!size_or_err)
+ return size_or_err.takeError();
+ child_byte_size = *size_or_err;
+ child_byte_offset = 0;
+ return pointee_clang_type;
}
- break;
+ } break;
default:
break;
}
- return CompilerType();
+ return llvm::createStringError("cannot enumerate children");
}
uint32_t TypeSystemClang::GetIndexForRecordBase(
diff --git a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py
index 6753f988c4187..036fb0288f248 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/PointerArithmetic/TestFrameVarDILPointerArithmetic.py
@@ -42,5 +42,5 @@ def test_dereference(self):
self.expect(
"frame var '&*p_void'",
error=True,
- substrs=["dereference failed: (void *) p_void"],
+ substrs=["dereference failed: cannot dereference void *: (void *) p_void"],
)
More information about the lldb-commits
mailing list