Просмотр исходного кода

Initial, extern-ignoring support for extern class decls. (#3891)

This doesn't actually track whether a declaration is `extern`. It does,
however:

- Factor out and expand merge support for classes, sharing handling with
functions.
- This makes the ClassRedefinition diagnostic redundant, as the
redeclaration checking overlaps.
- Add partial `extern` handling to class handling; just some
verifications of correct use.
- Factor out `extern` on member handling for sharing with `fn`.
- Fixes a bug in import_ref where a class's definition_id wasn't
assigned when defining.

This changes how a redefinition is handled (replaced, rather than
merged). I don't know whether that's ideal, but I think it results in
easy-to-understand consequences, and it's more consistent with how `fn`
works.

There's enough work here that this felt like a decent cut point,
particularly as the amount of work to actually add `extern` tracking
will be significant.
Jon Ross-Perkins 2 лет назад
Родитель
Сommit
db324c7247

+ 2 - 0
toolchain/check/BUILD

@@ -50,6 +50,7 @@ cc_library(
 cc_library(
     name = "context",
     srcs = [
+        "class.cpp",
         "context.cpp",
         "convert.cpp",
         "decl_name_stack.cpp",
@@ -63,6 +64,7 @@ cc_library(
         "subst.cpp",
     ],
     hdrs = [
+        "class.h",
         "context.h",
         "convert.h",
         "decl_name_stack.h",

+ 57 - 0
toolchain/check/class.cpp

@@ -0,0 +1,57 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "toolchain/check/class.h"
+
+#include "toolchain/check/merge.h"
+
+namespace Carbon::Check {
+
+auto MergeClassRedecl(Context& context, SemIRLoc new_loc,
+                      SemIR::Class& new_class, bool /*new_is_import*/,
+                      bool new_is_definition, bool new_is_extern,
+                      SemIR::ClassId prev_class_id, bool prev_is_extern,
+                      SemIR::ImportIRInstId prev_import_ir_inst_id) -> bool {
+  auto& prev_class = context.classes().Get(prev_class_id);
+  SemIRLoc prev_loc =
+      prev_class.is_defined() ? prev_class.definition_id : prev_class.decl_id;
+
+  // TODO: Check that the generic parameter list agrees with the prior
+  // declaration.
+
+  CheckIsAllowedRedecl(context, Lex::TokenKind::Class, prev_class.name_id,
+                       {.loc = new_loc,
+                        .is_definition = new_is_definition,
+                        .is_extern = new_is_extern},
+                       {.loc = prev_loc,
+                        .is_definition = prev_class.is_defined(),
+                        .is_extern = prev_is_extern},
+                       prev_import_ir_inst_id);
+
+  // The introducer kind must match the previous declaration.
+  // TODO: The rule here is not yet decided. See #3384.
+  if (prev_class.inheritance_kind != new_class.inheritance_kind) {
+    CARBON_DIAGNOSTIC(ClassRedeclarationDifferentIntroducer, Error,
+                      "Class redeclared with different inheritance kind.");
+    CARBON_DIAGNOSTIC(ClassRedeclarationDifferentIntroducerPrevious, Note,
+                      "Previously declared here.");
+    context.emitter()
+        .Build(new_loc, ClassRedeclarationDifferentIntroducer)
+        .Note(prev_loc, ClassRedeclarationDifferentIntroducerPrevious)
+        .Emit();
+  }
+
+  if (new_is_definition) {
+    prev_class.definition_id = new_class.definition_id;
+    prev_class.scope_id = new_class.scope_id;
+    prev_class.body_block_id = new_class.body_block_id;
+    prev_class.adapt_id = new_class.adapt_id;
+    prev_class.base_id = new_class.base_id;
+    prev_class.object_repr_id = new_class.object_repr_id;
+  }
+
+  return true;
+}
+
+}  // namespace Carbon::Check

+ 28 - 0
toolchain/check/class.h

@@ -0,0 +1,28 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef CARBON_TOOLCHAIN_CHECK_CLASS_H_
+#define CARBON_TOOLCHAIN_CHECK_CLASS_H_
+
+#include "toolchain/check/context.h"
+#include "toolchain/sem_ir/class.h"
+#include "toolchain/sem_ir/ids.h"
+
+namespace Carbon::Check {
+
+// Tries to merge new_class into prev_class_id. Since new_class won't have a
+// definition even if one is upcoming, set is_definition to indicate the planned
+// result.
+//
+// If merging is successful, returns true and may update the previous class.
+// Otherwise, returns false. Prints a diagnostic when appropriate.
+auto MergeClassRedecl(Context& context, SemIRLoc new_loc,
+                      SemIR::Class& new_class, bool new_is_import,
+                      bool new_is_definition, bool new_is_extern,
+                      SemIR::ClassId prev_class_id, bool prev_is_extern,
+                      SemIR::ImportIRInstId prev_import_ir_inst_id) -> bool;
+
+}  // namespace Carbon::Check
+
+#endif  // CARBON_TOOLCHAIN_CHECK_CLASS_H_

+ 2 - 2
toolchain/check/function.h

@@ -40,8 +40,8 @@ auto CheckFunctionTypeMatches(Context& context,
 // have a definition even if one is upcoming, set is_definition to indicate the
 // planned result.
 //
-// If merging is successful, updates the FunctionId on new_function and returns
-// true. Otherwise, returns false. Prints a diagnostic when appropriate.
+// If merging is successful, returns true and may update the previous function.
+// Otherwise, returns false. Prints a diagnostic when appropriate.
 auto MergeFunctionRedecl(Context& context, SemIRLoc new_loc,
                          SemIR::Function& new_function, bool new_is_import,
                          bool new_is_definition,

+ 39 - 44
toolchain/check/handle_class.cpp

@@ -2,8 +2,10 @@
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
+#include "toolchain/check/class.h"
 #include "toolchain/check/context.h"
 #include "toolchain/check/convert.h"
+#include "toolchain/check/merge.h"
 #include "toolchain/check/modifiers.h"
 #include "toolchain/sem_ir/typed_insts.h"
 
@@ -33,7 +35,8 @@ auto HandleClassIntroducer(Context& context, Parse::ClassIntroducerId node_id)
   return true;
 }
 
-static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id)
+static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
+                           bool is_definition)
     -> std::tuple<SemIR::ClassId, SemIR::InstId> {
   if (context.node_stack().PopIf<Parse::NodeKind::TuplePattern>()) {
     context.TODO(node_id, "generic class");
@@ -50,8 +53,11 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id)
   CheckAccessModifiersOnDecl(context, Lex::TokenKind::Class,
                              name_context.target_scope_id);
   LimitModifiersOnDecl(context,
-                       KeywordModifierSet::Class | KeywordModifierSet::Access,
+                       KeywordModifierSet::Class | KeywordModifierSet::Access |
+                           KeywordModifierSet::Extern,
                        Lex::TokenKind::Class);
+  RestrictExternModifierOnDecl(context, Lex::TokenKind::Class,
+                               name_context.target_scope_id, is_definition);
 
   auto modifiers = context.decl_state_stack().innermost().modifier_set;
   if (!!(modifiers & KeywordModifierSet::Access)) {
@@ -59,6 +65,8 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id)
                      ModifierOrder::Access),
                  "access modifier");
   }
+
+  bool is_extern = !!(modifiers & KeywordModifierSet::Extern);
   auto inheritance_kind =
       !!(modifiers & KeywordModifierSet::Abstract) ? SemIR::Class::Abstract
       : !!(modifiers & KeywordModifierSet::Base)   ? SemIR::Class::Base
@@ -72,34 +80,35 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id)
                                      SemIR::ClassId::Invalid, decl_block_id};
   auto class_decl_id = context.AddPlaceholderInst({node_id, class_decl});
 
