// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef CARBON_TOOLCHAIN_SEM_IR_TYPED_INSTS_H_
#define CARBON_TOOLCHAIN_SEM_IR_TYPED_INSTS_H_

#include "common/template_string.h"
#include "toolchain/base/int.h"
#include "toolchain/parse/node_ids.h"
#include "toolchain/parse/typed_nodes.h"
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/inst_kind.h"
#include "toolchain/sem_ir/singleton_insts.h"
#include "toolchain/sem_ir/specific_interface.h"

// Representations for specific kinds of instructions.
//
// Each type should be a struct with the following members, in this order:
//
// - Either a `Kind` constant, or a `Kinds` constant and an `InstKind kind;`
//   member. These are described below.
// - Optionally, a `InstId` if it is a singleton instruction. Similarly, there
//   may be `ConstantId` and `TypeId`.
//   - These are named based on the `Id` types they represent.
// - Optionally, a `TypeId type_id;` member, for instructions that produce a
//   value. This includes instructions that produce an abstract value, such as a
//   `Namespace`, for which a placeholder type should be used.
// - Up to two members describing the contents of the struct. These are types
//   listed in the `IdKind` type-enum, typically derived from `IdBase`.
//
// The field names here matter -- the fields must have the names specified
// above, when present. When converting to a `Inst`, the `kind` and `type_id`
// fields will become the kind and type associated with the type-erased
// instruction.
//
// Each type that describes a single kind of instructions provides a constant
// `Kind` that associates the type with a particular member of the `InstKind`
// enumeration. This `Kind` declaration also defines the instruction kind by
// calling `InstKind::Define` and specifying additional information about the
// instruction kind. This information is available through the member functions
// of the `InstKind` value declared in `inst_kind.h`, and includes the name used
// in textual IR and whether the instruction is a terminator instruction.
//
// Struct types can also be provided for categories of instructions with a
// common representation, to allow the common representation to be accessed
// conveniently. In this case, instead of providing a constant `Kind` member,
// the struct should have a constant `InstKind Kinds[];` member that lists the
// kinds of instructions in the category, and an `InstKind kind;` member that is
// used to identify the specific kind of the instruction. Separate struct types
// still need to be defined for each instruction kind in the category.

namespace Carbon::SemIR {

// A template for singleton types. Most uses will not add members, and so may
// apply a `using` alias. Some children add static members; non-static members
// must not be added.
//
// For a `TypeId`, `GetSingletonType` should generally be used so that the type
// is completed when referenced. In a few cases where completeness is always
// known (particularly `TypeType` and `ErrorInst`), a `TypeId` may be provided
// by a child.
template <InstKind::RawEnumType KindT, TemplateString IrName,
          ExprCategory Cat = ExprCategory::Value>
struct SingletonTypeInst {
  static constexpr auto Kind = InstKind::Make(KindT).Define<Parse::NoneNodeId>(
      InstKind::DefinitionInfo{.ir_name = IrName,
                               .expr_category = Cat,
                               .is_type = InstIsType::Always,
                               .constant_kind = InstConstantKind::Always});
  static constexpr auto TypeInstId = MakeSingletonTypeInstId<Kind>();

  // Singleton types have a type of `TypeType`, except for `ErrorInst` which
  // uses itself.
  SemIR::TypeId type_id;
};

// An action that performs simple member access, `base.name`.
struct AccessMemberAction {
  static constexpr auto Kind =
      InstKind::AccessMemberAction.Define<Parse::NodeId>(
          {.ir_name = "access_member_action",
           .constant_kind = InstConstantKind::InstAction,
           .is_lowered = false});

  TypeId type_id;
  MetaInstId base_id;
  NameId name_id;
};

// An action that performs member access which should fail silently. For
// example, `base.destroy`.
struct AccessOptionalMemberAction {
  static constexpr auto Kind =
      InstKind::AccessOptionalMemberAction.Define<Parse::NodeId>(
          {.ir_name = "access_optional_member_action",
           .constant_kind = InstConstantKind::InstAction,
           .is_lowered = false});

  TypeId type_id;
  MetaInstId base_id;
  NameId name_id;
};

// A value acquisition. Used when an expression contains a reference and we want
// a value.
struct AcquireValue {
  static constexpr auto Kind = InstKind::AcquireValue.Define<Parse::NodeId>({
      .ir_name = "acquire_value",
      .constant_needs_inst_id = InstConstantNeedsInstIdKind::DuringEvaluation,
  });

  TypeId type_id;
  InstId value_id;
};

// An adapted type declaration in a class, of the form `adapt T;`.
struct AdaptDecl {
  static constexpr auto Kind = InstKind::AdaptDecl.Define<Parse::AdaptDeclId>(
      {.ir_name = "adapt_decl",
       .constant_kind = InstConstantKind::AlwaysUnique,
       .is_lowered = false});

  // No type_id; this is not a value.
  TypeInstId adapted_type_inst_id;
};

// Takes the address of a reference expression, such as for the `&` address-of
// operator, `&lvalue`.
struct AddrOf {
  // Parse node is usually Parse::PrefixOperatorAmpId.
  static constexpr auto Kind = InstKind::AddrOf.Define<Parse::NodeId>(
      {.ir_name = "addr_of",
       .constant_kind = InstConstantKind::WheneverPossible});

  TypeId type_id;
  InstId lvalue_id;
};

// Binds a name as an alias. See AnyBinding for member documentation.
struct AliasBinding {
  static constexpr auto Kind = InstKind::AliasBinding.Define<Parse::NodeId>(
      {.ir_name = "alias_binding",
       .expr_category = ComputedExprCategory::SameAsSecondOperand});

  TypeId type_id;
  EntityNameId entity_name_id;
  InstId value_id;
};

// An array indexing operation, such as `array[index]`.
struct ArrayIndex {
  // Parse node is usually Parse::IndexExprId.
  static constexpr auto Kind = InstKind::ArrayIndex.Define<Parse::NodeId>(
      {.ir_name = "array_index",
       .expr_category = ComputedExprCategory::SameAsFirstOperand,
       .is_type = InstIsType::Maybe,
       // TODO: This should probably be SymbolicOrReference.
       .constant_kind = InstConstantKind::SymbolicOnly});

  TypeId type_id;
  InstId array_id;
  InstId index_id;
};

// Initializes an array from a tuple. `tuple_id` is the source tuple
// expression. `inits_id` contains one initializer per array element.
// `dest_id` is the destination array object for the initialization.
struct ArrayInit {
  static constexpr auto Kind = InstKind::ArrayInit.Define<Parse::NodeId>(
      {.ir_name = "array_init",
       .expr_category = ExprCategory::ReprInitializing});

  TypeId type_id;
  InstBlockId inits_id;
  DestInstId dest_id;
};

// An array of `element_type_id` values, sized to `bound_id`.
struct ArrayType {
  static constexpr auto Kind = InstKind::ArrayType.Define<Parse::ArrayExprId>(
      {.ir_name = "array_type",
       .is_type = InstIsType::Always,
       .constant_kind = InstConstantKind::Conditional,
       .constant_needs_inst_id = InstConstantNeedsInstIdKind::DuringEvaluation,
       .deduce_through = true});

  TypeId type_id;
  InstId bound_id;
  TypeInstId element_type_inst_id;
};

// Perform a no-op conversion to a compatible type.
struct AsCompatible {
  static constexpr auto Kind = InstKind::AsCompatible.Define<Parse::NodeId>(
      {.ir_name = "as_compatible",
       .expr_category = ComputedExprCategory::SameAsFirstOperand});

  TypeId type_id;
  InstId source_id;
};

// Consumes the initializer `rhs_id`, and uses it to performs a source-level
// initialization or assignment of `lhs_id`. If `rhs_id` has a storage argument,
// it is required to already be `lhs_id`.
struct Assign {
  // TODO: Make Parse::NodeId more specific.
  static constexpr auto Kind = InstKind::Assign.Define<Parse::NodeId>(
      {.ir_name = "assign", .constant_kind = InstConstantKind::Never});

  // Assignments are statements, and so have no type.
  InstId lhs_id;
  InstId rhs_id;
};

// An associated constant declaration in an interface, such as `let T:! type;`.
struct AssociatedConstantDecl {
  static constexpr auto Kind =
      InstKind::AssociatedConstantDecl
          .Define<Parse::AssociatedConstantNameAndTypeId>(
              {.ir_name = "assoc_const_decl",
               .constant_kind = InstConstantKind::AlwaysUnique,
               .is_lowered = false});

  TypeId type_id;
  AssociatedConstantId assoc_const_id;
  DeclInstBlockId decl_block_id;
};

// An associated entity declared in an interface. This is either an associated
// function or a non-function associated constant such as an associated type.
// This represents the entity before impl lookup is performed, and identifies
// the slot within a witness where the constant value will be found.
struct AssociatedEntity {
  static constexpr auto Kind = InstKind::AssociatedEntity.Define<Parse::NodeId>(
      {.ir_name = "assoc_entity", .constant_kind = InstConstantKind::Always});

  // The type of the associated entity. This is an AssociatedEntityType.
  TypeId type_id;
  ElementIndex index;
  AbsoluteInstId decl_id;
};

// The type of an expression that names an associated entity, such as
// `InterfaceName.Function`.
struct AssociatedEntityType {
  static constexpr auto Kind =
      InstKind::AssociatedEntityType.Define<Parse::NoneNodeId>(
          {.ir_name = "assoc_entity_type",
           .is_type = InstIsType::Always,
           .constant_kind = InstConstantKind::WheneverPossible});

  TypeId type_id;
  // The interface in which the entity was declared.
  InterfaceId interface_id;
  // The specific for the interface in which the entity was declared.
  SpecificId interface_without_self_specific_id;

  auto GetSpecificInterface() -> SpecificInterface {
    return {.interface_id = interface_id,
            .specific_id = interface_without_self_specific_id};
  }
};

// Used for the type of patterns that do not match a fixed type.
using AutoType = SingletonTypeInst<InstKind::AutoType, "auto">;

// A base in a class, of the form `base: base_type;`. A base class is an
// element of the derived class, and the type of the `BaseDecl` instruction is
// an `UnboundElementType`.
struct BaseDecl {
  static constexpr auto Kind = InstKind::BaseDecl.Define<Parse::BaseDeclId>(
      {.ir_name = "base_decl",
       .expr_category = ExprCategory::NotExpr,
       .constant_kind = InstConstantKind::AlwaysUnique});

