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

Code before:

  auto& class_info = context.classes().Get(parent_class_decl->class_id);
  if (class_info.base_id.is_valid()) {
    DiagnoseClassSpecificDeclRepeated(context, node_id, class_info.base_id,
                                      Lex::TokenKind::Base);
    return true;
  }

  auto base_info = CheckBaseType(context, base_type_node_id, base_type_expr_id);

  // The `base` value in the class scope has an unbound element type. Instance
  // binding will be performed when it's found by name lookup into an instance.
  auto field_type_id =
      context.GetUnboundElementType(class_info.self_type_id, base_info.type_id);
  class_info.base_id = context.AddInst<SemIR::BaseDecl>(
      node_id,
      {.type_id = field_type_id,
       .base_type_id = base_info.type_id,
       .index = SemIR::ElementIndex(
           context.args_type_info_stack().PeekCurrentBlockContents().size())});

  if (base_info.type_id != SemIR::TypeId::Error) {
    auto base_class_info = context.classes().Get(
        context.types().GetAs<SemIR::ClassType>(base_info.type_id).class_id);
    CARBON_CHECK(!class_info.is_dynamic);
    class_info.is_dynamic = base_class_info.is_dynamic;
  }

  // Add a corresponding field to the object representation of the class.
  // TODO: Consider whether we want to use `partial T` here.
  // TODO: Should we diagnose if there are already any fields?
  context.args_type_info_stack().AddInstId(
      context.AddInstInNoBlock<SemIR::StructTypeField>(
          node_id, {.name_id = SemIR::NameId::Base,
                    .field_type_id = base_info.type_id}));

  // Bind the name `base` in the class to the base field.
  context.decl_name_stack().AddNameOrDiagnoseDuplicate(
      context.decl_name_stack().MakeUnqualifiedName(node_id,
                                                    SemIR::NameId::Base),
      class_info.base_id, introducer.modifier_set.GetAccessKind());

  // Extend the class scope with the base class.
  if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) {
    auto& class_scope = context.name_scopes().Get(class_info.scope_id);
    if (base_info.scope_id.is_valid()) {
      class_scope.extended_scopes.push_back(base_info.scope_id);
    } else {
      class_scope.has_error = true;
    }
  }

Review comment (bug):
What happens for 
```carbon
base class A {
  virtual fn F[addr self: Self*]();
}
class B {
  virtual fn G[addr self: Self*]();
  extend base: A;
}
```
or similarly if a class attempts to derive from a dynamic base class twice? During error recovery I'm concerned that we could end up here with `class_info.is_dynamic` already set to `true`. Could we `|=` the base class flag instead of `CHECK`-failing?

Code after:

  auto& class_info = context.classes().Get(parent_class_decl->class_id);
  if (class_info.base_id.is_valid()) {
    DiagnoseClassSpecificDeclRepeated(context, node_id, class_info.base_id,
                                      Lex::TokenKind::Base);
    return true;
  }

  auto base_info = CheckBaseType(context, base_type_node_id, base_type_expr_id);

  // The `base` value in the class scope has an unbound element type. Instance
  // binding will be performed when it's found by name lookup into an instance.
  auto field_type_id =
      context.GetUnboundElementType(class_info.self_type_id, base_info.type_id);
  class_info.base_id = context.AddInst<SemIR::BaseDecl>(
      node_id,
      {.type_id = field_type_id,
       .base_type_id = base_info.type_id,
       .index = SemIR::ElementIndex(
           context.args_type_info_stack().PeekCurrentBlockContents().size())});

  if (base_info.type_id != SemIR::TypeId::Error) {
    auto base_class_info = context.classes().Get(
        context.types().GetAs<SemIR::ClassType>(base_info.type_id).class_id);
    class_info.is_dynamic |= base_class_info.is_dynamic;
  }

  // Add a corresponding field to the object representation of the class.
  // TODO: Consider whether we want to use `partial T` here.
  // TODO: Should we diagnose if there are already any fields?
  context.args_type_info_stack().AddInstId(
      context.AddInstInNoBlock<SemIR::StructTypeField>(
          node_id, {.name_id = SemIR::NameId::Base,
                    .field_type_id = base_info.type_id}));

  // Bind the name `base` in the class to the base field.
  context.decl_name_stack().AddNameOrDiagnoseDuplicate(
      context.decl_name_stack().MakeUnqualifiedName(node_id,
                                                    SemIR::NameId::Base),
      class_info.base_id, introducer.modifier_set.GetAccessKind());

  // Extend the class scope with the base class.
  if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) {
    auto& class_scope = context.name_scopes().Get(class_info.scope_id);
    if (base_info.scope_id.is_valid()) {
      class_scope.extended_scopes.push_back(base_info.scope_id);
    } else {
      class_scope.has_error = true;
    }
  }
  return true;