//===-- CompilerType.cpp --------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "lldb/Symbol/CompilerType.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Symbol/Type.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/Utility/DataBufferHeap.h"
#include "lldb/Utility/DataExtractor.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/Scalar.h"
#include "lldb/Utility/Stream.h"
#include "lldb/Utility/StreamString.h"
#include "lldb/lldb-enumerations.h"
#include "llvm/Support/ErrorExtras.h"

#include <iterator>
#include <mutex>
#include <optional>

using namespace lldb;
using namespace lldb_private;

// Tests

bool CompilerType::IsAggregateType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsAggregateType(m_type);
  return false;
}

bool CompilerType::IsAnonymousType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsAnonymousType(m_type);
  return false;
}

bool CompilerType::IsScopedEnumerationType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsScopedEnumerationType(m_type);
  return false;
}

bool CompilerType::IsArrayType(CompilerType *element_type_ptr, uint64_t *size,
                               bool *is_incomplete) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsArrayType(m_type, element_type_ptr, size,
                                      is_incomplete);

  if (element_type_ptr)
    element_type_ptr->Clear();
  if (size)
    *size = 0;
  if (is_incomplete)
    *is_incomplete = false;
  return false;
}

bool CompilerType::IsVectorType(CompilerType *element_type,
                                uint64_t *size) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsVectorType(m_type, element_type, size);
  return false;
}

bool CompilerType::IsRuntimeGeneratedType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsRuntimeGeneratedType(m_type);
  return false;
}

bool CompilerType::IsCharType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsCharType(m_type);
  return false;
}

bool CompilerType::IsCompleteType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsCompleteType(m_type);
  return false;
}

bool CompilerType::IsForcefullyCompleted() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsForcefullyCompleted(m_type);
  return false;
}

bool CompilerType::IsConst() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsConst(m_type);
  return false;
}

unsigned CompilerType::GetPtrAuthKey() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetPtrAuthKey(m_type);
  return 0;
}

unsigned CompilerType::GetPtrAuthDiscriminator() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetPtrAuthDiscriminator(m_type);
  return 0;
}

bool CompilerType::GetPtrAuthAddressDiversity() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetPtrAuthAddressDiversity(m_type);
  return false;
}

bool CompilerType::IsFunctionType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsFunctionType(m_type);
  return false;
}

// Used to detect "Homogeneous Floating-point Aggregates"
uint32_t
CompilerType::IsHomogeneousAggregate(CompilerType *base_type_ptr) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsHomogeneousAggregate(m_type, base_type_ptr);
  return 0;
}

size_t CompilerType::GetNumberOfFunctionArguments() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetNumberOfFunctionArguments(m_type);
  return 0;
}

CompilerType
CompilerType::GetFunctionArgumentAtIndex(const size_t index) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetFunctionArgumentAtIndex(m_type, index);
  return CompilerType();
}

bool CompilerType::IsFunctionPointerType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsFunctionPointerType(m_type);
  return false;
}

bool CompilerType::IsMemberFunctionPointerType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsMemberFunctionPointerType(m_type);
  return false;
}

bool CompilerType::IsMemberDataPointerType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsMemberDataPointerType(m_type);
  return false;
}

bool CompilerType::IsBlockPointerType(
    CompilerType *function_pointer_type_ptr) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsBlockPointerType(m_type, function_pointer_type_ptr);
  return false;
}

bool CompilerType::IsIntegerType(bool &is_signed) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsIntegerType(m_type, is_signed);
  return false;
}

bool CompilerType::IsEnumerationType(bool &is_signed) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsEnumerationType(m_type, is_signed);
  return false;
}

bool CompilerType::IsIntegerOrEnumerationType(bool &is_signed) const {
  return IsIntegerType(is_signed) || IsEnumerationType(is_signed);
}

bool CompilerType::IsPointerType(CompilerType *pointee_type) const {
  if (IsValid()) {
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsPointerType(m_type, pointee_type);
  }
  if (pointee_type)
    pointee_type->Clear();
  return false;
}

bool CompilerType::IsPointerOrReferenceType(CompilerType *pointee_type) const {
  if (IsValid()) {
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsPointerOrReferenceType(m_type, pointee_type);
  }
  if (pointee_type)
    pointee_type->Clear();
  return false;
}