  TypeId type_id;
  TypeInstId base_type_inst_id;
  ElementIndex index;
};

// Reads an argument from `BranchWithArg`.
struct BlockArg {
  static constexpr auto Kind = InstKind::BlockArg.Define<Parse::NodeId>(
      {.ir_name = "block_arg", .constant_kind = InstConstantKind::Never});

  TypeId type_id;
  LabelId block_id;
};

// A literal bool value, `true` or `false`.
struct BoolLiteral {
  // TODO: Make Parse::NodeId more specific.
  static constexpr auto Kind = InstKind::BoolLiteral.Define<Parse::NodeId>(
      {.ir_name = "bool_literal", .constant_kind = InstConstantKind::Always});

  TypeId type_id;
  BoolValue value;
};

// The type of bool literals and branch conditions, bool.
//
// This is a singleton instruction. However, it may still evolve into a more
// standard type and be removed.
using BoolType = SingletonTypeInst<InstKind::BoolType, "bool">;

// For member access such as `object.MethodName`, combines a member function
// with the value to use for `self`. This is a callable structure; `Call` will
// handle the argument assignment.
struct BoundMethod {
  static constexpr auto Kind = InstKind::BoundMethod.Define<Parse::NodeId>(
      {.ir_name = "bound_method",
       .constant_kind = InstConstantKind::WheneverPossible});

  TypeId type_id;
  // The object argument in the bound method, which will be used to initialize
  // `self`, or whose address will be used to initialize `self` for an `addr
  // self` parameter.
  InstId object_id;
  // The function being bound, whose type_id is always a `FunctionType`.
  InstId function_decl_id;
};

// The type of bound method values.
//
// This is a singleton instruction. However, it may still evolve into a more
// standard type and be removed.
using BoundMethodType =
    SingletonTypeInst<InstKind::BoundMethodType, "<bound method>">;

// Control flow to branch to the target block.
struct Branch {
  // TODO: Make Parse::NodeId more specific.
  static constexpr auto Kind = InstKind::Branch.Define<Parse::NodeId>(
      {.ir_name = "br",
       .constant_kind = InstConstantKind::Never,
       .terminator_kind = TerminatorKind::Terminator});

  // Branches don't produce a value, so have no type.
  LabelId target_id;
};

// Control flow to branch to the target block if `cond_id` is true.
struct BranchIf {
  // TODO: Make Parse::NodeId more specific.
  static constexpr auto Kind = InstKind::BranchIf.Define<Parse::NodeId>(
      {.ir_name = "br",
       .constant_kind = InstConstantKind::Never,
       .terminator_kind = TerminatorKind::TerminatorSequence});

  // Branches don't produce a value, so have no type.
  LabelId target_id;
  InstId cond_id;
};

// Control flow to branch to the target block, passing an argument for
// `BlockArg` to read.
struct BranchWithArg {
  // TODO: Make Parse::NodeId more specific.
  static constexpr auto Kind = InstKind::BranchWithArg.Define<Parse::NodeId>(
      {.ir_name = "br",
       .constant_kind = InstConstantKind::Never,
       .terminator_kind = TerminatorKind::Terminator});

  // Branches don't produce a value, so have no type.
  LabelId target_id;
  InstId arg_id;
};

// An abstract `callee(args)` call, where the callee may be a function, but
// could also be a generic or other callable structure.
struct Call {
  // For a syntactic call, the parse node will be a CallExprStartId. However,
  // calls can arise from other syntaxes, such as operators and implicit
  // conversions.
  static constexpr auto Kind = InstKind::Call.Define<Parse::NodeId>(
      {.ir_name = "call",
       .expr_category = ComputedExprCategory::DependsOnOperands,
       .is_type = InstIsType::Maybe,
       .constant_kind = InstConstantKind::SymbolicOnly,
       .constant_needs_inst_id =
           InstConstantNeedsInstIdKind::DuringEvaluation});

  TypeId type_id;
  InstId callee_id;
  // Runtime arguments in lexical order of the parameter declarations, followed
  // by the argument for the return slot, if present.
  InstBlockId args_id;
};

// A unicode code point character literal. This type only provides compile-time
// operations, and is represented as an empty type at runtime.
//
// This is a singleton instruction. However, it may still evolve into a more
// standard type and be removed.
using CharLiteralType =
    SingletonTypeInst<InstKind::CharLiteralType, "Core.CharLiteral">;

// A unicode code point character value, whose type is `CharLiteralType`.
struct CharLiteralValue {
  // TODO: Make Parse::NodeId more specific.
  static constexpr auto Kind = InstKind::CharLiteralValue.Define<Parse::NodeId>(
      {.ir_name = "char_value", .constant_kind = InstConstantKind::Always});

  TypeId type_id;

  CharId value;
};

// A class declaration.
struct ClassDecl {
  static constexpr auto Kind =
      InstKind::ClassDecl.Define<Parse::AnyClassDeclId>(
          {.ir_name = "class_decl"});

  TypeId type_id;
  // TODO: For a generic class declaration, the name of the class declaration
  // should become a parameterized entity name value.
  ClassId class_id;
  // The declaration block, containing the class name's qualifiers and the
  // class's generic parameters.
  DeclInstBlockId decl_block_id;
};

// Access to a member of a class, such as `base.index`. This provides a
// reference for either reading or writing.
struct ClassElementAccess {
  // TODO: Make Parse::NodeId more specific.
  static constexpr auto Kind =
      InstKind::ClassElementAccess.Define<Parse::NodeId>(
          {.ir_name = "class_element_access",
           .expr_category = ComputedExprCategory::DependsOnOperands,
           .is_type = InstIsType::Maybe,
           .constant_kind = InstConstantKind::SymbolicOrReference});

  TypeId type_id;
  InstId base_id;
  ElementIndex index;
};

// Initializes a class object at dest_id with the contents of elements_id.
struct ClassInit {
  static constexpr auto Kind = InstKind::ClassInit.Define<Parse::NodeId>(
      {.ir_name = "class_init",
       .expr_category = ExprCategory::ReprInitializing});

  TypeId type_id;
  InstBlockId elements_id;
  DestInstId dest_id;
};

// The type for a class, either non-generic or specific.
struct ClassType {
  static constexpr auto Kind = InstKind::ClassType.Define<Parse::NodeId>(
      {.ir_name = "class_type",
       .is_type = InstIsType::Always,
       .constant_kind = InstConstantKind::Always,
       .deduce_through = true});

  TypeId type_id;
  ClassId class_id;
  SpecificId specific_id;
};

// A witness that a type is complete. For now, this only tracks the object
// representation corresponding to the type, and this instruction is currently
// only created for class types, because all other types are their own object
// representation.
//
// TODO: Eventually this should be replaced by a witness for an interface that
// models type completeness, and should track other information such as the
// value representation.
struct CompleteTypeWitness {
  static constexpr auto Kind =
      InstKind::CompleteTypeWitness.Define<Parse::NodeId>(
          {.ir_name = "complete_type_witness",
           .constant_kind = InstConstantKind::Always});
  // Always the builtin witness type.
  TypeId type_id;
  // The type that is used as the object representation of this type.
  TypeInstId object_repr_type_inst_id;
};

// Indicates `const` on a type, such as `var x: const i32`.
struct ConstType {
  static constexpr auto Kind =
      InstKind::ConstType.Define<Parse::PrefixOperatorConstId>(
          {.ir_name = "const_type",
           .is_type = InstIsType::Always,
           .constant_kind = InstConstantKind::Conditional,
           .deduce_through = true});

  TypeId type_id;
  TypeInstId inner_id;
};

// Records that a type conversion `original as new_type` was done, producing the
// result.
struct Converted {
  static constexpr auto Kind = InstKind::Converted.Define<Parse::NodeId>(
      {.ir_name = "converted",
       .expr_category = ComputedExprCategory::SameAsSecondOperand});

  TypeId type_id;
  // The operand prior to being converted. This is tracked only for tooling
  // purposes and has no associated semantics.
  AbsoluteInstId original_id;
  InstId result_id;
};

// An action that performs simple conversion to a value expression of a given
// type.
struct ConvertToValueAction {
  static constexpr auto Kind =
      InstKind::ConvertToValueAction.Define<Parse::NodeId>(
          {.ir_name = "convert_to_value_action",
           .constant_kind = InstConstantKind::InstAction,
           .is_lowered = false});

  TypeId type_id;
  MetaInstId inst_id;
  TypeInstId target_type_inst_id;
};

// The type of an overloaded C++ function.
struct CppOverloadSetType {
  static constexpr auto Kind =
      InstKind::CppOverloadSetType.Define<Parse::NodeId>(
          {.ir_name = "cpp_overload_set_type",
           .is_type = InstIsType::Always,
           .constant_kind = InstConstantKind::WheneverPossible});

  TypeId type_id;
  CppOverloadSetId overload_set_id;
  SpecificId specific_id;
};

// An unresolved C++ overload set value.
struct CppOverloadSetValue {
  static constexpr auto Kind =
      InstKind::CppOverloadSetValue.Define<Parse::NodeId>(
          {.ir_name = "cpp_overload_set_value",
           .constant_kind = InstConstantKind::Always});

  TypeId type_id;
  CppOverloadSetId overload_set_id;
};

// The type of the name of a C++ template. The corresponding value is an empty
// `StructValue`. This does not handle function templates, which are instead
// represented as a `CppOverloadSetValue` of type `CppOverloadSetType`.
struct CppTemplateNameType {
  // This is only ever created as a constant, so doesn't have a location.
  static constexpr auto Kind =
      InstKind::CppTemplateNameType.Define<Parse::NoneNodeId>(
          {.ir_name = "cpp_type_template_type",
           .is_type = InstIsType::Always,
           .constant_kind = InstConstantKind::Always});

  TypeId type_id;
  EntityNameId name_id;
  ClangDeclId decl_id;
};

// A type whose layout is determined externally. This is used as the object
// representation of class types imported from C++.
struct CustomLayoutType {
  static constexpr auto Kind = InstKind::CustomLayoutType.Define<Parse::NodeId>(
      {.ir_name = "custom_layout_type",
       .is_type = InstIsType::Always,
       .constant_kind = InstConstantKind::WheneverPossible,
       .deduce_through = true});

  TypeId type_id;
  StructTypeFieldsId fields_id;
  CustomLayoutId layout_id;
};

// A witness synthesized for an arbitrary construct. For example, a `Destroy`
// witness, or a C++ overloaded operator.
struct CustomWitness {
  static constexpr auto Kind = InstKind::CustomWitness.Define<Parse::NodeId>(
      {.ir_name = "custom_witness",
       .constant_kind = InstConstantKind::Always,
       // TODO: For dynamic dispatch, we might want to lower witness tables as
       // constants.
       .is_lowered = false});

