Repository: carbon-language/carbon-lang
File: toolchain/check/handle_class.cpp (C++)

Code before:
  }

  auto fields_id = context.struct_type_fields().AddCanonical(
      context.struct_type_fields_stack().PeekArray());
  context.struct_type_fields_stack().PopArray();

  return context.AddInst<SemIR::CompleteTypeWitness>(
      node_id,
      {.type_id = context.GetBuiltinType(SemIR::BuiltinInstKind::WitnessType),
       .object_repr_id = context.GetStructType(fields_id)});
}

auto HandleParseNode(Context& context, Parse::ClassDefinitionId node_id)
    -> bool {
  auto class_id =
      context.node_stack().Pop<Parse::NodeKind::ClassDefinitionStart>();

  // The class type is now fully defined. Compute its object representation.
  auto complete_type_witness_id =
      CheckCompleteClassType(context, node_id, class_id);
  auto& class_info = context.classes().Get(class_id);
  class_info.complete_type_witness_id = complete_type_witness_id;

  int32_t field_index = class_info.is_dynamic;

  // This is pretty subtle code, not something we'd want to endorse in general -
  // lazily updating the indexes to account for whether this class needs a
  // virtual table pointer - which can only be determined after examining all
  // the member functions to see if any are virtual. (perhaps we could consider
  // more explicit Carbon syntax to opt-in to having a vptr up-front/at the
  // start of the class definition, at least before any base and field decls are
  // encountered)
  for (auto inst_id :
       context.inst_blocks().Get(context.inst_block_stack().Pop())) {
    if (auto field_decl = context.insts().TryGetAs<SemIR::FieldDecl>(inst_id)) {
      field_decl->index = SemIR::ElementIndex(field_index++);
      context.sem_ir().insts().Set(inst_id, *field_decl);
    } else if (auto base_decl =
                   context.insts().TryGetAs<SemIR::BaseDecl>(inst_id)) {
      // `extends base` comes before fields, so we don't have to worry about
      // going back to fix any fields up if we find that the base class is
      // dynamic and so already has the vptr in it (so this class, the derived
      // class, doesn't need to add a vptr).
      if (auto* base_class_info =
              TryGetAsClass(context, base_decl->base_type_id)) {
        if (base_class_info->is_dynamic) {
          field_index = 0;
        }
      }
      base_decl->index = SemIR::ElementIndex(field_index++);
      context.sem_ir().insts().Set(inst_id, *base_decl);

Review comment (suggestion):
I think if we go in this direction, it might make sense to build a list of `InstId`s of the `FieldDecl`s as we process the class, instead of building a list of `StructTypeField`s. Then in `CheckCompleteClassType` we can build the `StructTypeField`s for the `FieldDecl`s and assign indexes to them in the same pass.

Code after:
  }

  auto field_decls = context.field_decls_stack().PeekArray();
  llvm::SmallVector<SemIR::StructTypeField> struct_type_fields;
  struct_type_fields.reserve(defining_vptr + class_info.base_id.is_valid() +
                             field_decls.size());
  if (defining_vptr) {
    struct_type_fields.push_back(
        {.name_id = SemIR::NameId::Vptr,
         .type_id = context.GetPointerType(
             context.GetBuiltinType(SemIR::BuiltinInstKind::VtableType))});
  }
  if (class_info.base_id.is_valid()) {
    auto base_decl = context.insts().GetAs<SemIR::BaseDecl>(class_info.base_id);
    base_decl.index =
        SemIR::ElementIndex{static_cast<int>(struct_type_fields.size())};
    context.ReplaceInstPreservingConstantValue(class_info.base_id, base_decl);
    struct_type_fields.push_back(
        {.name_id = SemIR::NameId::Base,
         .type_id = context.insts()
                        .GetAs<SemIR::BaseDecl>(class_info.base_id)
                        .base_type_id});
  }

  return context.AddInst<SemIR::CompleteTypeWitness>(
      node_id,
      {.type_id = context.GetBuiltinType(SemIR::BuiltinInstKind::WitnessType),
       .object_repr_id = context.GetStructType(
           AddStructTypeFields(context, struct_type_fields))});
}

auto HandleParseNode(Context& context, Parse::ClassDefinitionId node_id)
    -> bool {
  auto class_id =
      context.node_stack().Pop<Parse::NodeKind::ClassDefinitionStart>();

  // The class type is now fully defined. Compute its object representation.
  auto complete_type_witness_id =
      CheckCompleteClassType(context, node_id, class_id);
  auto& class_info = context.classes().Get(class_id);
  class_info.complete_type_witness_id = complete_type_witness_id;

  context.inst_block_stack().Pop();
  context.field_decls_stack().PopArray();

  FinishGenericDefinition(context, class_info.generic_id);

  // The decl_name_stack and scopes are popped by `ProcessNodeIds`.
  return true;
}