bool CompilerType::IsReferenceType(CompilerType *pointee_type,
                                   bool *is_rvalue) const {
  if (IsValid()) {
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsReferenceType(m_type, pointee_type, is_rvalue);
  }
  if (pointee_type)
    pointee_type->Clear();
  return false;
}

bool CompilerType::ShouldTreatScalarValueAsAddress() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->ShouldTreatScalarValueAsAddress(m_type);
  return false;
}

bool CompilerType::IsComplexType() const {
  return GetTypeClass() & eTypeClassComplexFloat ||
         GetTypeClass() & eTypeClassComplexInteger;
}

bool CompilerType::IsFloatingPointType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsFloatingPointType(m_type);

  return false;
}

bool CompilerType::IsRealFloatingPointType() const {
  return IsFloatingPointType() && !IsComplexType() && !IsVectorType();
}

bool CompilerType::IsDefined() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsDefined(m_type);
  return true;
}

bool CompilerType::IsPolymorphicClass() const {
  if (IsValid()) {
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsPolymorphicClass(m_type);
  }
  return false;
}

bool CompilerType::IsPossibleDynamicType(CompilerType *dynamic_pointee_type,
                                         bool check_cplusplus,
                                         bool check_objc) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsPossibleDynamicType(m_type, dynamic_pointee_type,
                                                check_cplusplus, check_objc);
  return false;
}

bool CompilerType::IsScalarType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsScalarType(m_type);
  return false;
}

bool CompilerType::IsTemplateType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsTemplateType(m_type);
  return false;
}

bool CompilerType::IsTypedefType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsTypedefType(m_type);
  return false;
}

bool CompilerType::IsVoidType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsVoidType(m_type);
  return false;
}

bool CompilerType::HasPointerAuthQualifier() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->HasPointerAuthQualifier(m_type);
  return false;
}

bool CompilerType::IsPointerToScalarType() const {
  if (!IsValid())
    return false;

  return IsPointerType() && GetPointeeType().IsScalarType();
}

bool CompilerType::IsArrayOfScalarType() const {
  CompilerType element_type;
  if (IsArrayType(&element_type))
    return element_type.IsScalarType();
  return false;
}

bool CompilerType::IsBeingDefined() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsBeingDefined(m_type);
  return false;
}

bool CompilerType::IsInteger() const {
  bool is_signed = false; // May be reset by the call below.
  return IsIntegerType(is_signed);
}

bool CompilerType::IsEnumerationType() const {
  bool is_signed = false; // May be reset by the call below.
  return IsEnumerationType(is_signed);
}

bool CompilerType::IsUnscopedEnumerationType() const {
  return IsEnumerationType() && !IsScopedEnumerationType();
}

bool CompilerType::IsIntegerOrUnscopedEnumerationType() const {
  return IsInteger() || IsUnscopedEnumerationType();
}

bool CompilerType::IsSigned() const {
  return GetTypeInfo() & lldb::eTypeIsSigned;
}

bool CompilerType::IsNullPtrType() const {
  return GetBasicTypeEnumeration() == lldb::eBasicTypeNullPtr;
}

bool CompilerType::IsBoolean() const {
  return GetBasicTypeEnumeration() == lldb::eBasicTypeBool;
}

bool CompilerType::IsEnumerationIntegerTypeSigned() const {
  if (IsValid())
    return GetEnumerationIntegerType().GetTypeInfo() & lldb::eTypeIsSigned;

  return false;
}

bool CompilerType::IsScalarOrUnscopedEnumerationType() const {
  return IsScalarType() || IsUnscopedEnumerationType();
}

bool CompilerType::IsPromotableIntegerType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsPromotableIntegerType(m_type);
  return false;
}

bool CompilerType::IsPointerToVoid() const {
  if (!IsValid())
    return false;

  return IsPointerType() &&
         GetPointeeType().GetBasicTypeEnumeration() == lldb::eBasicTypeVoid;
}

bool CompilerType::IsRecordType() const {
  if (!IsValid())
    return false;

  return GetCanonicalType().GetTypeClass() &
         (lldb::eTypeClassClass | lldb::eTypeClassStruct |
          lldb::eTypeClassUnion);
}