  // Always the type of the builtin `WitnessType` singleton instruction.
  TypeId type_id;
  // The witness table of instructions.
  InstBlockId elements_id;
  // The `SpecificInterface` of the lookup query.
  SpecificInterfaceId query_specific_interface_id;
};

// The `*` dereference operator, as in `*pointer`.
struct Deref {
  static constexpr auto Kind = InstKind::Deref.Define<Parse::NodeId>(
      {.ir_name = "deref", .expr_category = ExprCategory::DurableRef});

  TypeId type_id;
  InstId pointer_id;
};

// Used when a semantic error has been detected, and a SemIR InstId is still
// required. For example, when there is a type checking issue, this will be used
// in the type_id. It's typically used as a cue that semantic checking doesn't
// need to issue further diagnostics.
struct ErrorInst : public SingletonTypeInst<InstKind::ErrorInst, "<error>",
                                            ExprCategory::Error> {
  // Convenience for returning error InstIds and ConstantIds directly.
  static constexpr InstId InstId = TypeInstId;
  static constexpr auto ConstantId =
      ConstantId::ForConcreteConstant(TypeInstId);

  // `ErrorInst` is always set complete in file.cpp.
  static constexpr auto TypeId = TypeId::ForTypeConstant(ConstantId);
};

// An `export bind_name` declaration.
struct ExportDecl {
  static constexpr auto Kind = InstKind::ExportDecl.Define<Parse::ExportDeclId>(
      {.ir_name = "export",
       .expr_category = ComputedExprCategory::SameAsSecondOperand});

  TypeId type_id;
  EntityNameId entity_name_id;
  // The exported entity.
  InstId value_id;
};

// Represents accessing the `type` field in a facet value, which is notionally a
// pair of a type and a witness.
struct FacetAccessType {
  static constexpr auto Kind = InstKind::FacetAccessType.Define<Parse::NodeId>(
      {.ir_name = "facet_access_type",
       .is_type = InstIsType::Always,
       .constant_kind = InstConstantKind::SymbolicOnly});

  // Always the builtin type TypeType.
  TypeId type_id;
  // An instruction that evaluates to a `FacetValue`.
  InstId facet_value_inst_id;
};

// A facet type value.
struct FacetType {
  static constexpr auto Kind = InstKind::FacetType.Define<Parse::NodeId>(
      {.ir_name = "facet_type",
       .is_type = InstIsType::Always,
       .constant_kind = InstConstantKind::Always});

  TypeId type_id;
  // TODO: Rename this to facet_type_info_id.
  FacetTypeId facet_type_id;
};

// A facet value is a general value of type FacetType. This consists of a type
// and a set of witnesses that it satisfies the required interfaces of the
// FacetType.
//
// This instruction is never a type. Though it can be converted to type, doing
// so evaluates to the `type_inst_id` within.
//
// If the FacetValue is just a wrapper around a SymbolicBinding (converted to
// `type` and back, for example), it evaluates back to the SymbolicBinding.
struct FacetValue {
  static constexpr auto Kind = InstKind::FacetValue.Define<Parse::NodeId>(
      {.ir_name = "facet_value",
       .constant_kind = InstConstantKind::Conditional,
       .deduce_through = true});

  // A `FacetType`.
  TypeId type_id;
  // The type that you will get if you cast this value to `type`.
  TypeInstId type_inst_id;
  // The set of `ImplWitness` instructions for a `FacetType`. The witnesses are
  // in the same order as the set of `required_interfaces` in the
  // `IdentifiedFacetType` of the `FacetType` from `type_id`, so that an index
  // from one can be used with the other.
  InstBlockId witnesses_block_id;
};

// A field in a class, of the form `var field: field_type;`. The type of the
// `FieldDecl` instruction is an `UnboundElementType`.
struct FieldDecl {
  static constexpr auto Kind =
      InstKind::FieldDecl.Define<Parse::FieldNameAndTypeId>(
          {.ir_name = "field_decl",
           .expr_category = ExprCategory::NotExpr,
           .constant_kind = InstConstantKind::AlwaysUnique});

  TypeId type_id;
  NameId name_id;
  ElementIndex index;
};

// The float literal type.
// TODO: Replace this with a rational number type, following the design.
//
// This is a singleton instruction. However, it may still evolve into a more
// standard type and be removed.
using FloatLiteralType =
    SingletonTypeInst<InstKind::FloatLiteralType, "Core.FloatLiteral">;

// A floating point literal value.
// TODO: Eventually this should be represented as a rational number, and should
// support arithmetic. For now, we preserve the exact form of the literal
// produced by the lexer, and don't support any operations, not even unary
// negation.
struct FloatLiteralValue {
  static constexpr auto Kind =
      InstKind::FloatLiteralValue.Define<Parse::RealLiteralId>(
          {.ir_name = "float_literal_value",
           .constant_kind = InstConstantKind::Always});

  TypeId type_id;
  RealId real_id;
};

// A floating point type.
struct FloatType {
  static constexpr auto Kind = InstKind::FloatType.Define<Parse::NoneNodeId>(
      {.ir_name = "float_type",
       .is_type = InstIsType::Always,
       .constant_kind = InstConstantKind::Conditional,
       .constant_needs_inst_id = InstConstantNeedsInstIdKind::DuringEvaluation,
       .deduce_through = true});

  TypeId type_id;
  // TODO: Consider adding a more compact way of representing either a small
  // float bit width or an inst_id.
  InstId bit_width_id;
  FloatKind float_kind;
};

// A floating point value.
struct FloatValue {
  static constexpr auto Kind =
      InstKind::FloatValue.Define<Parse::RealLiteralId>(
          {.ir_name = "float_value",
           .constant_kind = InstConstantKind::Always});

  TypeId type_id;
  FloatId float_id;
};

// A form binding, such as the `x` declared by `x:? F`. See `AnyBinding` for
// member documentation.
struct FormBinding {
  static constexpr auto Kind =
      InstKind::FormBinding.Define<Parse::FormBindingPatternId>(
          {.ir_name = "form_binding",
           .expr_category = ComputedExprCategory::DependsOnOperands,
           .constant_kind = InstConstantKind::Never});

  TypeId type_id;
  EntityNameId entity_name_id;
  InstId value_id;
};

// A form binding pattern, such as `x:? F`. See `AnyBindingPattern` for member
// documentation.
struct FormBindingPattern {
  static constexpr auto Kind =
      InstKind::FormBindingPattern.Define<Parse::FormBindingPatternId>(
          {.ir_name = "form_binding_pattern",
           .expr_category = ExprCategory::Pattern,
           .constant_kind = InstConstantKind::AlwaysUnique,
           .is_lowered = false});

  TypeId type_id;
  // Note that the EntityName's `form_id` represents the scrutinee form, so it
  // doesn't directly correspond to `type_id` (which is a pattern type).
  EntityNameId entity_name_id;
};

// A pattern that represents a form-parameterized parameter, such as `x:? F`.
// See `AnyParamPattern` for member documentation.
struct FormParamPattern {
  // TODO: Replace `Parse::NodeId` with `Parse::FormBindingPattern`.
  static constexpr auto Kind = InstKind::FormParamPattern.Define<Parse::NodeId>(
      {.ir_name = "form_param_pattern",
       .expr_category = ExprCategory::Pattern,
       .constant_kind = InstConstantKind::AlwaysUnique,
       .is_lowered = false});

  TypeId type_id;
  InstId subpattern_id;
};

// The type `Core.Form`.
struct FormType : public SingletonTypeInst<InstKind::FormType, "Core.Form"> {
  // `FormType` is always set complete in file.cpp.
  static constexpr auto TypeId =
      TypeId::ForTypeConstant(ConstantId::ForConcreteConstant(TypeInstId));
};

// A function declaration.
struct FunctionDecl {
  static constexpr auto Kind =
      InstKind::FunctionDecl.Define<Parse::AnyFunctionDeclId>(
          {.ir_name = "fn_decl",
           .expr_category = ExprCategory::NotExpr,
           .is_lowered = false});

  TypeId type_id;
  FunctionId function_id;
  // The declaration block, containing the function declaration's parameters and
  // their types.
  DeclInstBlockId decl_block_id;
};

// The type of a function.
struct FunctionType {
  static constexpr auto Kind =
      InstKind::FunctionType.Define<Parse::AnyFunctionDeclId>(
          {.ir_name = "fn_type",
           .is_type = InstIsType::Always,
           .constant_kind = InstConstantKind::WheneverPossible});

  TypeId type_id;
  FunctionId function_id;
  SpecificId specific_id;
};

// The type of an associated function within an `impl`, modeled as an underlying
// `FunctionType` plus the value of the `Self` parameter. This is the type of
// `(SelfType as Interface).AssociatedFunction`.
struct FunctionTypeWithSelfType {
  static constexpr auto Kind =
      InstKind::FunctionTypeWithSelfType.Define<Parse::NoneNodeId>(
          {.ir_name = "fn_type_with_self_type",
           .is_type = InstIsType::Always,
           .constant_kind = InstConstantKind::WheneverPossible,
           .is_lowered = false});

  TypeId type_id;
  // The type of the function within the interface. This includes the
  // interface's SpecificId if applicable. This will be a `FunctionType` except
  // in error cases.
  TypeInstId interface_function_type_id;
  // The value to use for `Self` in this function. May be a type or a facet
  // value.
  InstId self_id;
};

// The type of the name of a generic class. The corresponding value is an empty
// `StructValue`.
struct GenericClassType {
  // This is only ever created as a constant, so doesn't have a location.
  static constexpr auto Kind =
      InstKind::GenericClassType.Define<Parse::NoneNodeId>(
          {.ir_name = "generic_class_type",
           .is_type = InstIsType::Always,
           .constant_kind = InstConstantKind::WheneverPossible});

  TypeId type_id;
  ClassId class_id;
  SpecificId enclosing_specific_id;
};

// The type of the name of a generic interface. The corresponding value is an
// empty `StructValue`.
struct GenericInterfaceType {
  // This is only ever created as a constant, so doesn't have a location.
  static constexpr auto Kind =
      InstKind::GenericInterfaceType.Define<Parse::NoneNodeId>(
          {.ir_name = "generic_interface_type",
           .is_type = InstIsType::Always,
           .constant_kind = InstConstantKind::WheneverPossible});

