Repository: carbon-language/carbon-lang
File: toolchain/sem_ir/typed_insts.h (C/C++)

Code before:
// 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>
struct SingletonTypeInst {
  static constexpr auto Kind = InstKind::Make(KindT).Define<Parse::NoneNodeId>(
      InstKind::DefinitionInfo{.ir_name = IrName,
                               .is_type = InstIsType::Always,
                               .constant_kind = InstConstantKind::Always});
  static constexpr auto TypeInstId = MakeSingletonTypeInstId<Kind>();
  static constexpr SemIR::InstId InstId = TypeInstId;
  static constexpr auto ConstantId = ConstantId::ForConcreteConstant(InstId);

  // 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",

Review comment (suggestion):
If the `TypeInstId` isn't meant to be used directly for most insts, then that would mean the `InstId` and `ConstantId` are not meant to be used directly either. Maybe they shouldn't exist for the instructions that `TypeId` does not exist?

We could do this without inheritance still by making a template parameter that the `type_needs_completion` and in the struct specialization of `false` we provide the `TypeId` (and the `InstId` and `ConstantId`?) WDYT of that?

I wonder if the constant should be IncompleteTypeInstId in the insts that require completion, so it's more clear it's not to be used as such. Which is then a bigger pain to write, so maybe it should also be GetSingletonType<SemIR::InstType>(context) to avoid spelling it out/using the incomplete thing at all.

Code after:
// 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>
struct SingletonTypeInst {
  static constexpr auto Kind = InstKind::Make(KindT).Define<Parse::NoneNodeId>(
      InstKind::DefinitionInfo{.ir_name = IrName,
                               .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});