bool CompilerType::IsVirtualBase(CompilerType target_base,
                                 CompilerType *virtual_base,
                                 bool carry_virtual) const {
  if (CompareTypes(target_base))
    return carry_virtual;

  if (!carry_virtual) {
    uint32_t num_virtual_bases = GetNumVirtualBaseClasses();
    for (uint32_t i = 0; i < num_virtual_bases; ++i) {
      uint32_t bit_offset;
      auto base = GetVirtualBaseClassAtIndex(i, &bit_offset);
      if (base.IsVirtualBase(target_base, virtual_base,
                             /*carry_virtual*/ true)) {
        if (virtual_base)
          *virtual_base = base;

        return true;
      }
    }
  }

  uint32_t num_direct_bases = GetNumDirectBaseClasses();
  for (uint32_t i = 0; i < num_direct_bases; ++i) {
    uint32_t bit_offset;
    auto base = GetDirectBaseClassAtIndex(i, &bit_offset);
    if (base.IsVirtualBase(target_base, virtual_base, carry_virtual))
      return true;
  }

  return false;
}

bool CompilerType::IsContextuallyConvertibleToBool() const {
  return IsScalarType() || IsUnscopedEnumerationType() || IsPointerType() ||
         IsNullPtrType() || IsArrayType();
}

bool CompilerType::IsBasicType() const {
  return GetBasicTypeEnumeration() != lldb::eBasicTypeInvalid;
}

std::string CompilerType::TypeDescription() {
  auto name = GetTypeName();
  auto canonical_name = GetCanonicalType().GetTypeName();
  if (name.IsEmpty() || canonical_name.IsEmpty())
    return "''"; // Should not happen, unless the input is broken somehow.

  if (name == canonical_name)
    return llvm::formatv("'{0}'", name);

  return llvm::formatv("'{0}' (canonically referred to as '{1}')", name,
                       canonical_name);
}

bool CompilerType::CompareTypes(CompilerType rhs) const {
  if (*this == rhs)
    return true;

  const ConstString name = GetFullyUnqualifiedType().GetTypeName();
  const ConstString rhs_name = rhs.GetFullyUnqualifiedType().GetTypeName();
  return name == rhs_name;
}

const char *CompilerType::GetTypeTag() {
  switch (GetTypeClass()) {
  case lldb::eTypeClassClass:
    return "class";
  case lldb::eTypeClassEnumeration:
    return "enum";
  case lldb::eTypeClassStruct:
    return "struct";
  case lldb::eTypeClassUnion:
    return "union";
  default:
    return "unknown";
  }
  llvm_unreachable("All cases are covered by code above.");
}

uint32_t CompilerType::GetNumberOfNonEmptyBaseClasses() {
  uint32_t ret = 0;
  uint32_t num_direct_bases = GetNumDirectBaseClasses();

  for (uint32_t i = 0; i < num_direct_bases; ++i) {
    uint32_t bit_offset;
    CompilerType base_type = GetDirectBaseClassAtIndex(i, &bit_offset);
    if (base_type.GetNumFields() > 0 ||
        base_type.GetNumberOfNonEmptyBaseClasses() > 0)
      ret += 1;
  }
  return ret;
}

// Type Completion

bool CompilerType::GetCompleteType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetCompleteType(m_type);
  return false;
}

// AST related queries
size_t CompilerType::GetPointerByteSize() const {
  if (auto type_system_sp = GetTypeSystem())
    return type_system_sp->GetPointerByteSize();
  return 0;
}

ConstString CompilerType::GetTypeName(bool BaseOnly) const {
  if (IsValid()) {
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetTypeName(m_type, BaseOnly);
  }
  return ConstString("<invalid>");
}

ConstString CompilerType::GetDisplayTypeName() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetDisplayTypeName(m_type);
  return ConstString("<invalid>");
}

ConstString CompilerType::GetMangledTypeName() const {
  if (IsValid()) {
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetMangledTypeName(m_type);
  }
  return ConstString("<invalid>");
}

uint32_t CompilerType::GetTypeInfo(
    CompilerType *pointee_or_element_compiler_type) const {
  if (IsValid())
  if (auto type_system_sp = GetTypeSystem())
    return type_system_sp->GetTypeInfo(m_type,
                                       pointee_or_element_compiler_type);
  return 0;
}

lldb::LanguageType CompilerType::GetMinimumLanguage() {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetMinimumLanguage(m_type);
  return lldb::eLanguageTypeC;
}

lldb::TypeClass CompilerType::GetTypeClass() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetTypeClass(m_type);
  return lldb::eTypeClassInvalid;
}

void CompilerType::SetCompilerType(lldb::TypeSystemWP type_system,
                                   lldb::opaque_compiler_type_t type) {
  m_type_system = type_system;
  m_type = type;
}

