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

Code before:

  auto& class_info = context.classes().Get(class_decl->class_id);
  if (virtual_modifier == SemIR::Function::VirtualModifier::Impl &&
      !class_info.base_id.has_value()) {
    CARBON_DIAGNOSTIC(ImplWithoutBase, Error, "impl without base class");
    context.emitter().Emit(node_id, ImplWithoutBase);
  }

  if (generic_id != SemIR::GenericId::None) {
    bool function_is_generic = true;
    if (class_info.generic_id != SemIR::GenericId::None) {
      const auto& function_generic = context.generics().Get(generic_id);
      const auto& class_generic = context.generics().Get(class_info.generic_id);

      auto function_bindings =
          context.inst_blocks().Get(function_generic.bindings_id);
      auto class_bindings =
          context.inst_blocks().Get(class_generic.bindings_id);

      // If the function's bindings are the same size as the class's bindings,
      // then there are no extra bindings for the function, so it is effectively
      // non-generic within the scope of a specific of the class.
      if (class_bindings.size() == function_bindings.size()) {
        function_is_generic = false;
      }
    }
    if (function_is_generic) {
      CARBON_DIAGNOSTIC(GenericVirtual, Error, "generic virtual function");
      context.emitter().Emit(node_id, GenericVirtual);
      return;
    }
  }

  // TODO: If this is an `impl` function, check there's a matching base
  // function that's impl or virtual.
  class_info.is_dynamic = true;
  context.vtable_stack().AddInstId(decl_id);
}

// Validates the `destroy` function's signature. May replace invalid values for
// recovery.
static auto ValidateIfDestroy(Context& context, bool is_redecl,
                              std::optional<SemIR::Inst> parent_scope_inst,
                              SemIR::Function& function_info) -> void {
  if (function_info.name_id != SemIR::NameId::Destroy) {
    return;
  }

  // For recovery, always force explicit parameters to be empty. We do this
  // before any of the returns for simplicity.
  auto orig_param_patterns_id = function_info.param_patterns_id;

Review comment (refactor):
My sad comment is, maybe make this a `IsGenericFunction` helper to allow for early returns rather than nested conditions.

Code after:
  auto class_bindings = context.inst_blocks().Get(class_generic.bindings_id);

  // If the function's bindings are the same size as the class's bindings,
  // then there are no extra bindings for the function, so it is effectively
  // non-generic within the scope of a specific of the class.
  return class_bindings.size() != function_bindings.size();
}

// Requests a vtable be created when processing a virtual function.
static auto RequestVtableIfVirtual(
    Context& context, Parse::AnyFunctionDeclId node_id,
    SemIR::Function::VirtualModifier virtual_modifier,
    const std::optional<SemIR::Inst>& parent_scope_inst, SemIR::InstId decl_id,
    SemIR::GenericId generic_id) -> void {
  // In order to request a vtable, the function must be virtual, and in a class
  // scope.
  if (virtual_modifier == SemIR::Function::VirtualModifier::None ||
      !parent_scope_inst) {
    return;
  }
  auto class_decl = parent_scope_inst->TryAs<SemIR::ClassDecl>();
  if (!class_decl) {
    return;
  }

  auto& class_info = context.classes().Get(class_decl->class_id);
  if (virtual_modifier == SemIR::Function::VirtualModifier::Impl &&
      !class_info.base_id.has_value()) {
    CARBON_DIAGNOSTIC(ImplWithoutBase, Error, "impl without base class");
    context.emitter().Emit(node_id, ImplWithoutBase);
  }

  if (IsGenericFunction(context, generic_id, class_info.generic_id)) {
    CARBON_DIAGNOSTIC(GenericVirtual, Error, "generic virtual function");
    context.emitter().Emit(node_id, GenericVirtual);
    return;
  }

  // TODO: If this is an `impl` function, check there's a matching base
  // function that's impl or virtual.
  class_info.is_dynamic = true;
  context.vtable_stack().AddInstId(decl_id);
}

// Validates the `destroy` function's signature. May replace invalid values for
// recovery.
static auto ValidateIfDestroy(Context& context, bool is_redecl,
                              std::optional<SemIR::Inst> parent_scope_inst,
                              SemIR::Function& function_info) -> void {
  if (function_info.name_id != SemIR::NameId::Destroy) {
    return;