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

Code before:
    }
    auto unbound_element_type =
        context.sem_ir().types().GetAs<SemIR::UnboundElementType>(
            field_decl.type_id);
    struct_type_fields.push_back(
        {.name_id = field_decl.name_id,
         .type_id = unbound_element_type.element_type_id});
  }
  auto fields_id =
      context.struct_type_fields().AddCanonical(struct_type_fields);
  context.field_decls_stack().PopArray();
  return fields_id;
}

// Checks that the specified finished class definition is valid and builds and
// returns a corresponding complete type witness instruction.
static auto CheckCompleteClassType(Context& context, Parse::NodeId node_id,
                                   SemIR::ClassId class_id) -> SemIR::InstId {
  auto& class_info = context.classes().Get(class_id);
  if (class_info.adapt_id.is_valid()) {
    llvm::SmallVector<SemIR::StructTypeField> struct_type_fields;
    struct_type_fields.reserve(context.field_decls_stack().PeekArray().size());

    return CheckCompleteAdapterClassType(
        context, node_id, class_id,
        AddStructTypeFields(context, struct_type_fields));
  }

  bool defining_vptr = class_info.is_dynamic;
  if (class_info.base_id.is_valid()) {
    auto base_info = context.insts().GetAs<SemIR::BaseDecl>(class_info.base_id);
    // TODO: If the base class is template dependent, we will need to decide
    // whether to add a vptr as part of instantiation.
    if (auto* base_class_info = TryGetAsClass(context, base_info.base_type_id);
        base_class_info && base_class_info->is_dynamic) {
      defining_vptr = false;
    }
  }

  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);

Review comment (suggestion):
We're going to a bit of effort to build a collection of fields for an adapter here (and adapters can't have fields). I wonder if we could sink this field handling code down into `CheckCompleteAdapterClassType` and have it just look at and diagnose the first element in the top of `field_decls_stack` if it's non-empty (and in any case pop it).

Code after:
  auto fields_id =
      context.struct_type_fields().AddCanonical(struct_type_fields);
  return fields_id;
}

// Checks that the specified finished class definition is valid and builds and
// returns a corresponding complete type witness instruction.
static auto CheckCompleteClassType(Context& context, Parse::NodeId node_id,
                                   SemIR::ClassId class_id) -> SemIR::InstId {
  auto& class_info = context.classes().Get(class_id);
  if (class_info.adapt_id.is_valid()) {
    return CheckCompleteAdapterClassType(context, node_id, class_id);
  }

  bool defining_vptr = class_info.is_dynamic;
  if (class_info.base_id.is_valid()) {
    auto base_info = context.insts().GetAs<SemIR::BaseDecl>(class_info.base_id);
    // TODO: If the base class is template dependent, we will need to decide
    // whether to add a vptr as part of instantiation.
    if (auto* base_class_info = TryGetAsClass(context, base_info.base_type_id);
        base_class_info && base_class_info->is_dynamic) {
      defining_vptr = false;
    }
  }

  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(