void CompilerType::SetCompilerType(CompilerType::TypeSystemSPWrapper type_system,
                                   lldb::opaque_compiler_type_t type) {
  m_type_system = type_system.GetSharedPointer();
  m_type = type;
}

unsigned CompilerType::GetTypeQualifiers() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetTypeQualifiers(m_type);
  return 0;
}

// Creating related types

CompilerType
CompilerType::GetArrayElementType(ExecutionContextScope *exe_scope) const {
  if (IsValid()) {
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetArrayElementType(m_type, exe_scope);
  }
  return CompilerType();
}

CompilerType CompilerType::GetArrayType(uint64_t size) const {
  if (IsValid()) {
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetArrayType(m_type, size);
  }
  return CompilerType();
}

CompilerType CompilerType::GetCanonicalType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetCanonicalType(m_type);
  return CompilerType();
}

CompilerType CompilerType::GetFullyUnqualifiedType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetFullyUnqualifiedType(m_type);
  return CompilerType();
}

CompilerType CompilerType::GetEnumerationIntegerType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetEnumerationIntegerType(m_type);
  return CompilerType();
}

int CompilerType::GetFunctionArgumentCount() const {
  if (IsValid()) {
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetFunctionArgumentCount(m_type);
  }
  return -1;
}

CompilerType CompilerType::GetFunctionArgumentTypeAtIndex(size_t idx) const {
  if (IsValid()) {
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetFunctionArgumentTypeAtIndex(m_type, idx);
  }
  return CompilerType();
}

CompilerType CompilerType::GetFunctionReturnType() const {
  if (IsValid()) {
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetFunctionReturnType(m_type);
  }
  return CompilerType();
}

size_t CompilerType::GetNumMemberFunctions() const {
  if (IsValid()) {
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetNumMemberFunctions(m_type);
  }
  return 0;
}

TypeMemberFunctionImpl CompilerType::GetMemberFunctionAtIndex(size_t idx) {
  if (IsValid()) {
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetMemberFunctionAtIndex(m_type, idx);
  }
  return TypeMemberFunctionImpl();
}

CompilerType CompilerType::GetNonReferenceType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetNonReferenceType(m_type);
  return CompilerType();
}

CompilerType CompilerType::GetPointeeType() const {
  if (IsValid()) {
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetPointeeType(m_type);
  }
  return CompilerType();
}

CompilerType CompilerType::GetPointerType() const {
  if (IsValid()) {
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetPointerType(m_type);
  }
  return CompilerType();
}

CompilerType CompilerType::AddPtrAuthModifier(uint32_t payload) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->AddPtrAuthModifier(m_type, payload);
  return CompilerType();
}

CompilerType CompilerType::GetLValueReferenceType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetLValueReferenceType(m_type);
  return CompilerType();
}

CompilerType CompilerType::GetRValueReferenceType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetRValueReferenceType(m_type);
  return CompilerType();
}

CompilerType CompilerType::GetAtomicType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetAtomicType(m_type);
  return CompilerType();
}

CompilerType CompilerType::AddConstModifier() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->AddConstModifier(m_type);
  return CompilerType();
}

CompilerType CompilerType::AddVolatileModifier() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->AddVolatileModifier(m_type);
  return CompilerType();
}

CompilerType CompilerType::AddRestrictModifier() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->AddRestrictModifier(m_type);
  return CompilerType();
}

CompilerType CompilerType::CreateTypedef(const char *name,
                                         const CompilerDeclContext &decl_ctx,
                                         uint32_t payload) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->CreateTypedef(m_type, name, decl_ctx, payload);
  return CompilerType();
}

CompilerType CompilerType::GetTypedefedType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetTypedefedType(m_type);
  return CompilerType();
}

// Create related types using the current type's AST

CompilerType
CompilerType::GetBasicTypeFromAST(lldb::BasicType basic_type) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetBasicTypeFromAST(basic_type);
  return CompilerType();
}
// Exploring the type

llvm::Expected<uint64_t>
CompilerType::GetBitSize(ExecutionContextScope *exe_scope) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetBitSize(m_type, exe_scope);
  return llvm::createStringError("invalid type: cannot determine size");
}

llvm::Expected<uint64_t>
CompilerType::GetByteSize(ExecutionContextScope *exe_scope) const {
  auto bit_size_or_err = GetBitSize(exe_scope);
  if (!bit_size_or_err)
    return bit_size_or_err.takeError();
  return (*bit_size_or_err + 7) / 8;
}

