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

Code before:
  // TODO: The object representation of `const T` should also be the object
  // representation of `T`.
  auto adapted_type_id = context.insts()
                             .GetAs<SemIR::AdaptDecl>(class_info.adapt_id)
                             .adapted_type_id;
  if (auto adapted_class =
          context.types().TryGetAs<SemIR::ClassType>(adapted_type_id)) {
    auto& adapted_class_info = context.classes().Get(adapted_class->class_id);
    if (adapted_class_info.adapt_id.is_valid()) {
      return adapted_class_info.complete_type_witness_id;
    }
  }

  return context.AddInst<SemIR::CompleteTypeWitness>(
      node_id,
      {.type_id = context.GetBuiltinType(SemIR::BuiltinInstKind::WitnessType),
       .object_repr_id = adapted_type_id});
}
static auto AddStructTypeFields(
    Context& context,
    llvm::SmallVector<SemIR::StructTypeField>& struct_type_fields) {
  for (auto field_decl_id : context.field_decls_stack().PeekArray()) {
    auto field_decl = context.insts().GetAs<SemIR::FieldDecl>(field_decl_id);
    field_decl.index =
        SemIR::ElementIndex{static_cast<int>(struct_type_fields.size())};
    context.sem_ir().insts().Set(field_decl_id, field_decl);
    if (field_decl.type_id == SemIR::TypeId::Error) {
      struct_type_fields.push_back(
          {.name_id = field_decl.name_id, .type_id = SemIR::TypeId::Error});
      continue;
    }
    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;

Review comment (bug):
Rather than directly setting the instruction, I think it'd be good to add something like `auto Check::Context::ReplaceInstPreservingConstantValue(SemIR::InstId inst_id, SemIR::Inst inst) -> void`, analogous to the existing `ReplaceInstBeforeConstantUse`. For extra safety, maybe we could make it recompute the constant value after the replacement and CHECK-fail if it's different.

Code after:
      return adapted_class_info.complete_type_witness_id;
    }
  }

  return context.AddInst<SemIR::CompleteTypeWitness>(
      node_id,
      {.type_id = context.GetBuiltinType(SemIR::BuiltinInstKind::WitnessType),
       .object_repr_id = adapted_type_id});
}
static auto AddStructTypeFields(
    Context& context,
    llvm::SmallVector<SemIR::StructTypeField>& struct_type_fields)
    -> SemIR::StructTypeFieldsId {
  for (auto field_decl_id : context.field_decls_stack().PeekArray()) {
    auto field_decl = context.insts().GetAs<SemIR::FieldDecl>(field_decl_id);
    field_decl.index =
        SemIR::ElementIndex{static_cast<int>(struct_type_fields.size())};
    context.ReplaceInstPreservingConstantValue(field_decl_id, field_decl);
    if (field_decl.type_id == SemIR::TypeId::Error) {
      struct_type_fields.push_back(
          {.name_id = field_decl.name_id, .type_id = SemIR::TypeId::Error});
      continue;
    }
    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);
  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) {