Explorar el Código

Implement 'extern library' support for functions. (#4220)

Support for types (particularly classes) is left as a TODO.

There's also an issue I'm observing with a "define in impl" test, but
this is probably an issue with resolving the prior declaration which is
imported indirectly. The PR was already feeling big, so I'm choosing to
cut here.

Note, this does not implement the rule "The owning library's API file
must import the `extern` declaration, and must also contain a
declaration."
Jon Ross-Perkins hace 1 año
padre
commit
2d3842fc06
Se han modificado 34 ficheros con 1729 adiciones y 937 borrados
  1. 3 0
      toolchain/check/decl_introducer_state.h
  2. 7 4
      toolchain/check/decl_name_stack.h
  3. 1 1
      toolchain/check/global_init.cpp
  4. 18 18
      toolchain/check/handle_class.cpp
  5. 7 9
      toolchain/check/handle_function.cpp
  6. 15 5
      toolchain/check/handle_import_and_package.cpp
  7. 8 10
      toolchain/check/handle_interface.cpp
  8. 23 8
      toolchain/check/handle_modifier.cpp
  9. 1 1
      toolchain/check/handle_struct.cpp
  10. 22 5
      toolchain/check/import_ref.cpp
  11. 43 14
      toolchain/check/merge.cpp
  12. 9 0
      toolchain/check/merge.h
  13. 25 3
      toolchain/check/modifiers.cpp
  14. 2 2
      toolchain/check/modifiers.h
  15. 18 3
      toolchain/check/node_stack.h
  16. 15 0
      toolchain/check/sem_ir_diagnostic_converter.cpp
  17. 1 1
      toolchain/check/testdata/class/fail_import_misuses.carbon
  18. 2 16
      toolchain/check/testdata/class/fail_modifiers.carbon
  19. 95 305
      toolchain/check/testdata/class/no_prelude/extern.carbon
  20. 32 0
      toolchain/check/testdata/class/no_prelude/extern_library.carbon
  21. 185 477
      toolchain/check/testdata/function/declaration/import.carbon
  22. 44 18
      toolchain/check/testdata/function/declaration/no_prelude/extern.carbon
  23. 381 0
      toolchain/check/testdata/function/declaration/no_prelude/extern_library.carbon
  24. 102 0
      toolchain/check/testdata/function/declaration/no_prelude/extern_library_for_default.carbon
  25. 100 0
      toolchain/check/testdata/function/declaration/no_prelude/extern_library_from_default.carbon
  26. 1 5
      toolchain/check/testdata/function/declaration/no_prelude/fail_modifiers.carbon
  27. 2 2
      toolchain/check/testdata/function/declaration/no_prelude/implicit_import.carbon
  28. 34 12
      toolchain/check/testdata/function/definition/import.carbon
  29. 125 11
      toolchain/check/testdata/function/definition/no_prelude/extern.carbon
  30. 382 0
      toolchain/check/testdata/function/definition/no_prelude/extern_library.carbon
  31. 10 4
      toolchain/check/testdata/function/definition/no_prelude/implicit_import.carbon
  32. 1 1
      toolchain/check/testdata/packages/no_prelude/cross_package_import.carbon
  33. 5 1
      toolchain/diagnostics/diagnostic_kind.def
  34. 10 1
      toolchain/sem_ir/entity_with_params_base.h

+ 3 - 0
toolchain/check/decl_introducer_state.h

@@ -35,6 +35,9 @@ struct DeclIntroducerState {
 
   // Invariant: contains just the modifiers represented by `saw_*_modifier`.
   KeywordModifierSet modifier_set = KeywordModifierSet();
+
+  // If there's an `extern library` in use, the library name.
+  SemIR::LibraryNameId extern_library = SemIR::LibraryNameId::Invalid;
 };
 
 // Stack of `DeclIntroducerState` values, representing all the declaration

+ 7 - 4
toolchain/check/decl_name_stack.h

@@ -91,7 +91,8 @@ class DeclNameStack {
     // Combines name information to produce a base struct for entity
     // construction.
     auto MakeEntityWithParamsBase(const NameComponent& name,
-                                  SemIR::InstId decl_id, bool is_extern)
+                                  SemIR::InstId decl_id, bool is_extern,
+                                  SemIR::LibraryNameId extern_library)
         -> SemIR::EntityWithParamsBase {
       return {
           .name_id = name_id_for_new_inst(),
@@ -102,9 +103,11 @@ class DeclNameStack {
           .implicit_param_refs_id = name.implicit_params_id,
           .param_refs_id = name.params_id,
           .is_extern = is_extern,
-          .extern_library_id = StringLiteralValueId::Invalid,
-          .non_owning_decl_id = SemIR::InstId::Invalid,
-          .first_owning_decl_id = decl_id,
+          .extern_library_id = extern_library,
+          .non_owning_decl_id =
+              extern_library.is_valid() ? decl_id : SemIR::InstId::Invalid,
+          .first_owning_decl_id =
+              extern_library.is_valid() ? SemIR::InstId::Invalid : decl_id,
       };
     }
 

+ 1 - 1
toolchain/check/global_init.cpp

@@ -43,7 +43,7 @@ auto GlobalInit::Finalize() -> void {
         .implicit_param_refs_id = SemIR::InstBlockId::Invalid,
         .param_refs_id = SemIR::InstBlockId::Empty,
         .is_extern = false,
-        .extern_library_id = StringLiteralValueId::Invalid,
+        .extern_library_id = SemIR::LibraryNameId::Invalid,
         .non_owning_decl_id = SemIR::InstId::Invalid,
         .first_owning_decl_id = SemIR::InstId::Invalid},
        {.return_storage_id = SemIR::InstId::Invalid,

+ 18 - 18
toolchain/check/handle_class.cpp

@@ -52,8 +52,8 @@ auto HandleParseNode(Context& context, Parse::ClassIntroducerId node_id)
 // Otherwise, returns false. Prints a diagnostic when appropriate.
 static 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,
+                             bool new_is_definition,
+                             SemIR::ClassId prev_class_id,
                              SemIR::ImportIRId prev_import_ir_id) -> bool {
   auto& prev_class = context.classes().Get(prev_class_id);
   SemIRLoc prev_loc = prev_class.latest_decl_id();
@@ -64,14 +64,11 @@ static auto MergeClassRedecl(Context& context, SemIRLoc new_loc,
     return false;
   }
 
-  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_id);
+  CheckIsAllowedRedecl(
+      context, Lex::TokenKind::Class, prev_class.name_id,
+      RedeclInfo(new_class, new_loc, new_is_definition),
+      RedeclInfo(prev_class, prev_loc, prev_class.is_defined()),
+      prev_import_ir_id);
 
   if (new_is_definition && prev_class.is_defined()) {
     // Don't attempt to merge multiple definitions.
@@ -101,7 +98,7 @@ static auto MergeClassRedecl(Context& context, SemIRLoc new_loc,
   }
 
   if ((prev_import_ir_id.is_valid() && !new_is_import) ||
-      (prev_is_extern && !new_is_extern)) {
+      (prev_class.is_extern && !new_class.is_extern)) {
     prev_class.first_owning_decl_id = new_class.first_owning_decl_id;
     ReplacePrevInstForMerge(
         context, prev_class.parent_scope_id, prev_class.name_id,
@@ -117,8 +114,7 @@ static auto MergeOrAddName(Context& context, Parse::AnyClassDeclId node_id,
                            SemIR::InstId class_decl_id,
                            SemIR::ClassDecl& class_decl,
                            SemIR::Class& class_info, bool is_definition,
-                           bool is_extern, SemIR::AccessKind access_kind)
-    -> void {
+                           SemIR::AccessKind access_kind) -> void {
   auto prev_id = context.decl_name_stack().LookupOrAddName(
       name_context, class_decl_id, access_kind);
   if (!prev_id.is_valid()) {
@@ -168,10 +164,10 @@ static auto MergeOrAddName(Context& context, Parse::AnyClassDeclId node_id,
     return;
   }
 
-  // TODO: Fix prev_is_extern logic.
+  // TODO: Fix `extern` logic. It doesn't work correctly, but doesn't seem worth
+  // ripping out because existing code may incrementally help.
   if (MergeClassRedecl(context, node_id, class_info,
-                       /*new_is_import=*/false, is_definition, is_extern,
-                       prev_class_id, /*prev_is_extern=*/false,
+                       /*new_is_import=*/false, is_definition, prev_class_id,
                        prev_import_ir_id)) {
     // When merging, use the existing entity rather than adding a new one.
     class_decl.class_id = prev_class_id;
@@ -201,6 +197,9 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
                                is_definition);
 
   bool is_extern = introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extern);
+  if (introducer.extern_library.is_valid()) {
+    context.TODO(node_id, "extern library");
+  }
   auto inheritance_kind =
       introducer.modifier_set.HasAnyOf(KeywordModifierSet::Abstract)
           ? SemIR::Class::Abstract
@@ -219,7 +218,8 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
 
   // TODO: Store state regarding is_extern.
   SemIR::Class class_info = {
-      name_context.MakeEntityWithParamsBase(name, class_decl_id, is_extern),
+      name_context.MakeEntityWithParamsBase(name, class_decl_id, is_extern,
+                                            SemIR::LibraryNameId::Invalid),
       {// `.self_type_id` depends on the ClassType, so is set below.
        .self_type_id = SemIR::TypeId::Invalid,
        .inheritance_kind = inheritance_kind}};
@@ -228,7 +228,7 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
   RequireGenericParams(context, class_info.param_refs_id);
 
   MergeOrAddName(context, node_id, name_context, class_decl_id, class_decl,
-                 class_info, is_definition, is_extern,
+                 class_info, is_definition,
                  introducer.modifier_set.GetAccessKind());
 
   // Create a new class if this isn't a valid redeclaration.

+ 7 - 9
toolchain/check/handle_function.cpp

@@ -82,12 +82,9 @@ static auto MergeFunctionRedecl(Context& context, SemIRLoc new_loc,
   }
 
   CheckIsAllowedRedecl(context, Lex::TokenKind::Fn, prev_function.name_id,
-                       {.loc = new_loc,
-                        .is_definition = new_is_definition,
-                        .is_extern = new_function.is_extern},
-                       {.loc = prev_function.latest_decl_id(),
-                        .is_definition = prev_function.definition_id.is_valid(),
-                        .is_extern = prev_function.is_extern},
+                       RedeclInfo(new_function, new_loc, new_is_definition),
+                       RedeclInfo(prev_function, prev_function.latest_decl_id(),
+                                  prev_function.definition_id.is_valid()),
                        prev_import_ir_id);
 
   if (new_is_definition) {
@@ -216,9 +213,10 @@ static auto BuildFunctionDecl(Context& context,
 
   // 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)},
-      {.return_storage_id = return_storage_id}};
+  auto function_info =
+      SemIR::Function{{name_context.MakeEntityWithParamsBase(
+                          name, decl_id, is_extern, introducer.extern_library)},
+                      {.return_storage_id = return_storage_id}};
   if (is_definition) {
     function_info.definition_id = decl_id;
   }

+ 15 - 5
toolchain/check/handle_import_and_package.cpp

@@ -20,6 +20,7 @@ auto HandleParseNode(Context& context, Parse::ImportIntroducerId /*node_id*/)
 
 auto HandleParseNode(Context& context, Parse::ImportDeclId /*node_id*/)
     -> bool {
+  context.node_stack().PopIf<SemIR::LibraryNameId>();
   auto introducer =
       context.decl_introducer_state_stack().Pop<Lex::TokenKind::Import>();
   LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Export);
@@ -34,6 +35,7 @@ auto HandleParseNode(Context& context, Parse::LibraryIntroducerId /*node_id*/)
 
 auto HandleParseNode(Context& context, Parse::LibraryDeclId /*node_id*/)
     -> bool {
+  context.node_stack().PopIf<SemIR::LibraryNameId>();
   auto introducer =
       context.decl_introducer_state_stack().Pop<Lex::TokenKind::Library>();
   LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Impl);
@@ -48,14 +50,16 @@ auto HandleParseNode(Context& context, Parse::PackageIntroducerId /*node_id*/)
 
 auto HandleParseNode(Context& context, Parse::PackageDeclId /*node_id*/)
     -> bool {
+  context.node_stack().PopIf<SemIR::LibraryNameId>();
   auto introducer =
       context.decl_introducer_state_stack().Pop<Lex::TokenKind::Package>();
   LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Impl);
   return true;
 }
 
-auto HandleParseNode(Context& /*context*/,
-                     Parse::LibrarySpecifierId /*node_id*/) -> bool {
+auto HandleParseNode(Context& context, Parse::LibrarySpecifierId /*node_id*/)
+    -> bool {
+  CARBON_CHECK(context.node_stack().PeekIs<SemIR::LibraryNameId>());
   return true;
 }
 
@@ -64,13 +68,19 @@ auto HandleParseNode(Context& /*context*/, Parse::PackageNameId /*node_id*/)
   return true;
 }
 
-auto HandleParseNode(Context& /*context*/, Parse::LibraryNameId /*node_id*/)
-    -> bool {
+auto HandleParseNode(Context& context, Parse::LibraryNameId node_id) -> bool {
+  // This is discarded in this file's uses, but is used by modifiers for `extern
+  // library`.
+  auto literal_id = context.tokens().GetStringLiteralValue(
+      context.parse_tree().node_token(node_id));
+  context.node_stack().Push(
+      node_id, SemIR::LibraryNameId::ForStringLiteralValueId(literal_id));
   return true;
 }
 
-auto HandleParseNode(Context& /*context*/, Parse::DefaultLibraryId /*node_id*/)
+auto HandleParseNode(Context& context, Parse::DefaultLibraryId node_id)
     -> bool {
+  context.node_stack().Push(node_id, SemIR::LibraryNameId::Default);
   return true;
 }
 

+ 8 - 10
toolchain/check/handle_interface.cpp

@@ -54,7 +54,8 @@ static auto BuildInterfaceDecl(Context& context,
       context.AddPlaceholderInst(SemIR::LocIdAndInst(node_id, interface_decl));
 
   SemIR::Interface interface_info = {name_context.MakeEntityWithParamsBase(
-      name, interface_decl_id, /*is_extern=*/false)};
+      name, interface_decl_id, /*is_extern=*/false,
+      SemIR::LibraryNameId::Invalid)};
   RequireGenericParams(context, interface_info.implicit_param_refs_id);
   RequireGenericParams(context, interface_info.param_refs_id);
 
@@ -75,15 +76,12 @@ static auto BuildInterfaceDecl(Context& context,
         // TODO: This should be refactored a little, particularly for
         // prev_import_ir_id. See similar logic for classes and functions, which
         // might also be refactored to merge.
-        CheckIsAllowedRedecl(context, Lex::TokenKind::Interface,
-                             existing_interface.name_id,
-                             {.loc = node_id,
-                              .is_definition = is_definition,
-                              .is_extern = false},
-                             {.loc = existing_interface.latest_decl_id(),
-                              .is_definition = existing_interface.is_defined(),
-                              .is_extern = false},
-                             /*prev_import_ir_id=*/SemIR::ImportIRId::Invalid);
+        CheckIsAllowedRedecl(
+            context, Lex::TokenKind::Interface, existing_interface.name_id,
+            RedeclInfo(interface_info, node_id, is_definition),
+            RedeclInfo(existing_interface, existing_interface.latest_decl_id(),
+                       existing_interface.is_defined()),
+            /*prev_import_ir_id=*/SemIR::ImportIRId::Invalid);
 
         // Can't merge interface definitions due to the generic requirements.
         // TODO: Should this also be mirrored to classes/functions for generics?

+ 23 - 8
toolchain/check/handle_modifier.cpp

@@ -34,6 +34,10 @@ static auto DiagnoseNotAllowedWith(Context& context, Parse::NodeId first_node,
       .Emit();
 }
 
+// Handles the keyword that starts a modifier. This may a standalone keyword,
+// such as `private`, or the first in a complex modifier, such as `extern` in
+// `extern library ...`. If valid, adds it to the modifier set and returns true.
+// Otherwise, diagnoses and returns false.
 static auto HandleModifier(Context& context, Parse::NodeId node_id,
                            KeywordModifierSet keyword) -> bool {
   auto& s = context.decl_introducer_state_stack().innermost();
@@ -54,10 +58,14 @@ static auto HandleModifier(Context& context, Parse::NodeId node_id,
   auto current_modifier_node_id = s.modifier_node_id(order);
   if (s.modifier_set.HasAnyOf(keyword)) {
     DiagnoseRepeated(context, current_modifier_node_id, node_id);
-  } else if (current_modifier_node_id.is_valid()) {
+    return false;
+  }
+  if (current_modifier_node_id.is_valid()) {
     DiagnoseNotAllowedWith(context, current_modifier_node_id, node_id);
-  } else if (auto later_modifier_set = s.modifier_set & later_modifiers;
-             !later_modifier_set.empty()) {
+    return false;
+  }
+  if (auto later_modifier_set = s.modifier_set & later_modifiers;
+      !later_modifier_set.empty()) {
     // At least one later modifier is present. Diagnose using the closest.
     Parse::NodeId closest_later_modifier = Parse::NodeId::Invalid;
     for (auto later_order = static_cast<int8_t>(order) + 1;
@@ -79,10 +87,11 @@ static auto HandleModifier(Context& context, Parse::NodeId node_id,
         .Note(closest_later_modifier, ModifierPrevious,
               context.token_kind(closest_later_modifier))
         .Emit();
-  } else {
-    s.modifier_set.Add(keyword);
-    s.set_modifier_node_id(order, node_id);
+    return false;
   }
+
+  s.modifier_set.Add(keyword);
+  s.set_modifier_node_id(order, node_id);
   return true;
 }
 
@@ -90,13 +99,19 @@ static auto HandleModifier(Context& context, Parse::NodeId node_id,
 #define CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Name, ...)                  \
   auto HandleParseNode(Context& context, Parse::Name##ModifierId node_id) \
       -> bool {                                                           \
-    return HandleModifier(context, node_id, KeywordModifierSet::Name);    \
+    HandleModifier(context, node_id, KeywordModifierSet::Name);           \
+    return true;                                                          \
   }
 #include "toolchain/parse/node_kind.def"
 
 auto HandleParseNode(Context& context,
                      Parse::ExternModifierWithLibraryId node_id) -> bool {
-  return context.TODO(node_id, "extern library syntax");
+  auto name_literal_id = context.node_stack().Pop<SemIR::LibraryNameId>();
+  if (HandleModifier(context, node_id, KeywordModifierSet::Extern)) {
+    auto& s = context.decl_introducer_state_stack().innermost();
+    s.extern_library = name_literal_id;
+  }
+  return true;
 }
 
 auto HandleParseNode(Context& context, Parse::ExternModifierId node_id)

+ 1 - 1
toolchain/check/handle_struct.cpp

@@ -29,7 +29,7 @@ auto HandleParseNode(Context& context, Parse::StructLiteralStartId node_id)
 auto HandleParseNode(Context& context,
                      Parse::StructFieldDesignatorId /*node_id*/) -> bool {
   // This leaves the designated name on top because the `.` isn't interesting.
-  CARBON_CHECK(context.node_stack().PeekIsName());
+  CARBON_CHECK(context.node_stack().PeekIs<SemIR::NameId>());
   return true;
 }
 

+ 22 - 5
toolchain/check/import_ref.cpp

@@ -844,6 +844,19 @@ class ImportRefResolver {
   auto GetIncompleteLocalEntityBase(
       SemIR::InstId decl_id, const SemIR::EntityWithParamsBase& import_base)
       -> SemIR::EntityWithParamsBase {
+    // Translate the extern_library_id if present.
+    auto extern_library_id = SemIR::LibraryNameId::Invalid;
+    if (import_base.extern_library_id.is_valid()) {
+      if (import_base.extern_library_id.index >= 0) {
+        auto val = import_ir_.string_literal_values().Get(
+            import_base.extern_library_id.AsStringLiteralValueId());
+        extern_library_id = SemIR::LibraryNameId::ForStringLiteralValueId(
+            context_.string_literal_values().Add(val));
+      } else {
+        extern_library_id = import_base.extern_library_id;
+      }
+    }
+
     return {
         .name_id = GetLocalNameId(import_base.name_id),
         .parent_scope_id = SemIR::NameScopeId::Invalid,
@@ -857,9 +870,13 @@ class ImportRefResolver {
                              ? SemIR::InstBlockId::Empty
                              : SemIR::InstBlockId::Invalid,
         .is_extern = import_base.is_extern,
-        .extern_library_id = StringLiteralValueId::Invalid,
-        .non_owning_decl_id = SemIR::InstId::Invalid,
-        .first_owning_decl_id = decl_id,
+        .extern_library_id = extern_library_id,
+        .non_owning_decl_id = import_base.non_owning_decl_id.is_valid()
+                                  ? decl_id
+                                  : SemIR::InstId::Invalid,
+        .first_owning_decl_id = import_base.first_owning_decl_id.is_valid()
+                                    ? decl_id
+                                    : SemIR::InstId::Invalid,
     };
   }
 
@@ -1373,7 +1390,7 @@ class ImportRefResolver {
         .decl_block_id = SemIR::InstBlockId::Empty};
     auto function_decl_id =
         context_.AddPlaceholderInstInNoBlock(SemIR::LocIdAndInst(
-            AddImportIRInst(import_function.latest_decl_id()), function_decl));
+            AddImportIRInst(import_function.first_decl_id()), function_decl));
 
     // Start with an incomplete function.
     function_decl.function_id = context_.functions().Add(
@@ -1471,7 +1488,7 @@ class ImportRefResolver {
   auto TryResolveTypedInst(SemIR::FunctionType inst) -> ResolveResult {
     CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
     auto fn_val_id = GetLocalConstantInstId(
-        import_ir_.functions().Get(inst.function_id).first_owning_decl_id);
+        import_ir_.functions().Get(inst.function_id).first_decl_id());
     auto specific_data = GetLocalSpecificData(inst.specific_id);
     if (HasNewWork()) {
       return Retry();

+ 43 - 14
toolchain/check/merge.cpp

@@ -44,8 +44,7 @@ static auto DiagnoseExternMismatch(Context& context, Lex::TokenKind decl_kind,
                                    SemIR::NameId name_id, SemIRLoc new_loc,
                                    SemIRLoc prev_loc) {
   CARBON_DIAGNOSTIC(RedeclExternMismatch, Error,
-                    "Redeclarations of `{0} {1}` in the same library must "
-                    "match use of `extern`.",
+                    "Redeclarations of `{0} {1}` must match use of `extern`.",
                     Lex::TokenKind, SemIR::NameId);
   context.emitter()
       .Build(new_loc, RedeclExternMismatch, decl_kind, name_id)
@@ -53,19 +52,36 @@ static auto DiagnoseExternMismatch(Context& context, Lex::TokenKind decl_kind,
       .Emit();
 }
 
-// Diagnoses when multiple non-`extern` declarations are found.
-static auto DiagnoseNonExtern(Context& context, Lex::TokenKind decl_kind,
-                              SemIR::NameId name_id, SemIRLoc new_loc,
-                              SemIRLoc prev_loc) {
-  CARBON_DIAGNOSTIC(RedeclNonExtern, Error,
-                    "Only one library can declare `{0} {1}` without `extern`.",
+// Diagnoses `extern library` declared in a library importing the owned entity.
+static auto DiagnoseExternLibraryInImporter(Context& context,
+                                            Lex::TokenKind decl_kind,
+                                            SemIR::NameId name_id,
+                                            SemIRLoc new_loc,
+                                            SemIRLoc prev_loc) {
+  CARBON_DIAGNOSTIC(ExternLibraryInImporter, Error,
+                    "Cannot declare imported `{0} {1}` as `extern library`.",
                     Lex::TokenKind, SemIR::NameId);
   context.emitter()
-      .Build(new_loc, RedeclNonExtern, decl_kind, name_id)
+      .Build(new_loc, ExternLibraryInImporter, decl_kind, name_id)
       .Note(prev_loc, RedeclPrevDecl)
       .Emit();
 }
 
+// Diagnoses `extern library` pointing to the wrong library.
+static auto DiagnoseExternLibraryIncorrect(Context& context, SemIRLoc new_loc,
+                                           SemIRLoc prev_loc) {
+  CARBON_DIAGNOSTIC(
+      ExternLibraryIncorrect, Error,
+      "Declaration in {0} doesn't match `extern library` declaration.",
+      SemIR::LibraryNameId);
+  CARBON_DIAGNOSTIC(ExternLibraryExpected, Note,
+                    "Previously declared with `extern library` here.");
+  context.emitter()
+      .Build(new_loc, ExternLibraryIncorrect, context.sem_ir().library_id())
+      .Note(prev_loc, ExternLibraryExpected)
+      .Emit();
+}
+
 // Checks to see if a structurally valid redeclaration is allowed in context.
 // These all still merge.
 auto CheckIsAllowedRedecl(Context& context, Lex::TokenKind decl_kind,
@@ -83,9 +99,7 @@ auto CheckIsAllowedRedecl(Context& context, Lex::TokenKind decl_kind,
       DiagnoseRedef(context, decl_kind, name_id, new_decl.loc, prev_decl.loc);
       return;
     }
-    // `extern` definitions are prevented at creation; this is only
-    // checking for a non-`extern` definition after an `extern` declaration.
-    if (prev_decl.is_extern) {
+    if (prev_decl.is_extern != new_decl.is_extern) {
       DiagnoseExternMismatch(context, decl_kind, name_id, new_decl.loc,
                              prev_decl.loc);
       return;
@@ -114,10 +128,25 @@ auto CheckIsAllowedRedecl(Context& context, Lex::TokenKind decl_kind,
   }
 
   // Check for disallowed redeclarations cross-library.
-  if (!new_decl.is_extern && !prev_decl.is_extern) {
-    DiagnoseNonExtern(context, decl_kind, name_id, new_decl.loc, prev_decl.loc);
+  if (prev_decl.is_extern != new_decl.is_extern) {
+    DiagnoseExternMismatch(context, decl_kind, name_id, new_decl.loc,
+                           prev_decl.loc);
     return;
   }
+  if (!prev_decl.extern_library_id.is_valid()) {
+    if (new_decl.extern_library_id.is_valid()) {
+      DiagnoseExternLibraryInImporter(context, decl_kind, name_id, new_decl.loc,
+                                      prev_decl.loc);
+    } else {
+      DiagnoseRedundant(context, decl_kind, name_id, new_decl.loc,
+                        prev_decl.loc);
+    }
+    return;
+  }
+  if (prev_decl.extern_library_id != SemIR::LibraryNameId::Error &&
+      prev_decl.extern_library_id != context.sem_ir().library_id()) {
+    DiagnoseExternLibraryIncorrect(context, new_decl.loc, prev_decl.loc);
+  }
 }
 
 auto ReplacePrevInstForMerge(Context& context, SemIR::NameScopeId scope_id,

+ 9 - 0
toolchain/check/merge.h

@@ -13,12 +13,21 @@ namespace Carbon::Check {
 
 // Information on new and previous declarations for CheckIsAllowedRedecl.
 struct RedeclInfo {
+  explicit RedeclInfo(SemIR::EntityWithParamsBase params, SemIRLoc loc,
+                      bool is_definition)
+      : loc(loc),
+        is_definition(is_definition),
+        is_extern(params.is_extern),
+        extern_library_id(params.extern_library_id) {}
+
   // The associated diagnostic location.
   SemIRLoc loc;
   // True if a definition.
   bool is_definition;
   // True if an `extern` declaration.
   bool is_extern;
+  // The library name in `extern library`, or invalid if not present.
+  SemIR::LibraryNameId extern_library_id;
 };
 
 // Checks if a redeclaration is allowed prior to merging. This may emit a

+ 25 - 3
toolchain/check/modifiers.cpp

@@ -120,13 +120,35 @@ auto RestrictExternModifierOnDecl(Context& context,
                                   DeclIntroducerState& introducer,
                                   std::optional<SemIR::Inst> parent_scope_inst,
                                   bool is_definition) -> void {
-  if (is_definition) {
-    ForbidModifiersOnDecl(context, introducer, KeywordModifierSet::Extern,
-                          " that provides a definition");
+  if (!introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extern)) {
+    return;
   }
+
   if (parent_scope_inst && !parent_scope_inst->Is<SemIR::Namespace>()) {
     ForbidModifiersOnDecl(context, introducer, KeywordModifierSet::Extern,
                           " that is a member");
+    // Treat as unset.
+    introducer.extern_library = SemIR::LibraryNameId::Invalid;
+    return;
+  }
+
+  if (introducer.extern_library == context.sem_ir().library_id()) {
+    // This prints an error for `extern library`, but doesn't drop it because we
+    // assume there is some other, correct value that we just don't know here.
+    CARBON_DIAGNOSTIC(ExternLibraryIsCurrentLibrary, Error,
+                      "`extern library` cannot specify the current library.");
+    context.emitter().Emit(introducer.modifier_node_id(ModifierOrder::Extern),
+                           ExternLibraryIsCurrentLibrary);
+    introducer.extern_library = SemIR::LibraryNameId::Error;
+    // Right now this can produce both this and the below diagnostic.
+  }
+
+  if (is_definition && introducer.extern_library.is_valid()) {
+    CARBON_DIAGNOSTIC(ExternLibraryOnDefinition, Error,
+                      "A library cannot be provided for an `extern` modifier "
+                      "on a definition.");
+    context.emitter().Emit(introducer.modifier_node_id(ModifierOrder::Extern),
+                           ExternLibraryOnDefinition);
   }
 }
 

+ 2 - 2
toolchain/check/modifiers.h

@@ -47,8 +47,8 @@ inline auto LimitModifiersOnDecl(Context& context,
 }
 
 // Restricts the `extern` modifier to only be used on namespace-scoped
-// declarations, diagnosing and removing it on:
-// - `extern` on a definition.
+// declarations. Diagnoses and cleans up:
+// - `extern library` on a definition.
 // - `extern` on a scoped entity.
 //
 // `parent_scope_inst` may be nullopt for a declaration in a block scope.

+ 18 - 3
toolchain/check/node_stack.h

@@ -130,10 +130,12 @@ class NodeStack {
     return PeekIs(RequiredParseCategory);
   }
 
-  // Returns whether there is a name on top of the stack.
-  auto PeekIsName() const -> bool {
+  // Returns whether there is a node with the corresponding ID on top of the
+  // stack.
+  template <typename IdT>
+  auto PeekIs() const -> bool {
     return !stack_.empty() &&
-           NodeKindToIdKind(PeekNodeKind()) == Id::KindFor<SemIR::NameId>();
+           NodeKindToIdKind(PeekNodeKind()) == Id::KindFor<IdT>();
   }
 
   // Returns whether the *next* node on the stack is a given kind. This doesn't
@@ -274,6 +276,16 @@ class NodeStack {
     return std::nullopt;
   }
 
+  // Pops the top of the stack if it has the given category, and returns the ID.
+  // Otherwise returns std::nullopt.
+  template <typename IdT>
+  auto PopIf() -> std::optional<IdT> {
+    if (PeekIs<IdT>()) {
+      return Pop<IdT>();
+    }
+    return std::nullopt;
+  }
+
   // Pops the top of the stack and returns the node_id and the ID if it is
   // of the specified kind.
   template <const Parse::NodeKind& RequiredParseKind>
@@ -436,6 +448,9 @@ class NodeStack {
           return Id::KindFor<SemIR::ImplId>();
         case Parse::NodeKind::SelfValueName:
           return Id::KindFor<SemIR::NameId>();
+        case Parse::NodeKind::DefaultLibrary:
+        case Parse::NodeKind::LibraryName:
+          return Id::KindFor<SemIR::LibraryNameId>();
         case Parse::NodeKind::ArrayExprSemi:
         case Parse::NodeKind::BuiltinName:
         case Parse::NodeKind::ClassIntroducer:

+ 15 - 0
toolchain/check/sem_ir_diagnostic_converter.cpp

@@ -113,6 +113,21 @@ auto SemIRDiagnosticConverter::ConvertLoc(SemIRLoc loc,
 }
 
 auto SemIRDiagnosticConverter::ConvertArg(llvm::Any arg) const -> llvm::Any {
+  if (auto* library_name_id = llvm::any_cast<SemIR::LibraryNameId>(&arg)) {
+    std::string library_name;
+    if (*library_name_id == SemIR::LibraryNameId::Default) {
+      library_name = "default library";
+    } else if (!library_name_id->is_valid()) {
+      library_name = "library <invalid>";
+    } else {
+      llvm::raw_string_ostream stream(library_name);
+      stream << "library \"";
+      stream << sem_ir_->string_literal_values().Get(
+          library_name_id->AsStringLiteralValueId());
+      stream << "\"";
+    }
+    return library_name;
+  }
   if (auto* name_id = llvm::any_cast<SemIR::NameId>(&arg)) {
     return sem_ir_->names().GetFormatted(*name_id).str();
   }

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

@@ -23,7 +23,7 @@ library "b";
 
 import library "a";
 
-// CHECK:STDERR: fail_b.carbon:[[@LINE+10]]:1: ERROR: Only one library can declare `class Empty` without `extern`.
+// CHECK:STDERR: fail_b.carbon:[[@LINE+10]]:1: ERROR: Redeclaration of `class Empty` is redundant.
 // CHECK:STDERR: class Empty {
 // CHECK:STDERR: ^~~~~~~~~~~~~
 // CHECK:STDERR: fail_b.carbon:[[@LINE-5]]:1: In import.

+ 2 - 16
toolchain/check/testdata/class/fail_modifiers.carbon

@@ -61,20 +61,14 @@ protected virtual base class Virtual {}
 // CHECK:STDERR:
 abstract protected class WrongOrder;
 
-// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+7]]:10: ERROR: `base` not allowed on declaration with `abstract`.
+// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+6]]:10: ERROR: `base` not allowed on declaration with `abstract`.
 // CHECK:STDERR: abstract base class AbstractAndBase {}
 // CHECK:STDERR:          ^~~~
-// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:1: `abstract` previously appeared here.
+// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+3]]:1: `abstract` previously appeared here.
 // CHECK:STDERR: abstract base class AbstractAndBase {}
 // CHECK:STDERR: ^~~~~~~~
-// CHECK:STDERR:
 abstract base class AbstractAndBase {}
 
-// 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 {}
-
 // CHECK:STDOUT: --- fail_modifiers.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
@@ -85,7 +79,6 @@ extern class ExternDefined {}
 // CHECK:STDOUT:   %Virtual: type = class_type @Virtual [template]
 // CHECK:STDOUT:   %WrongOrder: type = class_type @WrongOrder [template]
 // CHECK:STDOUT:   %AbstractAndBase: type = class_type @AbstractAndBase [template]
-// CHECK:STDOUT:   %ExternDefined: type = class_type @ExternDefined [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -109,7 +102,6 @@ extern class ExternDefined {}
 // CHECK:STDOUT:     .Virtual = %Virtual.decl
 // CHECK:STDOUT:     .WrongOrder = %WrongOrder.decl
 // CHECK:STDOUT:     .AbstractAndBase = %AbstractAndBase.decl
-// CHECK:STDOUT:     .ExternDefined = %ExternDefined.decl
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %DuplicatePrivate.decl: type = class_decl @DuplicatePrivate [template = constants.%DuplicatePrivate] {}
@@ -118,7 +110,6 @@ extern class ExternDefined {}
 // CHECK:STDOUT:   %Virtual.decl: type = class_decl @Virtual [template = constants.%Virtual] {}
 // CHECK:STDOUT:   %WrongOrder.decl: type = class_decl @WrongOrder [template = constants.%WrongOrder] {}
 // CHECK:STDOUT:   %AbstractAndBase.decl: type = class_decl @AbstractAndBase [template = constants.%AbstractAndBase] {}
-// CHECK:STDOUT:   %ExternDefined.decl: type = class_decl @ExternDefined [template = constants.%ExternDefined] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @DuplicatePrivate;
@@ -142,8 +133,3 @@ extern class ExternDefined {}
 // CHECK:STDOUT:   .Self = constants.%AbstractAndBase
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @ExternDefined {
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = constants.%ExternDefined
-// CHECK:STDOUT: }
-// CHECK:STDOUT:

+ 95 - 305
toolchain/check/testdata/class/extern.carbon → toolchain/check/testdata/class/no_prelude/extern.carbon

@@ -4,9 +4,9 @@
 //
 // AUTOUPDATE
 // TIP: To test this file alone, run:
-// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/extern.carbon
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/no_prelude/extern.carbon
 // TIP: To dump output, run:
-// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/extern.carbon
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/no_prelude/extern.carbon
 
 // ============================================================================
 // Setup files
@@ -54,14 +54,10 @@ extern class C;
 // CHECK:STDERR:
 fn C.F();
 
-// --- fail_extern_def.carbon
+// --- extern_def.carbon
 
 library "fail_extern_def";
 
-// 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
@@ -104,11 +100,18 @@ class C {
   extern class D;
 }
 
-// --- todo_fail_def_after_extern_decl.carbon
+// --- fail_def_after_extern_decl.carbon
 
 library "fail_def_after_extern_decl";
 
 extern class C;
+// CHECK:STDERR: fail_def_after_extern_decl.carbon:[[@LINE+7]]:1: ERROR: Redeclarations of `class C` must match use of `extern`.
+// CHECK:STDERR: class C {}
+// CHECK:STDERR: ^~~~~~~~~
+// CHECK:STDERR: fail_def_after_extern_decl.carbon:[[@LINE-4]]:1: Previously declared here.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR:
 class C {}
 
 // --- fail_extern_decl_after_decl.carbon
@@ -125,17 +128,17 @@ class C;
 // CHECK:STDERR:
 extern class C;
 
-// --- fail_todo_import_extern_decl_then_decl.carbon
+// --- fail_import_extern_decl_then_decl.carbon
 
 library "import_extern_decl_then_decl";
 
-// CHECK:STDERR: fail_todo_import_extern_decl_then_decl.carbon:[[@LINE+13]]:1: In import.
+// CHECK:STDERR: fail_import_extern_decl_then_decl.carbon:[[@LINE+13]]:1: In import.
 // CHECK:STDERR: import library "extern_decl";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: decl.carbon:4:1: ERROR: Duplicate name being declared in the same scope.
 // CHECK:STDERR: class C;
 // CHECK:STDERR: ^~~~~~~~
-// CHECK:STDERR: fail_todo_import_extern_decl_then_decl.carbon:[[@LINE+7]]:1: In import.
+// CHECK:STDERR: fail_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: Name is previously declared here.
@@ -145,17 +148,17 @@ library "import_extern_decl_then_decl";
 import library "extern_decl";
 import library "decl";
 
-// --- fail_todo_import_decl_then_extern_decl.carbon
+// --- fail_import_decl_then_extern_decl.carbon
 
 library "import_decl_then_extern_decl";
 
-// CHECK:STDERR: fail_todo_import_decl_then_extern_decl.carbon:[[@LINE+13]]:1: In import.
+// CHECK:STDERR: fail_import_decl_then_extern_decl.carbon:[[@LINE+13]]:1: In import.
 // CHECK:STDERR: import library "decl";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: extern_decl.carbon:4:1: ERROR: Duplicate name being declared in the same scope.
 // CHECK:STDERR: extern class C;
 // CHECK:STDERR: ^~~~~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_import_decl_then_extern_decl.carbon:[[@LINE+7]]:1: In import.
+// CHECK:STDERR: fail_import_decl_then_extern_decl.carbon:[[@LINE+7]]:1: In import.
 // CHECK:STDERR: import library "decl";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: decl.carbon:4:1: Name is previously declared here.
@@ -165,17 +168,17 @@ library "import_decl_then_extern_decl";
 import library "decl";
 import library "extern_decl";
 
-// --- fail_todo_import_extern_decl_then_def.carbon
+// --- fail_import_extern_decl_then_def.carbon
 
 library "import_extern_decl_then_def";
 
-// CHECK:STDERR: fail_todo_import_extern_decl_then_def.carbon:[[@LINE+13]]:1: In import.
+// CHECK:STDERR: fail_import_extern_decl_then_def.carbon:[[@LINE+13]]:1: In import.
 // CHECK:STDERR: import library "extern_decl";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: def.carbon:4:1: ERROR: Duplicate name being declared in the same scope.
 // CHECK:STDERR: class C {}
 // CHECK:STDERR: ^~~~~~~~~
-// CHECK:STDERR: fail_todo_import_extern_decl_then_def.carbon:[[@LINE+7]]:1: In import.
+// CHECK:STDERR: fail_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: Name is previously declared here.
@@ -223,51 +226,91 @@ import library "def";
 
 library "import_extern_decl_copy";
 
-// CHECK:STDERR: fail_todo_import_extern_decl_copy.carbon:[[@LINE+12]]:1: In import.
+// CHECK:STDERR: fail_todo_import_extern_decl_copy.carbon:[[@LINE+13]]:1: In import.
 // CHECK:STDERR: import library "extern_decl";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: extern_decl_copy.carbon:4:1: ERROR: Duplicate name being declared in the same scope.
 // CHECK:STDERR: extern class C;
 // CHECK:STDERR: ^~~~~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_import_extern_decl_copy.carbon:[[@LINE+6]]:1: In import.
+// 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: Name is previously declared here.
 // CHECK:STDERR: extern class C;
 // CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR:
 import library "extern_decl";
 import library "extern_decl_copy";
 
-// --- extern_decl_after_import_extern_decl.carbon
+// --- fail_extern_decl_after_import_extern_decl.carbon
 
 library "extern_decl_after_import_extern_decl";
 
 import library "extern_decl";
 
+// CHECK:STDERR: fail_extern_decl_after_import_extern_decl.carbon:[[@LINE+10]]:1: ERROR: Redeclaration of `class C` is redundant.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_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: Previously declared here.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR:
 extern class C;
 
-// --- decl_after_import_extern_decl.carbon
+// --- fail_decl_after_import_extern_decl.carbon
 
 library "decl_after_import_extern_decl";
 
 import library "decl";
 
+// CHECK:STDERR: fail_decl_after_import_extern_decl.carbon:[[@LINE+10]]:1: ERROR: Redeclarations of `class C` must match use of `extern`.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_decl_after_import_extern_decl.carbon:[[@LINE-5]]: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:
 extern class C;
 
-// --- def_after_import_extern_decl.carbon
+// --- fail_def_after_import_extern_decl.carbon
 
 library "def_after_import_extern_decl";
 
 import library "def";
 
+// CHECK:STDERR: fail_def_after_import_extern_decl.carbon:[[@LINE+10]]:1: ERROR: Redeclarations of `class C` must match use of `extern`.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_def_after_import_extern_decl.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: import library "def";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: def.carbon:4:1: Previously declared here.
+// CHECK:STDERR: class C {}
+// CHECK:STDERR: ^~~~~~~~~
+// CHECK:STDERR:
 extern class C;
 
-// --- extern_decl_after_import_def.carbon
+// --- fail_extern_decl_after_import_def.carbon
 
 library "extern_decl_after_import_def";
 
 import library "def";
 
+// CHECK:STDERR: fail_extern_decl_after_import_def.carbon:[[@LINE+9]]:1: ERROR: Redeclarations of `class C` must match use of `extern`.
+// CHECK:STDERR: extern class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_extern_decl_after_import_def.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: import library "def";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: def.carbon:4:1: Previously declared here.
+// CHECK:STDERR: class C {}
+// CHECK:STDERR: ^~~~~~~~~
 extern class C;
 
 // CHECK:STDOUT: --- decl.carbon
@@ -276,24 +319,10 @@ extern class C;
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
-// CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -305,24 +334,10 @@ extern class C;
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
-// CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -334,24 +349,10 @@ extern class C;
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
-// CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -364,24 +365,10 @@ extern class C;
 // CHECK:STDOUT:   %.1: type = struct_type {} [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
-// CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -399,24 +386,10 @@ extern class C;
 // CHECK:STDOUT:   %.2: %.type = struct_value () [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
-// CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT:   %.decl: %.type = fn_decl @.1 [template = constants.%.2] {}
 // CHECK:STDOUT: }
@@ -425,31 +398,17 @@ extern class C;
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @.1();
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_extern_def.carbon
+// CHECK:STDOUT: --- 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: imports {
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
-// CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -464,24 +423,10 @@ extern class C;
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
-// CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:     .C = %C.decl.loc4
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
 // 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: }
@@ -494,24 +439,10 @@ extern class C;
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
-// CHECK:STDOUT:     .Core = imports.%Core
-// CHECK:STDOUT:     .C = %C.decl.loc4
+// CHECK:STDOUT:     .C = %C.decl.loc12
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
 // 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: }
@@ -526,24 +457,10 @@ extern class C;
 // CHECK:STDOUT:   %.1: type = struct_type {} [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
-// CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -557,33 +474,19 @@ extern class C;
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @D;
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- todo_fail_def_after_extern_decl.carbon
+// CHECK:STDOUT: --- 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: imports {
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
-// CHECK:STDOUT:     .Core = imports.%Core
-// CHECK:STDOUT:     .C = %C.decl.loc4
+// CHECK:STDOUT:     .C = %C.decl.loc12
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
 // 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:   %C.decl.loc12: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C {
@@ -597,219 +500,128 @@ extern class C;
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
-// CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:     .C = %C.decl.loc4
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
 // 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: --- fail_import_extern_decl_then_decl.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %import_ref.1 = import_ref Main//extern_decl, inst+3, unloaded
-// CHECK:STDOUT:   %import_ref.2 = import_ref Main//decl, inst+3, unloaded
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1 = import_ref Main//extern_decl, inst+1, unloaded
+// CHECK:STDOUT:   %import_ref.2 = import_ref Main//decl, inst+1, unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
 // CHECK:STDOUT:     .C = imports.%import_ref.1
-// CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %default.import = import <invalid>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_import_decl_then_extern_decl.carbon
+// CHECK:STDOUT: --- fail_import_decl_then_extern_decl.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %import_ref.1 = import_ref Main//decl, inst+3, unloaded
-// CHECK:STDOUT:   %import_ref.2 = import_ref Main//extern_decl, inst+3, unloaded
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1 = import_ref Main//decl, inst+1, unloaded
+// CHECK:STDOUT:   %import_ref.2 = import_ref Main//extern_decl, inst+1, unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
 // CHECK:STDOUT:     .C = imports.%import_ref.1
-// CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %default.import = import <invalid>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_import_extern_decl_then_def.carbon
+// CHECK:STDOUT: --- fail_import_extern_decl_then_def.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %import_ref.1 = import_ref Main//extern_decl, inst+3, unloaded
-// CHECK:STDOUT:   %import_ref.2 = import_ref Main//def, inst+3, unloaded
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1 = import_ref Main//extern_decl, inst+1, unloaded
+// CHECK:STDOUT:   %import_ref.2 = import_ref Main//def, inst+1, unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
 // CHECK:STDOUT:     .C = imports.%import_ref.1
-// CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %default.import = import <invalid>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_import_ownership_conflict.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %import_ref.1 = import_ref Main//extern_decl, inst+3, unloaded
-// CHECK:STDOUT:   %import_ref.2 = import_ref Main//decl, inst+3, unloaded
-// CHECK:STDOUT:   %import_ref.3 = import_ref Main//def, inst+3, unloaded
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1 = import_ref Main//extern_decl, inst+1, unloaded
+// CHECK:STDOUT:   %import_ref.2 = import_ref Main//decl, inst+1, unloaded
+// CHECK:STDOUT:   %import_ref.3 = import_ref Main//def, inst+1, unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
 // CHECK:STDOUT:     .C = imports.%import_ref.1
-// CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %default.import = import <invalid>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_todo_import_extern_decl_copy.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %import_ref.1 = import_ref Main//extern_decl, inst+3, unloaded
-// CHECK:STDOUT:   %import_ref.2 = import_ref Main//extern_decl_copy, inst+3, unloaded
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1 = import_ref Main//extern_decl, inst+1, unloaded
+// CHECK:STDOUT:   %import_ref.2 = import_ref Main//extern_decl_copy, inst+1, unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
 // CHECK:STDOUT:     .C = imports.%import_ref.1
-// CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %default.import = import <invalid>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- extern_decl_after_import_extern_decl.carbon
+// CHECK:STDOUT: --- fail_extern_decl_after_import_extern_decl.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %import_ref: type = import_ref Main//extern_decl, inst+3, loaded [template = constants.%C]
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref: type = import_ref Main//extern_decl, inst+1, loaded [template = constants.%C]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
 // CHECK:STDOUT:     .C = %C.decl
-// CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %default.import = import <invalid>
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C;
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- decl_after_import_extern_decl.carbon
+// CHECK:STDOUT: --- fail_decl_after_import_extern_decl.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %import_ref: type = import_ref Main//decl, inst+3, loaded [template = constants.%C]
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref: type = import_ref Main//decl, inst+1, loaded [template = constants.%C]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
 // CHECK:STDOUT:     .C = %C.decl
-// CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %default.import = import <invalid>
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C;
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- def_after_import_extern_decl.carbon
+// CHECK:STDOUT: --- fail_def_after_import_extern_decl.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %C: type = class_type @C [template]
@@ -817,25 +629,14 @@ extern class C;
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %import_ref.1: type = import_ref Main//def, inst+3, loaded [template = constants.%C]
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %import_ref.2 = import_ref Main//def, inst+4, unloaded
+// CHECK:STDOUT:   %import_ref.1: type = import_ref Main//def, inst+1, loaded [template = constants.%C]
+// CHECK:STDOUT:   %import_ref.2 = import_ref Main//def, inst+2, unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
 // CHECK:STDOUT:     .C = %C.decl
-// CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %default.import = import <invalid>
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT: }
@@ -845,7 +646,7 @@ extern class C;
 // CHECK:STDOUT:   .Self = imports.%import_ref.2
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- extern_decl_after_import_def.carbon
+// CHECK:STDOUT: --- fail_extern_decl_after_import_def.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %C: type = class_type @C [template]
@@ -853,25 +654,14 @@ extern class C;
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %import_ref.1: type = import_ref Main//def, inst+3, loaded [template = constants.%C]
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %import_ref.2 = import_ref Main//def, inst+4, unloaded
+// CHECK:STDOUT:   %import_ref.1: type = import_ref Main//def, inst+1, loaded [template = constants.%C]
+// CHECK:STDOUT:   %import_ref.2 = import_ref Main//def, inst+2, unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
 // CHECK:STDOUT:     .C = %C.decl
-// CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %default.import = import <invalid>
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT: }

+ 32 - 0
toolchain/check/testdata/class/no_prelude/extern_library.carbon

@@ -0,0 +1,32 @@
+// 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
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/no_prelude/extern_library.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/no_prelude/extern_library.carbon
+
+// --- fail_todo.carbon
+
+// CHECK:STDERR: fail_todo.carbon:[[@LINE+3]]:1: ERROR: Semantics TODO: `extern library`.
+// CHECK:STDERR: extern library "foo" class C;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+extern library "foo" class C;
+
+// CHECK:STDOUT: --- fail_todo.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:     .C = %C.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C;
+// CHECK:STDOUT:

+ 185 - 477
toolchain/check/testdata/function/declaration/import.carbon

@@ -28,13 +28,13 @@ fn NS.E();
 
 library "extern_api";
 
-extern fn A();
-extern fn B(b: i32) -> i32;
-extern fn C(c: (i32,)) -> {.c: i32};
-extern fn D();
+extern library "redecl_extern_api" fn A();
+extern library "redecl_extern_api" fn B(b: i32) -> i32;
+extern library "redecl_extern_api" fn C(c: (i32,)) -> {.c: i32};
+extern library "redecl_extern_api" fn D();
 
 namespace NS;
-extern fn NS.E();
+extern library "redecl_extern_api" fn NS.E();
 
 // ============================================================================
 // Test files
@@ -52,16 +52,66 @@ var c: {.c: i32} = C((1,));
 var d: () = D();
 var e: () = NS.E();
 
-// --- redecl_api.carbon
+// --- fail_redecl_api.carbon
 
 library "redecl_api";
 
 import library "api";
 
+// CHECK:STDERR: fail_redecl_api.carbon:[[@LINE+10]]:1: ERROR: Redeclarations of `fn A` must match use of `extern`.
+// CHECK:STDERR: extern fn A();
+// CHECK:STDERR: ^~~~~~~~~~~~~~
+// CHECK:STDERR: fail_redecl_api.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: import library "api";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: api.carbon:4:1: Previously declared here.
+// CHECK:STDERR: fn A();
+// CHECK:STDERR: ^~~~~~~
+// CHECK:STDERR:
 extern fn A();
+// CHECK:STDERR: fail_redecl_api.carbon:[[@LINE+10]]:1: ERROR: Redeclarations of `fn B` must match use of `extern`.
+// CHECK:STDERR: extern fn B(b: i32) -> i32;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_redecl_api.carbon:[[@LINE-16]]:1: In import.
+// CHECK:STDERR: import library "api";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: api.carbon:5:1: Previously declared here.
+// CHECK:STDERR: fn B(b: i32) -> i32;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
 extern fn B(b: i32) -> i32;
+// CHECK:STDERR: fail_redecl_api.carbon:[[@LINE+10]]:1: ERROR: Redeclarations of `fn C` must match use of `extern`.
+// CHECK:STDERR: extern fn C(c: (i32,)) -> {.c: i32};
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_redecl_api.carbon:[[@LINE-27]]:1: In import.
+// CHECK:STDERR: import library "api";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: api.carbon:6:1: Previously declared here.
+// CHECK:STDERR: fn C(c: (i32,)) -> {.c: i32};
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
 extern fn C(c: (i32,)) -> {.c: i32};
+// CHECK:STDERR: fail_redecl_api.carbon:[[@LINE+10]]:1: ERROR: Redeclaration of `fn D` is redundant.
+// CHECK:STDERR: extern fn D();
+// CHECK:STDERR: ^~~~~~~~~~~~~~
+// CHECK:STDERR: fail_redecl_api.carbon:[[@LINE-38]]:1: In import.
+// CHECK:STDERR: import library "api";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: api.carbon:7:1: Previously declared here.
+// CHECK:STDERR: extern fn D();
+// CHECK:STDERR: ^~~~~~~~~~~~~~
+// CHECK:STDERR:
 extern fn D();
+// CHECK:STDERR: fail_redecl_api.carbon:[[@LINE+10]]:1: ERROR: Redeclarations of `fn E` must match use of `extern`.
+// CHECK:STDERR: extern fn NS.E();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_redecl_api.carbon:[[@LINE-49]]:1: In import.
+// CHECK:STDERR: import library "api";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: api.carbon:10:1: Previously declared here.
+// CHECK:STDERR: fn NS.E();
+// CHECK:STDERR: ^~~~~~~~~~
+// CHECK:STDERR:
 extern fn NS.E();
 
 var a: () = A();
@@ -88,69 +138,69 @@ var c: {.c: i32} = C((1,));
 var d: () = D();
 var e: () = NS.E();
 
-// --- fail_todo_merge.carbon
+// --- fail_merge.carbon
 
 library "merge";
 
-// CHECK:STDERR: fail_todo_merge.carbon:[[@LINE+65]]:1: In import.
+// CHECK:STDERR: fail_merge.carbon:[[@LINE+65]]:1: In import.
 // CHECK:STDERR: import library "api";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: extern_api.carbon:4:1: ERROR: Duplicate name being declared in the same scope.
-// CHECK:STDERR: extern fn A();
-// CHECK:STDERR: ^~~~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_merge.carbon:[[@LINE+59]]:1: In import.
+// CHECK:STDERR: extern library "redecl_extern_api" fn A();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_merge.carbon:[[@LINE+59]]:1: In import.
 // CHECK:STDERR: import library "api";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: api.carbon:4:1: Name is previously declared here.
 // CHECK:STDERR: fn A();
 // CHECK:STDERR: ^~~~~~~
 // CHECK:STDERR:
-// CHECK:STDERR: fail_todo_merge.carbon:[[@LINE+52]]:1: In import.
+// CHECK:STDERR: fail_merge.carbon:[[@LINE+52]]:1: In import.
 // CHECK:STDERR: import library "api";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: extern_api.carbon:5:1: ERROR: Duplicate name being declared in the same scope.
-// CHECK:STDERR: extern fn B(b: i32) -> i32;
-// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_merge.carbon:[[@LINE+46]]:1: In import.
+// CHECK:STDERR: extern library "redecl_extern_api" fn B(b: i32) -> i32;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_merge.carbon:[[@LINE+46]]:1: In import.
 // CHECK:STDERR: import library "api";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: api.carbon:5:1: Name is previously declared here.
 // CHECK:STDERR: fn B(b: i32) -> i32;
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR:
-// CHECK:STDERR: fail_todo_merge.carbon:[[@LINE+39]]:1: In import.
+// CHECK:STDERR: fail_merge.carbon:[[@LINE+39]]:1: In import.
 // CHECK:STDERR: import library "api";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: extern_api.carbon:6:1: ERROR: Duplicate name being declared in the same scope.
-// CHECK:STDERR: extern fn C(c: (i32,)) -> {.c: i32};
-// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_merge.carbon:[[@LINE+33]]:1: In import.
+// CHECK:STDERR: extern library "redecl_extern_api" fn C(c: (i32,)) -> {.c: i32};
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_merge.carbon:[[@LINE+33]]:1: In import.
 // CHECK:STDERR: import library "api";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: api.carbon:6:1: Name is previously declared here.
 // CHECK:STDERR: fn C(c: (i32,)) -> {.c: i32};
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR:
-// CHECK:STDERR: fail_todo_merge.carbon:[[@LINE+26]]:1: In import.
+// CHECK:STDERR: fail_merge.carbon:[[@LINE+26]]:1: In import.
 // CHECK:STDERR: import library "api";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: extern_api.carbon:7:1: ERROR: Duplicate name being declared in the same scope.
-// CHECK:STDERR: extern fn D();
-// CHECK:STDERR: ^~~~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_merge.carbon:[[@LINE+20]]:1: In import.
+// CHECK:STDERR: extern library "redecl_extern_api" fn D();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_merge.carbon:[[@LINE+20]]:1: In import.
 // CHECK:STDERR: import library "api";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: api.carbon:7:1: Name is previously declared here.
 // CHECK:STDERR: extern fn D();
 // CHECK:STDERR: ^~~~~~~~~~~~~~
 // CHECK:STDERR:
-// CHECK:STDERR: fail_todo_merge.carbon:[[@LINE+13]]:1: In import.
+// CHECK:STDERR: fail_merge.carbon:[[@LINE+13]]:1: In import.
 // CHECK:STDERR: import library "api";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: extern_api.carbon:10:1: ERROR: Duplicate name being declared in the same scope.
-// CHECK:STDERR: extern fn NS.E();
-// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_merge.carbon:[[@LINE+7]]:1: In import.
+// CHECK:STDERR: extern library "redecl_extern_api" fn NS.E();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_merge.carbon:[[@LINE+7]]:1: In import.
 // CHECK:STDERR: import library "api";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: api.carbon:10:1: Name is previously declared here.
@@ -166,75 +216,74 @@ var c: {.c: i32} = C((1,));
 var d: () = D();
 var e: () = NS.E();
 
-// --- fail_todo_merge_reverse.carbon
+// --- fail_merge_reverse.carbon
 
 library "merge_reverse";
 
-// CHECK:STDERR: fail_todo_merge_reverse.carbon:[[@LINE+65]]:1: In import.
+// CHECK:STDERR: fail_merge_reverse.carbon:[[@LINE+64]]:1: In import.
 // CHECK:STDERR: import library "extern_api";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: api.carbon:4:1: ERROR: Duplicate name being declared in the same scope.
 // CHECK:STDERR: fn A();
 // CHECK:STDERR: ^~~~~~~
-// CHECK:STDERR: fail_todo_merge_reverse.carbon:[[@LINE+59]]:1: In import.
+// CHECK:STDERR: fail_merge_reverse.carbon:[[@LINE+58]]:1: In import.
 // CHECK:STDERR: import library "extern_api";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: extern_api.carbon:4:1: Name is previously declared here.
-// CHECK:STDERR: extern fn A();
-// CHECK:STDERR: ^~~~~~~~~~~~~~
+// CHECK:STDERR: extern library "redecl_extern_api" fn A();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR:
-// CHECK:STDERR: fail_todo_merge_reverse.carbon:[[@LINE+52]]:1: In import.
+// CHECK:STDERR: fail_merge_reverse.carbon:[[@LINE+51]]:1: In import.
 // CHECK:STDERR: import library "extern_api";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: api.carbon:5:1: ERROR: Duplicate name being declared in the same scope.
 // CHECK:STDERR: fn B(b: i32) -> i32;
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_merge_reverse.carbon:[[@LINE+46]]:1: In import.
+// CHECK:STDERR: fail_merge_reverse.carbon:[[@LINE+45]]:1: In import.
 // CHECK:STDERR: import library "extern_api";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: extern_api.carbon:5:1: Name is previously declared here.
-// CHECK:STDERR: extern fn B(b: i32) -> i32;
-// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: extern library "redecl_extern_api" fn B(b: i32) -> i32;
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR:
-// CHECK:STDERR: fail_todo_merge_reverse.carbon:[[@LINE+39]]:1: In import.
+// CHECK:STDERR: fail_merge_reverse.carbon:[[@LINE+38]]:1: In import.
 // CHECK:STDERR: import library "extern_api";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: api.carbon:6:1: ERROR: Duplicate name being declared in the same scope.
 // CHECK:STDERR: fn C(c: (i32,)) -> {.c: i32};
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_merge_reverse.carbon:[[@LINE+33]]:1: In import.
+// CHECK:STDERR: fail_merge_reverse.carbon:[[@LINE+32]]:1: In import.
 // CHECK:STDERR: import library "extern_api";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: extern_api.carbon:6:1: Name is previously declared here.
-// CHECK:STDERR: extern fn C(c: (i32,)) -> {.c: i32};
-// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: extern library "redecl_extern_api" fn C(c: (i32,)) -> {.c: i32};
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR:
-// CHECK:STDERR: fail_todo_merge_reverse.carbon:[[@LINE+26]]:1: In import.
+// CHECK:STDERR: fail_merge_reverse.carbon:[[@LINE+25]]:1: In import.
 // CHECK:STDERR: import library "extern_api";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: api.carbon:7:1: ERROR: Duplicate name being declared in the same scope.
 // CHECK:STDERR: extern fn D();
 // CHECK:STDERR: ^~~~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_merge_reverse.carbon:[[@LINE+20]]:1: In import.
+// CHECK:STDERR: fail_merge_reverse.carbon:[[@LINE+19]]:1: In import.
 // CHECK:STDERR: import library "extern_api";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: extern_api.carbon:7:1: Name is previously declared here.
-// CHECK:STDERR: extern fn D();
-// CHECK:STDERR: ^~~~~~~~~~~~~~
+// CHECK:STDERR: extern library "redecl_extern_api" fn D();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR:
-// CHECK:STDERR: fail_todo_merge_reverse.carbon:[[@LINE+13]]:1: In import.
+// CHECK:STDERR: fail_merge_reverse.carbon:[[@LINE+12]]:1: In import.
 // CHECK:STDERR: import library "extern_api";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: api.carbon:10:1: ERROR: Duplicate name being declared in the same scope.
 // CHECK:STDERR: fn NS.E();
 // CHECK:STDERR: ^~~~~~~~~~
-// CHECK:STDERR: fail_todo_merge_reverse.carbon:[[@LINE+7]]:1: In import.
+// CHECK:STDERR: fail_merge_reverse.carbon:[[@LINE+6]]:1: In import.
 // CHECK:STDERR: import library "extern_api";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: extern_api.carbon:10:1: Name is previously declared here.
-// CHECK:STDERR: extern fn NS.E();
-// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
-// CHECK:STDERR:
+// CHECK:STDERR: extern library "redecl_extern_api" fn NS.E();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 import library "extern_api";
 import library "api";
 
@@ -244,46 +293,6 @@ var c: {.c: i32} = C((1,));
 var d: () = D();
 var e: () = NS.E();
 
-// --- decl_after_use.carbon
-
-library "decl_after_use";
-
-import library "extern_api";
-
-var a: () = A();
-
-fn A();
-
-// --- fail_redecl_mismatch_after_use.carbon
-
-library "redecl_mismatch_after_use";
-
-import library "extern_api";
-
-var a: () = A();
-
-// CHECK:STDERR: fail_redecl_mismatch_after_use.carbon:[[@LINE+10]]:1: ERROR: Function redeclaration differs because return type is `i32`.
-// CHECK:STDERR: fn A() -> i32;
-// CHECK:STDERR: ^~~~~~~~~~~~~~
-// CHECK:STDERR: fail_redecl_mismatch_after_use.carbon:[[@LINE-7]]:1: In import.
-// CHECK:STDERR: import library "extern_api";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: extern_api.carbon:4:1: Previously declared with no return type.
-// CHECK:STDERR: extern fn A();
-// CHECK:STDERR: ^~~~~~~~~~~~~~
-// CHECK:STDERR:
-fn A() -> i32;
-
-// --- todo_fail_extern_after_use.carbon
-
-library "extern_after_use";
-
-import library "api";
-
-var a: () = A();
-
-extern fn A();
-
 // --- unloaded.carbon
 
 library "unloaded";
@@ -296,77 +305,6 @@ library "unloaded_extern";
 
 import library "extern_api";
 
-// --- fail_todo_loaded_merge.carbon
-
-library "loaded_merge";
-
-// CHECK:STDERR: fail_todo_loaded_merge.carbon:[[@LINE+64]]:1: In import.
-// CHECK:STDERR: import library "api";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: extern_api.carbon:4:1: ERROR: Duplicate name being declared in the same scope.
-// CHECK:STDERR: extern fn A();
-// CHECK:STDERR: ^~~~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_loaded_merge.carbon:[[@LINE+58]]:1: In import.
-// CHECK:STDERR: import library "api";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: api.carbon:4:1: Name is previously declared here.
-// CHECK:STDERR: fn A();
-// CHECK:STDERR: ^~~~~~~
-// CHECK:STDERR:
-// CHECK:STDERR: fail_todo_loaded_merge.carbon:[[@LINE+51]]:1: In import.
-// CHECK:STDERR: import library "api";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: extern_api.carbon:5:1: ERROR: Duplicate name being declared in the same scope.
-// CHECK:STDERR: extern fn B(b: i32) -> i32;
-// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_loaded_merge.carbon:[[@LINE+45]]:1: In import.
-// CHECK:STDERR: import library "api";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: api.carbon:5:1: Name is previously declared here.
-// CHECK:STDERR: fn B(b: i32) -> i32;
-// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~
-// CHECK:STDERR:
-// CHECK:STDERR: fail_todo_loaded_merge.carbon:[[@LINE+38]]:1: In import.
-// CHECK:STDERR: import library "api";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: extern_api.carbon:6:1: ERROR: Duplicate name being declared in the same scope.
-// CHECK:STDERR: extern fn C(c: (i32,)) -> {.c: i32};
-// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_loaded_merge.carbon:[[@LINE+32]]:1: In import.
-// CHECK:STDERR: import library "api";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: api.carbon:6:1: Name is previously declared here.
-// CHECK:STDERR: fn C(c: (i32,)) -> {.c: i32};
-// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// CHECK:STDERR:
-// CHECK:STDERR: fail_todo_loaded_merge.carbon:[[@LINE+25]]:1: In import.
-// CHECK:STDERR: import library "api";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: extern_api.carbon:7:1: ERROR: Duplicate name being declared in the same scope.
-// CHECK:STDERR: extern fn D();
-// CHECK:STDERR: ^~~~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_loaded_merge.carbon:[[@LINE+19]]:1: In import.
-// CHECK:STDERR: import library "api";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: api.carbon:7:1: Name is previously declared here.
-// CHECK:STDERR: extern fn D();
-// CHECK:STDERR: ^~~~~~~~~~~~~~
-// CHECK:STDERR:
-// CHECK:STDERR: fail_todo_loaded_merge.carbon:[[@LINE+12]]:1: In import.
-// CHECK:STDERR: import library "api";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: extern_api.carbon:10:1: ERROR: Duplicate name being declared in the same scope.
-// CHECK:STDERR: extern fn NS.E();
-// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_loaded_merge.carbon:[[@LINE+6]]:1: In import.
-// CHECK:STDERR: import library "api";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: api.carbon:10:1: Name is previously declared here.
-// CHECK:STDERR: fn NS.E();
-// CHECK:STDERR: ^~~~~~~~~~
-import library "api";
-import library "extern_api";
-
 // CHECK:STDOUT: --- api.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
@@ -504,28 +442,28 @@ import library "extern_api";
 // CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %A.decl: %A.type = fn_decl @A [template = constants.%A] {}
 // CHECK:STDOUT:   %B.decl: %B.type = fn_decl @B [template = constants.%B] {
-// CHECK:STDOUT:     %int.make_type_32.loc5_16: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:     %.loc5_16.1: type = value_of_initializer %int.make_type_32.loc5_16 [template = i32]
-// CHECK:STDOUT:     %.loc5_16.2: type = converted %int.make_type_32.loc5_16, %.loc5_16.1 [template = i32]
-// CHECK:STDOUT:     %b.loc5_13.1: i32 = param b
-// CHECK:STDOUT:     @B.%b: i32 = bind_name b, %b.loc5_13.1
-// CHECK:STDOUT:     %int.make_type_32.loc5_24: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:     %.loc5_24.1: type = value_of_initializer %int.make_type_32.loc5_24 [template = i32]
-// CHECK:STDOUT:     %.loc5_24.2: type = converted %int.make_type_32.loc5_24, %.loc5_24.1 [template = i32]
+// CHECK:STDOUT:     %int.make_type_32.loc5_44: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc5_44.1: type = value_of_initializer %int.make_type_32.loc5_44 [template = i32]
+// CHECK:STDOUT:     %.loc5_44.2: type = converted %int.make_type_32.loc5_44, %.loc5_44.1 [template = i32]
+// CHECK:STDOUT:     %b.loc5_41.1: i32 = param b
+// CHECK:STDOUT:     @B.%b: i32 = bind_name b, %b.loc5_41.1
+// CHECK:STDOUT:     %int.make_type_32.loc5_52: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc5_52.1: type = value_of_initializer %int.make_type_32.loc5_52 [template = i32]
+// CHECK:STDOUT:     %.loc5_52.2: type = converted %int.make_type_32.loc5_52, %.loc5_52.1 [template = i32]
 // CHECK:STDOUT:     @B.%return: ref i32 = var <return slot>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %C.decl: %C.type = fn_decl @C [template = constants.%C] {
-// CHECK:STDOUT:     %int.make_type_32.loc6_17: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:     %.loc6_21.1: %.2 = tuple_literal (%int.make_type_32.loc6_17)
-// CHECK:STDOUT:     %.loc6_21.2: type = value_of_initializer %int.make_type_32.loc6_17 [template = i32]
-// CHECK:STDOUT:     %.loc6_21.3: type = converted %int.make_type_32.loc6_17, %.loc6_21.2 [template = i32]
-// CHECK:STDOUT:     %.loc6_21.4: type = converted %.loc6_21.1, constants.%.3 [template = constants.%.3]
-// CHECK:STDOUT:     %c.loc6_13.1: %.3 = param c
-// CHECK:STDOUT:     @C.%c: %.3 = bind_name c, %c.loc6_13.1
-// CHECK:STDOUT:     %int.make_type_32.loc6_32: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:     %.loc6_32.1: type = value_of_initializer %int.make_type_32.loc6_32 [template = i32]
-// CHECK:STDOUT:     %.loc6_32.2: type = converted %int.make_type_32.loc6_32, %.loc6_32.1 [template = i32]
-// CHECK:STDOUT:     %.loc6_35: type = struct_type {.c: i32} [template = constants.%.4]
+// CHECK:STDOUT:     %int.make_type_32.loc6_45: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc6_49.1: %.2 = tuple_literal (%int.make_type_32.loc6_45)
+// CHECK:STDOUT:     %.loc6_49.2: type = value_of_initializer %int.make_type_32.loc6_45 [template = i32]
+// CHECK:STDOUT:     %.loc6_49.3: type = converted %int.make_type_32.loc6_45, %.loc6_49.2 [template = i32]
+// CHECK:STDOUT:     %.loc6_49.4: type = converted %.loc6_49.1, constants.%.3 [template = constants.%.3]
+// CHECK:STDOUT:     %c.loc6_41.1: %.3 = param c
+// CHECK:STDOUT:     @C.%c: %.3 = bind_name c, %c.loc6_41.1
+// CHECK:STDOUT:     %int.make_type_32.loc6_60: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc6_60.1: type = value_of_initializer %int.make_type_32.loc6_60 [template = i32]
+// CHECK:STDOUT:     %.loc6_60.2: type = converted %int.make_type_32.loc6_60, %.loc6_60.1 [template = i32]
+// CHECK:STDOUT:     %.loc6_63: type = struct_type {.c: i32} [template = constants.%.4]
 // CHECK:STDOUT:     @C.%return: ref %.4 = var <return slot>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %D.decl: %D.type = fn_decl @D [template = constants.%D] {}
@@ -671,7 +609,7 @@ import library "extern_api";
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- redecl_api.carbon
+// CHECK:STDOUT: --- fail_redecl_api.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %A.type: type = fn_type @A [template]
@@ -726,8 +664,8 @@ import library "extern_api";
 // CHECK:STDOUT:     .NS = imports.%NS
 // CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:     .a = %a
-// CHECK:STDOUT:     .b = %b.loc13
-// CHECK:STDOUT:     .c = %c.loc14
+// CHECK:STDOUT:     .b = %b.loc63
+// CHECK:STDOUT:     .c = %c.loc64
 // CHECK:STDOUT:     .d = %d
 // CHECK:STDOUT:     .e = %e
 // CHECK:STDOUT:   }
@@ -735,53 +673,53 @@ import library "extern_api";
 // CHECK:STDOUT:   %default.import = import <invalid>
 // CHECK:STDOUT:   %A.decl: %A.type = fn_decl @A [template = constants.%A] {}
 // CHECK:STDOUT:   %B.decl: %B.type = fn_decl @B [template = constants.%B] {
-// CHECK:STDOUT:     %int.make_type_32.loc7_16: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:     %.loc7_16.1: type = value_of_initializer %int.make_type_32.loc7_16 [template = i32]
-// CHECK:STDOUT:     %.loc7_16.2: type = converted %int.make_type_32.loc7_16, %.loc7_16.1 [template = i32]
-// CHECK:STDOUT:     %b.loc7_13.1: i32 = param b
-// CHECK:STDOUT:     %b.loc7_13.2: i32 = bind_name b, %b.loc7_13.1
-// CHECK:STDOUT:     %int.make_type_32.loc7_24: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:     %.loc7_24.1: type = value_of_initializer %int.make_type_32.loc7_24 [template = i32]
-// CHECK:STDOUT:     %.loc7_24.2: type = converted %int.make_type_32.loc7_24, %.loc7_24.1 [template = i32]
-// CHECK:STDOUT:     %return.var.loc7: ref i32 = var <return slot>
+// CHECK:STDOUT:     %int.make_type_32.loc27_16: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc27_16.1: type = value_of_initializer %int.make_type_32.loc27_16 [template = i32]
+// CHECK:STDOUT:     %.loc27_16.2: type = converted %int.make_type_32.loc27_16, %.loc27_16.1 [template = i32]
+// CHECK:STDOUT:     %b.loc27_13.1: i32 = param b
+// CHECK:STDOUT:     %b.loc27_13.2: i32 = bind_name b, %b.loc27_13.1
+// CHECK:STDOUT:     %int.make_type_32.loc27_24: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc27_24.1: type = value_of_initializer %int.make_type_32.loc27_24 [template = i32]
+// CHECK:STDOUT:     %.loc27_24.2: type = converted %int.make_type_32.loc27_24, %.loc27_24.1 [template = i32]
+// CHECK:STDOUT:     %return.var.loc27: ref i32 = var <return slot>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %C.decl: %C.type = fn_decl @C [template = constants.%C] {
-// CHECK:STDOUT:     %int.make_type_32.loc8_17: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:     %.loc8_21.1: %.2 = tuple_literal (%int.make_type_32.loc8_17)
-// CHECK:STDOUT:     %.loc8_21.2: type = value_of_initializer %int.make_type_32.loc8_17 [template = i32]
-// CHECK:STDOUT:     %.loc8_21.3: type = converted %int.make_type_32.loc8_17, %.loc8_21.2 [template = i32]
-// CHECK:STDOUT:     %.loc8_21.4: type = converted %.loc8_21.1, constants.%.3 [template = constants.%.3]
-// CHECK:STDOUT:     %c.loc8_13.1: %.3 = param c
-// CHECK:STDOUT:     %c.loc8_13.2: %.3 = bind_name c, %c.loc8_13.1
-// CHECK:STDOUT:     %int.make_type_32.loc8_32: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:     %.loc8_32.1: type = value_of_initializer %int.make_type_32.loc8_32 [template = i32]
-// CHECK:STDOUT:     %.loc8_32.2: type = converted %int.make_type_32.loc8_32, %.loc8_32.1 [template = i32]
-// CHECK:STDOUT:     %.loc8_35: type = struct_type {.c: i32} [template = constants.%.4]
-// CHECK:STDOUT:     %return.var.loc8: ref %.4 = var <return slot>
+// CHECK:STDOUT:     %int.make_type_32.loc38_17: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc38_21.1: %.2 = tuple_literal (%int.make_type_32.loc38_17)
+// CHECK:STDOUT:     %.loc38_21.2: type = value_of_initializer %int.make_type_32.loc38_17 [template = i32]
+// CHECK:STDOUT:     %.loc38_21.3: type = converted %int.make_type_32.loc38_17, %.loc38_21.2 [template = i32]
+// CHECK:STDOUT:     %.loc38_21.4: type = converted %.loc38_21.1, constants.%.3 [template = constants.%.3]
+// CHECK:STDOUT:     %c.loc38_13.1: %.3 = param c
+// CHECK:STDOUT:     %c.loc38_13.2: %.3 = bind_name c, %c.loc38_13.1
+// CHECK:STDOUT:     %int.make_type_32.loc38_32: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc38_32.1: type = value_of_initializer %int.make_type_32.loc38_32 [template = i32]
+// CHECK:STDOUT:     %.loc38_32.2: type = converted %int.make_type_32.loc38_32, %.loc38_32.1 [template = i32]
+// CHECK:STDOUT:     %.loc38_35: type = struct_type {.c: i32} [template = constants.%.4]
+// CHECK:STDOUT:     %return.var.loc38: ref %.4 = var <return slot>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %D.decl: %D.type = fn_decl @D [template = constants.%D] {}
 // CHECK:STDOUT:   %E.decl: %E.type = fn_decl @E [template = constants.%E] {}
-// CHECK:STDOUT:   %.loc12_9.1: %.1 = tuple_literal ()
-// CHECK:STDOUT:   %.loc12_9.2: type = converted %.loc12_9.1, constants.%.1 [template = constants.%.1]
+// CHECK:STDOUT:   %.loc62_9.1: %.1 = tuple_literal ()
+// CHECK:STDOUT:   %.loc62_9.2: type = converted %.loc62_9.1, constants.%.1 [template = constants.%.1]
 // CHECK:STDOUT:   %a.var: ref %.1 = var a
 // CHECK:STDOUT:   %a: ref %.1 = bind_name a, %a.var
-// CHECK:STDOUT:   %int.make_type_32.loc13: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:   %.loc13_8.1: type = value_of_initializer %int.make_type_32.loc13 [template = i32]
-// CHECK:STDOUT:   %.loc13_8.2: type = converted %int.make_type_32.loc13, %.loc13_8.1 [template = i32]
+// CHECK:STDOUT:   %int.make_type_32.loc63: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:   %.loc63_8.1: type = value_of_initializer %int.make_type_32.loc63 [template = i32]
+// CHECK:STDOUT:   %.loc63_8.2: type = converted %int.make_type_32.loc63, %.loc63_8.1 [template = i32]
 // CHECK:STDOUT:   %b.var: ref i32 = var b
-// CHECK:STDOUT:   %b.loc13: ref i32 = bind_name b, %b.var
-// CHECK:STDOUT:   %int.make_type_32.loc14: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:   %.loc14_13.1: type = value_of_initializer %int.make_type_32.loc14 [template = i32]
-// CHECK:STDOUT:   %.loc14_13.2: type = converted %int.make_type_32.loc14, %.loc14_13.1 [template = i32]
-// CHECK:STDOUT:   %.loc14_16: type = struct_type {.c: i32} [template = constants.%.4]
+// CHECK:STDOUT:   %b.loc63: ref i32 = bind_name b, %b.var
+// CHECK:STDOUT:   %int.make_type_32.loc64: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:   %.loc64_13.1: type = value_of_initializer %int.make_type_32.loc64 [template = i32]
+// CHECK:STDOUT:   %.loc64_13.2: type = converted %int.make_type_32.loc64, %.loc64_13.1 [template = i32]
+// CHECK:STDOUT:   %.loc64_16: type = struct_type {.c: i32} [template = constants.%.4]
 // CHECK:STDOUT:   %c.var: ref %.4 = var c
-// CHECK:STDOUT:   %c.loc14: ref %.4 = bind_name c, %c.var
-// CHECK:STDOUT:   %.loc15_9.1: %.1 = tuple_literal ()
-// CHECK:STDOUT:   %.loc15_9.2: type = converted %.loc15_9.1, constants.%.1 [template = constants.%.1]
+// CHECK:STDOUT:   %c.loc64: ref %.4 = bind_name c, %c.var
+// CHECK:STDOUT:   %.loc65_9.1: %.1 = tuple_literal ()
+// CHECK:STDOUT:   %.loc65_9.2: type = converted %.loc65_9.1, constants.%.1 [template = constants.%.1]
 // CHECK:STDOUT:   %d.var: ref %.1 = var d
 // CHECK:STDOUT:   %d: ref %.1 = bind_name d, %d.var
-// CHECK:STDOUT:   %.loc16_9.1: %.1 = tuple_literal ()
-// CHECK:STDOUT:   %.loc16_9.2: type = converted %.loc16_9.1, constants.%.1 [template = constants.%.1]
+// CHECK:STDOUT:   %.loc66_9.1: %.1 = tuple_literal ()
+// CHECK:STDOUT:   %.loc66_9.2: type = converted %.loc66_9.1, constants.%.1 [template = constants.%.1]
 // CHECK:STDOUT:   %e.var: ref %.1 = var e
 // CHECK:STDOUT:   %e: ref %.1 = bind_name e, %e.var
 // CHECK:STDOUT: }
@@ -804,15 +742,15 @@ import library "extern_api";
 // CHECK:STDOUT:   %A.call: init %.1 = call %A.ref()
 // CHECK:STDOUT:   assign file.%a.var, %A.call
 // CHECK:STDOUT:   %B.ref: %B.type = name_ref B, file.%B.decl [template = constants.%B]
-// CHECK:STDOUT:   %.loc13: i32 = int_literal 1 [template = constants.%.5]
-// CHECK:STDOUT:   %B.call: init i32 = call %B.ref(%.loc13)
+// CHECK:STDOUT:   %.loc63: i32 = int_literal 1 [template = constants.%.5]
+// CHECK:STDOUT:   %B.call: init i32 = call %B.ref(%.loc63)
 // CHECK:STDOUT:   assign file.%b.var, %B.call
 // CHECK:STDOUT:   %C.ref: %C.type = name_ref C, file.%C.decl [template = constants.%C]
-// CHECK:STDOUT:   %.loc14_23: i32 = int_literal 1 [template = constants.%.5]
-// CHECK:STDOUT:   %.loc14_25: %.3 = tuple_literal (%.loc14_23)
-// CHECK:STDOUT:   %tuple: %.3 = tuple_value (%.loc14_23) [template = constants.%tuple]
-// CHECK:STDOUT:   %.loc14_21: %.3 = converted %.loc14_25, %tuple [template = constants.%tuple]
-// CHECK:STDOUT:   %C.call: init %.4 = call %C.ref(%.loc14_21)
+// CHECK:STDOUT:   %.loc64_23: i32 = int_literal 1 [template = constants.%.5]
+// CHECK:STDOUT:   %.loc64_25: %.3 = tuple_literal (%.loc64_23)
+// CHECK:STDOUT:   %tuple: %.3 = tuple_value (%.loc64_23) [template = constants.%tuple]
+// CHECK:STDOUT:   %.loc64_21: %.3 = converted %.loc64_25, %tuple [template = constants.%tuple]
+// CHECK:STDOUT:   %C.call: init %.4 = call %C.ref(%.loc64_21)
 // CHECK:STDOUT:   assign file.%c.var, %C.call
 // CHECK:STDOUT:   %D.ref: %D.type = name_ref D, file.%D.decl [template = constants.%D]
 // CHECK:STDOUT:   %D.call: init %.1 = call %D.ref()
@@ -977,7 +915,7 @@ import library "extern_api";
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_merge.carbon
+// CHECK:STDOUT: --- fail_merge.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %.1: type = tuple_type () [template]
@@ -1106,7 +1044,7 @@ import library "extern_api";
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_merge_reverse.carbon
+// CHECK:STDOUT: --- fail_merge_reverse.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %.1: type = tuple_type () [template]
@@ -1172,27 +1110,27 @@ import library "extern_api";
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %default.import = import <invalid>
-// CHECK:STDOUT:   %.loc72_9.1: %.1 = tuple_literal ()
-// CHECK:STDOUT:   %.loc72_9.2: type = converted %.loc72_9.1, constants.%.1 [template = constants.%.1]
+// CHECK:STDOUT:   %.loc71_9.1: %.1 = tuple_literal ()
+// CHECK:STDOUT:   %.loc71_9.2: type = converted %.loc71_9.1, constants.%.1 [template = constants.%.1]
 // CHECK:STDOUT:   %a.var: ref %.1 = var a
 // CHECK:STDOUT:   %a: ref %.1 = bind_name a, %a.var
-// CHECK:STDOUT:   %int.make_type_32.loc73: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:   %.loc73_8.1: type = value_of_initializer %int.make_type_32.loc73 [template = i32]
-// CHECK:STDOUT:   %.loc73_8.2: type = converted %int.make_type_32.loc73, %.loc73_8.1 [template = i32]
+// CHECK:STDOUT:   %int.make_type_32.loc72: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:   %.loc72_8.1: type = value_of_initializer %int.make_type_32.loc72 [template = i32]
+// CHECK:STDOUT:   %.loc72_8.2: type = converted %int.make_type_32.loc72, %.loc72_8.1 [template = i32]
 // CHECK:STDOUT:   %b.var: ref i32 = var b
 // CHECK:STDOUT:   %b: ref i32 = bind_name b, %b.var
-// CHECK:STDOUT:   %int.make_type_32.loc74: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:   %.loc74_13.1: type = value_of_initializer %int.make_type_32.loc74 [template = i32]
-// CHECK:STDOUT:   %.loc74_13.2: type = converted %int.make_type_32.loc74, %.loc74_13.1 [template = i32]
-// CHECK:STDOUT:   %.loc74_16: type = struct_type {.c: i32} [template = constants.%.3]
+// CHECK:STDOUT:   %int.make_type_32.loc73: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:   %.loc73_13.1: type = value_of_initializer %int.make_type_32.loc73 [template = i32]
+// CHECK:STDOUT:   %.loc73_13.2: type = converted %int.make_type_32.loc73, %.loc73_13.1 [template = i32]
+// CHECK:STDOUT:   %.loc73_16: type = struct_type {.c: i32} [template = constants.%.3]
 // CHECK:STDOUT:   %c.var: ref %.3 = var c
 // CHECK:STDOUT:   %c: ref %.3 = bind_name c, %c.var
-// CHECK:STDOUT:   %.loc75_9.1: %.1 = tuple_literal ()
-// CHECK:STDOUT:   %.loc75_9.2: type = converted %.loc75_9.1, constants.%.1 [template = constants.%.1]
+// CHECK:STDOUT:   %.loc74_9.1: %.1 = tuple_literal ()
+// CHECK:STDOUT:   %.loc74_9.2: type = converted %.loc74_9.1, constants.%.1 [template = constants.%.1]
 // CHECK:STDOUT:   %d.var: ref %.1 = var d
 // CHECK:STDOUT:   %d: ref %.1 = bind_name d, %d.var
-// CHECK:STDOUT:   %.loc76_9.1: %.1 = tuple_literal ()
-// CHECK:STDOUT:   %.loc76_9.2: type = converted %.loc76_9.1, constants.%.1 [template = constants.%.1]
+// CHECK:STDOUT:   %.loc75_9.1: %.1 = tuple_literal ()
+// CHECK:STDOUT:   %.loc75_9.2: type = converted %.loc75_9.1, constants.%.1 [template = constants.%.1]
 // CHECK:STDOUT:   %e.var: ref %.1 = var e
 // CHECK:STDOUT:   %e: ref %.1 = bind_name e, %e.var
 // CHECK:STDOUT: }
@@ -1215,15 +1153,15 @@ import library "extern_api";
 // CHECK:STDOUT:   %A.call: init %.1 = call %A.ref()
 // CHECK:STDOUT:   assign file.%a.var, %A.call
 // CHECK:STDOUT:   %B.ref: %B.type = name_ref B, imports.%import_ref.2 [template = constants.%B]
-// CHECK:STDOUT:   %.loc73: i32 = int_literal 1 [template = constants.%.2]
-// CHECK:STDOUT:   %B.call: init i32 = call %B.ref(%.loc73)
+// CHECK:STDOUT:   %.loc72: i32 = int_literal 1 [template = constants.%.2]
+// CHECK:STDOUT:   %B.call: init i32 = call %B.ref(%.loc72)
 // CHECK:STDOUT:   assign file.%b.var, %B.call
 // CHECK:STDOUT:   %C.ref: %C.type = name_ref C, imports.%import_ref.3 [template = constants.%C]
-// CHECK:STDOUT:   %.loc74_23: i32 = int_literal 1 [template = constants.%.2]
-// CHECK:STDOUT:   %.loc74_25: %.4 = tuple_literal (%.loc74_23)
-// CHECK:STDOUT:   %tuple: %.4 = tuple_value (%.loc74_23) [template = constants.%tuple]
-// CHECK:STDOUT:   %.loc74_21: %.4 = converted %.loc74_25, %tuple [template = constants.%tuple]
-// CHECK:STDOUT:   %C.call: init %.3 = call %C.ref(%.loc74_21)
+// CHECK:STDOUT:   %.loc73_23: i32 = int_literal 1 [template = constants.%.2]
+// CHECK:STDOUT:   %.loc73_25: %.4 = tuple_literal (%.loc73_23)
+// CHECK:STDOUT:   %tuple: %.4 = tuple_value (%.loc73_23) [template = constants.%tuple]
+// CHECK:STDOUT:   %.loc73_21: %.4 = converted %.loc73_25, %tuple [template = constants.%tuple]
+// CHECK:STDOUT:   %C.call: init %.3 = call %C.ref(%.loc73_21)
 // CHECK:STDOUT:   assign file.%c.var, %C.call
 // CHECK:STDOUT:   %D.ref: %D.type = name_ref D, imports.%import_ref.4 [template = constants.%D]
 // CHECK:STDOUT:   %D.call: init %.1 = call %D.ref()
@@ -1235,195 +1173,6 @@ import library "extern_api";
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- decl_after_use.carbon
-// CHECK:STDOUT:
-// CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %.1: type = tuple_type () [template]
-// CHECK:STDOUT:   %A.type: type = fn_type @A [template]
-// CHECK:STDOUT:   %A: %A.type = struct_value () [template]
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %import_ref.1: %A.type = import_ref Main//extern_api, inst+3, loaded [template = constants.%A]
-// CHECK:STDOUT:   %import_ref.2 = import_ref Main//extern_api, inst+21, unloaded
-// CHECK:STDOUT:   %import_ref.3 = import_ref Main//extern_api, inst+41, unloaded
-// CHECK:STDOUT:   %import_ref.4 = import_ref Main//extern_api, inst+44, unloaded
-// CHECK:STDOUT:   %import_ref.5: <namespace> = import_ref Main//extern_api, inst+47, loaded
-// CHECK:STDOUT:   %NS: <namespace> = namespace %import_ref.5, [template] {
-// CHECK:STDOUT:     .E = %import_ref.6
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %import_ref.6 = import_ref Main//extern_api, inst+48, unloaded
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: file {
-// CHECK:STDOUT:   package: <namespace> = namespace [template] {
-// CHECK:STDOUT:     .A = %A.decl
-// CHECK:STDOUT:     .B = imports.%import_ref.2
-// CHECK:STDOUT:     .C = imports.%import_ref.3
-// CHECK:STDOUT:     .D = imports.%import_ref.4
-// CHECK:STDOUT:     .NS = imports.%NS
-// CHECK:STDOUT:     .Core = imports.%Core
-// CHECK:STDOUT:     .a = %a
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
-// CHECK:STDOUT:   %default.import = import <invalid>
-// CHECK:STDOUT:   %.loc6_9.1: %.1 = tuple_literal ()
-// CHECK:STDOUT:   %.loc6_9.2: type = converted %.loc6_9.1, constants.%.1 [template = constants.%.1]
-// CHECK:STDOUT:   %a.var: ref %.1 = var a
-// CHECK:STDOUT:   %a: ref %.1 = bind_name a, %a.var
-// CHECK:STDOUT:   %A.decl: %A.type = fn_decl @A [template = constants.%A] {}
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @A();
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @__global_init() {
-// CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %A.ref: %A.type = name_ref A, imports.%import_ref.1 [template = constants.%A]
-// CHECK:STDOUT:   %A.call: init %.1 = call %A.ref()
-// CHECK:STDOUT:   assign file.%a.var, %A.call
-// CHECK:STDOUT:   return
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_redecl_mismatch_after_use.carbon
-// CHECK:STDOUT:
-// CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %.1: type = tuple_type () [template]
-// CHECK:STDOUT:   %A.type: type = fn_type @A [template]
-// CHECK:STDOUT:   %A: %A.type = struct_value () [template]
-// CHECK:STDOUT:   %Int32.type: type = fn_type @Int32 [template]
-// CHECK:STDOUT:   %Int32: %Int32.type = struct_value () [template]
-// CHECK:STDOUT:   %.type: type = fn_type @.1 [template]
-// CHECK:STDOUT:   %.2: %.type = struct_value () [template]
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %import_ref.1: %A.type = import_ref Main//extern_api, inst+3, loaded [template = constants.%A]
-// CHECK:STDOUT:   %import_ref.2 = import_ref Main//extern_api, inst+21, unloaded
-// CHECK:STDOUT:   %import_ref.3 = import_ref Main//extern_api, inst+41, unloaded
-// CHECK:STDOUT:   %import_ref.4 = import_ref Main//extern_api, inst+44, unloaded
-// CHECK:STDOUT:   %import_ref.5: <namespace> = import_ref Main//extern_api, inst+47, loaded
-// CHECK:STDOUT:   %NS: <namespace> = namespace %import_ref.5, [template] {
-// CHECK:STDOUT:     .E = %import_ref.6
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %import_ref.6 = import_ref Main//extern_api, inst+48, unloaded
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     .Int32 = %import_ref.7
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %import_ref.7: %Int32.type = import_ref Core//prelude/types, inst+4, loaded [template = constants.%Int32]
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: file {
-// CHECK:STDOUT:   package: <namespace> = namespace [template] {
-// CHECK:STDOUT:     .A = imports.%import_ref.1
-// CHECK:STDOUT:     .B = imports.%import_ref.2
-// CHECK:STDOUT:     .C = imports.%import_ref.3
-// CHECK:STDOUT:     .D = imports.%import_ref.4
-// CHECK:STDOUT:     .NS = imports.%NS
-// CHECK:STDOUT:     .Core = imports.%Core
-// CHECK:STDOUT:     .a = %a
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
-// CHECK:STDOUT:   %default.import = import <invalid>
-// CHECK:STDOUT:   %.loc6_9.1: %.1 = tuple_literal ()
-// CHECK:STDOUT:   %.loc6_9.2: type = converted %.loc6_9.1, constants.%.1 [template = constants.%.1]
-// CHECK:STDOUT:   %a.var: ref %.1 = var a
-// CHECK:STDOUT:   %a: ref %.1 = bind_name a, %a.var
-// CHECK:STDOUT:   %.decl: %.type = fn_decl @.1 [template = constants.%.2] {
-// CHECK:STDOUT:     %int.make_type_32: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:     %.loc18_11.1: type = value_of_initializer %int.make_type_32 [template = i32]
-// CHECK:STDOUT:     %.loc18_11.2: type = converted %int.make_type_32, %.loc18_11.1 [template = i32]
-// CHECK:STDOUT:     @.1.%return: ref i32 = var <return slot>
-// CHECK:STDOUT:   }
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: extern fn @A();
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @.1() -> i32;
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @__global_init() {
-// CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %A.ref: %A.type = name_ref A, imports.%import_ref.1 [template = constants.%A]
-// CHECK:STDOUT:   %A.call: init %.1 = call %A.ref()
-// CHECK:STDOUT:   assign file.%a.var, %A.call
-// CHECK:STDOUT:   return
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: --- todo_fail_extern_after_use.carbon
-// CHECK:STDOUT:
-// CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %.1: type = tuple_type () [template]
-// CHECK:STDOUT:   %A.type: type = fn_type @A [template]
-// CHECK:STDOUT:   %A: %A.type = struct_value () [template]
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %import_ref.1: %A.type = import_ref Main//api, inst+3, loaded [template = constants.%A]
-// CHECK:STDOUT:   %import_ref.2 = import_ref Main//api, inst+21, unloaded
-// CHECK:STDOUT:   %import_ref.3 = import_ref Main//api, inst+41, unloaded
-// CHECK:STDOUT:   %import_ref.4 = import_ref Main//api, inst+44, unloaded
-// CHECK:STDOUT:   %import_ref.5: <namespace> = import_ref Main//api, inst+47, loaded
-// CHECK:STDOUT:   %NS: <namespace> = namespace %import_ref.5, [template] {
-// CHECK:STDOUT:     .E = %import_ref.6
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %import_ref.6 = import_ref Main//api, inst+48, unloaded
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: file {
-// CHECK:STDOUT:   package: <namespace> = namespace [template] {
-// CHECK:STDOUT:     .A = %A.decl
-// CHECK:STDOUT:     .B = imports.%import_ref.2
-// CHECK:STDOUT:     .C = imports.%import_ref.3
-// CHECK:STDOUT:     .D = imports.%import_ref.4
-// CHECK:STDOUT:     .NS = imports.%NS
-// CHECK:STDOUT:     .Core = imports.%Core
-// CHECK:STDOUT:     .a = %a
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
-// CHECK:STDOUT:   %default.import = import <invalid>
-// CHECK:STDOUT:   %.loc6_9.1: %.1 = tuple_literal ()
-// CHECK:STDOUT:   %.loc6_9.2: type = converted %.loc6_9.1, constants.%.1 [template = constants.%.1]
-// CHECK:STDOUT:   %a.var: ref %.1 = var a
-// CHECK:STDOUT:   %a: ref %.1 = bind_name a, %a.var
-// CHECK:STDOUT:   %A.decl: %A.type = fn_decl @A [template = constants.%A] {}
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: extern fn @A();
-// CHECK:STDOUT:
-// CHECK:STDOUT: fn @__global_init() {
-// CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %A.ref: %A.type = name_ref A, imports.%import_ref.1 [template = constants.%A]
-// CHECK:STDOUT:   %A.call: init %.1 = call %A.ref()
-// CHECK:STDOUT:   assign file.%a.var, %A.call
-// CHECK:STDOUT:   return
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
 // CHECK:STDOUT: --- unloaded.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -1496,44 +1245,3 @@ import library "extern_api";
 // CHECK:STDOUT:   %default.import = import <invalid>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_loaded_merge.carbon
-// CHECK:STDOUT:
-// CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %import_ref.1 = import_ref Main//api, inst+3, unloaded
-// CHECK:STDOUT:   %import_ref.2 = import_ref Main//api, inst+21, unloaded
-// CHECK:STDOUT:   %import_ref.3 = import_ref Main//api, inst+41, unloaded
-// CHECK:STDOUT:   %import_ref.4 = import_ref Main//api, inst+44, unloaded
-// CHECK:STDOUT:   %import_ref.5: <namespace> = import_ref Main//api, inst+47, loaded
-// CHECK:STDOUT:   %NS: <namespace> = namespace %import_ref.5, [template] {
-// CHECK:STDOUT:     .E = %import_ref.6
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %import_ref.6 = import_ref Main//api, inst+48, unloaded
-// CHECK:STDOUT:   %import_ref.7 = import_ref Main//extern_api, inst+3, unloaded
-// CHECK:STDOUT:   %import_ref.8 = import_ref Main//extern_api, inst+21, unloaded
-// CHECK:STDOUT:   %import_ref.9 = import_ref Main//extern_api, inst+41, unloaded
-// CHECK:STDOUT:   %import_ref.10 = import_ref Main//extern_api, inst+44, unloaded
-// CHECK:STDOUT:   %import_ref.11 = import_ref Main//extern_api, inst+48, unloaded
-// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
-// CHECK:STDOUT:     import Core//prelude
-// CHECK:STDOUT:     import Core//prelude/operators
-// CHECK:STDOUT:     import Core//prelude/types
-// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
-// CHECK:STDOUT:     import Core//prelude/operators/bitwise
-// CHECK:STDOUT:     import Core//prelude/operators/comparison
-// CHECK:STDOUT:     import Core//prelude/types/bool
-// CHECK:STDOUT:   }
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: file {
-// CHECK:STDOUT:   package: <namespace> = namespace [template] {
-// CHECK:STDOUT:     .A = imports.%import_ref.1
-// CHECK:STDOUT:     .B = imports.%import_ref.2
-// CHECK:STDOUT:     .C = imports.%import_ref.3
-// CHECK:STDOUT:     .D = imports.%import_ref.4
-// CHECK:STDOUT:     .NS = imports.%NS
-// CHECK:STDOUT:     .Core = imports.%Core
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import = import Core
-// CHECK:STDOUT:   %default.import = import <invalid>
-// CHECK:STDOUT: }
-// CHECK:STDOUT:

+ 44 - 18
toolchain/check/testdata/function/declaration/no_prelude/extern.carbon

@@ -14,6 +14,12 @@ library "basic";
 
 extern fn F();
 
+// --- basic_use.carbon
+
+import library "basic";
+
+var x: () = F();
+
 // --- fail_redecl.carbon
 
 library "redecl";
@@ -52,22 +58,12 @@ class C {
   // CHECK:STDERR:   ^~~~~~
   // CHECK:STDERR:
   extern fn F();
-  // CHECK:STDERR: fail_member_extern.carbon:[[@LINE+4]]:3: ERROR: `extern` not allowed on `fn` declaration that is a member.
+  // CHECK:STDERR: fail_member_extern.carbon:[[@LINE+3]]:3: ERROR: `extern` not allowed on `fn` declaration that is a member.
   // CHECK:STDERR:   extern fn G[self: Self]();
   // CHECK:STDERR:   ^~~~~~
-  // CHECK:STDERR:
   extern fn G[self: Self]();
 }
 
-// --- fail_todo_extern_library.carbon
-
-library "extern_library";
-
-// CHECK:STDERR: fail_todo_extern_library.carbon:[[@LINE+3]]:1: ERROR: Semantics TODO: `extern library syntax`.
-// CHECK:STDERR: extern library "foo" fn F();
-// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~
-extern library "foo" fn F();
-
 // CHECK:STDOUT: --- basic.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
@@ -85,6 +81,40 @@ extern library "foo" fn F();
 // CHECK:STDOUT:
 // CHECK:STDOUT: extern fn @F();
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- basic_use.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %import_ref: %F.type = import_ref Main//basic, inst+1, loaded [template = constants.%F]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = imports.%import_ref
+// CHECK:STDOUT:     .x = %x
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %default.import = import <invalid>
+// CHECK:STDOUT:   %.loc4_9.1: %.1 = tuple_literal ()
+// CHECK:STDOUT:   %.loc4_9.2: type = converted %.loc4_9.1, constants.%.1 [template = constants.%.1]
+// CHECK:STDOUT:   %x.var: ref %.1 = var x
+// CHECK:STDOUT:   %x: ref %.1 = bind_name x, %x.var
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @__global_init() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %F.ref: %F.type = name_ref F, imports.%import_ref [template = constants.%F]
+// CHECK:STDOUT:   %F.call: init %.1 = call %F.ref()
+// CHECK:STDOUT:   assign file.%x.var, %F.call
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_redecl.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
@@ -144,8 +174,8 @@ extern library "foo" fn F();
 // CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
 // CHECK:STDOUT:   %G.decl: %G.type = fn_decl @G [template = constants.%G] {
 // CHECK:STDOUT:     %Self.ref: type = name_ref Self, constants.%C [template = constants.%C]
-// CHECK:STDOUT:     %self.loc14_15.1: %C = param self
-// CHECK:STDOUT:     %self.loc14_15.2: %C = bind_name self, %self.loc14_15.1
+// CHECK:STDOUT:     %self.loc13_15.1: %C = param self
+// CHECK:STDOUT:     %self.loc13_15.2: %C = bind_name self, %self.loc13_15.1
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -156,9 +186,5 @@ extern library "foo" fn F();
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F();
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @G[@C.%self.loc14_15.2: %C]();
-// CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_extern_library.carbon
-// CHECK:STDOUT:
-// CHECK:STDOUT: file {}
+// CHECK:STDOUT: fn @G[@C.%self.loc13_15.2: %C]();
 // CHECK:STDOUT:

+ 381 - 0
toolchain/check/testdata/function/declaration/no_prelude/extern_library.carbon

@@ -0,0 +1,381 @@
+// 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
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/no_prelude/extern_library.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/no_prelude/extern_library.carbon
+
+// --- extern_library.carbon
+
+library "extern_library";
+
+extern library "extern_library_owner" fn F();
+
+// --- extern_library_owner.carbon
+
+library "extern_library_owner";
+
+import library "extern_library";
+
+extern fn F();
+
+// --- fail_extern_library_nonowner.carbon
+
+library "extern_library_nonowner";
+
+import library "extern_library";
+
+// CHECK:STDERR: fail_extern_library_nonowner.carbon:[[@LINE+10]]:1: ERROR: Declaration in library "extern_library_nonowner" doesn't match `extern library` declaration.
+// CHECK:STDERR: extern fn F();
+// CHECK:STDERR: ^~~~~~~~~~~~~~
+// CHECK:STDERR: fail_extern_library_nonowner.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: import library "extern_library";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: extern_library.carbon:4:1: Previously declared with `extern library` here.
+// CHECK:STDERR: extern library "extern_library_owner" fn F();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+extern fn F();
+
+// --- fail_extern_library_nonextern.carbon
+
+library "extern_library_nonextern";
+
+import library "extern_library";
+
+// CHECK:STDERR: fail_extern_library_nonextern.carbon:[[@LINE+10]]:1: ERROR: Redeclarations of `fn F` must match use of `extern`.
+// CHECK:STDERR: fn F();
+// CHECK:STDERR: ^~~~~~~
+// CHECK:STDERR: fail_extern_library_nonextern.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: import library "extern_library";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: extern_library.carbon:4:1: Previously declared here.
+// CHECK:STDERR: extern library "extern_library_owner" fn F();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn F();
+
+// --- fail_extern_library_redecl.carbon
+
+library "fail_extern_library_redecl";
+
+import library "extern_library";
+
+// CHECK:STDERR: fail_extern_library_redecl.carbon:[[@LINE+10]]:1: ERROR: Declaration in library "fail_extern_library_redecl" doesn't match `extern library` declaration.
+// CHECK:STDERR: extern library "extern_library_owner" fn F();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_extern_library_redecl.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: import library "extern_library";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: extern_library.carbon:4:1: Previously declared with `extern library` here.
+// CHECK:STDERR: extern library "extern_library_owner" fn F();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+extern library "extern_library_owner" fn F();
+
+// --- extern_library_copy.carbon
+
+library "extern_library_copy";
+
+extern library "extern_library_owner" fn F();
+
+// --- fail_extern_library_collision.carbon
+
+library "extern_library_collision";
+
+// CHECK:STDERR: fail_extern_library_collision.carbon:[[@LINE+13]]:1: In import.
+// CHECK:STDERR: import library "extern_library";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: extern_library_copy.carbon:4:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: extern library "extern_library_owner" fn F();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_extern_library_collision.carbon:[[@LINE+7]]:1: In import.
+// CHECK:STDERR: import library "extern_library";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: extern_library.carbon:4:1: Name is previously declared here.
+// CHECK:STDERR: extern library "extern_library_owner" fn F();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+import library "extern_library";
+import library "extern_library_copy";
+
+// --- extern_library_mismatch.carbon
+
+library "extern_library_mismatch";
+
+extern library "extern_library_owner" fn F();
+
+// --- fail_extern_library_mismatch_owner.carbon
+
+library "extern_library_mismatch_owner";
+
+import library "extern_library_mismatch"
+
+// CHECK:STDERR: fail_extern_library_mismatch_owner.carbon:[[@LINE+4]]:1: ERROR: `import` declarations must end with a `;`.
+// CHECK:STDERR: extern fn F();
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR:
+extern fn F();
+
+// --- fail_extern_self_library.carbon
+
+library "extern_self_library";
+
+// CHECK:STDERR: fail_extern_self_library.carbon:[[@LINE+4]]:1: ERROR: `extern library` cannot specify the current library.
+// CHECK:STDERR: extern library "extern_self_library" fn F();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+extern library "extern_self_library" fn F();
+
+// --- extern_of_import.carbon
+
+library "extern_of_import";
+
+fn F();
+
+// --- fail_extern_of_import_redecl.carbon
+
+library "extern_of_import_redecl";
+
+import library "extern_of_import";
+
+// CHECK:STDERR: fail_extern_of_import_redecl.carbon:[[@LINE+9]]:1: ERROR: Redeclarations of `fn F` must match use of `extern`.
+// CHECK:STDERR: extern library "extern_of_import" fn F();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_extern_of_import_redecl.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: import library "extern_of_import";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: extern_of_import.carbon:4:1: Previously declared here.
+// CHECK:STDERR: fn F();
+// CHECK:STDERR: ^~~~~~~
+extern library "extern_of_import" fn F();
+
+// CHECK:STDOUT: --- extern_library.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- extern_library_owner.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %import_ref: %F.type = import_ref Main//extern_library, inst+1, loaded [template = constants.%F]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %default.import = import <invalid>
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_extern_library_nonowner.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %import_ref: %F.type = import_ref Main//extern_library, inst+1, loaded [template = constants.%F]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %default.import = import <invalid>
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_extern_library_nonextern.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %import_ref: %F.type = import_ref Main//extern_library, inst+1, loaded [template = constants.%F]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %default.import = import <invalid>
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_extern_library_redecl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %import_ref: %F.type = import_ref Main//extern_library, inst+1, loaded [template = constants.%F]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = invalid
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %default.import = import <invalid>
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- extern_library_copy.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_extern_library_collision.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %import_ref.1 = import_ref Main//extern_library, inst+1, unloaded
+// CHECK:STDOUT:   %import_ref.2 = import_ref Main//extern_library_copy, inst+1, unloaded
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = imports.%import_ref.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %default.import = import <invalid>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- extern_library_mismatch.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_extern_library_mismatch_owner.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_extern_self_library.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- extern_of_import.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_extern_of_import_redecl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %import_ref: %F.type = import_ref Main//extern_of_import, inst+1, loaded [template = constants.%F]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = invalid
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %default.import = import <invalid>
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:

+ 102 - 0
toolchain/check/testdata/function/declaration/no_prelude/extern_library_for_default.carbon

@@ -0,0 +1,102 @@
+// 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
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/no_prelude/extern_library_for_default.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/no_prelude/extern_library_for_default.carbon
+
+// --- default.carbon
+
+package Foo;
+
+extern library "expected" fn F();
+
+// --- expected.carbon
+
+package Foo library "expected";
+
+import library default;
+
+extern fn F();
+
+// --- fail_wrong_library.carbon
+
+package Foo library "wrong_library";
+
+import library default;
+
+// CHECK:STDERR: fail_wrong_library.carbon:[[@LINE+9]]:1: ERROR: Declaration in library "wrong_library" doesn't match `extern library` declaration.
+// CHECK:STDERR: extern fn F();
+// CHECK:STDERR: ^~~~~~~~~~~~~~
+// CHECK:STDERR: fail_wrong_library.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: import library default;
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: default.carbon:4:1: Previously declared with `extern library` here.
+// CHECK:STDERR: extern library "expected" fn F();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+extern fn F();
+
+// CHECK:STDOUT: --- default.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- expected.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %import_ref: %F.type = import_ref Foo//default, inst+1, loaded [template = constants.%F]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %default.import = import <invalid>
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_wrong_library.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %import_ref: %F.type = import_ref Foo//default, inst+1, loaded [template = constants.%F]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %default.import = import <invalid>
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:

+ 100 - 0
toolchain/check/testdata/function/declaration/no_prelude/extern_library_from_default.carbon

@@ -0,0 +1,100 @@
+// 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
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/no_prelude/extern_library_from_default.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/no_prelude/extern_library_from_default.carbon
+
+// --- extern_library.carbon
+
+library "extern_library";
+
+extern library default fn F();
+
+// --- default.carbon
+
+import library "extern_library";
+
+extern fn F();
+
+// --- fail_wrong_library.carbon
+
+library "wrong_library";
+
+import library "extern_library";
+
+// CHECK:STDERR: fail_wrong_library.carbon:[[@LINE+9]]:1: ERROR: Declaration in library "wrong_library" doesn't match `extern library` declaration.
+// CHECK:STDERR: extern fn F();
+// CHECK:STDERR: ^~~~~~~~~~~~~~
+// CHECK:STDERR: fail_wrong_library.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: import library "extern_library";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: extern_library.carbon:4:1: Previously declared with `extern library` here.
+// CHECK:STDERR: extern library default fn F();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+extern fn F();
+
+// CHECK:STDOUT: --- extern_library.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- default.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %import_ref: %F.type = import_ref Main//extern_library, inst+1, loaded [template = constants.%F]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %default.import = import <invalid>
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_wrong_library.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %import_ref: %F.type = import_ref Main//extern_library, inst+1, loaded [template = constants.%F]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %default.import = import <invalid>
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:

+ 1 - 5
toolchain/check/testdata/function/declaration/no_prelude/fail_modifiers.carbon

@@ -82,10 +82,6 @@ base fn InvalidModifier();
 // CHECK:STDERR:
 default final virtual fn ModifiersConflict2() {}
 
-// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+10]]:1: ERROR: `extern` not allowed on `fn` declaration that provides a definition.
-// CHECK:STDERR: extern private fn ExternOrderAndConflict() {}
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR:
 // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+6]]:8: ERROR: `private` must appear before `extern`.
 // CHECK:STDERR: extern private fn ExternOrderAndConflict() {}
 // CHECK:STDERR:        ^~~~~~~
@@ -154,7 +150,7 @@ extern private fn ExternOrderAndConflict() {}
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @ExternOrderAndConflict() {
+// CHECK:STDOUT: extern fn @ExternOrderAndConflict() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 2 - 2
toolchain/check/testdata/function/declaration/no_prelude/implicit_import.carbon

@@ -30,7 +30,7 @@ extern fn A();
 
 impl library "extern_api";
 
-// CHECK:STDERR: fail_extern_api.impl.carbon:[[@LINE+14]]:1: ERROR: Redeclarations of `fn A` in the same library must match use of `extern`.
+// CHECK:STDERR: fail_extern_api.impl.carbon:[[@LINE+14]]:1: ERROR: Redeclarations of `fn A` must match use of `extern`.
 // CHECK:STDERR: fn A();
 // CHECK:STDERR: ^~~~~~~
 // CHECK:STDERR: fail_extern_api.impl.carbon:[[@LINE-5]]:6: In import.
@@ -56,7 +56,7 @@ fn A();
 
 impl library "extern_impl";
 
-// CHECK:STDERR: fail_extern_impl.impl.carbon:[[@LINE+9]]:1: ERROR: Redeclarations of `fn A` in the same library must match use of `extern`.
+// CHECK:STDERR: fail_extern_impl.impl.carbon:[[@LINE+9]]:1: ERROR: Redeclarations of `fn A` must match use of `extern`.
 // CHECK:STDERR: extern fn A();
 // CHECK:STDERR: ^~~~~~~~~~~~~~
 // CHECK:STDERR: fail_extern_impl.impl.carbon:[[@LINE-5]]:6: In import.

+ 34 - 12
toolchain/check/testdata/function/definition/import.carbon

@@ -47,7 +47,7 @@ library "def_ownership";
 
 import library "fns";
 
-// CHECK:STDERR: fail_def_ownership.carbon:[[@LINE+10]]:1: ERROR: Only one library can declare `fn A` without `extern`.
+// CHECK:STDERR: fail_def_ownership.carbon:[[@LINE+10]]:1: ERROR: Redeclaration of `fn A` is redundant.
 // CHECK:STDERR: fn A() {};
 // CHECK:STDERR: ^~~~~~~~
 // CHECK:STDERR: fail_def_ownership.carbon:[[@LINE-5]]:1: In import.
@@ -58,7 +58,7 @@ import library "fns";
 // CHECK:STDERR: ^~~~~~~~
 // CHECK:STDERR:
 fn A() {};
-// CHECK:STDERR: fail_def_ownership.carbon:[[@LINE+10]]:1: ERROR: Only one library can declare `fn B` without `extern`.
+// CHECK:STDERR: fail_def_ownership.carbon:[[@LINE+10]]:1: ERROR: Redeclaration of `fn B` is redundant.
 // CHECK:STDERR: fn B(b: i32) -> i32;
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR: fail_def_ownership.carbon:[[@LINE-16]]:1: In import.
@@ -70,13 +70,24 @@ fn A() {};
 // CHECK:STDERR:
 fn B(b: i32) -> i32;
 
-// --- redecl_then_def.carbon
+// --- fail_redecl_then_def.carbon
 
 library "redecl_then_def";
 
 import library "extern";
 
+// CHECK:STDERR: fail_redecl_then_def.carbon:[[@LINE+10]]:1: ERROR: Redeclarations of `fn A` must match use of `extern`.
+// CHECK:STDERR: fn A();
+// CHECK:STDERR: ^~~~~~~
+// CHECK:STDERR: fail_redecl_then_def.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: import library "extern";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: extern.carbon:4:1: Previously declared here.
+// CHECK:STDERR: extern fn A();
+// CHECK:STDERR: ^~~~~~~~~~~~~~
+// CHECK:STDERR:
 fn A();
+
 fn A() {}
 
 // --- fail_mix_extern_decl.carbon
@@ -85,11 +96,22 @@ library "mix_extern_decl";
 
 import library "fns";
 
+// CHECK:STDERR: fail_mix_extern_decl.carbon:[[@LINE+10]]:1: ERROR: Redeclarations of `fn D` must match use of `extern`.
+// CHECK:STDERR: extern fn D();
+// CHECK:STDERR: ^~~~~~~~~~~~~~
+// CHECK:STDERR: fail_mix_extern_decl.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: import library "fns";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: fns.carbon:7:1: Previously declared here.
+// CHECK:STDERR: fn D();
+// CHECK:STDERR: ^~~~~~~
+// CHECK:STDERR:
 extern fn D();
-// CHECK:STDERR: fail_mix_extern_decl.carbon:[[@LINE+6]]:1: ERROR: Redeclarations of `fn D` in the same library must match use of `extern`.
+
+// CHECK:STDERR: fail_mix_extern_decl.carbon:[[@LINE+6]]:1: ERROR: Redeclarations of `fn D` must match use of `extern`.
 // CHECK:STDERR: fn D() {}
 // CHECK:STDERR: ^~~~~~~~
-// CHECK:STDERR: fail_mix_extern_decl.carbon:[[@LINE-4]]:1: Previously declared here.
+// CHECK:STDERR: fail_mix_extern_decl.carbon:[[@LINE-5]]:1: Previously declared here.
 // CHECK:STDERR: extern fn D();
 // CHECK:STDERR: ^~~~~~~~~~~~~~
 fn D() {}
@@ -379,7 +401,7 @@ fn D() {}
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @B(%b: i32) -> i32;
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- redecl_then_def.carbon
+// CHECK:STDOUT: --- fail_redecl_then_def.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %A.type: type = fn_type @A [template]
@@ -402,13 +424,13 @@ fn D() {}
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
-// CHECK:STDOUT:     .A = %A.decl.loc6
+// CHECK:STDOUT:     .A = %A.decl.loc16
 // CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %default.import = import <invalid>
-// CHECK:STDOUT:   %A.decl.loc6: %A.type = fn_decl @A [template = constants.%A] {}
-// CHECK:STDOUT:   %A.decl.loc7: %A.type = fn_decl @A [template = constants.%A] {}
+// CHECK:STDOUT:   %A.decl.loc16: %A.type = fn_decl @A [template = constants.%A] {}
+// CHECK:STDOUT:   %A.decl.loc18: %A.type = fn_decl @A [template = constants.%A] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @A() {
@@ -445,13 +467,13 @@ fn D() {}
 // CHECK:STDOUT:     .A = imports.%import_ref.1
 // CHECK:STDOUT:     .B = imports.%import_ref.2
 // CHECK:STDOUT:     .C = imports.%import_ref.3
-// CHECK:STDOUT:     .D = %D.decl.loc13
+// CHECK:STDOUT:     .D = %D.decl.loc24
 // CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %default.import = import <invalid>
-// CHECK:STDOUT:   %D.decl.loc6: %D.type = fn_decl @D [template = constants.%D] {}
-// CHECK:STDOUT:   %D.decl.loc13: %D.type = fn_decl @D [template = constants.%D] {}
+// CHECK:STDOUT:   %D.decl.loc16: %D.type = fn_decl @D [template = constants.%D] {}
+// CHECK:STDOUT:   %D.decl.loc24: %D.type = fn_decl @D [template = constants.%D] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @D() {

+ 125 - 11
toolchain/check/testdata/function/definition/no_prelude/extern.carbon

@@ -8,30 +8,59 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/definition/no_prelude/extern.carbon
 
-// --- fail_extern_def.carbon
+// --- extern_def.carbon
 
 library "extern_def";
 
-// CHECK:STDERR: fail_extern_def.carbon:[[@LINE+4]]:1: ERROR: `extern` not allowed on `fn` declaration that provides a definition.
-// CHECK:STDERR: extern fn F() {}
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR:
 extern fn F() {}
 
-// --- fail_def_for_extern_decl.carbon
+// --- def_for_extern_decl.carbon
 
 library "def_for_extern_decl";
 
 extern fn F();
-// CHECK:STDERR: fail_def_for_extern_decl.carbon:[[@LINE+7]]:1: ERROR: Redeclarations of `fn F` in the same library must match use of `extern`.
+extern fn F() {}
+
+// --- split_library.carbon
+
+library "split_library";
+
+extern fn F();
+
+// --- split_library.impl.carbon
+
+impl library "split_library";
+
+extern fn F() {}
+
+// --- fail_def_extern_mismatch.carbon
+
+library "def_extern_mismatch";
+
+extern fn F();
+// CHECK:STDERR: fail_def_extern_mismatch.carbon:[[@LINE+7]]:1: ERROR: Redeclarations of `fn F` must match use of `extern`.
 // CHECK:STDERR: fn F() {}
 // CHECK:STDERR: ^~~~~~~~
-// CHECK:STDERR: fail_def_for_extern_decl.carbon:[[@LINE-4]]:1: Previously declared here.
+// CHECK:STDERR: fail_def_extern_mismatch.carbon:[[@LINE-4]]:1: Previously declared here.
 // CHECK:STDERR: extern fn F();
 // CHECK:STDERR: ^~~~~~~~~~~~~~
 // CHECK:STDERR:
 fn F() {}
 
+// --- fail_def_extern_mismatch_reverse.carbon
+
+library "def_extern_mismatch_reverse";
+
+fn F();
+// CHECK:STDERR: fail_def_extern_mismatch_reverse.carbon:[[@LINE+7]]:1: ERROR: Redeclarations of `fn F` must match use of `extern`.
+// CHECK:STDERR: extern fn F() {}
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_def_extern_mismatch_reverse.carbon:[[@LINE-4]]:1: Previously declared here.
+// CHECK:STDERR: fn F();
+// CHECK:STDERR: ^~~~~~~
+// CHECK:STDERR:
+extern fn F() {}
+
 // --- fail_extern_diag_suppressed.carbon
 
 library "extern_diag_suppressed";
@@ -60,7 +89,7 @@ fn F() {}
 // CHECK:STDERR: ^~~~~~~~
 extern fn F();
 
-// CHECK:STDOUT: --- fail_extern_def.carbon
+// CHECK:STDOUT: --- extern_def.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %F.type: type = fn_type @F [template]
@@ -75,12 +104,76 @@ extern fn F();
 // CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: extern fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- def_for_extern_decl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl.loc4
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl.loc4: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT:   %F.decl.loc5: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- split_library.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- split_library.impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %import_ref: %F.type = import_ref Main//split_library, inst+1, loaded [template = constants.%F]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %default.import.loc2_6.1 = import <invalid>
+// CHECK:STDOUT:   %default.import.loc2_6.2 = import <invalid>
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_def_for_extern_decl.carbon
+// CHECK:STDOUT: --- fail_def_extern_mismatch.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %F.type: type = fn_type @F [template]
@@ -101,6 +194,27 @@ extern fn F();
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_def_extern_mismatch_reverse.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl.loc4
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl.loc4: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT:   %F.decl.loc12: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_extern_diag_suppressed.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {

+ 382 - 0
toolchain/check/testdata/function/definition/no_prelude/extern_library.carbon

@@ -0,0 +1,382 @@
+// 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
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/definition/no_prelude/extern_library.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/definition/no_prelude/extern_library.carbon
+
+// --- one_file_extern.carbon
+
+library "one_file_extern";
+
+extern library "one_file" fn F();
+
+// --- one_file.carbon
+
+library "one_file";
+
+extern fn F() {}
+
+// --- two_file_extern.carbon
+
+library "two_file_extern";
+
+extern library "two_file" fn F();
+
+// --- two_file.carbon
+
+library "two_file";
+
+extern fn F();
+
+// --- two_file.impl.carbon
+
+impl library "two_file";
+
+extern fn F() {}
+
+// --- two_file_impl_mismatch_extern.carbon
+
+library "two_file_impl_mismatch_extern";
+
+extern library "two_file_impl_mismatch" fn F();
+
+// --- two_file_impl_mismatch.carbon
+
+library "two_file_impl_mismatch";
+
+extern fn F();
+
+// --- fail_two_file_impl_mismatch.impl.carbon
+
+impl library "two_file_impl_mismatch";
+
+// CHECK:STDERR: fail_two_file_impl_mismatch.impl.carbon:[[@LINE+10]]:1: ERROR: Redeclarations of `fn F` must match use of `extern`.
+// CHECK:STDERR: fn F() {}
+// CHECK:STDERR: ^~~~~~~~
+// CHECK:STDERR: fail_two_file_impl_mismatch.impl.carbon:[[@LINE-5]]:6: In import.
+// CHECK:STDERR: impl library "two_file_impl_mismatch";
+// CHECK:STDERR:      ^~~~~~~
+// CHECK:STDERR: two_file_impl_mismatch.carbon:4:1: Previously declared here.
+// CHECK:STDERR: extern fn F();
+// CHECK:STDERR: ^~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn F() {}
+
+// --- indirect_two_file_extern.carbon
+
+library "indirect_two_file_extern";
+
+extern library "indirect_two_file" fn F();
+
+// --- indirect_two_file.carbon
+
+library "indirect_two_file";
+
+import library "indirect_two_file_extern";
+
+// --- fail_todo_indirect_two_file.impl.carbon
+
+impl library "indirect_two_file";
+
+// CHECK:STDERR: fail_todo_indirect_two_file.impl.carbon:[[@LINE+12]]:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: extern fn F() {}
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_todo_indirect_two_file.impl.carbon:[[@LINE-5]]:6: In import.
+// CHECK:STDERR: impl library "indirect_two_file";
+// CHECK:STDERR:      ^~~~~~~
+// CHECK:STDERR: indirect_two_file.carbon:4:1: In import.
+// CHECK:STDERR: import library "indirect_two_file_extern";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: indirect_two_file_extern.carbon:4:1: Name is previously declared here.
+// CHECK:STDERR: extern library "indirect_two_file" fn F();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+extern fn F() {}
+
+// --- in_impl_extern.carbon
+
+library "in_impl_extern";
+
+extern library "in_impl" fn F();
+
+// --- in_impl.carbon
+
+library "in_impl";
+
+// --- in_impl.impl.carbon
+
+impl library "in_impl";
+
+import library "in_impl_extern";
+
+extern fn F() {}
+
+// CHECK:STDOUT: --- one_file_extern.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- one_file.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- two_file_extern.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- two_file.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- two_file.impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %import_ref: %F.type = import_ref Main//two_file, inst+1, loaded [template = constants.%F]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %default.import.loc2_6.1 = import <invalid>
+// CHECK:STDOUT:   %default.import.loc2_6.2 = import <invalid>
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- two_file_impl_mismatch_extern.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- two_file_impl_mismatch.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_two_file_impl_mismatch.impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %import_ref: %F.type = import_ref Main//two_file_impl_mismatch, inst+1, loaded [template = constants.%F]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %default.import.loc2_6.1 = import <invalid>
+// CHECK:STDOUT:   %default.import.loc2_6.2 = import <invalid>
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- indirect_two_file_extern.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- indirect_two_file.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %import_ref = import_ref Main//indirect_two_file_extern, inst+1, unloaded
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = imports.%import_ref
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %default.import = import <invalid>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_indirect_two_file.impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT:   %.type: type = fn_type @.1 [template]
+// CHECK:STDOUT:   %.2: %.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %import_ref: %F.type = import_ref Main//indirect_two_file, inst+2, loaded [template = constants.%F]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = imports.%import_ref
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %default.import.loc2_6.1 = import <invalid>
+// CHECK:STDOUT:   %default.import.loc2_6.2 = import <invalid>
+// CHECK:STDOUT:   %.decl: %.type = fn_decl @.1 [template = constants.%.2] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @.1() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- in_impl_extern.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- in_impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- in_impl.impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %import_ref: %F.type = import_ref Main//in_impl_extern, inst+1, loaded [template = constants.%F]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %default.import.loc2_6.1 = import <invalid>
+// CHECK:STDOUT:   %default.import.loc2_6.2 = import <invalid>
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: extern fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 10 - 4
toolchain/check/testdata/function/definition/no_prelude/implicit_import.carbon

@@ -30,7 +30,7 @@ extern fn A();
 
 impl library "extern_api";
 
-// CHECK:STDERR: fail_extern_api.impl.carbon:[[@LINE+10]]:1: ERROR: Redeclarations of `fn A` in the same library must match use of `extern`.
+// CHECK:STDERR: fail_extern_api.impl.carbon:[[@LINE+10]]:1: ERROR: Redeclarations of `fn A` must match use of `extern`.
 // CHECK:STDERR: fn A() {}
 // CHECK:STDERR: ^~~~~~~~
 // CHECK:STDERR: fail_extern_api.impl.carbon:[[@LINE-5]]:6: In import.
@@ -52,9 +52,15 @@ fn A();
 
 impl library "extern_impl";
 
-// CHECK:STDERR: fail_extern_impl.impl.carbon:[[@LINE+4]]:1: ERROR: `extern` not allowed on `fn` declaration that provides a definition.
+// CHECK:STDERR: fail_extern_impl.impl.carbon:[[@LINE+10]]:1: ERROR: Redeclarations of `fn A` must match use of `extern`.
 // CHECK:STDERR: extern fn A() {}
-// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: ^~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_extern_impl.impl.carbon:[[@LINE-5]]:6: In import.
+// CHECK:STDERR: impl library "extern_impl";
+// CHECK:STDERR:      ^~~~~~~
+// CHECK:STDERR: extern_impl.carbon:4:1: Previously declared here.
+// CHECK:STDERR: fn A();
+// CHECK:STDERR: ^~~~~~~
 // CHECK:STDERR:
 extern fn A() {}
 
@@ -248,7 +254,7 @@ fn B() {}
 // CHECK:STDOUT:   %A.decl: %A.type = fn_decl @A [template = constants.%A] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @A() {
+// CHECK:STDOUT: extern fn @A() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 1 - 1
toolchain/check/testdata/packages/no_prelude/cross_package_import.carbon

@@ -144,7 +144,7 @@ import library "other_ns";
 // CHECK:STDERR:
 import Other library "fn";
 
-// CHECK:STDERR: fail_main_namespace_conflict.carbon:[[@LINE+10]]:1: ERROR: Only one library can declare `fn F` without `extern`.
+// CHECK:STDERR: fail_main_namespace_conflict.carbon:[[@LINE+10]]:1: ERROR: Redeclaration of `fn F` is redundant.
 // CHECK:STDERR: fn Other.F() {}
 // CHECK:STDERR: ^~~~~~~~~~~~~~
 // CHECK:STDERR: fail_main_namespace_conflict.carbon:[[@LINE-5]]:1: In import.

+ 5 - 1
toolchain/diagnostics/diagnostic_kind.def

@@ -155,7 +155,6 @@ CARBON_DIAGNOSTIC_KIND(MissingDefinitionInImpl)
 // Merge-related redeclaration checking.
 CARBON_DIAGNOSTIC_KIND(RedeclPrevDecl)
 CARBON_DIAGNOSTIC_KIND(RedeclRedundant)
-CARBON_DIAGNOSTIC_KIND(RedeclNonExtern)
 CARBON_DIAGNOSTIC_KIND(RedeclPrevDef)
 CARBON_DIAGNOSTIC_KIND(RedeclRedef)
 CARBON_DIAGNOSTIC_KIND(RedeclExternMismatch)
@@ -167,6 +166,9 @@ CARBON_DIAGNOSTIC_KIND(RedeclParamDiffers)
 CARBON_DIAGNOSTIC_KIND(RedeclParamPrevious)
 CARBON_DIAGNOSTIC_KIND(RedeclParamSyntaxDiffers)
 CARBON_DIAGNOSTIC_KIND(RedeclParamSyntaxPrevious)
+CARBON_DIAGNOSTIC_KIND(ExternLibraryInImporter)
+CARBON_DIAGNOSTIC_KIND(ExternLibraryIncorrect)
+CARBON_DIAGNOSTIC_KIND(ExternLibraryExpected)
 
 // Function call checking.
 CARBON_DIAGNOSTIC_KIND(AddrSelfIsNonRef)
@@ -335,6 +337,8 @@ CARBON_DIAGNOSTIC_KIND(ModifierRepeated)
 CARBON_DIAGNOSTIC_KIND(ModifierNotAllowedWith)
 CARBON_DIAGNOSTIC_KIND(ModifierMustAppearBefore)
 CARBON_DIAGNOSTIC_KIND(ModifierPrevious)
+CARBON_DIAGNOSTIC_KIND(ExternLibraryOnDefinition)
+CARBON_DIAGNOSTIC_KIND(ExternLibraryIsCurrentLibrary)
 
 // Alias diagnostics.
 CARBON_DIAGNOSTIC_KIND(AliasRequiresNameRef)

+ 10 - 1
toolchain/sem_ir/entity_with_params_base.h

@@ -49,6 +49,15 @@ struct EntityWithParamsBase {
     definition_id = definition.definition_id;
   }
 
+  // Returns the instruction for the first declaration.
+  auto first_decl_id() const -> SemIR::InstId {
+    if (non_owning_decl_id.is_valid()) {
+      return non_owning_decl_id;
+    }
+    CARBON_CHECK(first_owning_decl_id.is_valid());
+    return first_owning_decl_id;
+  }
+
   // Returns the instruction for the latest declaration.
   auto latest_decl_id() const -> SemIR::InstId {
     if (definition_id.is_valid()) {
@@ -86,7 +95,7 @@ struct EntityWithParamsBase {
   // True if declarations are `extern`.
   bool is_extern;
   // For an `extern library` declaration, the library name.
-  StringLiteralValueId extern_library_id;
+  SemIR::LibraryNameId extern_library_id;
   // The non-owning declaration of the entity, if present. This will be a
   // <entity>Decl.
   InstId non_owning_decl_id;