std::optional<size_t>
CompilerType::GetTypeBitAlign(ExecutionContextScope *exe_scope) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetTypeBitAlign(m_type, exe_scope);
  return {};
}

lldb::Encoding CompilerType::GetEncoding() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetEncoding(m_type);
  return lldb::eEncodingInvalid;
}

lldb::Format CompilerType::GetFormat() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetFormat(m_type);
  return lldb::eFormatDefault;
}

llvm::Expected<uint32_t>
CompilerType::GetNumChildren(bool omit_empty_base_classes,
                             const ExecutionContext *exe_ctx) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetNumChildren(m_type, omit_empty_base_classes,
                                       exe_ctx);
  return llvm::createStringError("invalid type");
}

lldb::BasicType CompilerType::GetBasicTypeEnumeration() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetBasicTypeEnumeration(m_type);
  return eBasicTypeInvalid;
}

void CompilerType::ForEachEnumerator(
    std::function<bool(const CompilerType &integer_type,
                       ConstString name,
                       const llvm::APSInt &value)> const &callback) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->ForEachEnumerator(m_type, callback);
}

uint32_t CompilerType::GetNumFields() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetNumFields(m_type);
  return 0;
}

CompilerType CompilerType::GetFieldAtIndex(size_t idx, std::string &name,
                                           uint64_t *bit_offset_ptr,
                                           uint32_t *bitfield_bit_size_ptr,
                                           bool *is_bitfield_ptr) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetFieldAtIndex(m_type, idx, name, bit_offset_ptr,
                                        bitfield_bit_size_ptr, is_bitfield_ptr);
  return CompilerType();
}

uint32_t CompilerType::GetNumDirectBaseClasses() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetNumDirectBaseClasses(m_type);
  return 0;
}

uint32_t CompilerType::GetNumVirtualBaseClasses() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetNumVirtualBaseClasses(m_type);
  return 0;
}

CompilerType
CompilerType::GetDirectBaseClassAtIndex(size_t idx,
                                        uint32_t *bit_offset_ptr) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetDirectBaseClassAtIndex(m_type, idx,
                                                    bit_offset_ptr);
  return CompilerType();
}

CompilerType
CompilerType::GetVirtualBaseClassAtIndex(size_t idx,
                                         uint32_t *bit_offset_ptr) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetVirtualBaseClassAtIndex(m_type, idx,
                                                     bit_offset_ptr);
  return CompilerType();
}

CompilerDecl CompilerType::GetStaticFieldWithName(llvm::StringRef name) const {
  if (IsValid())
    return GetTypeSystem()->GetStaticFieldWithName(m_type, name);
  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,
    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,
    bool &child_is_deref_of_parent, ValueObject *valobj,
    uint64_t &language_flags) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetChildCompilerTypeAtIndex(
          m_type, 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, child_is_deref_of_parent, valobj,
          language_flags);
  return CompilerType();
}

// Look for a child member (doesn't include base classes, but it does include
// their members) in the type hierarchy. Returns an index path into
// "clang_type" on how to reach the appropriate member.
//
//    class A
//    {
//    public:
//        int m_a;
//        int m_b;
//    };
//
//    class B
//    {
//    };
//
//    class C :
//        public B,
//        public A
//    {
//    };
//
// If we have a clang type that describes "class C", and we wanted to looked
// "m_b" in it:
//
// With omit_empty_base_classes == false we would get an integer array back
// with: { 1,  1 } The first index 1 is the child index for "class A" within
// class C The second index 1 is the child index for "m_b" within class A
//
// With omit_empty_base_classes == true we would get an integer array back
// with: { 0,  1 } The first index 0 is the child index for "class A" within
// class C (since class B doesn't have any members it doesn't count) The second
// index 1 is the child index for "m_b" within class A

size_t CompilerType::GetIndexOfChildMemberWithName(
    llvm::StringRef name, bool omit_empty_base_classes,
    std::vector<uint32_t> &child_indexes) const {
  if (IsValid() && !name.empty()) {
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetIndexOfChildMemberWithName(
        m_type, name, omit_empty_base_classes, child_indexes);
  }
  return 0;
}

CompilerType
CompilerType::GetDirectNestedTypeWithName(llvm::StringRef name) const {
  if (IsValid() && !name.empty()) {
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetDirectNestedTypeWithName(m_type, name);
  }
  return CompilerType();
}