+  SemIR::Class class_info = {
+      .name_id = name_context.name_id_for_new_inst(),
+      .enclosing_scope_id = name_context.enclosing_scope_id_for_new_inst(),
+      // `.self_type_id` depends on the ClassType, so is set below.
+      .self_type_id = SemIR::TypeId::Invalid,
+      .decl_id = class_decl_id,
+      .inheritance_kind = inheritance_kind};
+
   // Check whether this is a redeclaration.
-  auto existing_id =
+  auto prev_id =
       context.decl_name_stack().LookupOrAddName(name_context, class_decl_id);
-  if (existing_id.is_valid()) {
-    if (auto existing_class_decl =
-            context.insts().Get(existing_id).TryAs<SemIR::ClassDecl>()) {
-      // This is a redeclaration of an existing class.
-      class_decl.class_id = existing_class_decl->class_id;
-      auto& class_info = context.classes().Get(class_decl.class_id);
-
-      // The introducer kind must match the previous declaration.
-      // TODO: The rule here is not yet decided. See #3384.
-      if (class_info.inheritance_kind != inheritance_kind) {
-        CARBON_DIAGNOSTIC(ClassRedeclarationDifferentIntroducer, Error,
-                          "Class redeclared with different inheritance kind.");
-        CARBON_DIAGNOSTIC(ClassRedeclarationDifferentIntroducerPrevious, Note,
-                          "Previously declared here.");
-        context.emitter()
-            .Build(node_id, ClassRedeclarationDifferentIntroducer)
-            .Note(existing_id, ClassRedeclarationDifferentIntroducerPrevious)
-            .Emit();
+  if (prev_id.is_valid()) {
+    auto prev_inst_for_merge =
+        ResolvePrevInstForMerge(context, node_id, prev_id);
+
+    if (auto prev_class_decl =
+            prev_inst_for_merge.inst.TryAs<SemIR::ClassDecl>()) {
+      // TODO: Fix prev_is_extern.
+      if (MergeClassRedecl(context, node_id, class_info,
+                           /*new_is_import=*/false, is_definition, is_extern,
+                           prev_class_decl->class_id,
+                           /*prev_is_extern=*/false,
+                           prev_inst_for_merge.import_ir_inst_id)) {
+        // When merging, use the existing entity rather than adding a new one.
+        class_decl.class_id = prev_class_decl->class_id;
       }
-
-      // TODO: Check that the generic parameter list agrees with the prior
-      // declaration.
     } else {
       // This is a redeclaration of something other than a class.
-      context.DiagnoseDuplicateName(class_decl_id, existing_id);
+      context.DiagnoseDuplicateName(class_decl_id, prev_id);
     }
   }
 
@@ -109,13 +118,7 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id)
     // TODO: If this is an invalid redeclaration of a non-class entity or there
     // was an error in the qualifier, we will have lost track of the class name
     // here. We should keep track of it even if the name is invalid.
-    class_decl.class_id = context.classes().Add(
-        {.name_id = name_context.name_id_for_new_inst(),
-         .enclosing_scope_id = name_context.enclosing_scope_id_for_new_inst(),
-         // `.self_type_id` depends on the ClassType, so is set below.
-         .self_type_id = SemIR::TypeId::Invalid,
-         .decl_id = class_decl_id,
-         .inheritance_kind = inheritance_kind});
+    class_decl.class_id = context.classes().Add(class_info);
   }
 
   // Write the class ID into the ClassDecl.
@@ -131,27 +134,19 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id)
 }
 
 auto HandleClassDecl(Context& context, Parse::ClassDeclId node_id) -> bool {
-  BuildClassDecl(context, node_id);
+  BuildClassDecl(context, node_id, /*is_definition=*/false);
   context.decl_name_stack().PopScope();
   return true;
 }
 
 auto HandleClassDefinitionStart(Context& context,
                                 Parse::ClassDefinitionStartId node_id) -> bool {
-  auto [class_id, class_decl_id] = BuildClassDecl(context, node_id);
+  auto [class_id, class_decl_id] =
+      BuildClassDecl(context, node_id, /*is_definition=*/true);
   auto& class_info = context.classes().Get(class_id);
 
   // Track that this declaration is the definition.
-  if (class_info.is_defined()) {
-    CARBON_DIAGNOSTIC(ClassRedefinition, Error, "Redefinition of class {0}.",
-                      SemIR::NameId);
-    CARBON_DIAGNOSTIC(ClassPreviousDefinition, Note,
-                      "Previous definition was here.");
-    context.emitter()
-        .Build(node_id, ClassRedefinition, class_info.name_id)
-        .Note(class_info.definition_id, ClassPreviousDefinition)
-        .Emit();
-  } else {
+  if (!class_info.is_defined()) {
     class_info.definition_id = class_decl_id;
     class_info.scope_id = context.name_scopes().Add(
         class_decl_id, SemIR::NameId::Invalid, class_info.enclosing_scope_id);

+ 5 - 14
toolchain/check/handle_function.cpp

@@ -47,22 +47,13 @@ static auto DiagnoseModifiers(Context& context, bool is_definition,
     -> KeywordModifierSet {
   const Lex::TokenKind decl_kind = Lex::TokenKind::Fn;
   CheckAccessModifiersOnDecl(context, decl_kind, target_scope_id);
-  if (is_definition) {
-    ForbidExternModifierOnDefinition(context, decl_kind);
-  }
-  if (target_scope_id.is_valid()) {
-    auto target_id = context.name_scopes().Get(target_scope_id).inst_id;
-    if (target_id.is_valid() &&
-        !context.insts().Is<SemIR::Namespace>(target_id)) {
-      ForbidModifiersOnDecl(context, KeywordModifierSet::Extern, decl_kind,
-                            " that is a member");
-    }
-  }
   LimitModifiersOnDecl(context,
                        KeywordModifierSet::Access | KeywordModifierSet::Extern |
                            KeywordModifierSet::Method |
                            KeywordModifierSet::Interface,
                        decl_kind);
+  RestrictExternModifierOnDecl(context, decl_kind, target_scope_id,
+                               is_definition);
   CheckMethodModifiersOnFunction(context, target_scope_id);
   RequireDefaultFinalOnlyInInterfaces(context, decl_kind, target_scope_id);
 
@@ -163,14 +154,14 @@ static auto BuildFunctionDecl(Context& context,
     auto prev_inst_for_merge =
         ResolvePrevInstForMerge(context, node_id, prev_id);
 
-    if (auto existing_function_decl =
+    if (auto prev_function_decl =
             prev_inst_for_merge.inst.TryAs<SemIR::FunctionDecl>()) {
       if (MergeFunctionRedecl(context, node_id, function_info,
                               /*new_is_import=*/false, is_definition,
-                              existing_function_decl->function_id,
+                              prev_function_decl->function_id,
                               prev_inst_for_merge.import_ir_inst_id)) {
         // When merging, use the existing function rather than adding a new one.
-        function_decl.function_id = existing_function_decl->function_id;
+        function_decl.function_id = prev_function_decl->function_id;
       }
     } else {
       // This is a redeclaration of something other than a function. This

+ 2 - 0
toolchain/check/import_ref.cpp

@@ -571,6 +571,8 @@ class ImportRefResolver {
                           SemIR::Class& new_class,
                           SemIR::ConstantId object_repr_const_id,
                           SemIR::ConstantId base_const_id) -> void {
+    new_class.definition_id = new_class.decl_id;
+
     new_class.object_repr_id =
         context_.GetTypeIdForTypeConstant(object_repr_const_id);
 

+ 15 - 0
toolchain/check/merge.cpp

@@ -5,6 +5,7 @@
 #include "toolchain/check/merge.h"
 
 #include "toolchain/base/kind_switch.h"
+#include "toolchain/check/class.h"
 #include "toolchain/check/function.h"
 #include "toolchain/check/import_ref.h"
 #include "toolchain/sem_ir/ids.h"
@@ -232,6 +233,20 @@ auto MergeImportRef(Context& context, SemIR::InstId new_inst_id,
                           prev_inst->import_ir_inst_id);
       return;
     }
+    case CARBON_KIND(SemIR::ClassType new_type): {
+      auto prev_type = prev_inst->inst.TryAs<SemIR::ClassType>();
+      if (!prev_type) {
+        break;
+      }
+
+      auto new_class = context.classes().Get(new_type.class_id);
+      // TODO: Fix new_is_extern and prev_is_extern.
+      MergeClassRedecl(context, new_inst_id, new_class,
+                       /*new_is_import=*/true, new_class.is_defined(),
+                       /*new_is_extern=*/false, prev_type->class_id,
+                       /*prev_is_extern=*/false, prev_inst->import_ir_inst_id);
+      return;
+    }
     default:
       context.TODO(new_inst_id, llvm::formatv("Merging {0} not yet supported.",
                                               new_inst->inst.kind()));

+ 15 - 4
toolchain/check/modifiers.cpp

@@ -136,10 +136,21 @@ auto CheckMethodModifiersOnFunction(Context& context,
                         " outside of a class");
 }
 
-auto ForbidExternModifierOnDefinition(Context& context,
-                                      Lex::TokenKind decl_kind) -> void {
-  ForbidModifiersOnDecl(context, KeywordModifierSet::Extern, decl_kind,
-                        " that provides a definition");
+auto RestrictExternModifierOnDecl(Context& context, Lex::TokenKind decl_kind,
+                                  SemIR::NameScopeId target_scope_id,
+                                  bool is_definition) -> void {
+  if (is_definition) {
+    ForbidModifiersOnDecl(context, KeywordModifierSet::Extern, decl_kind,
+                          " that provides a definition");
+  }
+  if (target_scope_id.is_valid()) {
+    auto target_id = context.name_scopes().Get(target_scope_id).inst_id;
+    if (target_id.is_valid() &&
+        !context.insts().Is<SemIR::Namespace>(target_id)) {
+      ForbidModifiersOnDecl(context, KeywordModifierSet::Extern, decl_kind,
+                            " that is a member");
+    }
+  }
 }
 
 auto RequireDefaultFinalOnlyInInterfaces(Context& context,

+ 7 - 4
toolchain/check/modifiers.h

@@ -43,10 +43,13 @@ inline auto LimitModifiersOnDecl(Context& context, KeywordModifierSet allowed,
   ForbidModifiersOnDecl(context, ~allowed, decl_kind, "");
 }
 
-// If the `extern` modifier is present, diagnoses and updates the declaration
-// state to remove it. Only called for declarations with definitions.
-auto ForbidExternModifierOnDefinition(Context& context,
-                                      Lex::TokenKind decl_kind) -> void;
+// Restricts the `extern` modifier to only be used on namespace-scoped
+// declarations, diagnosing and removing it on:
+// - `extern` on a definition.
+// - `extern` on a scoped entity.
+auto RestrictExternModifierOnDecl(Context& context, Lex::TokenKind decl_kind,
+                                  SemIR::NameScopeId target_scope_id,
+                                  bool is_definition) -> void;
 
 // Report a diagonostic if `default` and `final` modifiers are used on
 // declarations where they are not allowed. Right now they are only allowed

+ 22 - 17
toolchain/check/testdata/class/cross_package_import.carbon

@@ -18,8 +18,7 @@ class C {}
 
 package Other library "extern" api;
 
-// TODO: Mark extern
-class C;
+extern class C;
 
 // --- other_conflict.carbon
 
@@ -51,26 +50,32 @@ import Other library "extern";
 // CHECK:STDERR: fail_extern.carbon:[[@LINE-5]]:1: In import.
 // CHECK:STDERR: import Other library "extern";
 // CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: other_extern.carbon:5:1: Class was forward declared here.
-// CHECK:STDERR: class C;
-// CHECK:STDERR: ^~~~~~~~
+// CHECK:STDERR: other_extern.carbon:4:1: Class was forward declared here.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
 // CHECK:STDERR:
 var c: Other.C = {};
 
-// --- fail_merge_define_extern.carbon
+// --- fail_todo_merge_define_extern.carbon
 
-library "fail_merge_define_extern" api;
+library "merge_define_extern" api;
 
 import Other library "define";
-// CHECK:STDERR: fail_merge_define_extern.carbon:[[@LINE+6]]:1: In import.
+// CHECK:STDERR: fail_todo_merge_define_extern.carbon:[[@LINE+12]]:1: In import.
 // CHECK:STDERR: import Other library "extern";
 // CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: other_extern.carbon:5:1: ERROR: Semantics TODO: `Merging ClassType not yet supported.`.
-// CHECK:STDERR: class C;
-// CHECK:STDERR: ^~~~~~~~
+// CHECK:STDERR: other_extern.carbon:4:1: ERROR: Only one library can declare `class C` without `extern`.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_todo_merge_define_extern.carbon:[[@LINE-7]]:1: In import.
+// CHECK:STDERR: import Other library "define";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: other_define.carbon:4:1: Previously declared here.
+// CHECK:STDERR: class C {}
+// CHECK:STDERR: ^~~~~~~~~
 import Other library "extern";
 
-// CHECK:STDERR: fail_merge_define_extern.carbon:[[@LINE+4]]:8: In name lookup for `C`.
+// CHECK:STDERR: fail_todo_merge_define_extern.carbon:[[@LINE+4]]:8: In name lookup for `C`.
 // CHECK:STDERR: var c: Other.C = {};
 // CHECK:STDERR:        ^~~~~~~
 // CHECK:STDERR:
@@ -227,7 +232,7 @@ var c: Other.C = {};
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_merge_define_extern.carbon
+// CHECK:STDOUT: --- fail_todo_merge_define_extern.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %C.1: type = class_type @C.1 [template]
@@ -266,10 +271,10 @@ var c: Other.C = {};
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %.loc17_19.1: {} = struct_literal ()
-// CHECK:STDOUT:   %.loc17_19.2: init C = class_init (), file.%c.var [template = constants.%.4]
-// CHECK:STDOUT:   %.loc17_20: init C = converted %.loc17_19.1, %.loc17_19.2 [template = constants.%.4]
-// CHECK:STDOUT:   assign file.%c.var, %.loc17_20
+// CHECK:STDOUT:   %.loc23_19.1: {} = struct_literal ()
+// CHECK:STDOUT:   %.loc23_19.2: init C = class_init (), file.%c.var [template = constants.%.4]
+// CHECK:STDOUT:   %.loc23_20: init C = converted %.loc23_19.1, %.loc23_19.2 [template = constants.%.4]
+// CHECK:STDOUT:   assign file.%c.var, %.loc23_20
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 762 - 0
toolchain/check/testdata/class/extern.carbon

@@ -0,0 +1,762 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+
+// ============================================================================
+// Setup files
+// ============================================================================
+
+// --- decl.carbon
+
+library "decl" api;
+
+class C;
+
+// --- extern_decl.carbon
+
+library "extern_decl" api;
+
+extern class C;
+
+// --- extern_decl_copy.carbon
+
+library "extern_decl_copy" api;
+
+extern class C;
+
+// --- def.carbon
+
+library "def" api;
+
+class C {}
+
+// ============================================================================
+// Test files
+// ============================================================================
+
+// --- fail_decl_fn_in_extern.carbon
+
+library "fail_decl_fn_in_extern" api;
+
+extern class C;
+// CHECK:STDERR: fail_decl_fn_in_extern.carbon:[[@LINE+7]]:4: ERROR: Cannot declare a member of incomplete class `C`.
+// CHECK:STDERR: fn C.F();
+// CHECK:STDERR:    ^
+// CHECK:STDERR: fail_decl_fn_in_extern.carbon:[[@LINE-4]]:1: Class was forward declared here.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn C.F();
+
+// --- fail_extern_def.carbon
+
+library "fail_extern_def" api;
+
+// CHECK:STDERR: fail_extern_def.carbon:[[@LINE+4]]:1: ERROR: `extern` not allowed on `class` declaration that provides a definition.
+// CHECK:STDERR: extern class C {}
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR:
+extern class C {}
+
+// --- fail_extern_decl_after_extern_decl.carbon
+
+library "fail_extern_decl_after_extern_decl" api;
+
+extern class C;
+// CHECK:STDERR: fail_extern_decl_after_extern_decl.carbon:[[@LINE+7]]:1: ERROR: Redeclaration of `class C` is redundant.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_extern_decl_after_extern_decl.carbon:[[@LINE-4]]:1: Previously declared here.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR:
+extern class C;
+
+// --- fail_decl_after_extern_decl.carbon
+
+library "fail_decl_after_extern_decl" api;
+
+extern class C;
+// CHECK:STDERR: fail_decl_after_extern_decl.carbon:[[@LINE+7]]:1: ERROR: Redeclaration of `class C` is redundant.
+// CHECK:STDERR: class C;
+// CHECK:STDERR: ^~~~~~~~
+// CHECK:STDERR: fail_decl_after_extern_decl.carbon:[[@LINE-4]]:1: Previously declared here.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR:
+class C;
+
+// --- fail_extern_member_class.carbon
+
+library "fail_extern_member_class" api;
+
+class C {
+  // CHECK:STDERR: fail_extern_member_class.carbon:[[@LINE+4]]:3: ERROR: `extern` not allowed on `class` declaration that is a member.
+  // CHECK:STDERR:   extern class D;
+  // CHECK:STDERR:   ^~~~~~
+  // CHECK:STDERR:
+  extern class D;
+}
+
+// --- todo_fail_def_after_extern_decl.carbon
+
+library "fail_def_after_extern_decl" api;
+
+extern class C;
+class C {}
+
+// --- fail_extern_decl_after_decl.carbon
+
+library "fail_extern_decl_after_decl" api;
+
+class C;
+// CHECK:STDERR: fail_extern_decl_after_decl.carbon:[[@LINE+7]]:1: ERROR: Redeclaration of `class C` is redundant.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_extern_decl_after_decl.carbon:[[@LINE-4]]:1: Previously declared here.
+// CHECK:STDERR: class C;
+// CHECK:STDERR: ^~~~~~~~
+// CHECK:STDERR:
+extern class C;
+
+// --- fail_todo_import_extern_decl_then_decl.carbon
+
+library "import_extern_decl_then_decl" api;
+
+import library "extern_decl";
+// CHECK:STDERR: fail_todo_import_extern_decl_then_decl.carbon:[[@LINE+13]]:1: In import.
+// CHECK:STDERR: import library "decl";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: decl.carbon:4:1: ERROR: Only one library can declare `class C` without `extern`.
+// CHECK:STDERR: class C;
+// CHECK:STDERR: ^~~~~~~~
+// CHECK:STDERR: fail_todo_import_extern_decl_then_decl.carbon:[[@LINE-7]]:1: In import.
+// CHECK:STDERR: import library "extern_decl";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: extern_decl.carbon:4:1: Previously declared here.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR:
+import library "decl";
+
+// --- fail_todo_import_decl_then_extern_decl.carbon
+
+library "import_decl_then_extern_decl" api;
+
+import library "decl";
+// CHECK:STDERR: fail_todo_import_decl_then_extern_decl.carbon:[[@LINE+13]]:1: In import.
+// CHECK:STDERR: import library "extern_decl";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: extern_decl.carbon:4:1: ERROR: Only one library can declare `class C` without `extern`.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_todo_import_decl_then_extern_decl.carbon:[[@LINE-7]]:1: In import.
+// CHECK:STDERR: import library "decl";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: decl.carbon:4:1: Previously declared here.
+// CHECK:STDERR: class C;
+// CHECK:STDERR: ^~~~~~~~
+// CHECK:STDERR:
+import library "extern_decl";
+
+// --- fail_todo_import_extern_decl_then_def.carbon
+
+library "import_extern_decl_then_def" api;
+
+import library "extern_decl";
+// CHECK:STDERR: fail_todo_import_extern_decl_then_def.carbon:[[@LINE+13]]:1: In import.
+// CHECK:STDERR: import library "def";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: def.carbon:4:1: ERROR: Only one library can declare `class C` without `extern`.
+// CHECK:STDERR: class C {}
+// CHECK:STDERR: ^~~~~~~~~
+// CHECK:STDERR: fail_todo_import_extern_decl_then_def.carbon:[[@LINE-7]]:1: In import.
+// CHECK:STDERR: import library "extern_decl";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: extern_decl.carbon:4:1: Previously declared here.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR:
+import library "def";
+
+// --- fail_import_ownership_conflict.carbon
+
+library "fail_import_ownership_conflict" api;
+
+import library "extern_decl";
+// CHECK:STDERR: fail_import_ownership_conflict.carbon:[[@LINE+13]]:1: In import.
+// CHECK:STDERR: import library "decl";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: decl.carbon:4:1: ERROR: Only one library can declare `class C` without `extern`.
+// CHECK:STDERR: class C;
+// CHECK:STDERR: ^~~~~~~~
+// CHECK:STDERR: fail_import_ownership_conflict.carbon:[[@LINE-7]]:1: In import.
+// CHECK:STDERR: import library "extern_decl";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: extern_decl.carbon:4:1: Previously declared here.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR:
+import library "decl";
+// CHECK:STDERR: fail_import_ownership_conflict.carbon:[[@LINE+13]]:1: In import.
+// CHECK:STDERR: import library "def";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: def.carbon:4:1: ERROR: Only one library can declare `class C` without `extern`.
+// CHECK:STDERR: class C {}
+// CHECK:STDERR: ^~~~~~~~~
+// CHECK:STDERR: fail_import_ownership_conflict.carbon:[[@LINE-21]]:1: In import.
+// CHECK:STDERR: import library "extern_decl";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: extern_decl.carbon:4:1: Previously declared here.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR:
+import library "def";
+
+// --- fail_todo_import_extern_decl_copy.carbon
+
+library "import_extern_decl_copy" api;
+
+import library "extern_decl";
+// CHECK:STDERR: fail_todo_import_extern_decl_copy.carbon:[[@LINE+13]]:1: In import.
+// CHECK:STDERR: import library "extern_decl_copy";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: extern_decl_copy.carbon:4:1: ERROR: Only one library can declare `class C` without `extern`.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_todo_import_extern_decl_copy.carbon:[[@LINE-7]]:1: In import.
+// CHECK:STDERR: import library "extern_decl";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: extern_decl.carbon:4:1: Previously declared here.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR:
+import library "extern_decl_copy";
+
+// --- fail_todo_extern_decl_after_import_extern_decl.carbon
+
+library "extern_decl_after_import_extern_decl" api;
+
+import library "extern_decl";
+
+// CHECK:STDERR: fail_todo_extern_decl_after_import_extern_decl.carbon:[[@LINE+10]]:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_todo_extern_decl_after_import_extern_decl.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: import library "extern_decl";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: extern_decl.carbon:4:1: Name is previously declared here.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR:
+extern class C;
+
+// --- fail_todo_decl_after_import_extern_decl.carbon
+
+library "decl_after_import_extern_decl" api;
+
+import library "decl";
+
+// CHECK:STDERR: fail_todo_decl_after_import_extern_decl.carbon:[[@LINE+10]]:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_todo_decl_after_import_extern_decl.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: import library "decl";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: decl.carbon:4:1: Name is previously declared here.
+// CHECK:STDERR: class C;
+// CHECK:STDERR: ^~~~~~~~
+// CHECK:STDERR:
+extern class C;
+
+// --- fail_todo_def_after_import_extern_decl.carbon
+
+library "def_after_import_extern_decl" api;
+
+import library "def";
+
+// CHECK:STDERR: fail_todo_def_after_import_extern_decl.carbon:[[@LINE+10]]:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_todo_def_after_import_extern_decl.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: import library "def";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: def.carbon:4:1: Name is previously declared here.
+// CHECK:STDERR: class C {}
+// CHECK:STDERR: ^~~~~~~~~
+// CHECK:STDERR:
+extern class C;
+
+// --- fail_todo_extern_decl_after_import_def.carbon
+
+library "extern_decl_after_import_def" api;
+
+import library "def";
+
+// CHECK:STDERR: fail_todo_extern_decl_after_import_def.carbon:[[@LINE+9]]:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_todo_extern_decl_after_import_def.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: import library "def";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: def.carbon:4:1: Name is previously declared here.
+// CHECK:STDERR: class C {}
+// CHECK:STDERR: ^~~~~~~~~
+extern class C;
+
+// CHECK:STDOUT: --- decl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C;
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- extern_decl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C;
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- extern_decl_copy.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C;
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- def.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_decl_fn_in_extern.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   %.loc12: <function> = fn_decl @.1 [template] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @.1();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_extern_def.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_extern_decl_after_extern_decl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl.loc4
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl.loc4: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   %C.decl.loc12: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C;
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_decl_after_extern_decl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl.loc4
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl.loc4: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   %C.decl.loc12: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C;
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_extern_member_class.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %D: type = class_type @D [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT:   %D.decl: type = class_decl @D [template = constants.%D] {}
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT:   .D = %D.decl
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @D;
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- todo_fail_def_after_extern_decl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl.loc4
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl.loc4: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   %C.decl.loc5: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_extern_decl_after_decl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl.loc4
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl.loc4: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   %C.decl.loc12: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C;
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_import_extern_decl_then_decl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C.1: type = class_type @C.1 [template]
+// CHECK:STDOUT:   %C.2: type = class_type @C.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .C = %import_ref.1
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir2, inst+2, loaded [template = constants.%C.2]
+// CHECK:STDOUT:   %import_ref.2: type = import_ref ir3, inst+2, loaded [template = constants.%C.1]
+// CHECK:STDOUT:   %C.decl.1: invalid = class_decl @C.1 [template = constants.%C.1] {}
+// CHECK:STDOUT:   %C.decl.2: invalid = class_decl @C.2 [template = constants.%C.2] {}
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C.1;
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C.2;
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_import_decl_then_extern_decl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C.1: type = class_type @C.1 [template]
+// CHECK:STDOUT:   %C.2: type = class_type @C.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .C = %import_ref.1
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir2, inst+2, loaded [template = constants.%C.2]
+// CHECK:STDOUT:   %import_ref.2: type = import_ref ir3, inst+2, loaded [template = constants.%C.1]
+// CHECK:STDOUT:   %C.decl.1: invalid = class_decl @C.1 [template = constants.%C.1] {}
+// CHECK:STDOUT:   %C.decl.2: invalid = class_decl @C.2 [template = constants.%C.2] {}
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C.1;
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C.2;
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_import_extern_decl_then_def.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C.1: type = class_type @C.1 [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %C.2: type = class_type @C.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .C = %import_ref.1
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir2, inst+2, loaded [template = constants.%C.2]
+// CHECK:STDOUT:   %import_ref.2: type = import_ref ir3, inst+2, loaded [template = constants.%C.1]
+// CHECK:STDOUT:   %C.decl.1: invalid = class_decl @C.1 [template = constants.%C.1] {}
+// CHECK:STDOUT:   %import_ref.3 = import_ref ir3, inst+3, unloaded
+// CHECK:STDOUT:   %C.decl.2: invalid = class_decl @C.2 [template = constants.%C.2] {}
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C.1 {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C.2 {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_import_ownership_conflict.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C.1: type = class_type @C.1 [template]
+// CHECK:STDOUT:   %C.2: type = class_type @C.2 [template]
+// CHECK:STDOUT:   %C.3: type = class_type @C.3 [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .C = %import_ref.1
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir2, inst+2, loaded [template = constants.%C.2]
+// CHECK:STDOUT:   %import_ref.2: type = import_ref ir3, inst+2, loaded [template = constants.%C.1]
+// CHECK:STDOUT:   %C.decl.1: invalid = class_decl @C.1 [template = constants.%C.1] {}
+// CHECK:STDOUT:   %C.decl.2: invalid = class_decl @C.2 [template = constants.%C.2] {}
+// CHECK:STDOUT:   %import_ref.3: type = import_ref ir4, inst+2, loaded [template = constants.%C.3]
+// CHECK:STDOUT:   %C.decl.3: invalid = class_decl @C.3 [template = constants.%C.3] {}
+// CHECK:STDOUT:   %import_ref.4 = import_ref ir4, inst+3, unloaded
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C.1;
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C.2 {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.4
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C.3 {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.4
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_import_extern_decl_copy.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C.1: type = class_type @C.1 [template]
+// CHECK:STDOUT:   %C.2: type = class_type @C.2 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .C = %import_ref.1
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir2, inst+2, loaded [template = constants.%C.2]
+// CHECK:STDOUT:   %import_ref.2: type = import_ref ir3, inst+2, loaded [template = constants.%C.1]
+// CHECK:STDOUT:   %C.decl.1: invalid = class_decl @C.1 [template = constants.%C.1] {}
+// CHECK:STDOUT:   %C.decl.2: invalid = class_decl @C.2 [template = constants.%C.2] {}
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C.1;
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C.2;
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_extern_decl_after_import_extern_decl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = class_type @.1 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .C = %import_ref
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref: type = import_ref ir2, inst+2, loaded [template = constants.%C]
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %.decl: type = class_decl @.1 [template = constants.%.1] {
+// CHECK:STDOUT:     %C.decl: invalid = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C;
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @.1;
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_decl_after_import_extern_decl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = class_type @.1 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .C = %import_ref
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref: type = import_ref ir2, inst+2, loaded [template = constants.%C]
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %.decl: type = class_decl @.1 [template = constants.%.1] {
+// CHECK:STDOUT:     %C.decl: invalid = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C;
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @.1;
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_def_after_import_extern_decl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: type = class_type @.1 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .C = %import_ref.1
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir2, inst+2, loaded [template = constants.%C]
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir2, inst+3, unloaded
+// CHECK:STDOUT:   %.decl: type = class_decl @.1 [template = constants.%.2] {
+// CHECK:STDOUT:     %C.decl: invalid = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @.1;
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_extern_decl_after_import_def.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: type = class_type @.1 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .C = %import_ref.1
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir2, inst+2, loaded [template = constants.%C]
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir2, inst+3, unloaded
+// CHECK:STDOUT:   %.decl: type = class_decl @.1 [template = constants.%.2] {
+// CHECK:STDOUT:     %C.decl: invalid = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @.1;
+// CHECK:STDOUT:

+ 1 - 1
toolchain/check/testdata/class/fail_modifiers.carbon

@@ -74,7 +74,7 @@ abstract protected class WrongOrder;
 // CHECK:STDERR:
 abstract base class AbstractAndBase {}
 
-// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+3]]:1: ERROR: `extern` not allowed on `class` declaration.
+// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+3]]:1: ERROR: `extern` not allowed on `class` declaration that provides a definition.
 // CHECK:STDERR: extern class ExternDefined {}
 // CHECK:STDERR: ^~~~~~
 extern class ExternDefined {}

+ 125 - 64
toolchain/check/testdata/class/fail_redeclaration_introducer.carbon

@@ -4,123 +4,104 @@
 //
 // AUTOUPDATE
 
+// --- fail_add_base.carbon
+
+library "fail_add_base" api;
+
 class A;
-// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE+7]]:1: ERROR: Class redeclared with different inheritance kind.
+// CHECK:STDERR: fail_add_base.carbon:[[@LINE+7]]:1: ERROR: Class redeclared with different inheritance kind.
 // CHECK:STDERR: base class A {}
 // CHECK:STDERR: ^~~~~~~~~~~~~~
-// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE-4]]:1: Previously declared here.
+// CHECK:STDERR: fail_add_base.carbon:[[@LINE-4]]:1: Previously declared here.
 // CHECK:STDERR: class A;
 // CHECK:STDERR: ^~~~~~~~
 // CHECK:STDERR:
 base class A {}
 
+// --- fail_add_abstract.carbon
+
+library "fail_add_abstract" api;
+
 class B;
-// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE+7]]:1: ERROR: Class redeclared with different inheritance kind.
+// CHECK:STDERR: fail_add_abstract.carbon:[[@LINE+7]]:1: ERROR: Class redeclared with different inheritance kind.
 // CHECK:STDERR: abstract class B {}
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~
-// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE-4]]:1: Previously declared here.
+// CHECK:STDERR: fail_add_abstract.carbon:[[@LINE-4]]:1: Previously declared here.
 // CHECK:STDERR: class B;
 // CHECK:STDERR: ^~~~~~~~
 // CHECK:STDERR:
 abstract class B {}
 
+// --- fail_remove_base.carbon
+
+library "fail_remove_base" api;
+
 base class C;
-// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE+7]]:1: ERROR: Class redeclared with different inheritance kind.
+// CHECK:STDERR: fail_remove_base.carbon:[[@LINE+7]]:1: ERROR: Class redeclared with different inheritance kind.
 // CHECK:STDERR: class C {}
 // CHECK:STDERR: ^~~~~~~~~
-// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE-4]]:1: Previously declared here.
+// CHECK:STDERR: fail_remove_base.carbon:[[@LINE-4]]:1: Previously declared here.
 // CHECK:STDERR: base class C;
 // CHECK:STDERR: ^~~~~~~~~~~~~
 // CHECK:STDERR:
 class C {}
 
+// --- fail_base_to_abstract.carbon
+
+library "fail_base_to_abstract" api;
+
 base class D;
-// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE+7]]:1: ERROR: Class redeclared with different inheritance kind.
+// CHECK:STDERR: fail_base_to_abstract.carbon:[[@LINE+7]]:1: ERROR: Class redeclared with different inheritance kind.
 // CHECK:STDERR: abstract class D {}
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~
-// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE-4]]:1: Previously declared here.
+// CHECK:STDERR: fail_base_to_abstract.carbon:[[@LINE-4]]:1: Previously declared here.
 // CHECK:STDERR: base class D;
 // CHECK:STDERR: ^~~~~~~~~~~~~
 // CHECK:STDERR:
 abstract class D {}
 
+// --- fail_remove_abstract.carbon
+
+library "fail_remove_abstract" api;
+
 abstract class E;
-// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE+7]]:1: ERROR: Class redeclared with different inheritance kind.
+// CHECK:STDERR: fail_remove_abstract.carbon:[[@LINE+7]]:1: ERROR: Class redeclared with different inheritance kind.
 // CHECK:STDERR: class E {}
 // CHECK:STDERR: ^~~~~~~~~
-// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE-4]]:1: Previously declared here.
+// CHECK:STDERR: fail_remove_abstract.carbon:[[@LINE-4]]:1: Previously declared here.
 // CHECK:STDERR: abstract class E;
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~
 // CHECK:STDERR:
 class E {}
 
+// --- fail_abstract_to_base.carbon
+
+library "fail_abstract_to_base" api;
+
 abstract class F;
-// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE+7]]:1: ERROR: Class redeclared with different inheritance kind.
+// CHECK:STDERR: fail_abstract_to_base.carbon:[[@LINE+6]]:1: ERROR: Class redeclared with different inheritance kind.
 // CHECK:STDERR: base class F {}
 // CHECK:STDERR: ^~~~~~~~~~~~~~
-// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE-4]]:1: Previously declared here.
+// CHECK:STDERR: fail_abstract_to_base.carbon:[[@LINE-4]]:1: Previously declared here.
 // CHECK:STDERR: abstract class F;
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~
-// CHECK:STDERR:
 base class F {}
 
-class G {}
-// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE+7]]:1: ERROR: Class redeclared with different inheritance kind.
-// CHECK:STDERR: abstract class G;
-// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
-// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE-4]]:1: Previously declared here.
-// CHECK:STDERR: class G {}
-// CHECK:STDERR: ^~~~~~~~~
-// CHECK:STDERR:
-abstract class G;
-// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE+6]]:1: ERROR: Class redeclared with different inheritance kind.
-// CHECK:STDERR: base class G;
-// CHECK:STDERR: ^~~~~~~~~~~~~
-// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE-12]]:1: Previously declared here.
-// CHECK:STDERR: class G {}
-// CHECK:STDERR: ^~~~~~~~~
-base class G;
-
-// CHECK:STDOUT: --- fail_redeclaration_introducer.carbon
+// CHECK:STDOUT: --- fail_add_base.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %A: type = class_type @A [template]
 // CHECK:STDOUT:   %.1: type = struct_type {} [template]
-// CHECK:STDOUT:   %B: type = class_type @B [template]
-// CHECK:STDOUT:   %C: type = class_type @C [template]
-// CHECK:STDOUT:   %D: type = class_type @D [template]
-// CHECK:STDOUT:   %E: type = class_type @E [template]
-// CHECK:STDOUT:   %F: type = class_type @F [template]
-// CHECK:STDOUT:   %G: type = class_type @G [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
 // CHECK:STDOUT:     .Core = %Core
-// CHECK:STDOUT:     .A = %A.decl.loc7
-// CHECK:STDOUT:     .B = %B.decl.loc17
-// CHECK:STDOUT:     .C = %C.decl.loc27
-// CHECK:STDOUT:     .D = %D.decl.loc37
-// CHECK:STDOUT:     .E = %E.decl.loc47
-// CHECK:STDOUT:     .F = %F.decl.loc57
-// CHECK:STDOUT:     .G = %G.decl.loc67
+// CHECK:STDOUT:     .A = %A.decl.loc4
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
-// CHECK:STDOUT:   %A.decl.loc7: type = class_decl @A [template = constants.%A] {}
-// CHECK:STDOUT:   %A.decl.loc15: type = class_decl @A [template = constants.%A] {}
-// CHECK:STDOUT:   %B.decl.loc17: type = class_decl @B [template = constants.%B] {}
-// CHECK:STDOUT:   %B.decl.loc25: type = class_decl @B [template = constants.%B] {}
-// CHECK:STDOUT:   %C.decl.loc27: type = class_decl @C [template = constants.%C] {}
-// CHECK:STDOUT:   %C.decl.loc35: type = class_decl @C [template = constants.%C] {}
-// CHECK:STDOUT:   %D.decl.loc37: type = class_decl @D [template = constants.%D] {}
-// CHECK:STDOUT:   %D.decl.loc45: type = class_decl @D [template = constants.%D] {}
-// CHECK:STDOUT:   %E.decl.loc47: type = class_decl @E [template = constants.%E] {}
-// CHECK:STDOUT:   %E.decl.loc55: type = class_decl @E [template = constants.%E] {}
-// CHECK:STDOUT:   %F.decl.loc57: type = class_decl @F [template = constants.%F] {}
-// CHECK:STDOUT:   %F.decl.loc65: type = class_decl @F [template = constants.%F] {}
-// CHECK:STDOUT:   %G.decl.loc67: type = class_decl @G [template = constants.%G] {}
-// CHECK:STDOUT:   %G.decl.loc75: type = class_decl @G [template = constants.%G] {}
-// CHECK:STDOUT:   %G.decl.loc82: type = class_decl @G [template = constants.%G] {}
+// CHECK:STDOUT:   %A.decl.loc4: type = class_decl @A [template = constants.%A] {}
+// CHECK:STDOUT:   %A.decl.loc12: type = class_decl @A [template = constants.%A] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @A {
@@ -128,33 +109,113 @@ base class G;
 // CHECK:STDOUT:   .Self = constants.%A
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_add_abstract.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %B: type = class_type @B [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .B = %B.decl.loc4
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %B.decl.loc4: type = class_decl @B [template = constants.%B] {}
+// CHECK:STDOUT:   %B.decl.loc12: type = class_decl @B [template = constants.%B] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: class @B {
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = constants.%B
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_remove_base.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl.loc4
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl.loc4: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   %C.decl.loc12: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: class @C {
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = constants.%C
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_base_to_abstract.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %D: type = class_type @D [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .D = %D.decl.loc4
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %D.decl.loc4: type = class_decl @D [template = constants.%D] {}
+// CHECK:STDOUT:   %D.decl.loc12: type = class_decl @D [template = constants.%D] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: class @D {
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = constants.%D
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_remove_abstract.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %E: type = class_type @E [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .E = %E.decl.loc4
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %E.decl.loc4: type = class_decl @E [template = constants.%E] {}
+// CHECK:STDOUT:   %E.decl.loc12: type = class_decl @E [template = constants.%E] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: class @E {
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = constants.%E
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @F {
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = constants.%F
+// CHECK:STDOUT: --- fail_abstract_to_base.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F: type = class_type @F [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @G {
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .F = %F.decl.loc4
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %F.decl.loc4: type = class_decl @F [template = constants.%F] {}
+// CHECK:STDOUT:   %F.decl.loc11: type = class_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @F {
 // CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = constants.%G
+// CHECK:STDOUT:   .Self = constants.%F
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 27 - 31
toolchain/check/testdata/class/fail_redefinition.carbon

@@ -10,40 +10,30 @@ class Class {
   fn I() {}
 }
 
-// CHECK:STDERR: fail_redefinition.carbon:[[@LINE+7]]:1: ERROR: Redefinition of class Class.
+// CHECK:STDERR: fail_redefinition.carbon:[[@LINE+7]]:1: ERROR: Redefinition of `class Class`.
 // CHECK:STDERR: class Class {
 // CHECK:STDERR: ^~~~~~~~~~~~~
-// CHECK:STDERR: fail_redefinition.carbon:[[@LINE-9]]:1: Previous definition was here.
+// CHECK:STDERR: fail_redefinition.carbon:[[@LINE-9]]:1: Previously defined here.
 // CHECK:STDERR: class Class {
 // CHECK:STDERR: ^~~~~~~~~~~~~
 // CHECK:STDERR:
 class Class {
   fn G();
-  // CHECK:STDERR: fail_redefinition.carbon:[[@LINE+7]]:3: ERROR: Redeclaration of `fn H` is redundant.
-  // CHECK:STDERR:   fn H();
-  // CHECK:STDERR:   ^~~~~~~
-  // CHECK:STDERR: fail_redefinition.carbon:[[@LINE-16]]:3: Previously declared here.
-  // CHECK:STDERR:   fn H();
-  // CHECK:STDERR:   ^~~~~~~
-  // CHECK:STDERR:
   fn H();
-  // CHECK:STDERR: fail_redefinition.carbon:[[@LINE+7]]:3: ERROR: Redefinition of `fn I`.
-  // CHECK:STDERR:   fn I() {}
-  // CHECK:STDERR:   ^~~~~~~~
-  // CHECK:STDERR: fail_redefinition.carbon:[[@LINE-23]]:3: Previously defined here.
-  // CHECK:STDERR:   fn I() {}
-  // CHECK:STDERR:   ^~~~~~~~
-  // CHECK:STDERR:
   fn I() {}
 }
 
+// CHECK:STDERR: fail_redefinition.carbon:[[@LINE+4]]:10: ERROR: Out-of-line declaration requires a declaration in scoped entity.
+// CHECK:STDERR: fn Class.F() {}
+// CHECK:STDERR:          ^
+// CHECK:STDERR:
 fn Class.F() {}
 fn Class.G() {}
 fn Class.H() {}
 // CHECK:STDERR: fail_redefinition.carbon:[[@LINE+6]]:1: ERROR: Redefinition of `fn I`.
 // CHECK:STDERR: fn Class.I() {}
 // CHECK:STDERR: ^~~~~~~~~~~~~~
-// CHECK:STDERR: fail_redefinition.carbon:[[@LINE-9]]:3: Previously defined here.
+// CHECK:STDERR: fail_redefinition.carbon:[[@LINE-13]]:3: Previously defined here.
 // CHECK:STDERR:   fn I() {}
 // CHECK:STDERR:   ^~~~~~~~
 fn Class.I() {}
@@ -63,47 +53,53 @@ fn Class.I() {}
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
 // CHECK:STDOUT:   %Class.decl.loc7: type = class_decl @Class [template = constants.%Class] {}
 // CHECK:STDOUT:   %Class.decl.loc20: type = class_decl @Class [template = constants.%Class] {}
-// CHECK:STDOUT:   %F: <function> = fn_decl @F [template] {}
+// CHECK:STDOUT:   %F: <function> = fn_decl @F.2 [template] {}
 // CHECK:STDOUT:   %G: <function> = fn_decl @G [template] {}
-// CHECK:STDOUT:   %H: <function> = fn_decl @H [template] {}
-// CHECK:STDOUT:   %I: <function> = fn_decl @I [template] {}
+// CHECK:STDOUT:   %H: <function> = fn_decl @H.2 [template] {}
+// CHECK:STDOUT:   %I: <function> = fn_decl @I.2 [template] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Class {
 // CHECK:STDOUT:   %G: <function> = fn_decl @G [template] {}
-// CHECK:STDOUT:   %H: <function> = fn_decl @H [template] {}
-// CHECK:STDOUT:   %I: <function> = fn_decl @I [template] {}
+// CHECK:STDOUT:   %H: <function> = fn_decl @H.2 [template] {}
+// CHECK:STDOUT:   %I: <function> = fn_decl @I.2 [template] {}
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = constants.%Class
-// CHECK:STDOUT:   .F = <unexpected instref inst+4>
-// CHECK:STDOUT:   .H = <unexpected instref inst+5>
-// CHECK:STDOUT:   .I = <unexpected instref inst+6>
 // CHECK:STDOUT:   .G = %G
+// CHECK:STDOUT:   .H = %H
+// CHECK:STDOUT:   .I = %I
+// CHECK:STDOUT:   .F = file.%F
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: fn @F.1();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @H.1();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @I.1() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @H() {
+// CHECK:STDOUT: fn @G() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @I() {
+// CHECK:STDOUT: fn @H.2() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
+// CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: !.loc37:
+// CHECK:STDOUT: fn @I.2() {
+// CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT:
-// CHECK:STDOUT: !.loc49:
+// CHECK:STDOUT: !.loc39:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @G() {
+// CHECK:STDOUT: fn @F.2() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 0 - 2
toolchain/diagnostics/diagnostic_kind.def

@@ -191,10 +191,8 @@ CARBON_DIAGNOSTIC_KIND(AdaptFieldHere)
 CARBON_DIAGNOSTIC_KIND(BaseIsFinal)
 CARBON_DIAGNOSTIC_KIND(BaseMissingExtend)
 CARBON_DIAGNOSTIC_KIND(ClassForwardDeclaredHere)
-CARBON_DIAGNOSTIC_KIND(ClassPreviousDefinition)
 CARBON_DIAGNOSTIC_KIND(ClassRedeclarationDifferentIntroducer)
 CARBON_DIAGNOSTIC_KIND(ClassRedeclarationDifferentIntroducerPrevious)
-CARBON_DIAGNOSTIC_KIND(ClassRedefinition)
 CARBON_DIAGNOSTIC_KIND(ClassSpecificDeclOutsideClass)
 CARBON_DIAGNOSTIC_KIND(ClassSpecificDeclPrevious)
 CARBON_DIAGNOSTIC_KIND(ClassSpecificDeclRepeated)