  TypeId type_id;
  InterfaceId interface_id;
  SpecificId enclosing_specific_id;
};

// The type of the name of a generic named constraint. The corresponding value
// is an empty `StructValue`.
struct GenericNamedConstraintType {
  // This is only ever created as a constant, so doesn't have a location.
  static constexpr auto Kind =
      InstKind::GenericNamedConstraintType.Define<Parse::NoneNodeId>(
          {.ir_name = "generic_named_constaint_type",
           .is_type = InstIsType::Always,
           .constant_kind = InstConstantKind::WheneverPossible});

  TypeId type_id;
  NamedConstraintId named_constraint_id;
  SpecificId enclosing_specific_id;
};

// An `impl` declaration.
struct ImplDecl {
  static constexpr auto Kind = InstKind::ImplDecl.Define<Parse::AnyImplDeclId>(
      {.ir_name = "impl_decl",
       // TODO: Modeling impls as unique doesn't properly handle impl
       // redeclarations.
       .constant_kind = InstConstantKind::AlwaysUnique,
       .is_lowered = false});

  // No type: an impl declaration is not a value.
  ImplId impl_id;
  // The declaration block, containing the impl's deduced parameters and its
  // self type and interface type.
  DeclInstBlockId decl_block_id;
};

// A witness that a type implements an interface.
struct ImplWitness {
  static constexpr auto Kind = InstKind::ImplWitness.Define<Parse::NodeId>(
      {.ir_name = "impl_witness",
       .constant_kind = InstConstantKind::Always,
       // TODO: For dynamic dispatch, we might want to lower witness tables as
       // constants.
       .is_lowered = false});

  // Always the type of the builtin `WitnessType` singleton instruction.
  TypeId type_id;
  // An `ImplWitnessTable` instruction.
  InstId witness_table_id;
  // The specific to be applied to instructions from the witness table to get
  // their constant values.
  SpecificId specific_id;
};

// Accesses an element of an impl witness by index.
struct ImplWitnessAccess {
  static constexpr auto Kind =
      InstKind::ImplWitnessAccess.Define<Parse::NodeId>(
          {.ir_name = "impl_witness_access",
           .is_type = InstIsType::Maybe,
           .constant_kind = InstConstantKind::SymbolicOnly,
           .constant_needs_inst_id =
               InstConstantNeedsInstIdKind::DuringEvaluation,
           .is_lowered = false});

  TypeId type_id;
  InstId witness_id;
  ElementIndex index;
};

// A substituted value to use in place of an ImplWitnessAccess (which comes from
// the RHS of a rewrite constraint in the same facet type), while preserving the
// original reference to an associated constant as an ImplWitnessAccess. This
// allows the substitution to occur on the LHS of rewrite constraints without
// losing what is being rewritten by them.
struct ImplWitnessAccessSubstituted {
  static constexpr auto Kind =
      InstKind::ImplWitnessAccessSubstituted.Define<Parse::NodeId>(
          {.ir_name = "impl_witness_access_substituted",
           .is_type = InstIsType::Maybe,
           .constant_kind = InstConstantKind::SymbolicOnly,
           .is_lowered = false});

  TypeId type_id;
  // The ImplWitnessAccess instruction that this was created from.
  InstId impl_witness_access_id;
  // The value instruction to use in place of the ImplWitnessAccess.
  InstId value_id;
};

// An instruction that just points to an associated constant, which exists to
// live inside the generic for an `impl` and be rewritten in the generic eval
// block, unlike the instruction which it points to. This allows a symbolic
// constant value of this instruction to be be substituted to be associated with
// the generic.
struct ImplWitnessAssociatedConstant {
  static constexpr auto Kind =
      InstKind::ImplWitnessAssociatedConstant.Define<Parse::NodeId>(
          {.ir_name = "impl_witness_assoc_constant",
           .expr_category = ComputedExprCategory::SameAsFirstOperand,
           .is_type = InstIsType::Maybe,
           // TODO: For dynamic dispatch, we might want to lower witness tables
           // as constants.
           .is_lowered = false});

  // The type of the `inst_id`.
  TypeId type_id;
  // The instruction of the associated constant.
  InstId inst_id;
};

// The witness table contains an instruction for each associated constant and
// function in the impl declaration (and definition, if seen). The `specific_id`
// from the `ImplWitness` should be applied to those instructions. Instructions
// will be `InstId::ImplWitnessTablePlaceholder` until a value is seen for them.
//
// An `ImplWitnessTable` can be shared by multiple `ImplWitness` instructions,
// to avoid the work of importing the full table with each witness.
//
// The instruction uses `constant_kind` of `Unique` to ensure the table is not
// substituted or re-evaluated in a generic context. The table is built up
// across multiple check steps (checking an impl declaration and definition), so
// we need there to be only a single table per `ImplId`. The constant values of
// instructions in the table are found lazily by explicitly applying the
// `specific_id` from an `ImplWitness` to them.
//
// Since the table itself is unique and not re-evaluated into the generic eval
// block, it is imperative that any symbolic instructions found in the table,
// for a generic impl, have an instruction in the generic's eval block. See
// `ImplWitnessAssociatedConstant` which serves this purpose for associated
// constant values.
struct ImplWitnessTable {
  static constexpr auto Kind = InstKind::ImplWitnessTable.Define<Parse::NodeId>(
      {.ir_name = "impl_witness_table",
       .constant_kind = InstConstantKind::AlwaysUnique,
       // TODO: For dynamic dispatch, we might want to lower witness tables as
       // constants.
       .is_lowered = false});

  // The witness table of instructions.
  //
  // We use AbsoluteInstBlockId since this block on import will contain
  // ImportRefLoaded instructions, and they can not be evaluated. We store
  // ImportRefLoaded instructions so that we can lazily load only the witness
  // table entries that are used.
  AbsoluteInstBlockId elements_id;

  // The `Impl` which this table is constructed for. This may be `None` in the
  // future if the witness was constructed from a facet value directly.
  //
  // TODO: When constructing from a facet value, should we store the facet value
  // instruction (and the `ImplDecl` instruction) in here, as that lets us get
  // the FacetType and its interface names?
  ImplId impl_id;
};

// An `import Cpp` declaration.
struct ImportCppDecl {
  static constexpr auto Kind =
      InstKind::ImportCppDecl.Define<Parse::ImportDeclId>(
          {.ir_name = "import_cpp",
           .constant_kind = InstConstantKind::Never,
           .is_lowered = false});
};

// An `import` declaration. This is mainly for `import` diagnostics, and a 1:1
// correspondence with actual `import`s isn't guaranteed.
struct ImportDecl {
  static constexpr auto Kind =
      InstKind::ImportDecl.Define<Parse::AnyPackagingDeclId>(
          {.ir_name = "import",
           .constant_kind = InstConstantKind::Never,
           .is_lowered = false});

  NameId package_id;
};

// An imported entity that is not yet been loaded. See `AnyImportRef` for
// member documentation.
struct ImportRefUnloaded {
  static constexpr auto Kind =
      InstKind::ImportRefUnloaded.Define<Parse::NodeId>(
          {.ir_name = "import_ref",
           .expr_category = ComputedExprCategory::DependsOnOperands,
           .is_lowered = false});

  ImportIRInstId import_ir_inst_id;
  EntityNameId entity_name_id;
};

// A imported entity that is loaded, and may be used. See `AnyImportRef` for
// member documentation.
struct ImportRefLoaded {
  static constexpr auto Kind = InstKind::ImportRefLoaded.Define<Parse::NodeId>(
      {.ir_name = "import_ref",
       .expr_category = ComputedExprCategory::DependsOnOperands,
       .is_lowered = false});

  TypeId type_id;
  ImportIRInstId import_ir_inst_id;
  EntityNameId entity_name_id;
};

// An initializing primitive form.
struct InitForm {
  static constexpr auto Kind = InstKind::InitForm.Define<Parse::NodeId>(
      {.ir_name = "init_form",
       .constant_kind = InstConstantKind::Always,
       .is_lowered = false});

  // Always FormType
  TypeId type_id;
  // The type component of the form.
  TypeInstId type_component_inst_id;
};

// Consumes the repr-initializing expression `src_id` and forms an in-place
// initializing expression that initializes the storage at `dest_id`, by
// performing a final copy from source to destination for types whose
// initialization is not in-place.
struct InPlaceInit {
  // Note this Parse::NodeId is unused. InPlaceInit is only constructed by
  // reusing locations.
  // TODO: Figure out if there's a better way to handle this case.
  static constexpr auto Kind = InstKind::InPlaceInit.Define<Parse::NodeId>(
      {.ir_name = "in_place_init",
       .expr_category = ExprCategory::InPlaceInitializing});

  TypeId type_id;
  InstId src_id;
  DestInstId dest_id;
};

// Used as the type of template actions that produce instructions.
using InstType = SingletonTypeInst<InstKind::InstType, "<instruction>">;

// A value of type `InstType` that refers to an instruction. This is used to
// represent an instruction as a value for use as a result of a template action.
struct InstValue {
  static constexpr auto Kind = InstKind::InstValue.Define<Parse::NoneNodeId>(
      {.ir_name = "inst_value",
       .is_type = InstIsType::Never,
       .constant_kind = InstConstantKind::Always,
       .is_lowered = false});

  TypeId type_id;
  MetaInstId inst_id;
};

// An interface declaration.
struct InterfaceDecl {
  static constexpr auto Kind =
      InstKind::InterfaceDecl.Define<Parse::AnyInterfaceDeclId>(
          {.ir_name = "interface_decl", .is_lowered = false});

  // If the interface is not generic, this is `type`, otherwise it's
  // `GenericInterfaceType`.
  TypeId type_id;
  InterfaceId interface_id;
  // The declaration block, containing the interface name's qualifiers and the
  // interface's generic parameters.
  DeclInstBlockId decl_block_id;
};

struct InterfaceWithSelfDecl {
  static constexpr auto Kind =
      InstKind::InterfaceWithSelfDecl.Define<Parse::AnyInterfaceDeclId>(
          {.ir_name = "interface_with_self_decl",
           .constant_kind = InstConstantKind::AlwaysUnique,
           .is_lowered = false});
  // No type: an interface-with-self declaration is not a value.
  InterfaceId interface_id;
};

// An arbitrary-precision integer type, which is used as the type of integer
// literals and as the parameter type of `Core.Int` and `Core.Float`. This type
// only provides compile-time operations, and is represented as an empty type at
// runtime.
//
// This is a singleton instruction. However, it may still evolve into a more
// standard type and be removed.
using IntLiteralType =
    SingletonTypeInst<InstKind::IntLiteralType, "Core.IntLiteral">;

// A primitive integer type whose representation and operations are defined by
// the toolchain. The `Core.Int` and `Core.UInt` classes are defined as adapters
// for this type.
struct IntType {
  static constexpr auto Kind = InstKind::IntType.Define<Parse::NoneNodeId>(
      {.ir_name = "int_type",
       .is_type = InstIsType::Always,
       .constant_kind = InstConstantKind::Conditional,
       .constant_needs_inst_id = InstConstantNeedsInstIdKind::DuringEvaluation,
       .deduce_through = true});

