[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