text: // Copyright 2017 the V8 project authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #ifndef V8_TORQUE_TYPES_H_
  #define V8_TORQUE_TYPES_H_
  
  #include <algorithm>
  #include <map>
  #include <set>
  #include <string>
  #include <vector>
  
  #include "src/base/optional.h"
  #include "src/torque/ast.h"
  #include "src/torque/constants.h"
  #include "src/torque/source-positions.h"
  #include "src/torque/utils.h"
  
  namespace v8 {
  namespace internal {
  namespace torque {
  
  class AggregateType;
  struct Identifier;
  class Macro;
  class Method;
  class GenericType;
  class StructType;
  class Type;
  class ClassType;
  class Value;
  class Namespace;
  
  class TypeBase {
   public:
    enum class Kind {
      kTopType,
      kAbstractType,
      kBuiltinPointerType,
      kUnionType,
      kBitFieldStructType,
      kStructType,
      kClassType
    };
    virtual ~TypeBase() = default;
    bool IsTopType() const { return kind() == Kind::kTopType; }
    bool IsAbstractType() const { return kind() == Kind::kAbstractType; }
    bool IsBuiltinPointerType() const {
      return kind() == Kind::kBuiltinPointerType;
    }
    bool IsUnionType() const { return kind() == Kind::kUnionType; }
    bool IsBitFieldStructType() const {
      return kind() == Kind::kBitFieldStructType;
    }
    bool IsStructType() const { return kind() == Kind::kStructType; }
    bool IsClassType() const { return kind() == Kind::kClassType; }
    bool IsAggregateType() const { return IsStructType() || IsClassType(); }
  
   protected:
    explicit TypeBase(Kind kind) : kind_(kind) {}
    Kind kind() const { return kind_; }
  
   private:
    const Kind kind_;
  };
  
  #define DECLARE_TYPE_BOILERPLATE(x)                         \
    static x* cast(TypeBase* declarable) {                    \
      DCHECK(declarable->Is##x());                            \
      return static_cast<x*>(declarable);                     \
    }                                                         \
    static const x* cast(const TypeBase* declarable) {        \
      DCHECK(declarable->Is##x());                            \
      return static_cast<const x*>(declarable);               \
    }                                                         \
    static x* DynamicCast(TypeBase* declarable) {             \
      if (!declarable) return nullptr;                        \
      if (!declarable->Is##x()) return nullptr;               \
      return static_cast<x*>(declarable);                     \
    }                                                         \
    static const x* DynamicCast(const TypeBase* declarable) { \
      if (!declarable) return nullptr;                        \
      if (!declarable->Is##x()) return nullptr;               \
      return static_cast<const x*>(declarable);               \
    }
  
  using TypeVector = std::vector<const Type*>;
  
  template <typename T>
  struct SpecializationKey {
    T* generic;
    TypeVector specialized_types;
  };
  
  using MaybeSpecializationKey = base::Optional<SpecializationKey<GenericType>>;
  
  struct TypeChecker {
    // The type of the object. This string is not guaranteed to correspond to a
    // C++ class, but just to a type checker function: for any type "Foo" here,
    // the function Object::IsFoo must exist.
    std::string type;
    // If {type} is "MaybeObject", then {weak_ref_to} indicates the corresponding
    // strong object type. Otherwise, {weak_ref_to} is empty.
    std::string weak_ref_to;
  };
  
  class V8_EXPORT_PRIVATE Type : public TypeBase {
   public:
    Type& operator=(const Type& other) = delete;
    virtual bool IsSubtypeOf(const Type* supertype) const;
  
    // Default rendering for error messages etc.
    std::string ToString() const;
  
    // This name is not unique, but short and somewhat descriptive.
    // Used for naming generated code.
    virtual std::string SimpleName() const;
  
    std::string UnhandlifiedCppTypeName() const;
    std::string HandlifiedCppTypeName() const;
  
    const Type* parent() const { return parent_; }
    bool IsVoid() const { return IsAbstractName(VOID_TYPE_STRING); }
    bool IsNever() const { return IsAbstractName(NEVER_TYPE_STRING); }
    bool IsBool() const { return IsAbstractName(BOOL_TYPE_STRING); }
    bool IsConstexprBool() const {
      return IsAbstractName(CONSTEXPR_BOOL_TYPE_STRING);
    }
    bool IsVoidOrNever() const { return IsVoid() || IsNever(); }
    std::string GetGeneratedTypeName() const;
    std::string GetGeneratedTNodeTypeName() const;
    virtual bool IsConstexpr() const {
      if (parent()) DCHECK(!parent()->IsConstexpr());
      return false;
    }
    virtual bool IsTransient() const { return false; }
    virtual const Type* NonConstexprVersion() const { return this; }
    std::string GetConstexprGeneratedTypeName() const;
    base::Optional<const ClassType*> ClassSupertype() const;
    base::Optional<const StructType*> StructSupertype() const;
    base::Optional<const AggregateType*> AggregateSupertype() const;
    virtual std::vector<TypeChecker> GetTypeCheckers() const { return {}; }
    virtual std::string GetRuntimeType() const;
    virtual std::string GetDebugType() const;
    static const Type* CommonSupertype(const Type* a, const Type* b);
    void AddAlias(std::string alias) const { aliases_.insert(std::move(alias)); }
    size_t id() const { return id_; }
    const MaybeSpecializationKey& GetSpecializedFrom() const {
      return specialized_from_;
    }
  
    static base::Optional<const Type*> MatchUnaryGeneric(const Type* type,
                                                         GenericType* generic);
  
    static std::string ComputeName(const std::string& basename,
                                   MaybeSpecializationKey specialized_from);
    virtual void SetConstexprVersion(const Type* type) const {
      constexpr_version_ = type;
    }
  
    virtual const Type* ConstexprVersion() const {
      if (constexpr_version_) return constexpr_version_;
      if (IsConstexpr()) return this;
      if (parent()) return parent()->ConstexprVersion();
      return nullptr;
    }
  
    virtual size_t AlignmentLog2() const;
  
   protected:
    Type(TypeBase::Kind kind, const Type* parent,
         MaybeSpecializationKey specialized_from = base::nullopt);
    Type(const Type& other) V8_NOEXCEPT;
    void set_parent(const Type* t) { parent_ = t; }
    int Depth() const;
    virtual std::string ToExplicitString() const = 0;
    virtual std::string GetGeneratedTypeNameImpl() const = 0;
    virtual std::string GetGeneratedTNodeTypeNameImpl() const = 0;
    virtual std::string SimpleNameImpl() const = 0;
  
   private:
    bool IsAbstractName(const std::string& name) const;
  
    // If {parent_} is not nullptr, then this type is a subtype of {parent_}.
    const Type* parent_;
    mutable std::set<std::string> aliases_;
    size_t id_;
    MaybeSpecializationKey specialized_from_;
    mutable const Type* constexpr_version_ = nullptr;
  };
  
  inline size_t hash_value(const TypeVector& types) {
    size_t hash = 0;
    for (const Type* t : types) {
      hash = base::hash_combine(hash, t);
    }
    return hash;
  }
  
  struct NameAndType {
    std::string name;
    const Type* type;
  };
  
  std::ostream& operator<<(std::ostream& os, const NameAndType& name_and_type);
  
  struct Field {
    // TODO(danno): This likely should be refactored, the handling of the types
    // using the universal grab-bag utility with std::tie, as well as the
    // reliance of string types is quite clunky.
    std::tuple<size_t, std::string> GetFieldSizeInformation() const;
  
    void ValidateAlignment(ResidueClass at_offset) const;
  
    SourcePosition pos;
    const AggregateType* aggregate;
    base::Optional<Expression*> index;
    NameAndType name_and_type;
  
    // The byte offset of this field from the beginning of the containing class or
    // struct. Most structs are never packed together in memory, and are only used
    // to hold a batch of related CSA TNode values, in which case |offset| is
    // irrelevant.
    // The offset may be unknown because the field is after an indexed field or
    // because we don't support the struct field for on-heap layouts.
    base::Optional<size_t> offset;
  
    bool is_weak;
    bool const_qualified;
    bool generate_verify;
    bool relaxed_write;
  };
  
  std::ostream& operator<<(std::ostream& os, const Field& name_and_type);
  
  class TopType final : public Type {
   public:
    DECLARE_TYPE_BOILERPLATE(TopType)
    std::string GetGeneratedTypeNameImpl() const override { UNREACHABLE(); }
    std::string GetGeneratedTNodeTypeNameImpl() const override {
      return source_type_->GetGeneratedTNodeTypeName();
    }
    std::string ToExplicitString() const override {
      std::stringstream s;
      s << "inaccessible " + source_type_->ToString();
      return s.str();
    }
  
    const Type* source_type() const { return source_type_; }
    const std::string reason() const { return reason_; }
  
   private:
    friend class TypeOracle;
    explicit TopType(std::string reason, const Type* source_type)
        : Type(Kind::kTopType, nullptr),
          reason_(std::move(reason)),
          source_type_(source_type) {}
    std::string SimpleNameImpl() const override { return "TopType"; }
  
    std::string reason_;
    const Type* source_type_;
  };
  
  class AbstractType final : public Type {
   public:
    DECLARE_TYPE_BOILERPLATE(AbstractType)
    const std::string& name() const { return name_; }
    std::string ToExplicitString() const override { return name(); }
    std::string GetGeneratedTypeNameImpl() const override {
      if (generated_type_.empty()) {
        return parent()->GetGeneratedTypeName();
      }
      return IsConstexpr() ? generated_type_ : "TNode<" + generated_type_ + ">";
    }
    std::string GetGeneratedTNodeTypeNameImpl() const override;
    bool IsConstexpr() const final {
      const bool is_constexpr = flags_ & AbstractTypeFlag::kConstexpr;
      DCHECK_IMPLIES(non_constexpr_version_ != nullptr, is_constexpr);
      return is_constexpr;
    }
  
    const Type* NonConstexprVersion() const override {
      if (non_constexpr_version_) return non_constexpr_version_;
      if (!IsConstexpr()) return this;
      if (parent()) return parent()->NonConstexprVersion();
      return nullptr;
    }
  
    std::vector<TypeChecker> GetTypeCheckers() const override;
  
    size_t AlignmentLog2() const override;
  
   private:
    friend class TypeOracle;
    AbstractType(const Type* parent, AbstractTypeFlags flags,
                 const std::string& name, const std::string& generated_type,
                 const Type* non_constexpr_version,
                 MaybeSpecializationKey specialized_from)
        : Type(Kind::kAbstractType, parent, specialized_from),
          flags_(flags),
          name_(name),
          generated_type_(generated_type),
          non_constexpr_version_(non_constexpr_version) {
      if (parent) DCHECK_EQ(parent->IsConstexpr(), IsConstexpr());
      DCHECK_EQ(IsConstexprName(name), IsConstexpr());
      DCHECK_IMPLIES(non_constexpr_version_ != nullptr, IsConstexpr());
      DCHECK(!(IsConstexpr() && (flags_ & AbstractTypeFlag::kTransient)));
    }
  
    std::string SimpleNameImpl() const override {
      if (IsConstexpr()) {
        const Type* non_constexpr_version = NonConstexprVersion();
        if (non_constexpr_version == nullptr) {
          ReportError("Cannot find non-constexpr type corresponding to ", *this);
        }
        return "constexpr_" + non_constexpr_version->SimpleName();
      }
      return name();
    }
  
    bool IsTransient() const override {
      return flags_ & AbstractTypeFlag::kTransient;
    }
  
    bool UseParentTypeChecker() const {
      return flags_ & AbstractTypeFlag::kUseParentTypeChecker;
    }
  
    AbstractTypeFlags flags_;
    const std::string name_;
    const std::string generated_type_;
    const Type* non_constexpr_version_;
  };
  
  // For now, builtin pointers are restricted to Torque-defined builtins.
  class V8_EXPORT_PRIVATE BuiltinPointerType final : public Type {
   public:
    DECLARE_TYPE_BOILERPLATE(BuiltinPointerType)
    std::string ToExplicitString() const override;
    std::string GetGeneratedTypeNameImpl() const override {
      return parent()->GetGeneratedTypeName();
    }
    std::string GetGeneratedTNodeTypeNameImpl() const override {
      return parent()->GetGeneratedTNodeTypeName();
    }
  
    const TypeVector& parameter_types() const { return parameter_types_; }
    const Type* return_type() const { return return_type_; }
  
    friend size_t hash_value(const BuiltinPointerType& p) {
      size_t result = base::hash_value(p.return_type_);
      for (const Type* parameter : p.parameter_types_) {
        result = base::hash_combine(result, parameter);
      }
      return result;
    }
    bool operator==(const BuiltinPointerType& other) const {
      return parameter_types_ == other.parameter_types_ &&
             return_type_ == other.return_type_;
    }
    size_t function_pointer_type_id() const { return function_pointer_type_id_; }
  
    std::vector<TypeChecker> GetTypeCheckers() const override {
      return {{"Smi", ""}};
    }
  
    bool HasContextParameter() const;
  
   private:
    friend class TypeOracle;
    BuiltinPointerType(const Type* parent, TypeVector parameter_types,
                       const Type* return_type, size_t function_pointer_type_id)
        : Type(Kind::kBuiltinPointerType, parent),
          parameter_types_(parameter_types),
          return_type_(return_type),
          function_pointer_type_id_(function_pointer_type_id) {}
    std::string SimpleNameImpl() const override;
  
    const TypeVector parameter_types_;
    const Type* const return_type_;
    const size_t function_pointer_type_id_;
  };
  
  bool operator<(const Type& a, const Type& b);
  struct TypeLess {
    bool operator()(const Type* const a, const Type* const b) const {
      return *a < *b;
    }
  };
  
  class V8_EXPORT_PRIVATE UnionType final : public Type {
   public:
    DECLARE_TYPE_BOILERPLATE(UnionType)
    std::string GetGeneratedTypeNameImpl() const override {
      return "TNode<" + GetGeneratedTNodeTypeName() + ">";
    }
    std::string GetGeneratedTNodeTypeNameImpl() const override;
    std::string GetRuntimeType() const override {
      return parent()->GetRuntimeType();
    }
    std::string GetDebugType() const override { return parent()->GetDebugType(); }
  
    friend size_t hash_value(const UnionType& p) {
      size_t result = 0;
      for (const Type* t : p.types_) {
        result = base::hash_combine(result, t);
      }
      return result;
    }
    bool operator==(const UnionType& other) const {
      return types_ == other.types_;
    }
  
    base::Optional<const Type*> GetSingleMember() const {
      if (types_.size() == 1) {
        DCHECK_EQ(*types_.begin(), parent());
        return *types_.begin();
      }
      return base::nullopt;
    }
  
    bool IsSubtypeOf(const Type* other) const override {
      for (const Type* member : types_) {
        if (!member->IsSubtypeOf(other)) return false;
      }
      return true;
    }
  
    bool IsSupertypeOf(const Type* other) const {
      for (const Type* member : types_) {
        if (other->IsSubtypeOf(member)) {
          return true;
        }
      }
      return false;
    }
  
    bool IsTransient() const override {
      for (const Type* member : types_) {
        if (member->IsTransient()) {
          return true;
        }
      }
      return false;
    }
  
    bool IsConstexpr() const override { return parent()->IsConstexpr(); }
  
    const Type* NonConstexprVersion() const override {
      if (!IsConstexpr()) return this;
      return parent()->NonConstexprVersion();
    }
  
    void Extend(const Type* t) {
      if (const UnionType* union_type = UnionType::DynamicCast(t)) {
        for (const Type* member : union_type->types_) {
          Extend(member);
        }
      } else {
        if (t->IsSubtypeOf(this)) return;
        set_parent(CommonSupertype(parent(), t));
        EraseIf(&types_,
                [&](const Type* member) { return member->IsSubtypeOf(t); });
        types_.insert(t);
      }
    }
    std::string ToExplicitString() const override;
  
    void Subtract(const Type* t);
  
    static UnionType FromType(const Type* t) {
      const UnionType* union_type = UnionType::DynamicCast(t);
      return union_type ? UnionType(*union_type) : UnionType(t);
    }
  
    std::vector<TypeChecker> GetTypeCheckers() const override {
      std::vector<TypeChecker> result;
      for (const Type* member : types_) {
        std::vector<TypeChecker> sub_result = member->GetTypeCheckers();
        result.insert(result.end(), sub_result.begin(), sub_result.end());
      }
      return result;
    }
  
   private:
    explicit UnionType(const Type* t) : Type(Kind::kUnionType, t), types_({t}) {}
    void RecomputeParent();
    std::string SimpleNameImpl() const override;
  
    std::set<const Type*, TypeLess> types_;
  };
  
  const Type* SubtractType(const Type* a, const Type* b);
  
  struct BitField {
    SourcePosition pos;
    NameAndType name_and_type;
    int offset;
    int num_bits;
  };
  
  class V8_EXPORT_PRIVATE BitFieldStructType final : public Type {
   public:
    DECLARE_TYPE_BOILERPLATE(BitFieldStructType)
    std::string ToExplicitString() const override;
    std::string GetGeneratedTypeNameImpl() const override {
      return parent()->GetGeneratedTypeName();
    }
    std::string GetGeneratedTNodeTypeNameImpl() const override {
      return parent()->GetGeneratedTNodeTypeName();
    }
  
    std::vector<TypeChecker> GetTypeCheckers() const override {
      return parent()->GetTypeCheckers();
    }
  
    void SetConstexprVersion(const Type*) const override { UNREACHABLE(); }
    const Type* ConstexprVersion() const override {
      return parent()->ConstexprVersion();
    }
  
    void RegisterField(BitField field) { fields_.push_back(std::move(field)); }
  
    const std::string& name() const { return decl_->name->value; }
    const std::vector<BitField>& fields() const { return fields_; }
  
    const BitField& LookupField(const std::string& name) const;
  
   private:
    friend class TypeOracle;
    BitFieldStructType(Namespace* nspace, const Type* parent,
                       const BitFieldStructDeclaration* decl)
        : Type(Kind::kBitFieldStructType, parent),
          namespace_(nspace),
          decl_(decl) {}
    std::string SimpleNameImpl() const override { return name(); }
  
    Namespace* namespace_;
    const BitFieldStructDeclaration* decl_;
    std::vector<BitField> fields_;
  };
  
  class AggregateType : public Type {
   public:
    DECLARE_TYPE_BOILERPLATE(AggregateType)
    std::string GetGeneratedTypeNameImpl() const override { UNREACHABLE(); }
    std::string GetGeneratedTNodeTypeNameImpl() const override { UNREACHABLE(); }
  
    virtual void Finalize() const = 0;
  
    void SetFields(std::vector<Field> fields) { fields_ = std::move(fields); }
    const std::vector<Field>& fields() const {
      if (!is_finalized_) Finalize();
      return fields_;
    }
    bool HasField(const std::string& name) const;
    const Field& LookupField(const std::string& name) const;
    const std::string& name() const { return name_; }
    Namespace* nspace() const { return namespace_; }
  
    virtual const Field& RegisterField(Field field) {
      fields_.push_back(field);
      return fields_.back();
    }
  
    void RegisterMethod(Method* method) { methods_.push_back(method); }
    const std::vector<Method*>& Methods() const {
      if (!is_finalized_) Finalize();
      return methods_;
    }
    std::vector<Method*> Methods(const std::string& name) const;
  
    std::vector<const AggregateType*> GetHierarchy() const;
    std::vector<TypeChecker> GetTypeCheckers() const override {
      return {{name_, ""}};
    }
  
   protected:
    AggregateType(Kind kind, const Type* parent, Namespace* nspace,
                  const std::string& name,
                  MaybeSpecializationKey specialized_from = base::nullopt)
        : Type(kind, parent, specialized_from),
          is_finalized_(false),
          namespace_(nspace),
          name_(name) {}
  
    void CheckForDuplicateFields() const;
    // Use this lookup if you do not want to trigger finalization on this type.
    const Field& LookupFieldInternal(const std::string& name) const;
    std::string SimpleNameImpl() const override { return name_; }
  
   protected:
    mutable bool is_finalized_;
    std::vector<Field> fields_;
  
   private:
    Namespace* namespace_;
    std::string name_;
    std::vector<Method*> methods_;
  };
  
  class StructType final : public AggregateType {
   public:
    DECLARE_TYPE_BOILERPLATE(StructType)
  
    std::string GetGeneratedTypeNameImpl() const override;
  
    // Returns the sum of the size of all members.
    size_t PackedSize() const;
  
    size_t AlignmentLog2() const override;
  
    enum class ClassificationFlag {
      kEmpty = 0,
      kTagged = 1 << 0,
      kUntagged = 1 << 1,
      kMixed = kTagged | kUntagged,
    };
    using Classification = base::Flags<ClassificationFlag>;
  
    // Classifies a struct as containing tagged data, untagged data, or both.
    Classification ClassifyContents() const;
  
    SourcePosition GetPosition() const { return decl_->pos; }
  
   private:
    friend class TypeOracle;
    StructType(Namespace* nspace, const StructDeclaration* decl,
               MaybeSpecializationKey specialized_from = base::nullopt);
  
    void Finalize() const override;
    std::string ToExplicitString() const override;
    std::string SimpleNameImpl() const override;
  
    const StructDeclaration* decl_;
    std::string generated_type_name_;
  };
  
  class TypeAlias;
  
  enum class ObjectSlotKind : uint8_t {
    kNoPointer,
    kStrongPointer,
    kMaybeObjectPointer,
    kCustomWeakPointer
  };
  
  inline base::Optional<ObjectSlotKind> Combine(ObjectSlotKind a,
                                                ObjectSlotKind b) {
    if (a == b) return {a};
    if (std::min(a, b) == ObjectSlotKind::kStrongPointer &&
        std::max(a, b) == ObjectSlotKind::kMaybeObjectPointer) {
      return {ObjectSlotKind::kMaybeObjectPointer};
    }
    return base::nullopt;
  }
  
  class ClassType final : public AggregateType {
   public:
    DECLARE_TYPE_BOILERPLATE(ClassType)
    std::string ToExplicitString() const override;
    std::string GetGeneratedTypeNameImpl() const override;
    std::string GetGeneratedTNodeTypeNameImpl() const override;
    bool IsExtern() const { return flags_ & ClassFlag::kExtern; }
    bool ShouldGeneratePrint() const {
      return !IsExtern() ||
             ((flags_ & ClassFlag::kGeneratePrint) && !HasUndefinedLayout());
    }
    bool ShouldGenerateVerify() const {
      return !IsExtern() || ((flags_ & ClassFlag::kGenerateVerify) &&
                             (!HasUndefinedLayout() && !IsShape()));
    }
    bool ShouldGenerateBodyDescriptor() const {
      return flags_ & ClassFlag::kGenerateBodyDescriptor ||
             (!IsAbstract() && !IsExtern());
    }
    bool DoNotGenerateCast() const {
      return flags_ & ClassFlag::kDoNotGenerateCast;
    }
    bool IsTransient() const override { return flags_ & ClassFlag::kTransient; }
    bool IsAbstract() const { return flags_ & ClassFlag::kAbstract; }
    bool HasSameInstanceTypeAsParent() const {
      return flags_ & ClassFlag::kHasSameInstanceTypeAsParent;
    }
    bool GenerateCppClassDefinitions() const {
      return flags_ & ClassFlag::kGenerateCppClassDefinitions || !IsExtern() ||
             ShouldGenerateBodyDescriptor();
    }
    bool ShouldGenerateFullClassDefinition() const {
      return !IsExtern() && !(flags_ & ClassFlag::kCustomCppClass);
    }
    // Class with multiple or non-standard maps, do not auto-generate map.
    bool HasCustomMap() const { return flags_ & ClassFlag::kCustomMap; }
    bool ShouldExport() const { return flags_ & ClassFlag::kExport; }
    bool IsShape() const { return flags_ & ClassFlag::kIsShape; }
    bool HasStaticSize() const;
    size_t header_size() const {
      if (!is_finalized_) Finalize();
      return header_size_;
    }
    ResidueClass size() const {
      if (!is_finalized_) Finalize();
      return size_;
    }
    const ClassType* GetSuperClass() const {
      if (parent() == nullptr) return nullptr;
      return parent()->IsClassType() ? ClassType::DynamicCast(parent()) : nullptr;
    }
    void GenerateAccessors();
    bool AllowInstantiation() const;
    const Field& RegisterField(Field field) override {
      return AggregateType::RegisterField(field);
    }
    void Finalize() const override;
  
    std::vector<Field> ComputeAllFields() const;
    std::vector<Field> ComputeHeaderFields() const;
    std::vector<Field> ComputeArrayFields() const;
    // The slots of an object are the tagged pointer sized offsets in an object
    // that may or may not require GC visiting. These helper functions determine
    // what kind of GC visiting the individual slots require.
    std::vector<ObjectSlotKind> ComputeHeaderSlotKinds() const;
    base::Optional<ObjectSlotKind> ComputeArraySlotKind() const;
    bool HasNoPointerSlots() const;
    bool HasIndexedFieldsIncludingInParents() const;
    const Field* GetFieldPreceding(size_t field_index) const;
  
    // Given that the field exists in this class or a superclass, returns the
    // specific class that declared the field.
    const ClassType* GetClassDeclaringField(const Field& f) const;
  
    std::string GetSliceMacroName(const Field& field) const;
  
    const InstanceTypeConstraints& GetInstanceTypeConstraints() const {
      return decl_->instance_type_constraints;
    }
    bool IsHighestInstanceTypeWithinParent() const {
      return flags_ & ClassFlag::kHighestInstanceTypeWithinParent;
    }
    bool IsLowestInstanceTypeWithinParent() const {
      return flags_ & ClassFlag::kLowestInstanceTypeWithinParent;
    }
    bool HasUndefinedLayout() const {
      return flags_ & ClassFlag::kUndefinedLayout;
    }
    SourcePosition GetPosition() const { return decl_->pos; }
    SourceId AttributedToFile() const;
  
    // TODO(tebbi): We should no longer pass around types as const pointers, so
    // that we can avoid mutable fields and const initializers for
    // late-initialized portions of types like this one.
    void InitializeInstanceTypes(base::Optional<int> own,
                                 base::Optional<std::pair<int, int>> range) const;
    base::Optional<int> OwnInstanceType() const;
    base::Optional<std::pair<int, int>> InstanceTypeRange() const;
  
   private:
    friend class TypeOracle;
    friend class TypeVisitor;
    ClassType(const Type* parent, Namespace* nspace, const std::string& name,
              ClassFlags flags, const std::string& generates,
              const ClassDeclaration* decl, const TypeAlias* alias);
  
    void GenerateSliceAccessor(size_t field_index);
  
    size_t header_size_;
    ResidueClass size_;
    mutable ClassFlags flags_;
    const std::string generates_;
    const ClassDeclaration* decl_;
    const TypeAlias* alias_;
    mutable base::Optional<int> own_instance_type_;
    mutable base::Optional<std::pair<int, int>> instance_type_range_;
  };
  
  inline std::ostream& operator<<(std::ostream& os, const Type& t) {
    os << t.ToString();
    return os;
  }
  
  class VisitResult {
   public:
    VisitResult() = default;
    VisitResult(const Type* type, const std::string& constexpr_value)
        : type_(type), constexpr_value_(constexpr_value) {
      DCHECK(type->IsConstexpr());
    }
    static VisitResult NeverResult();
    static VisitResult TopTypeResult(std::string top_reason,
                                     const Type* from_type);
    VisitResult(const Type* type, StackRange stack_range)
        : type_(type), stack_range_(stack_range) {
      DCHECK(!type->IsConstexpr());
    }
    const Type* type() const { return type_; }
    const std::string& constexpr_value() const { return *constexpr_value_; }
    const StackRange& stack_range() const { return *stack_range_; }
    void SetType(const Type* new_type) { type_ = new_type; }
    bool IsOnStack() const { return stack_range_ != base::nullopt; }
    bool operator==(const VisitResult& other) const {
      return type_ == other.type_ && constexpr_value_ == other.constexpr_value_ &&
             stack_range_ == other.stack_range_;
    }
  
   private:
    const Type* type_ = nullptr;
    base::Optional<std::string> constexpr_value_;
    base::Optional<StackRange> stack_range_;
  };
  
  VisitResult ProjectStructField(VisitResult structure,
                                 const std::string& fieldname);
  
  class VisitResultVector : public std::vector<VisitResult> {
   public:
    VisitResultVector() : std::vector<VisitResult>() {}
    VisitResultVector(std::initializer_list<VisitResult> init)
        : std::vector<VisitResult>(init) {}
    TypeVector ComputeTypeVector() const {
      TypeVector result;
      for (auto& visit_result : *this) {
        result.push_back(visit_result.type());
      }
      return result;
    }
  };
  
  std::ostream& operator<<(std::ostream& os, const TypeVector& types);
  
  using NameAndTypeVector = std::vector<NameAndType>;
  
  struct LabelDefinition {
    std::string name;
    NameAndTypeVector parameters;
  };
  
  using LabelDefinitionVector = std::vector<LabelDefinition>;
  
  struct LabelDeclaration {
    Identifier* name;
    TypeVector types;
  };
  
  using LabelDeclarationVector = std::vector<LabelDeclaration>;
  
  struct ParameterTypes {
    TypeVector types;
    bool var_args;
  };
  
  std::ostream& operator<<(std::ostream& os, const ParameterTypes& parameters);
  
  enum class ParameterMode { kProcessImplicit, kIgnoreImplicit };
  
  using NameVector = std::vector<Identifier*>;
  
  struct Signature {
    Signature(NameVector n, base::Optional<std::string> arguments_variable,
              ParameterTypes p, size_t i, const Type* r, LabelDeclarationVector l,
              bool transitioning)
        : parameter_names(std::move(n)),
          arguments_variable(arguments_variable),
          parameter_types(std::move(p)),
          implicit_count(i),
          return_type(r),
          labels(std::move(l)),
          transitioning(transitioning) {}
    Signature() = default;
    const TypeVector& types() const { return parameter_types.types; }
    NameVector parameter_names;
    base::Optional<std::string> arguments_variable;
    ParameterTypes parameter_types;
    size_t implicit_count = 0;
    size_t ExplicitCount() const { return types().size() - implicit_count; }
    const Type* return_type;
    LabelDeclarationVector labels;
    bool transitioning = false;
    bool HasSameTypesAs(
        const Signature& other,
        ParameterMode mode = ParameterMode::kProcessImplicit) const;
    TypeVector GetImplicitTypes() const {
      return TypeVector(parameter_types.types.begin(),
                        parameter_types.types.begin() + implicit_count);
    }
    TypeVector GetExplicitTypes() const {
      return TypeVector(parameter_types.types.begin() + implicit_count,
                        parameter_types.types.end());
    }
    bool HasContextParameter() const;
  };
  
  void PrintSignature(std::ostream& os, const Signature& sig, bool with_names);
  std::ostream& operator<<(std::ostream& os, const Signature& sig);
  
  bool IsAssignableFrom(const Type* to, const Type* from);
  
  TypeVector LowerType(const Type* type);
  size_t LoweredSlotCount(const Type* type);
  TypeVector LowerParameterTypes(const TypeVector& parameters);
  TypeVector LowerParameterTypes(const ParameterTypes& parameter_types,
                                 size_t vararg_count = 0);
  
  base::Optional<std::tuple<size_t, std::string>> SizeOf(const Type* type);
  bool IsAnyUnsignedInteger(const Type* type);
  bool IsAllowedAsBitField(const Type* type);
  bool IsPointerSizeIntegralType(const Type* type);
  bool Is32BitIntegralType(const Type* type);
  
  base::Optional<NameAndType> ExtractSimpleFieldArraySize(
      const ClassType& class_type, Expression* array_size);
  
  }  // namespace torque
  }  // namespace internal
  }  // namespace v8
  
  #endif  // V8_TORQUE_TYPES_H_
  