  TypeId type_id;
  IntKind int_kind;
  // TODO: Consider adding a more compact way of representing either a small
  // unsigned integer bit width or an inst_id.
  InstId bit_width_id;
};

// An integer value.
struct IntValue {
  // TODO: Make Parse::NodeId more specific.
  static constexpr auto Kind = InstKind::IntValue.Define<Parse::NodeId>(
      {.ir_name = "int_value", .constant_kind = InstConstantKind::Always});

  TypeId type_id;
  IntId int_id;
};

// A symbolic instruction that takes the place of an `ImplWitness` when the
// result is not fully known. When evaluated it does an impl lookup query, based
// on the stored query arguments, that a type implements an interface. The query
// can be symbolic, and thus modified to be more concrete by applying a
// specific. Once the query is concrete enough, or a final impl is found, the
// instruction evaluates to an `ImplWitness`.
//
// This instruction also represents a promise that an impl lookup query was
// satisfied, like `ImplWitness`, but without providing which impl declaration
// satisfies it.
struct LookupImplWitness {
  static constexpr auto Kind =
      InstKind::LookupImplWitness.Define<Parse::NodeId>(
          {.ir_name = "lookup_impl_witness",
           .constant_kind = InstConstantKind::SymbolicOnly,
           .constant_needs_inst_id =
               InstConstantNeedsInstIdKind::DuringEvaluation,
           .is_lowered = false});

  // Always the type of the builtin `WitnessType` singleton instruction.
  TypeId type_id;
  // The self type (or facet value) and interface of the impl lookup query.
  InstId query_self_inst_id;
  SpecificInterfaceId query_specific_interface_id;
};

// Records that evaluation of the expression `src_id` will initialize the
// storage identified by `dest_id` (even if its type's initializing
// representation is not normally in-place), and forms an in-place initializing
// expression to represent it. Note that `src_id` must find its target storage
// using the exact ID `dest_id`, not another ID that aliases it.
//
// This is used to model the initialization performed by C++ thunks, where
// in-place initialization is used even for types that would normally have a
// copy initializing representation.
struct MarkInPlaceInit {
  static constexpr auto Kind = InstKind::MarkInPlaceInit.Define<Parse::NodeId>(
      {.ir_name = "mark_in_place_init",
       .expr_category = ExprCategory::InPlaceInitializing,
       .constant_kind = InstConstantKind::Never});

  TypeId type_id;
  // Used only to track the source of the initialization; this has no semantic
  // meaning.
  InstId src_id;
  DestInstId dest_id;
};

// A type that holds an object representation of another type, that may or may
// not be a valid representation. In particular, it may also hold an unformed
// state.
struct MaybeUnformedType {
  static constexpr auto Kind =
      InstKind::MaybeUnformedType.Define<Parse::NodeId>({
          .ir_name = "maybe_unformed_type",
          .is_type = InstIsType::Always,
          .constant_kind = InstConstantKind::WheneverPossible,
          .deduce_through = true,
      });

  TypeId type_id;
  TypeInstId inner_id;
};

// A name-binding declaration, i.e. a declaration introduced with `let` or
// `var`.
struct NameBindingDecl {
  // TODO: Make Parse::NodeId more specific.
  static constexpr auto Kind = InstKind::NameBindingDecl.Define<Parse::NodeId>(
      {.ir_name = "name_binding_decl",
       .expr_category = ExprCategory::NotExpr,
       .constant_kind = InstConstantKind::Never});

  InstBlockId pattern_block_id;
};

// A named constraint declaration.
struct NamedConstraintDecl {
  static constexpr auto Kind =
      InstKind::NamedConstraintDecl.Define<Parse::AnyNamedConstraintDeclId>(
          {.ir_name = "constraint_decl", .is_lowered = false});

  // If the constraint is not generic, this is `type`, otherwise it's
  // `GenericNamedConstraintType`.
  TypeId type_id;
  NamedConstraintId named_constraint_id;
  // The declaration block, containing the constraint name's qualifiers and the
  // constraint's generic parameters.
  DeclInstBlockId decl_block_id;
};

struct NamedConstraintWithSelfDecl {
  static constexpr auto Kind =
      InstKind::NamedConstraintWithSelfDecl
          .Define<Parse::AnyNamedConstraintDeclId>(
              {.ir_name = "constraint_with_self_decl",
               .constant_kind = InstConstantKind::AlwaysUnique,
               .is_lowered = false});
  // No type: an constraint-with-self declaration is not a value.
  NamedConstraintId named_constraint_id;
};

// A name reference, with the value of the name. This only handles name
// resolution; the value may be used for reading or writing.
struct NameRef {
  // TODO: Make Parse::NodeId more specific.
  static constexpr auto Kind = InstKind::NameRef.Define<Parse::NodeId>(
      {.ir_name = "name_ref",
       .expr_category = ComputedExprCategory::SameAsSecondOperand});

  TypeId type_id;
  NameId name_id;
  InstId value_id;
};

// A namespace declaration.
struct Namespace {
  static constexpr auto Kind =
      InstKind::Namespace.Define<Parse::AnyNamespaceId>(
          {.ir_name = "namespace",
           .expr_category = ExprCategory::NotExpr,
           // TODO: Modeling namespaces as unique doesn't properly handle
           // namespace redeclarations.
           .constant_kind = InstConstantKind::AlwaysUnique});
  // The file's package namespace is a well-known instruction to help `package.`
  // qualified names. It will always be immediately after singletons.
  static constexpr InstId PackageInstId = InstId(SingletonInstKinds.size());

  TypeId type_id;
  NameScopeId name_scope_id;
  // If the namespace was produced by an `import` line, the associated line for
  // diagnostics.
  AbsoluteInstId import_id;
};

// The type of namespace and imported package names.
//
// This is a singleton instruction. However, it may still evolve into a more
// standard type and be removed.
using NamespaceType = SingletonTypeInst<InstKind::NamespaceType, "<namespace>">;

// An output `Call` parameter. See AnyParam for member documentation.
struct OutParam {
  // TODO: Make Parse::NodeId more specific.
  static constexpr auto Kind = InstKind::OutParam.Define<Parse::NodeId>(
      {.ir_name = "out_param",
       // TODO: Consider introducing a separate category for OutParam:
       // unlike other DurableRefs, it permits initialization.
       .expr_category = ExprCategory::DurableRef,
       .constant_kind = InstConstantKind::Never});

  TypeId type_id;
  CallParamIndex index;
  NameId pretty_name_id;
};

// A pattern that represents an output `Call` parameter. See `AnyParamPattern`
// for member documentation.
struct OutParamPattern {
  static constexpr auto Kind = InstKind::OutParamPattern.Define<Parse::NodeId>(
      {.ir_name = "out_param_pattern",
       .expr_category = ExprCategory::Pattern,
       .constant_kind = InstConstantKind::AlwaysUnique,
       .is_lowered = false});

  TypeId type_id;
  InstId subpattern_id;
};

// Indicates `partial` on a type, such as `partial MyClass`.
struct PartialType {
  static constexpr auto Kind =
      InstKind::PartialType.Define<Parse::PrefixOperatorPartialId>(
          {.ir_name = "partial_type",
           .is_type = InstIsType::Always,
           .constant_kind = InstConstantKind::Conditional,
           .deduce_through = true});

  TypeId type_id;
  TypeInstId inner_id;
};

// The type of a pattern that matches scrutinees of type
// `scrutinee_type_inst_id`.
struct PatternType {
  static constexpr auto Kind = InstKind::PatternType.Define<Parse::NoneNodeId>(
      {.ir_name = "pattern_type",
       .is_type = InstIsType::Always,
       .constant_kind = InstConstantKind::Always});

  // Always the builtin type TypeType.
  TypeId type_id;
  TypeInstId scrutinee_type_inst_id;
};

// Modifies a pointee type to be a pointer. This is tracking the `*` in
// `x: i32*`, where `pointee_id` is `i32` and `type_id` is `type`.
struct PointerType {
  static constexpr auto Kind =
      InstKind::PointerType.Define<Parse::PostfixOperatorStarId>(
          {.ir_name = "ptr_type",
           .is_type = InstIsType::Always,
           .constant_kind = InstConstantKind::WheneverPossible,
           .deduce_through = true});

  TypeId type_id;
  TypeInstId pointee_id;
};

// Binds a name as a reference expression, such as `x` in `var x: i32`.
// See AnyBinding for member documentation.
struct RefBinding {
  // TODO: Make Parse::NodeId more specific.
  static constexpr auto Kind = InstKind::RefBinding.Define<Parse::NodeId>(
      {.ir_name = "ref_binding",
       .expr_category = ExprCategory::DurableRef,
       .constant_kind = InstConstantKind::Indirect});

  TypeId type_id;
  EntityNameId entity_name_id;
  InstId value_id;
};

// An action that performs type refinement for an instruction, by creating an
// instruction that converts from a template symbolic type to a concrete type.
struct RefineTypeAction {
  static constexpr auto Kind = InstKind::RefineTypeAction.Define<Parse::NodeId>(
      {.ir_name = "refine_type_action",
       .constant_kind = InstConstantKind::InstAction,
       .is_lowered = false});

  TypeId type_id;
  MetaInstId inst_id;
  TypeInstId inst_type_inst_id;
};

// Represents a reference binding pattern. See `AnyBindingPattern` for member
// documentation.
struct RefBindingPattern {
  static constexpr auto Kind =
      InstKind::RefBindingPattern.Define<Parse::NodeId>(
          {.ir_name = "ref_binding_pattern",
           .expr_category = ExprCategory::Pattern,
           .constant_kind = InstConstantKind::AlwaysUnique,
           .is_lowered = false});