size_t CompilerType::GetNumTemplateArguments(bool expand_pack) const {
  if (IsValid()) {
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetNumTemplateArguments(m_type, expand_pack);
  }
  return 0;
}

TemplateArgumentKind
CompilerType::GetTemplateArgumentKind(size_t idx, bool expand_pack) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetTemplateArgumentKind(m_type, idx, expand_pack);
  return eTemplateArgumentKindNull;
}

CompilerType CompilerType::GetTypeTemplateArgument(size_t idx,
                                                   bool expand_pack) const {
  if (IsValid()) {
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetTypeTemplateArgument(m_type, idx, expand_pack);
  }
  return CompilerType();
}

std::optional<CompilerType::IntegralTemplateArgument>
CompilerType::GetIntegralTemplateArgument(size_t idx, bool expand_pack) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetIntegralTemplateArgument(m_type, idx, expand_pack);
  return std::nullopt;
}

CompilerType CompilerType::GetTypeForFormatters() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetTypeForFormatters(m_type);
  return CompilerType();
}

CompilerType CompilerType::GetPromotedIntegerType() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetPromotedIntegerType(m_type);
  return CompilerType();
}

LazyBool CompilerType::ShouldPrintAsOneLiner(ValueObject *valobj) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->ShouldPrintAsOneLiner(m_type, valobj);
  return eLazyBoolCalculate;
}

bool CompilerType::IsMeaninglessWithoutDynamicResolution() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->IsMeaninglessWithoutDynamicResolution(m_type);
  return false;
}

// Get the index of the child of "clang_type" whose name matches. This function
// doesn't descend into the children, but only looks one level deep and name
// matches can include base class names.

llvm::Expected<uint32_t>
CompilerType::GetIndexOfChildWithName(llvm::StringRef name,
                                      bool omit_empty_base_classes) const {
  if (IsValid() && !name.empty()) {
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->GetIndexOfChildWithName(m_type, name,
                                                     omit_empty_base_classes);
  }
  return llvm::createStringErrorV("type has no child named '{0}'", name);
}

// Dumping types

bool CompilerType::DumpTypeValue(Stream *s, lldb::Format format,
                                 const DataExtractor &data,
                                 lldb::offset_t byte_offset, size_t byte_size,
                                 uint32_t bitfield_bit_size,
                                 uint32_t bitfield_bit_offset,
                                 ExecutionContextScope *exe_scope) {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->DumpTypeValue(
          m_type, *s, format, data, byte_offset, byte_size, bitfield_bit_size,
          bitfield_bit_offset, exe_scope);
  return false;
}

void CompilerType::DumpTypeDescription(lldb::DescriptionLevel level) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      type_system_sp->DumpTypeDescription(m_type, level);
}

void CompilerType::DumpTypeDescription(Stream *s,
                                       lldb::DescriptionLevel level) const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      type_system_sp->DumpTypeDescription(m_type, *s, level);
}

#ifndef NDEBUG
LLVM_DUMP_METHOD void CompilerType::dump() const {
  if (IsValid())
    if (auto type_system_sp = GetTypeSystem())
      return type_system_sp->dump(m_type);
  llvm::errs() << "<invalid>\n";
}
#endif

