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

Code before:
      {.type_id = context.GetBuiltinType(SemIR::BuiltinInstKind::WitnessType),
       .object_repr_id = adapted_type_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::InstBlockId fields_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, fields_id);
  }

  bool defining_vtable_ptr = class_info.is_dynamic;
  if (class_info.base_id.is_valid()) {
    auto base_info = context.insts().GetAs<SemIR::BaseDecl>(class_info.base_id);
    if (auto* base_class_info = TryGetAsClass(context, base_info.base_type_id);
        base_class_info && base_class_info->is_dynamic) {
      defining_vtable_ptr = false;
    }
  }

  llvm::SmallVector<SemIR::InstId> object_rep_fields;
  auto fields = context.inst_blocks().Get(fields_id);
  if (defining_vtable_ptr) {
    if (!fields.empty()) {
      auto field =
          context.insts().GetAs<SemIR::StructTypeField>(fields.front());
      object_rep_fields.push_back(
          context.AddInstInNoBlock<SemIR::StructTypeField>(
              Parse::NodeId::Invalid,
              {.name_id = SemIR::NameId::Vptr,
               .field_type_id = context.GetPointerType(field.field_type_id)}));
    }
  }

  object_rep_fields.append(fields.begin(), fields.end());

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

auto HandleParseNode(Context& context, Parse::ClassDefinitionId node_id)
    -> bool {
  auto fields_id = context.args_type_info_stack().Pop();

Review comment (refactor):
In the caller of this function, we call `context.args_type_info_stack().Pop();` which will be the point at which the `InstBlock` is allocated. In the case where we define a vtable pointer, we never use that `InstBlock` for anything. I wonder if with some refactoring we could avoid allocating it at all? (Maybe, move the pop from the caller into this function, and add a mechanism to `InstBlockStack` to allow the vptr to be inserted in the right place before we build the block.)

Code after:
       .object_repr_id = adapted_type_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()) {
    auto fields_id = context.args_type_info_stack().Pop();

    return CheckCompleteAdapterClassType(context, node_id, class_id, fields_id);
  }

  bool defining_vtable_ptr = 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_vtable_ptr = false;
    }
  }

  if (defining_vtable_ptr) {
    context.args_type_info_stack().AddFrontInstId(
        context.AddInstInNoBlock<SemIR::StructTypeField>(
            Parse::NodeId::Invalid,
            {.name_id = SemIR::NameId::Vptr,
             .field_type_id = context.GetPointerType(
                 context.GetBuiltinType(SemIR::BuiltinInstKind::VtableType))}));
  }

  auto fields_id = context.args_type_info_stack().Pop();

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