  TypeId type_id;
  EntityNameId entity_name_id;
};

struct RefForm {
  static constexpr auto Kind =
      InstKind::RefForm.Define<Parse::NodeIdOneOf<Parse::PrefixOperatorRefId,
                                                  Parse::RefPrimitiveFormId>>(
          {.ir_name = "ref_form",
           .constant_kind = InstConstantKind::Always,
           .is_lowered = false});

  TypeId type_id;
  TypeInstId type_component_inst_id;
};

// A by-reference `Call` parameter. See AnyParam for member documentation. Note
// that this may correspond to either a RefParamPattern or a VarParamPattern.
struct RefParam {
  // TODO: Make Parse::NodeId more specific.
  static constexpr auto Kind = InstKind::RefParam.Define<Parse::NodeId>(
      {.ir_name = "ref_param",
       .expr_category = ExprCategory::DurableRef,
       .constant_kind = InstConstantKind::Never});

  TypeId type_id;
  CallParamIndex index;
  NameId pretty_name_id;
};

// A pattern that represents a `ref`-qualified `Call` parameter. See
// `AnyParamPattern` for member documentation.
struct RefParamPattern {
  // TODO: Make Parse::NodeId more specific.
  static constexpr auto Kind = InstKind::RefParamPattern.Define<Parse::NodeId>(
      {.ir_name = "ref_param_pattern",
       .expr_category = ExprCategory::Pattern,
       .constant_kind = InstConstantKind::AlwaysUnique,
       .is_lowered = false});

  TypeId type_id;
  InstId subpattern_id;
};

// A `ref x` expression. The semantics of this instruction depend on the usage
// context:
// - As an argument to a `ref` parameter, it evaluates to `x`, but requires
//   `x` to be a durable reference expression.
// - In a return type expression or form literal, it evaluates to a `Core.Form`
//   value representing a reference to `x`, which must be a type.
// - In any other context, it's an error.
//
// See issue #6342 for background.
struct RefTagExpr {
  static constexpr auto Kind =
      InstKind::RefTagExpr.Define<Parse::PrefixOperatorRefId>(
          {.ir_name = "ref_tag",
           .expr_category = ExprCategory::RefTagged,
           .constant_kind = InstConstantKind::Never});

  TypeId type_id;
  InstId expr_id;
};

// Requires a type to be complete. This is only created for generic types and
// produces a witness that the type is complete.
//
// TODO: Eventually this should be replaced by a witness for an interface that
// models type completeness, and should track other information such as the
// value representation.
struct RequireCompleteType {
  static constexpr auto Kind =
      InstKind::RequireCompleteType.Define<Parse::NodeId>(
          {.ir_name = "require_complete_type",
           .constant_kind = InstConstantKind::SymbolicOnly,
           .constant_needs_inst_id =
               InstConstantNeedsInstIdKind::DuringEvaluation,
           .is_lowered = false});
  // Always the builtin `WitnessType` type.
  TypeId type_id;
  // The type that is required to be complete.
  TypeInstId complete_type_inst_id;
};

// A `require` declaration, such as `require Self impls Z`.
struct RequireImplsDecl {
  static constexpr auto Kind =
      InstKind::RequireImplsDecl.Define<Parse::RequireDeclId>(
          {.ir_name = "require_decl",
           .constant_kind = InstConstantKind::AlwaysUnique,
           .is_lowered = false});

  RequireImplsId require_impls_id;
  DeclInstBlockId decl_block_id;
};

// As part of a generic region, requires that an `impl` which is initially part
// of a symbolic specific be defined once a concrete specific is formed.
struct RequireSpecificDefinition {
  static constexpr auto Kind =
      InstKind::RequireSpecificDefinition.Define<Parse::NodeId>(
          {.ir_name = "require_specific_def",
           .constant_kind = InstConstantKind::Conditional,
           .is_lowered = false});
  // Always the builtin `RequireSpecificDefinitionType` type.
  TypeId type_id;
  SemIR::SpecificId specific_id;
};

// A dedicated type for `RequireSpecificDefinition`.
using RequireSpecificDefinitionType =
    SingletonTypeInst<InstKind::RequireSpecificDefinitionType,
                      "require_specific_def_type">;

// A requirement that `.Self` implements a facet type, specified as the first
// operand of a `where` expression. This is always the first requirement in a
// requirement block for a `where` expression.
//
// Any constraints in the base facet type are available to other constraint
// operands in the `where` expression, and also become a part of the resulting
// facet type.
struct RequirementBaseFacetType {
  static constexpr auto Kind =
      InstKind::RequirementBaseFacetType.Define<Parse::NodeId>(
          {.ir_name = "requirement_base_facet_type",
           .constant_kind = InstConstantKind::Never,
           .is_lowered = false});

  // No type since not an expression

  // A FacetType, the TypeType singleton, or an ErrorInst.
  TypeInstId base_type_inst_id;
};

// A requirement that two expressions evaluate to the same constant, as
// specified by an `expr == expr` clause in a `where` expression or `require`
// declaration.
struct RequirementEquivalent {
  static constexpr auto Kind =
      InstKind::RequirementEquivalent.Define<Parse::RequirementEqualEqualId>(
          {.ir_name = "requirement_equivalent",
           .constant_kind = InstConstantKind::Never,
           .is_lowered = false});

  // No type since not an expression
  InstId lhs_id;
  InstId rhs_id;
};

// A requirement that the LHS expression is a facet type that implements the
// interface on the RHS and meets any constraints in the RHS, as specified by an
// `expr impls expr` clause in a `where` expression or `require` declaration.
struct RequirementImpls {
  static constexpr auto Kind =
      InstKind::RequirementImpls.Define<Parse::RequirementImplsId>(
          {.ir_name = "requirement_impls",
           .constant_kind = InstConstantKind::Never,
           .is_lowered = false});

  // No type since not an expression
  InstId lhs_id;
  InstId rhs_id;
};

// A requirement that assigns the expression on the RHS to the associated
// constant named on the LHS, as specified by a `.M = expr` clause in a `where`
// expression or `require` declaration.
struct RequirementRewrite {
  static constexpr auto Kind =
      InstKind::RequirementRewrite.Define<Parse::RequirementEqualId>(
          {.ir_name = "requirement_rewrite",
           .constant_kind = InstConstantKind::Never,
           .is_lowered = false});

  // No type since not an expression
  InstId lhs_id;
  InstId rhs_id;
};

struct Return {
  static constexpr auto Kind = InstKind::Return.Define<Parse::NodeId>(
      {.ir_name = "return",
       .constant_kind = InstConstantKind::Never,
       .terminator_kind = TerminatorKind::Terminator});

  // This is a statement, so has no type.
};

// A `return expr;` statement.
//
// If `expr_id` is an initializer, this consumes it. If `dest_id` is not `None`
// and `expr_id` has a storage argument, the storage argument must be `dest_id`.
struct ReturnExpr {
  static constexpr auto Kind = InstKind::ReturnExpr.Define<Parse::NodeId>(
      {.ir_name = "return",
       .constant_kind = InstConstantKind::Never,
       .terminator_kind = TerminatorKind::Terminator});

  // This is a statement, so has no type.
  InstId expr_id;
  // The return slot, if any. `None` if we're not returning through memory.
  DestInstId dest_id;
};

// The return slot of a function declaration, as exposed in the function body.
// This acts as an output parameter, analogous to `BindName` for input
// parameters.
struct ReturnSlot {
  static constexpr auto Kind = InstKind::ReturnSlot.Define<Parse::NodeId>(
      {.ir_name = "return_slot",
       .expr_category = ExprCategory::DurableRef,
       .constant_kind = InstConstantKind::Never});

  // The type of the value that will be stored in this slot (i.e. the return
  // type of the function).
  TypeId type_id;

  // The function return type as originally written by the user. For diagnostics
  // only; this has no semantic significance, and is not preserved across
  // imports.
  TypeInstId type_inst_id;

  // The storage that will be initialized by the function.
  InstId storage_id;
};

// The return slot of a function declaration, as exposed to the function's
// callers. This acts as an output parameter, analogous to `BindingPattern`
// for input parameters.
struct ReturnSlotPattern {
  static constexpr auto Kind =
      InstKind::ReturnSlotPattern.Define<Parse::NodeId>(
          {.ir_name = "return_slot_pattern",
           .expr_category = ExprCategory::Pattern,
           .constant_kind = InstConstantKind::AlwaysUnique,
           .is_lowered = false});

  // Always a PatternType whose scrutinee type is the return type of the
  // function.
  TypeId type_id;

  // The function return type as originally written by the user. For diagnostics
  // only; this has no semantic significance, and is not preserved across
  // imports.
  TypeInstId type_inst_id;
};

// Given an instruction with a constant value that depends on a generic
// parameter, selects a version of that instruction with the constant value
// corresponding to a particular specific.
struct SpecificConstant {
  // TODO: Can we make Parse::NodeId more specific?
  static constexpr auto Kind = InstKind::SpecificConstant.Define<Parse::NodeId>(
      {.ir_name = "specific_constant",
       .expr_category = ComputedExprCategory::SameAsFirstOperand,
       .is_lowered = false});

  TypeId type_id;
  AbsoluteInstId inst_id;
  SpecificId specific_id;
};

// A specific instance of a generic function. This represents the callee in a
// call instruction that is calling a generic function, where the specific
// arguments of the function have been deduced.
//
// TODO: This value corresponds to the `(FunctionType as Call(...)).Op` function
// in the overloaded calls design. Eventually we should represent it more
// directly as a member of the `Call` interface.
struct SpecificFunction {
  static constexpr auto Kind = InstKind::SpecificFunction.Define<Parse::NodeId>(
      {.ir_name = "specific_function",
       .constant_kind = InstConstantKind::Conditional,
       // InstId is added to definitions_required_by_use.
       .constant_needs_inst_id = InstConstantNeedsInstIdKind::Permanent});

  // Always the builtin SpecificFunctionType.
  TypeId type_id;
  // The expression denoting the callee.
  InstId callee_id;
  // The specific instance of the generic callee that will be called, including
  // all the compile-time arguments.
  SpecificId specific_id;
};

// The type of specific functions.
//
// This is a singleton instruction. However, it may still evolve into a more
// standard type and be removed.
using SpecificFunctionType =
    SingletonTypeInst<InstKind::SpecificFunctionType, "<specific function>">;

// A specific instance of a function from an impl, named as the function from
// the interface.
//
// This value is the callee in a call of the form `(T as Interface).F()`. The
// specific that we determine for such a call is a specific for `Interface.F`,
// but what we need is a specific for the function in the `impl`. This
// instruction computes that specific function.
struct SpecificImplFunction {
  static constexpr auto Kind =
      InstKind::SpecificImplFunction.Define<Parse::NodeId>(
          {.ir_name = "specific_impl_function",
           .constant_kind = InstConstantKind::SymbolicOnly,
           // InstId is added to definitions_required_by_use.
           .constant_needs_inst_id = InstConstantNeedsInstIdKind::Permanent});

