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

Code before:
      auto base_vtable_id = context.constant_values().GetConstantInstId(
          base_class_info->vtable_id);
      auto base_vtable_inst_block =
          context.inst_blocks().Get(context.insts()
                                        .GetAs<SemIR::Vtable>(base_vtable_id)
                                        .virtual_functions_id);
      // TODO: Avoid quadratic search. Perhaps build a map from `NameId` to the
      // elements of the top of `vtable_stack`.
      for (auto fn_decl_id : base_vtable_inst_block) {
        auto fn_decl = GetCalleeFunction(context.sem_ir(), fn_decl_id);
        const auto& fn = context.functions().Get(fn_decl.function_id);
        for (auto override_fn_decl_id :
             context.vtable_stack().PeekCurrentBlockContents()) {
          auto override_fn_decl =
              context.insts().GetAs<SemIR::FunctionDecl>(override_fn_decl_id);
          const auto& override_fn =
              context.functions().Get(override_fn_decl.function_id);
          if (override_fn.virtual_modifier ==
                  SemIR::FunctionFields::VirtualModifier::Impl &&
              override_fn.name_id == fn.name_id) {
            // TODO: Support generic base classes, rather than passing
            // `SpecificId::Invalid`.
            CheckFunctionTypeMatches(context, override_fn, fn,
                                     SemIR::SpecificId::Invalid,
                                     /*check_syntax=*/false);
            fn_decl_id = override_fn_decl_id;
          }
        }
        vtable.push_back(fn_decl_id);
      }
    }

    for (auto inst_id : context.vtable_stack().PeekCurrentBlockContents()) {
      auto fn_decl = context.insts().GetAs<SemIR::FunctionDecl>(inst_id);
      const auto& fn = context.functions().Get(fn_decl.function_id);
      if (fn.virtual_modifier != SemIR::FunctionFields::VirtualModifier::Impl) {
        vtable.push_back(inst_id);
      }
    }
    class_info.vtable_id = context.AddInst<SemIR::Vtable>(
        node_id, {.type_id = context.GetSingletonType(
                      SemIR::VtableType::SingletonInstId),
                  .virtual_functions_id = context.inst_blocks().Add(vtable)});
  }

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

Review comment (suggestion):
I think we'll need to somehow deal with the type of `self` changing here. I note that in your testcase the virtual function doesn't have a `self` at all, which is presumably something we'll need to reject.

Code after:
    if (!defining_vptr) {
      LoadImportRef(context, base_class_info->vtable_id);
      auto base_vtable_id = context.constant_values().GetConstantInstId(
          base_class_info->vtable_id);
      auto base_vtable_inst_block =
          context.inst_blocks().Get(context.insts()
                                        .GetAs<SemIR::Vtable>(base_vtable_id)
                                        .virtual_functions_id);
      // TODO: Avoid quadratic search. Perhaps build a map from `NameId` to the
      // elements of the top of `vtable_stack`.
      for (auto fn_decl_id : base_vtable_inst_block) {
        auto fn_decl = GetCalleeFunction(context.sem_ir(), fn_decl_id);
        const auto& fn = context.functions().Get(fn_decl.function_id);
        for (auto override_fn_decl_id :
             context.vtable_stack().PeekCurrentBlockContents()) {
          auto override_fn_decl =
              context.insts().GetAs<SemIR::FunctionDecl>(override_fn_decl_id);
          const auto& override_fn =
              context.functions().Get(override_fn_decl.function_id);
          if (override_fn.virtual_modifier ==
                  SemIR::FunctionFields::VirtualModifier::Impl &&
              override_fn.name_id == fn.name_id) {
            // TODO: Support generic base classes, rather than passing
            // `SpecificId::None`.
            CheckFunctionTypeMatches(context, override_fn, fn,
                                     SemIR::SpecificId::None,
                                     /*check_syntax=*/false);
            fn_decl_id = override_fn_decl_id;
          }
        }
        vtable.push_back(fn_decl_id);
      }
    }

    for (auto inst_id : context.vtable_stack().PeekCurrentBlockContents()) {
      auto fn_decl = context.insts().GetAs<SemIR::FunctionDecl>(inst_id);
      const auto& fn = context.functions().Get(fn_decl.function_id);
      if (fn.virtual_modifier != SemIR::FunctionFields::VirtualModifier::Impl) {
        vtable.push_back(inst_id);
      }
    }
    class_info.vtable_id = context.AddInst<SemIR::Vtable>(
        node_id, {.type_id = context.GetSingletonType(
                      SemIR::VtableType::SingletonInstId),
                  .virtual_functions_id = context.inst_blocks().Add(vtable)});
  }

  return context.AddInst<SemIR::CompleteTypeWitness>(
      node_id,
      {.type_id = context.GetSingletonType(SemIR::WitnessType::SingletonInstId),
       .object_repr_id = context.GetStructType(