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

Code before:
  context.node_stack()
      .PopAndDiscardSoloNodeId<Parse::NodeKind::FunctionIntroducer>();

  // Process modifiers.
  auto [parent_scope_inst_id, parent_scope_inst] =
      context.name_scopes().GetInstIfValid(name_context.parent_scope_id);
  auto introducer =
      context.decl_introducer_state_stack().Pop<Lex::TokenKind::Fn>();
  DiagnoseModifiers(context, introducer, is_definition, parent_scope_inst_id,
                    parent_scope_inst);
  bool is_extern = introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extern);
  auto virtual_modifier =
      introducer.modifier_set.ToEnum<SemIR::Function::VirtualModifier>()
          .Case(KeywordModifierSet::Virtual,
                SemIR::Function::VirtualModifier::Virtual)
          .Case(KeywordModifierSet::Abstract,
                SemIR::Function::VirtualModifier::Abstract)
          .Case(KeywordModifierSet::Impl,
                SemIR::Function::VirtualModifier::Impl)
          .Default(SemIR::Function::VirtualModifier::None);
  if (virtual_modifier != SemIR::Function::VirtualModifier::None &&
      parent_scope_inst) {
    if (auto class_decl = parent_scope_inst->TryAs<SemIR::ClassDecl>()) {
      auto& class_info = context.classes().Get(class_decl->class_id);
      if (virtual_modifier == SemIR::Function::VirtualModifier::Impl &&
          !class_info.base_id.is_valid()) {
        CARBON_DIAGNOSTIC(ImplWithoutBase, Error, "impl without base class");
        context.emitter().Build(node_id, ImplWithoutBase).Emit();
      }
      class_info.is_dynamic = true;
    }
  }
  if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Interface)) {
    // TODO: Once we are saving the modifiers for a function, add check that
    // the function may only be defined if it is marked `default` or `final`.
    context.TODO(introducer.modifier_node_id(ModifierOrder::Decl),
                 "interface modifier");
  }

  // Add the function declaration.
  auto decl_block_id = context.inst_block_stack().Pop();
  auto function_decl = SemIR::FunctionDecl{
      SemIR::TypeId::Invalid, SemIR::FunctionId::Invalid, decl_block_id};
  auto decl_id =
      context.AddPlaceholderInst(SemIR::LocIdAndInst(node_id, function_decl));

  // Build the function entity. This will be merged into an existing function if
  // there is one, or otherwise added to the function store.
  auto function_info =
      SemIR::Function{{name_context.MakeEntityWithParamsBase(
                          name, decl_id, is_extern, introducer.extern_library)},

Review comment (bug):
Looks like this used to CHECK-fail on things like
```
class C {
  impl fn F();
}
```
Would be nice to add a test for that.

Code after:
  context.node_stack()
      .PopAndDiscardSoloNodeId<Parse::NodeKind::FunctionIntroducer>();

  // Process modifiers.
  auto [parent_scope_inst_id, parent_scope_inst] =
      context.name_scopes().GetInstIfValid(name_context.parent_scope_id);
  auto introducer =
      context.decl_introducer_state_stack().Pop<Lex::TokenKind::Fn>();
  DiagnoseModifiers(context, introducer, is_definition, parent_scope_inst_id,
                    parent_scope_inst);
  bool is_extern = introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extern);
  auto virtual_modifier =
      introducer.modifier_set.ToEnum<SemIR::Function::VirtualModifier>()
          .Case(KeywordModifierSet::Virtual,
                SemIR::Function::VirtualModifier::Virtual)
          .Case(KeywordModifierSet::Abstract,
                SemIR::Function::VirtualModifier::Abstract)
          .Case(KeywordModifierSet::Impl,
                SemIR::Function::VirtualModifier::Impl)
          .Default(SemIR::Function::VirtualModifier::None);
  if (virtual_modifier != SemIR::Function::VirtualModifier::None &&
      parent_scope_inst) {
    if (auto class_decl = parent_scope_inst->TryAs<SemIR::ClassDecl>()) {
      auto& class_info = context.classes().Get(class_decl->class_id);
      if (virtual_modifier == SemIR::Function::VirtualModifier::Impl &&
          !class_info.base_id.is_valid()) {
        CARBON_DIAGNOSTIC(ImplWithoutBase, Error, "impl without base class");
        context.emitter().Build(node_id, ImplWithoutBase).Emit();
      }
      // TODO: If this is an `impl` function, check there's a matching base
      // function that's impl or virtual.
      class_info.is_dynamic = true;
    }
  }
  if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Interface)) {
    // TODO: Once we are saving the modifiers for a function, add check that
    // the function may only be defined if it is marked `default` or `final`.
    context.TODO(introducer.modifier_node_id(ModifierOrder::Decl),
                 "interface modifier");
  }

  // Add the function declaration.
  auto decl_block_id = context.inst_block_stack().Pop();
  auto function_decl = SemIR::FunctionDecl{
      SemIR::TypeId::Invalid, SemIR::FunctionId::Invalid, decl_block_id};
  auto decl_id =
      context.AddPlaceholderInst(SemIR::LocIdAndInst(node_id, function_decl));

  // Build the function entity. This will be merged into an existing function if
  // there is one, or otherwise added to the function store.
  auto function_info =