  // Always the builtin SpecificFunctionType.
  TypeId type_id;
  // The expression denoting the callee. This will be a function from an impl
  // witness.
  InstId callee_id;
  // The specific instance of the interface function that was called, including
  // all the compile-time arguments.
  SpecificId specific_id;
};

// Splices a block into the location where this appears. This may be an
// expression, producing a result with a given type. For example, when
// constructing from aggregates we may figure out which conversions are required
// late, and splice parts together.
struct SpliceBlock {
  static constexpr auto Kind = InstKind::SpliceBlock.Define<Parse::NodeId>(
      {.ir_name = "splice_block",
       .expr_category = ComputedExprCategory::SameAsSecondOperand});

  TypeId type_id;
  AbsoluteInstBlockId block_id;
  InstId result_id;
};

// Splices an instruction computed by an action into the location where this
// appears.
struct SpliceInst {
  static constexpr auto Kind = InstKind::SpliceInst.Define<Parse::NodeId>(
      {.ir_name = "splice_inst",
       // TODO: The expression category is in general dependent on
       // instantiation. Use ExprCategory::Dependent to model this.
       .expr_category = ExprCategory::Value});

  TypeId type_id;
  // The instruction that computes the instruction to splice. The type of this
  // instruction should be InstType. If evaluation has succeeded, this will be
  // an InstValue.
  InstId inst_id;
};

// A literal string value.
struct StringLiteral {
  static constexpr auto Kind =
      InstKind::StringLiteral.Define<Parse::StringLiteralId>(
          {.ir_name = "string_literal",
           .constant_kind = InstConstantKind::Always});

  TypeId type_id;
  StringLiteralValueId string_literal_id;
};

// Access to a struct type, with the index into the struct_id representation.
struct StructAccess {
  // TODO: Make Parse::NodeId more specific.
  static constexpr auto Kind = InstKind::StructAccess.Define<Parse::NodeId>(
      {.ir_name = "struct_access",
       .expr_category = ComputedExprCategory::SameAsFirstOperand,
       .is_type = InstIsType::Maybe,
       .constant_kind = InstConstantKind::SymbolicOrReference});

  TypeId type_id;
  InstId struct_id;
  ElementIndex index;
};

// Initializes a dest struct with the provided elements.
struct StructInit {
  static constexpr auto Kind = InstKind::StructInit.Define<Parse::NodeId>(
      {.ir_name = "struct_init",
       .expr_category = ExprCategory::ReprInitializing});

  TypeId type_id;
  InstBlockId elements_id;
  DestInstId dest_id;
};

// A literal struct value, such as `{.a = 1, .b = 2}`.
struct StructLiteral {
  static constexpr auto Kind = InstKind::StructLiteral.Define<
      Parse::NodeIdOneOf<Parse::ChoiceAlternativeListCommaId,
                         Parse::ChoiceDefinitionId, Parse::StructLiteralId>>(
      {.ir_name = "struct_literal",
       .expr_category = ExprCategory::Mixed,
       .constant_kind = InstConstantKind::Indirect});

  TypeId type_id;
  InstBlockId elements_id;
};

// The type of a struct.
struct StructType {
  static constexpr auto Kind = InstKind::StructType.Define<
      Parse::NodeIdOneOf<Parse::StructTypeLiteralId, Parse::ClassDefinitionId>>(
      {.ir_name = "struct_type",
       .is_type = InstIsType::Always,
       .constant_kind = InstConstantKind::WheneverPossible,
       .deduce_through = true});

  TypeId type_id;
  StructTypeFieldsId fields_id;
};

// A struct value.
struct StructValue {
  static constexpr auto Kind = InstKind::StructValue.Define<Parse::NodeId>(
      {.ir_name = "struct_value",
       .constant_kind = InstConstantKind::WheneverPossible});

  TypeId type_id;
  InstBlockId elements_id;
};

// Binds a symbolic name, such as `x` in `let x:! i32 = 7;`. See AnyBinding for
// member documentation.
struct SymbolicBinding {
  static constexpr auto Kind = InstKind::SymbolicBinding.Define<Parse::NodeId>(
      {.ir_name = "symbolic_binding",
       .is_type = InstIsType::Maybe,
       .constant_kind = InstConstantKind::SymbolicOnly});

  TypeId type_id;
  EntityNameId entity_name_id;
  InstId value_id;
};

// Represents a symbolic binding pattern. See `AnyBindingPattern` for member
// documentation.
struct SymbolicBindingPattern {
  static constexpr auto Kind =
      InstKind::SymbolicBindingPattern.Define<Parse::NodeId>({
          .ir_name = "symbolic_binding_pattern",
          .expr_category = ExprCategory::Pattern,
          .constant_kind = InstConstantKind::AlwaysUnique,
          .is_lowered = false,
      });

  TypeId type_id;
  EntityNameId entity_name_id;
};

// The constant value of a FacetAccessType for a symbolic facet value.
struct SymbolicBindingType {
  static constexpr auto Kind =
      InstKind::SymbolicBindingType.Define<Parse::NodeId>(
          {.ir_name = "symbolic_binding_type",
           .is_type = InstIsType::Always,
           .constant_kind = InstConstantKind::SymbolicOnly});

  // Always the builtin type TypeType.
  TypeId type_id;
  // The symbolic facet value binding for which this instruction accesses the
  // concrete type once it is known for the symbolic value.
  EntityNameId entity_name_id;
  // TODO: Remove this, and find it through a lookup on ScopeStack.
  InstId facet_value_inst_id;
};

// Consumes the initializer `init_id`, uses it to initialize a temporary
// object, and forms an ephemeral reference to it. If `init_id` has a
// storage arg, it must be a `TemporaryStorage` inst.
struct Temporary {
  static constexpr auto Kind = InstKind::Temporary.Define<Parse::NodeId>(
      {.ir_name = "temporary",
       .expr_category = ExprCategory::EphemeralRef,
       .has_cleanup = true});

  TypeId type_id;
  InstId storage_id;
  InstId init_id;
};

// Storage for a temporary value.
struct TemporaryStorage {
  // The cleanup is owned by the `Temporary` instruction, so has_cleanup is set
  // to `false` here.
  static constexpr auto Kind = InstKind::TemporaryStorage.Define<Parse::NodeId>(
      {.ir_name = "temporary_storage",
       .expr_category = ExprCategory::EphemeralRef,
       .constant_kind = InstConstantKind::Never});

  TypeId type_id;
};

// Access to a tuple member.
struct TupleAccess {
  // TODO: Make Parse::NodeId more specific.
  static constexpr auto Kind = InstKind::TupleAccess.Define<Parse::NodeId>(
      {.ir_name = "tuple_access",
       .expr_category = ComputedExprCategory::SameAsFirstOperand,
       .is_type = InstIsType::Maybe,
       .constant_kind = InstConstantKind::SymbolicOrReference});

  TypeId type_id;
  InstId tuple_id;
  ElementIndex index;
};

// Initializes the destination tuple with the given elements.
struct TupleInit {
  static constexpr auto Kind = InstKind::TupleInit.Define<Parse::NodeId>(
      {.ir_name = "tuple_init",
       .expr_category = ExprCategory::ReprInitializing});

  TypeId type_id;
  InstBlockId elements_id;
  DestInstId dest_id;
};

// A literal tuple value.
struct TupleLiteral {
  static constexpr auto Kind = InstKind::TupleLiteral.Define<
      Parse::NodeIdOneOf<Parse::ChoiceAlternativeListCommaId,
                         Parse::ChoiceDefinitionId, Parse::TupleLiteralId>>(
      {.ir_name = "tuple_literal",
       .expr_category = ExprCategory::Mixed,
       .constant_kind = InstConstantKind::Indirect});

  TypeId type_id;
  InstBlockId elements_id;
};

// A tuple pattern, such as `(x, y: i32)`.
struct TuplePattern {
  static constexpr auto Kind =
      InstKind::TuplePattern.Define<Parse::TuplePatternId>(
          {.ir_name = "tuple_pattern",
           .expr_category = ExprCategory::Pattern,
           .constant_kind = InstConstantKind::AlwaysUnique,
           .is_lowered = false});

  // Always a PatternType whose scrutinee type is a tuple of the scrutinee
  // types of the elements.
  TypeId type_id;
  InstBlockId elements_id;
};

// The type of a tuple.
struct TupleType {
  static constexpr auto Kind = InstKind::TupleType.Define<Parse::NoneNodeId>(
      {.ir_name = "tuple_type",
       .is_type = InstIsType::Always,
       .constant_kind = InstConstantKind::WheneverPossible,
       .deduce_through = true});

  TypeId type_id;
  InstBlockId type_elements_id;
};

// A tuple value.
struct TupleValue {
  static constexpr auto Kind = InstKind::TupleValue.Define<Parse::NodeId>(
      {.ir_name = "tuple_value",
       .constant_kind = InstConstantKind::WheneverPossible,
       .deduce_through = true});

  TypeId type_id;
  InstBlockId elements_id;
};

// Extracts the type component of `form_inst_id`, which must have type
// `Core.Form`.
struct TypeComponentOf {
  static constexpr auto Kind =
      InstKind::TypeComponentOf.Define<Parse::NoneNodeId>(
          {.ir_name = "type_component_of",
           .is_type = InstIsType::Always,
           .constant_kind = InstConstantKind::SymbolicOnly,
           .is_lowered = false});

  // Always TypeType.
  TypeId type_id;
  InstId form_inst_id;
};

// A type literal, such as `bool` or `type` or `i32`. The constant value of this
// instruction will be the type value that the type literal evaluates to, which
// is typically either a builtin type or a class defined in the prelude.
struct TypeLiteral {
  static constexpr auto Kind = InstKind::TypeLiteral.Define<Parse::NodeId>(
      {.ir_name = "type_literal", .expr_category = ExprCategory::Value});