bool CompilerType::GetValueAsScalar(const lldb_private::DataExtractor &data,
                                    lldb::offset_t data_byte_offset,
                                    size_t data_byte_size, Scalar &value,
                                    ExecutionContextScope *exe_scope) const {
  if (!IsValid())
    return false;

  if (IsAggregateType()) {
    return false; // Aggregate types don't have scalar values
  } else {
    // FIXME: check that type is scalar instead of checking encoding?
    lldb::Encoding encoding = GetEncoding();

    if (encoding == lldb::eEncodingInvalid || (GetTypeInfo() & eTypeIsComplex))
      return false;

    auto byte_size_or_err = GetByteSize(exe_scope);
    if (!byte_size_or_err) {
      LLDB_LOG_ERRORV(
          GetLog(LLDBLog::Types), byte_size_or_err.takeError(),
          "Cannot get value as scalar: Cannot determine type size: {0}");
      return false;
    }
    uint64_t byte_size = *byte_size_or_err;

    // A bit or byte size of 0 is not a bug, but it doesn't make sense to read a
    // scalar of zero size.
    if (byte_size == 0)
      return false;

    lldb::offset_t offset = data_byte_offset;
    switch (encoding) {
    case lldb::eEncodingInvalid:
      break;
    case lldb::eEncodingVector:
      break;
    case lldb::eEncodingUint:
      if (byte_size <= sizeof(unsigned long long)) {
        uint64_t uval64 = data.GetMaxU64(&offset, byte_size);
        if (byte_size <= sizeof(unsigned int)) {
          value = (unsigned int)uval64;
          return true;
        } else if (byte_size <= sizeof(unsigned long)) {
          value = (unsigned long)uval64;
          return true;
        } else if (byte_size <= sizeof(unsigned long long)) {
          value = (unsigned long long)uval64;
          return true;
        } else
          value.Clear();
      }
      break;

    case lldb::eEncodingSint:
      if (byte_size <= sizeof(long long)) {
        int64_t sval64 = data.GetMaxS64(&offset, byte_size);
        if (byte_size <= sizeof(int)) {
          value = (int)sval64;
          return true;
        } else if (byte_size <= sizeof(long)) {
          value = (long)sval64;
          return true;
        } else if (byte_size <= sizeof(long long)) {
          value = (long long)sval64;
          return true;
        } else
          value.Clear();
      }
      break;

    case lldb::eEncodingIEEE754:
      if (byte_size <= sizeof(long double)) {
        uint32_t u32;
        uint64_t u64;
        if (byte_size == sizeof(float)) {
          if (sizeof(float) == sizeof(uint32_t)) {
            u32 = data.GetU32(&offset);
            value = *((float *)&u32);
            return true;
          } else if (sizeof(float) == sizeof(uint64_t)) {
            u64 = data.GetU64(&offset);
            value = *((float *)&u64);
            return true;
          }
        } else if (byte_size == sizeof(double)) {
          if (sizeof(double) == sizeof(uint32_t)) {
            u32 = data.GetU32(&offset);
            value = *((double *)&u32);
            return true;
          } else if (sizeof(double) == sizeof(uint64_t)) {
            u64 = data.GetU64(&offset);
            value = *((double *)&u64);
            return true;
          }
        } else if (byte_size == sizeof(long double)) {
          if (sizeof(long double) == sizeof(uint32_t)) {
            u32 = data.GetU32(&offset);
            value = *((long double *)&u32);
            return true;
          } else if (sizeof(long double) == sizeof(uint64_t)) {
            u64 = data.GetU64(&offset);
            value = *((long double *)&u64);
            return true;
          }
        }
      }
      break;
    }
  }
  return false;
}

CompilerType::CompilerType(CompilerType::TypeSystemSPWrapper type_system,
                           lldb::opaque_compiler_type_t type)
    : m_type_system(type_system.GetSharedPointer()), m_type(type) {
  assert(Verify() && "verification failed");
}

CompilerType::CompilerType(lldb::TypeSystemWP type_system,
                           lldb::opaque_compiler_type_t type)
    : m_type_system(type_system), m_type(type) {
  assert(Verify() && "verification failed");
}

#ifndef NDEBUG
bool CompilerType::Verify() const {
  if (!IsValid())
    return true;
  if (auto type_system_sp = GetTypeSystem())
    return type_system_sp->Verify(m_type);
  return true;
}
#endif

CompilerType::TypeSystemSPWrapper CompilerType::GetTypeSystem() const {
  return {m_type_system.lock()};
}

bool CompilerType::TypeSystemSPWrapper::operator==(
    const CompilerType::TypeSystemSPWrapper &other) const {
  if (!m_typesystem_sp && !other.m_typesystem_sp)
    return true;
  if (m_typesystem_sp && other.m_typesystem_sp)
    return m_typesystem_sp.get() == other.m_typesystem_sp.get();
  return false;
}

TypeSystem *CompilerType::TypeSystemSPWrapper::operator->() const {
  assert(m_typesystem_sp);
  return m_typesystem_sp.get();
}

bool lldb_private::operator==(const lldb_private::CompilerType &lhs,
                              const lldb_private::CompilerType &rhs) {
  return lhs.GetTypeSystem() == rhs.GetTypeSystem() &&
         lhs.GetOpaqueQualType() == rhs.GetOpaqueQualType();
}

bool lldb_private::operator!=(const lldb_private::CompilerType &lhs,
                              const lldb_private::CompilerType &rhs) {
  return !(lhs == rhs);
}