  // Always the builtin type TypeType.
  TypeId type_id;
  // The type value that the type literal evaluates to.
  TypeInstId value_id;
};

// Returns the type of the instruction produced by an action. For example, given
//
//   %inst: <instruction> = some_action
//
// the instruction `type_of_inst %inst` evaluates to the type of the instruction
// that the action generates.
struct TypeOfInst {
  static constexpr auto Kind = InstKind::TypeOfInst.Define<Parse::NodeId>(
      {.ir_name = "type_of_inst",
       .is_type = InstIsType::Always,
       .constant_kind = InstConstantKind::SymbolicOnly});

  TypeId type_id;
  // The instruction that computes the instruction whose type is returned. The
  // type of this instruction should be InstType.
  InstId inst_id;
};

// Tracks expressions which are valid as types. This has a deliberately
// self-referential type.
struct TypeType : public SingletonTypeInst<InstKind::TypeType, "type"> {
  // `TypeType` is always set complete in file.cpp.
  static constexpr auto TypeId =
      TypeId::ForTypeConstant(ConstantId::ForConcreteConstant(TypeInstId));
};

// The `not` operator, such as `not operand`.
struct UnaryOperatorNot {
  // TODO: Make Parse::NodeId more specific.
  static constexpr auto Kind =
      InstKind::UnaryOperatorNot.Define<Parse::NodeId>({.ir_name = "not"});

  TypeId type_id;
  InstId operand_id;
};

// The type of an expression naming an unbound element of a class, such as
// `Class.field`. This can be used as the operand of a compound member access
// expression, such as `instance.(Class.field)`.
struct UnboundElementType {
  static constexpr auto Kind = InstKind::UnboundElementType.Define<
      Parse::NodeIdOneOf<Parse::BaseDeclId, Parse::VarBindingPatternId>>(
      {.ir_name = "unbound_element_type",
       .is_type = InstIsType::Always,
       .constant_kind = InstConstantKind::WheneverPossible});

  TypeId type_id;
  // The `ClassType` that a value of this type is an element of.
  TypeInstId class_type_inst_id;
  // The type of the element.
  TypeInstId element_type_inst_id;
};

// An uninitialized constant value.
struct UninitializedValue {
  static constexpr auto Kind =
      InstKind::UninitializedValue.Define<Parse::NodeId>(
          {.ir_name = "uninitialized_value",
           .constant_kind = InstConstantKind::Always});

  TypeId type_id;
};

// Initializes an object by performing a base initialization followed by an
// update step.
struct UpdateInit {
  static constexpr auto Kind = InstKind::UpdateInit.Define<Parse::NodeId>(
      {.ir_name = "update_init",
       .expr_category = ExprCategory::InPlaceInitializing});

  TypeId type_id;
  // The base initializer. Always an in-place initializer.
  InstId base_init_id;
  // The update expression. Always an in-place initializer.
  InstId update_init_id;
};

// Converts from a value expression to an ephemeral reference expression, in
// the case where the value representation of the type is a pointer. For
// example, when indexing a value expression of array type, this is used to
// form a reference to the array object.
struct ValueAsRef {
  static constexpr auto Kind = InstKind::ValueAsRef.Define<Parse::NodeId>(
      {.ir_name = "value_as_ref",
       .expr_category = ExprCategory::EphemeralRef,
       .constant_kind = InstConstantKind::Never});

  TypeId type_id;
  InstId value_id;
};

// Binds a name as a value expression, such as `x` in `let x: i32`. See
// AnyBinding for member documentation.
struct ValueBinding {
  // TODO: Make Parse::NodeId more specific.
  static constexpr auto Kind = InstKind::ValueBinding.Define<Parse::NodeId>(
      {.ir_name = "value_binding",
       .constant_kind = InstConstantKind::Indirect});

  TypeId type_id;
  EntityNameId entity_name_id;
  InstId value_id;
};

// Represents a value binding pattern. See `AnyBindingPattern` for member
// documentation.
struct ValueBindingPattern {
  static constexpr auto Kind =
      InstKind::ValueBindingPattern.Define<Parse::NodeId>(
          {.ir_name = "value_binding_pattern",
           .expr_category = ExprCategory::Pattern,
           .constant_kind = InstConstantKind::AlwaysUnique,
           .is_lowered = false});

  TypeId type_id;
  EntityNameId entity_name_id;
};

// A primitive value form.
struct ValueForm {
  static constexpr auto Kind =
      InstKind::ValueForm.Define<Parse::ValPrimitiveFormId>(
          {.ir_name = "value_form",
           .constant_kind = InstConstantKind::Always,
           .is_lowered = false});

  TypeId type_id;
  TypeInstId type_component_inst_id;
};

// Converts an initializing expression to a value expression, in the case
// where the initializing representation is the same as the value
// representation.
struct ValueOfInitializer {
  // TODO: Make Parse::NodeId more specific.
  static constexpr auto Kind =
      InstKind::ValueOfInitializer.Define<Parse::NodeId>(
          {.ir_name = "value_of_initializer"});

  TypeId type_id;
  InstId init_id;
};

// A by-value `Call` parameter. See AnyParam for member documentation.
struct ValueParam {
  // TODO: Make Parse::NodeId more specific.
  static constexpr auto Kind = InstKind::ValueParam.Define<Parse::NodeId>(
      {.ir_name = "value_param", .constant_kind = InstConstantKind::Never});

  TypeId type_id;
  CallParamIndex index;
  NameId pretty_name_id;
};

// A pattern that represents a by-value `Call` parameter. See `AnyParamPattern`
// for member documentation.
struct ValueParamPattern {
  // TODO: Make Parse::NodeId more specific.
  static constexpr auto Kind =
      InstKind::ValueParamPattern.Define<Parse::NodeId>(
          {.ir_name = "value_param_pattern",
           .expr_category = ExprCategory::Pattern,
           .constant_kind = InstConstantKind::AlwaysUnique,
           .is_lowered = false});

  TypeId type_id;
  InstId subpattern_id;
};

// A pattern that represents a `Call` parameter corresponding to a `var`
// pattern. See `AnyParamPattern` for member documentation. Note that there is
// no `VarParam` -- a `VarParamPattern` corresponds to a `RefParam`.
struct VarParamPattern {
  static constexpr auto Kind =
      InstKind::VarParamPattern.Define<Parse::VariablePatternId>(
          {.ir_name = "var_param_pattern",
           .expr_category = ExprCategory::Pattern,
           .constant_kind = InstConstantKind::AlwaysUnique,
           .is_lowered = false});

  TypeId type_id;
  InstId subpattern_id;
};

// A `var` pattern.
struct VarPattern {
  static constexpr auto Kind =
      InstKind::VarPattern.Define<Parse::VariablePatternId>(
          {.ir_name = "var_pattern",
           .expr_category = ExprCategory::Pattern,
           .constant_kind = InstConstantKind::AlwaysUnique,
           .is_lowered = false});

  // Always a PatternType that represents the same type as the type of
  // `subpattern_id`.
  TypeId type_id;
  InstId subpattern_id;
};

// Tracks storage for a `var` pattern.
struct VarStorage {
  // TODO: Make Parse::NodeId more specific.
  static constexpr auto Kind = InstKind::VarStorage.Define<Parse::NodeId>(
      {.ir_name = "var",
       .expr_category = ExprCategory::DurableRef,
       .constant_kind = InstConstantKind::ConditionalUnique,
       .constant_needs_inst_id = InstConstantNeedsInstIdKind::Permanent,
       .has_cleanup = true});

  TypeId type_id;

  // If this storage was created for a `var` pattern, the pattern. Otherwise,
  // such as the implicit storage in `for`, this is `None`.
  AbsoluteInstId pattern_id;
};

// The type of virtual function tables.
//
// This is a singleton instruction. However, it may still evolve into a more
// standard type and be removed.
using VtableType = SingletonTypeInst<InstKind::VtableType, "<vtable>">;

// Initializer for virtual function table pointers in object initialization.
struct VtablePtr {
  static constexpr auto Kind = InstKind::VtablePtr.Define<Parse::NodeId>(
      {.ir_name = "vtable_ptr",
       .expr_category = ExprCategory::EphemeralRef,
       .constant_kind = InstConstantKind::Always});
  TypeId type_id;
  VtableId vtable_id;
  SpecificId specific_id;
};

struct VtableDecl {
  static constexpr auto Kind = InstKind::VtableDecl.Define<Parse::NodeId>(
      {.ir_name = "vtable_decl",
       .expr_category = ExprCategory::EphemeralRef,
       .constant_kind = InstConstantKind::Always,
       .is_lowered = false});
  TypeId type_id;
  VtableId vtable_id;
};

// An `expr where requirements` expression.
struct WhereExpr {
  static constexpr auto Kind = InstKind::WhereExpr.Define<Parse::WhereExprId>(
      {.ir_name = "where_expr",
       .is_type = InstIsType::Always,
       .constant_kind = InstConstantKind::Conditional});

  TypeId type_id;
  // This is the `.Self` symbolic binding. Its type matches the left type
  // argument of the `where`.
  InstId period_self_id;
  InstBlockId requirements_id;
};

// The type of `ImplWitness`, `CustomWitness`, and `LookupImplWitness`
// instructions. The latter will evaluate at some point during specific
// computation into one of the former two, and their types should not change in
// the process.
//
// Also the type of `RequireCompleteType` instructions.
//
// This is a singleton instruction. However, it may still evolve into a more
// standard type and be removed.
using WitnessType = SingletonTypeInst<InstKind::WitnessType, "<witness>">;

// These concepts are an implementation detail of the library, not public API.
namespace Internal {

// HasNodeId is true if T has an associated parse node.
template <typename T>
concept HasNodeId =
    !std::same_as<typename decltype(T::Kind)::TypedNodeId, Parse::NoneNodeId>;

// HasUntypedNodeId is true if T has an associated parse node which can be any
// kind of node.
template <typename T>
concept HasUntypedNodeId =
    std::same_as<typename decltype(T::Kind)::TypedNodeId, Parse::NodeId>;

// HasKindMemberAsField<T> is true if T has a `InstKind kind` field, as opposed
// to a `static constexpr InstKind::Definition Kind` member or no kind at all.
template <typename T>
concept HasKindMemberAsField = std::same_as<decltype(T::kind), InstKind>;

// HasTypeIdMember<T> is true if T has a `TypeId type_id` field.
template <typename T>
concept HasTypeIdMember = std::same_as<decltype(T::type_id), TypeId>;

}  // namespace Internal

}  // namespace Carbon::SemIR

#endif  // CARBON_TOOLCHAIN_SEM_IR_TYPED_INSTS_H_
